dify-ai-provider
Version:
Dify provider for Vercel AI SDK
404 lines (397 loc) • 13.9 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
createDifyProvider: () => createDifyProvider,
difyProvider: () => difyProvider
});
module.exports = __toCommonJS(index_exports);
// src/dify-provider.ts
var import_provider_utils2 = require("@ai-sdk/provider-utils");
// src/dify-chat-language-model.ts
var import_provider = require("@ai-sdk/provider");
var import_provider_utils = require("@ai-sdk/provider-utils");
// src/dify-chat-schema.ts
var import_zod = require("zod");
var completionResponseSchema = import_zod.z.object({
id: import_zod.z.string(),
answer: import_zod.z.string(),
task_id: import_zod.z.string(),
conversation_id: import_zod.z.string(),
message_id: import_zod.z.string(),
metadata: import_zod.z.object({
usage: import_zod.z.object({
completion_tokens: import_zod.z.number(),
prompt_tokens: import_zod.z.number(),
total_tokens: import_zod.z.number()
})
})
});
var errorResponseSchema = import_zod.z.object({
code: import_zod.z.string(),
message: import_zod.z.string(),
status: import_zod.z.number()
});
var difyStreamEventBase = import_zod.z.object({
event: import_zod.z.string(),
conversation_id: import_zod.z.string().optional(),
message_id: import_zod.z.string().optional(),
task_id: import_zod.z.string().optional(),
created_at: import_zod.z.number().optional()
}).passthrough();
var workflowStartedSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("workflow_started"),
workflow_run_id: import_zod.z.string(),
data: import_zod.z.object({
id: import_zod.z.string(),
workflow_id: import_zod.z.string(),
created_at: import_zod.z.number()
}).passthrough()
});
var workflowFinishedSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("workflow_finished"),
workflow_run_id: import_zod.z.string(),
data: import_zod.z.object({
id: import_zod.z.string(),
workflow_id: import_zod.z.string(),
total_tokens: import_zod.z.number().optional(),
created_at: import_zod.z.number()
}).passthrough()
});
var nodeStartedSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("node_started"),
workflow_run_id: import_zod.z.string(),
data: import_zod.z.object({
id: import_zod.z.string(),
node_id: import_zod.z.string(),
node_type: import_zod.z.string()
}).passthrough()
});
var nodeFinishedSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("node_finished"),
workflow_run_id: import_zod.z.string(),
data: import_zod.z.object({
id: import_zod.z.string(),
node_id: import_zod.z.string(),
node_type: import_zod.z.string()
}).passthrough()
});
var messageSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("message"),
id: import_zod.z.string().optional(),
answer: import_zod.z.string(),
from_variable_selector: import_zod.z.array(import_zod.z.string()).optional()
});
var messageEndSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("message_end"),
id: import_zod.z.string(),
metadata: import_zod.z.object({
usage: import_zod.z.object({
prompt_tokens: import_zod.z.number(),
completion_tokens: import_zod.z.number(),
total_tokens: import_zod.z.number()
}).passthrough()
}).passthrough(),
files: import_zod.z.array(import_zod.z.unknown()).optional()
});
var ttsMessageSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("tts_message"),
audio: import_zod.z.string()
});
var ttsMessageEndSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("tts_message_end"),
audio: import_zod.z.string()
});
var agentMessageSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("agent_message"),
answer: import_zod.z.string()
});
var agentThoughtSchema = difyStreamEventBase.extend({
event: import_zod.z.literal("agent_thought"),
thought: import_zod.z.string(),
observation: import_zod.z.string(),
tool: import_zod.z.string(),
tool_labels: import_zod.z.record(import_zod.z.string(), import_zod.z.string()),
tool_input: import_zod.z.string(),
message_files: import_zod.z.array(import_zod.z.unknown())
});
var difyStreamEventSchema = import_zod.z.discriminatedUnion("event", [
workflowStartedSchema,
workflowFinishedSchema,
nodeStartedSchema,
nodeFinishedSchema,
messageSchema,
messageEndSchema,
ttsMessageSchema,
ttsMessageEndSchema,
agentThoughtSchema,
agentMessageSchema
]).or(difyStreamEventBase);
// src/dify-chat-language-model.ts
var difyFailedResponseHandler = (0, import_provider_utils.createJsonErrorResponseHandler)({
errorSchema: errorResponseSchema,
errorToMessage: (data) => {
console.log("Dify API error:", data);
return `Dify API error: ${data.message}`;
}
});
var DifyChatLanguageModel = class {
constructor(modelId, settings, config) {
this.settings = settings;
this.specificationVersion = "v1";
this.defaultObjectGenerationMode = void 0;
this.modelId = modelId;
this.config = config;
this.generateId = import_provider_utils.generateId;
this.chatMessagesEndpoint = `${this.config.baseURL}/chat-messages`;
if (!this.settings.responseMode) {
this.settings.responseMode = "streaming";
}
}
get provider() {
return this.config.provider;
}
async doGenerate(options) {
var _a, _b, _c, _d;
const { abortSignal } = options;
const requestBody = this.getRequestBody(options);
const { responseHeaders, value: data } = await (0, import_provider_utils.postJsonToApi)({
url: this.chatMessagesEndpoint,
headers: (0, import_provider_utils.combineHeaders)(this.config.headers(), options.headers),
body: requestBody,
abortSignal,
failedResponseHandler: difyFailedResponseHandler,
successfulResponseHandler: (0, import_provider_utils.createJsonResponseHandler)(
completionResponseSchema
),
fetch: this.config.fetch
});
return {
text: data.answer,
toolCalls: [],
// Dify doesn't currently support tool calls
finishReason: "stop",
// Dify doesn't specify finish reason
usage: {
promptTokens: ((_b = (_a = data.metadata) == null ? void 0 : _a.usage) == null ? void 0 : _b.prompt_tokens) || 0,
completionTokens: ((_d = (_c = data.metadata) == null ? void 0 : _c.usage) == null ? void 0 : _d.completion_tokens) || 0
},
rawCall: this.createRawCall(options),
providerMetadata: {
difyWorkflowData: {
conversationId: data.conversation_id,
messageId: data.message_id
}
},
rawResponse: {
headers: responseHeaders,
body: data
},
request: { body: JSON.stringify(requestBody) },
response: {
id: data.id,
timestamp: /* @__PURE__ */ new Date()
}
};
}
async doStream(options) {
const { abortSignal } = options;
const requestBody = this.getRequestBody(options);
const body = { ...requestBody, response_mode: "streaming" };
const { responseHeaders, value: responseStream } = await (0, import_provider_utils.postJsonToApi)({
url: this.chatMessagesEndpoint,
headers: (0, import_provider_utils.combineHeaders)(this.config.headers(), options.headers),
body,
failedResponseHandler: difyFailedResponseHandler,
successfulResponseHandler: (0, import_provider_utils.createEventSourceResponseHandler)(
difyStreamEventSchema
),
abortSignal,
fetch: this.config.fetch
});
let conversationId;
let messageId;
let taskId;
return {
stream: responseStream.pipeThrough(
new TransformStream({
transform(chunk, controller) {
if (!chunk.success) {
controller.enqueue({ type: "error", error: chunk.error });
return;
}
const data = chunk.value;
if (data.conversation_id) conversationId = data.conversation_id;
if (data.message_id) messageId = data.message_id;
if (data.task_id) taskId = data.task_id;
switch (data.event) {
case "workflow_finished":
case "message_end": {
let totalTokens = 0;
if ("data" in data && data.data && typeof data.data === "object" && "total_tokens" in data.data && typeof data.data.total_tokens === "number") {
totalTokens = data.data.total_tokens;
}
controller.enqueue({
type: "finish",
finishReason: "stop",
providerMetadata: {
difyWorkflowData: {
conversationId,
messageId,
taskId
}
},
usage: {
promptTokens: 0,
completionTokens: totalTokens
}
});
break;
}
case "message":
case "agent_message": {
if ("answer" in data && typeof data.answer === "string") {
controller.enqueue({
type: "text-delta",
textDelta: data.answer
});
if ("id" in data && typeof data.id === "string") {
controller.enqueue({
type: "response-metadata",
id: data.id
});
}
}
break;
}
}
}
})
),
rawCall: this.createRawCall(options),
rawResponse: { headers: responseHeaders },
request: { body: JSON.stringify(body) }
};
}
/**
* Get the request body for the Dify API
*/
getRequestBody(options) {
var _a, _b, _c;
const messages = options.messages || options.prompt;
if (!messages || !messages.length) {
throw new import_provider.APICallError({
message: "No messages provided",
url: this.chatMessagesEndpoint,
requestBodyValues: options
});
}
const latestMessage = messages[messages.length - 1];
if (latestMessage.role !== "user") {
throw new import_provider.APICallError({
message: "The last message must be a user message",
url: this.chatMessagesEndpoint,
requestBodyValues: { latestMessageRole: latestMessage.role }
});
}
const hasAttachments = Array.isArray(latestMessage.content) && latestMessage.content.some((part) => {
return typeof part !== "string" && part.type === "image";
});
if (hasAttachments) {
throw new import_provider.APICallError({
message: "Dify provider does not currently support image attachments",
url: this.chatMessagesEndpoint,
requestBodyValues: { hasAttachments: true }
});
}
let query = "";
if (typeof latestMessage.content === "string") {
query = latestMessage.content;
} else if (Array.isArray(latestMessage.content)) {
query = latestMessage.content.map((part) => {
if (typeof part === "string") {
return part;
} else if (part.type === "text") {
return part.text;
}
return "";
}).filter(Boolean).join(" ");
}
const conversationId = (_a = options.headers) == null ? void 0 : _a["chat-id"];
const userId = (_c = (_b = options.headers) == null ? void 0 : _b["user-id"]) != null ? _c : "you_should_pass_user-id";
const {
"chat-id": _,
"user-id": __,
...cleanHeaders
} = options.headers || {};
options.headers = cleanHeaders;
return {
inputs: this.settings.inputs || {},
query,
response_mode: this.settings.responseMode,
conversation_id: conversationId,
user: userId
};
}
/**
* Create the rawCall object for response
*/
createRawCall(options) {
return {
rawPrompt: options.messages || options.prompt,
rawSettings: { ...this.settings }
};
}
supportsUrl(url) {
return false;
}
};
// src/dify-provider.ts
function createDifyProvider(options = {}) {
const createChatModel = (modelId, settings = {}) => new DifyChatLanguageModel(modelId, settings, {
provider: "dify.chat",
baseURL: options.baseURL || "https://api.dify.ai/v1",
headers: () => ({
Authorization: `Bearer ${(0, import_provider_utils2.loadApiKey)({
apiKey: settings.apiKey,
environmentVariableName: "DIFY_API_KEY",
description: "Dify API Key"
})}`,
"Content-Type": "application/json",
...options.headers
})
});
const provider = function(modelId, settings) {
if (new.target) {
throw new Error(
"The model factory function cannot be called with the new keyword."
);
}
return createChatModel(modelId, settings);
};
provider.chat = createChatModel;
return provider;
}
var difyProvider = createDifyProvider();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createDifyProvider,
difyProvider
});