From 0b43b7f9490a0d561246ee1b78982960b626ef73 Mon Sep 17 00:00:00 2001 From: uf <15956652+udyr-f@users.noreply.github.com> Date: Sun, 1 Feb 2026 20:03:37 +0800 Subject: [PATCH] feat: Add support for dify-chat as a new model provider --- packages/opencode/src/provider/provider.ts | 9 ++ packages/opencode/src/session/dify-headers.ts | 85 +++++++++++++++++++ packages/opencode/src/session/llm.ts | 12 +++ 3 files changed, 106 insertions(+) create mode 100644 packages/opencode/src/session/dify-headers.ts diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 022ec3167956..c6fa1003ec3e 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -588,6 +588,15 @@ export namespace Provider { }, } }, + dify: async () => { + const difyLog = Log.create({ service: "dify" }) + return { + autoload: false, + async getModel(sdk: any, modelID: string, options?: Record) { + return (sdk as any)(modelID, { ...options, logger: difyLog }) + }, + } + }, } export const Model = z diff --git a/packages/opencode/src/session/dify-headers.ts b/packages/opencode/src/session/dify-headers.ts new file mode 100644 index 000000000000..46d1a3909379 --- /dev/null +++ b/packages/opencode/src/session/dify-headers.ts @@ -0,0 +1,85 @@ +import { Config } from "@/config/config" +import { Session } from "@/session" +import type { Provider } from "@/provider/provider" +import type { Agent } from "@/agent/agent" +import type { MessageV2 } from "./message-v2" + +export type DifyPartMetadata = { + difyWorkflowData?: { conversationId?: string } +} + +function findLastCompactionIndex(msgs: MessageV2.WithParts[]): number | undefined { + for (let i = msgs.length - 1; i >= 0; i--) { + const info = msgs[i].info + if ( + info.role === "assistant" && + (info.summary === true || info.mode === "compaction" || info.agent === "compaction") + ) { + return i + } + } + return undefined +} + +function findConversationId(msgs: MessageV2.WithParts[]): string | undefined { + for (let i = msgs.length - 1; i >= 0; i--) { + const msg = msgs[i] + if (msg.info.role !== "assistant") continue + + for (const part of msg.parts) { + if ("metadata" in part && part.metadata) { + const meta = part.metadata as DifyPartMetadata + if (meta.difyWorkflowData?.conversationId) { + return meta.difyWorkflowData.conversationId + } + } + } + } + return undefined +} + +export type ApplyDifyHeadersOptions = { + headers: Record + provider: Provider.Info + model: Provider.Model + agent: Agent.Info + sessionID: string + user: MessageV2.User +} + +export async function applyDifyHeaders(opts: ApplyDifyHeadersOptions): Promise { + const { headers: h, sessionID, user } = opts + const currentUserId = user.id + const providerOptions = opts.provider.options?.headers + const modelHeaders = opts.model.headers ?? {} + const isCompactionRequest = opts.agent.name === "compaction" + + Object.assign(h, providerOptions ?? {}, modelHeaders) + if (!h["user-id"]) { + const config = await Config.get() + h["user-id"] = config.username ?? "unknown" + } + + const msgs = await Session.messages({ sessionID, limit: 100 }) + const compactionIndex = findLastCompactionIndex(msgs) + const conversationId = findConversationId(msgs) + + if (compactionIndex === undefined || compactionIndex !== msgs.length - 3) { + if (conversationId) h["chat-id"] = conversationId + return + } + + const isCurrentSynthetic = (opts.user as any).parts?.some((p: any) => p.type === "text" && p.synthetic) ?? false + + /** + * Determines if we should reset the chat-id (start a new conversation). + * - true: Normal request after compaction/summary with a real (non-synthetic) user message. + * In this case, do NOT set chat-id to avoid inheriting old conversation. + * - false: Compaction request OR synthetic user message → preserve existing chat-id. + */ + const shouldResetChatId = !isCompactionRequest && !isCurrentSynthetic + + if (!shouldResetChatId && conversationId) { + h["chat-id"] = conversationId + } +} diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 4e42fb0d2ec7..649196d01ff4 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -1,6 +1,7 @@ import { Installation } from "@/installation" import { Provider } from "@/provider/provider" import { Log } from "@/util/log" +import { applyDifyHeaders } from "./dify-headers" import { streamText, wrapLanguageModel, @@ -144,6 +145,17 @@ export namespace LLM { }, ) + if (input.model.providerID === "dify") { + await applyDifyHeaders({ + headers: headers as Record, + provider, + model: input.model, + agent: input.agent, + sessionID: input.sessionID, + user: input.user, + }) + } + const maxOutputTokens = isCodex || provider.id.includes("github-copilot") ? undefined : ProviderTransform.maxOutputTokens(input.model)