From 5ae20dfe93f7d7935cf11c76996e5a097236b8b8 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 23 Jul 2026 15:43:44 -0500 Subject: [PATCH] fix(provider): preserve Alibaba DeepSeek effort --- packages/opencode/src/provider/provider.ts | 20 ++++--- packages/opencode/src/provider/transform.ts | 4 +- .../opencode/test/provider/transform.test.ts | 12 ++++ packages/opencode/test/session/llm.test.ts | 60 +++++++++++++++++++ 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index a0ab5eb347..0bfc81cb39 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1686,7 +1686,13 @@ const layer = Layer.effect( delete options.fetch } - if (model.api.npm.includes("@ai-sdk/openai-compatible") && options["includeUsage"] !== false) { + // Alibaba's SDK does not expose DeepSeek V4's native reasoning_effort field. + const npm = + model.api.npm === "@ai-sdk/alibaba" && ["deepseek-v4-pro", "deepseek-v4-flash"].includes(model.api.id) + ? "@ai-sdk/openai-compatible" + : model.api.npm + + if (npm.includes("@ai-sdk/openai-compatible") && options["includeUsage"] !== false) { options["includeUsage"] = true } @@ -1722,7 +1728,7 @@ const layer = Layer.effect( const key = Hash.fast( JSON.stringify({ providerID: model.providerID, - npm: model.api.npm, + npm, options, }), ) @@ -1762,7 +1768,7 @@ const layer = Layer.effect( return wrapSSE(res, chunkTimeout, chunkAbortCtl) } - const bundledLoader = BUNDLED_PROVIDERS[model.api.npm] + const bundledLoader = BUNDLED_PROVIDERS[npm] if (bundledLoader) { const factory = await bundledLoader() const loaded = factory({ @@ -1774,11 +1780,11 @@ const layer = Layer.effect( } const installedPath = await (async () => { - if (model.api.npm.startsWith("file://")) { - return model.api.npm + if (npm.startsWith("file://")) { + return npm } - const item = await Npm.add(model.api.npm) - if (!item.entrypoint) throw new Error(`Package ${model.api.npm} has no import entrypoint`) + const item = await Npm.add(npm) + if (!item.entrypoint) throw new Error(`Package ${npm} has no import entrypoint`) return item.entrypoint })() diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 81759160bf..7820615e74 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1744,9 +1744,11 @@ function reasoningEffort(model: Provider.Model, effort: string) { case "@ai-sdk/cohere": case "@ai-sdk/perplexity": case "@ai-sdk/vercel": - case "@ai-sdk/alibaba": case "gitlab-ai-provider": return + case "@ai-sdk/alibaba": + if (["deepseek-v4-pro", "deepseek-v4-flash"].includes(model.api.id)) return { reasoningEffort: effort } + return } } diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index ef2b275035..b8273346d3 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3289,6 +3289,18 @@ describe("ProviderTransform.reasoningVariants", () => { ).toEqual({ max: { effort: "max" } }) }) + test.each(["deepseek-v4-pro", "deepseek-v4-flash"])("preserves Alibaba %s reasoning effort", (id) => { + expect( + ProviderTransform.reasoningVariants( + model([{ type: "effort", values: ["high", "max"] }]), + target("@ai-sdk/alibaba", id), + ), + ).toEqual({ + high: { reasoningEffort: "high" }, + max: { reasoningEffort: "max" }, + }) + }) + test("maps Kimi effort metadata to adaptive thinking", () => { expect( ProviderTransform.reasoningVariants( diff --git a/packages/opencode/test/session/llm.test.ts b/packages/opencode/test/session/llm.test.ts index 3bfc722e2b..5de27ffe8c 100644 --- a/packages/opencode/test/session/llm.test.ts +++ b/packages/opencode/test/session/llm.test.ts @@ -832,6 +832,66 @@ describe("session.llm.stream", () => { }, ) + it.instance( + "preserves DeepSeek V4 effort and caching with the Alibaba SDK", + () => + Effect.gen(function* () { + const request = waitRequest( + "/chat/completions", + new Response(createChatStream("Hello"), { + status: 200, + headers: { "Content-Type": "text/event-stream" }, + }), + ) + const source = yield* Provider.use.getModel(ProviderV2.ID.make("alibaba"), ModelV2.ID.make("qwen-plus")) + const resolved = { + ...source, + id: ModelV2.ID.make("deepseek-v4-pro"), + api: { ...source.api, id: "deepseek-v4-pro", npm: "@ai-sdk/alibaba" }, + variants: { high: { reasoningEffort: "high" } }, + } + const sessionID = SessionID.make("session-test-alibaba-deepseek-effort") + const agent = { + name: "test", + mode: "primary", + options: {}, + permission: [{ permission: "*", pattern: "*", action: "allow" }], + } satisfies Agent.Info + const user = { + id: MessageID.make("msg_user-alibaba-deepseek-effort"), + sessionID, + role: "user", + time: { created: Date.now() }, + agent: agent.name, + model: { providerID: resolved.providerID, modelID: resolved.id, variant: "high" }, + } satisfies SessionV1.User + + yield* drain({ + user, + sessionID, + model: resolved, + agent, + system: ["You are a helpful assistant."], + messages: [{ role: "user", content: "Hello" }], + tools: {}, + }) + + const capture = yield* Effect.promise(() => request) + expect(capture.body.reasoning_effort).toBe("high") + expect(JSON.stringify(capture.body.messages)).toContain('"cache_control":{"type":"ephemeral"}') + }), + { + config: () => ({ + enabled_providers: ["alibaba"], + provider: { + alibaba: { + options: { apiKey: "test-key", baseURL: `${state.server!.url.origin}/v1` }, + }, + }, + }), + }, + ) + const cerebrasFixture = { providerID: "cerebras", modelID: "gpt-oss-120b" } it.instance( "replays Cerebras assistant reasoning using the provider-supported field",