From 996fb2a1511661ac4580e4601e5ef5c7961bdf2e Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Wed, 10 Dec 2025 11:11:04 -0600 Subject: [PATCH] wip --- packages/opencode/src/provider/transform.ts | 58 +++++++++++++++------ packages/opencode/src/session/compaction.ts | 1 - packages/opencode/src/session/prompt.ts | 4 +- packages/opencode/src/session/summary.ts | 4 +- 4 files changed, 45 insertions(+), 22 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 891025cde4..23b2f269c1 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -74,6 +74,7 @@ export namespace ProviderTransform { return result } + // TODO: is this actually correct??? Or should it just match the other reasoning_content handling // DeepSeek: Handle reasoning_content for tool call continuations // - With tool calls: Include reasoning_content in providerOptions so model can continue reasoning // - Without tool calls: Strip reasoning (new turn doesn't need previous reasoning) @@ -110,6 +111,45 @@ export namespace ProviderTransform { content: filteredContent, } } + + return msg + }) + } + + if ( + model.capabilities.interleaved && + typeof model.capabilities.interleaved === "object" && + model.capabilities.interleaved.field === "reasoning_content" + ) { + return msgs.map((msg) => { + if (msg.role === "assistant" && Array.isArray(msg.content)) { + const reasoningParts = msg.content.filter((part: any) => part.type === "reasoning") + const reasoningText = reasoningParts.map((part: any) => part.text).join("") + + // Filter out reasoning parts from content + const filteredContent = msg.content.filter((part: any) => part.type !== "reasoning") + + // Include reasoning_content directly on the message for all assistant messages + if (reasoningText) { + return { + ...msg, + content: filteredContent, + providerOptions: { + ...msg.providerOptions, + openaiCompatible: { + ...(msg.providerOptions as any)?.openaiCompatible, + reasoning_content: reasoningText, + }, + }, + } + } + + return { + ...msg, + content: filteredContent, + } + } + return msg }) } @@ -273,23 +313,7 @@ export namespace ProviderTransform { return options } - export function providerOptions(model: Provider.Model, options: { [x: string]: any }, messages: ModelMessage[]) { - if (model.capabilities.interleaved && typeof model.capabilities.interleaved === "object") { - const cot = [] - const assistantMessages = messages.filter((msg) => msg.role === "assistant") - for (const msg of assistantMessages) { - for (const part of msg.content) { - if (typeof part === "string") { - continue - } - if (part.type === "reasoning") { - cot.push(part) - } - } - } - options[model.capabilities.interleaved.field] = cot - } - + export function providerOptions(model: Provider.Model, options: { [x: string]: any }) { switch (model.api.npm) { case "@ai-sdk/openai": case "@ai-sdk/azure": diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index c25e16dc5e..45bab9ae6e 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -143,7 +143,6 @@ export namespace SessionCompaction { providerOptions: ProviderTransform.providerOptions( model, pipe({}, mergeDeep(ProviderTransform.options(model, input.sessionID)), mergeDeep(model.options)), - [], ), headers: model.headers, abortSignal: input.abort, diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 15e9349624..71b99ab0d5 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -593,7 +593,7 @@ export namespace SessionPrompt { OUTPUT_TOKEN_MAX, ), abortSignal: abort, - providerOptions: ProviderTransform.providerOptions(model, params.options, messages), + providerOptions: ProviderTransform.providerOptions(model, params.options), stopWhen: stepCountIs(1), temperature: params.temperature, topP: params.topP, @@ -1473,7 +1473,7 @@ export namespace SessionPrompt { await generateText({ // use higher # for reasoning models since reasoning tokens eat up a lot of the budget maxOutputTokens: small.capabilities.reasoning ? 3000 : 20, - providerOptions: ProviderTransform.providerOptions(small, options, []), + providerOptions: ProviderTransform.providerOptions(small, options), messages: [ ...SystemPrompt.title(small.providerID).map( (x): ModelMessage => ({ diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index 09cdeb23a8..ab6a986862 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -91,7 +91,7 @@ export namespace SessionSummary { if (textPart && !userMsg.summary?.title) { const result = await generateText({ maxOutputTokens: small.capabilities.reasoning ? 1500 : 20, - providerOptions: ProviderTransform.providerOptions(small, options, []), + providerOptions: ProviderTransform.providerOptions(small, options), messages: [ ...SystemPrompt.title(small.providerID).map( (x): ModelMessage => ({ @@ -144,7 +144,7 @@ export namespace SessionSummary { const result = await generateText({ model: language, maxOutputTokens: 100, - providerOptions: ProviderTransform.providerOptions(small, options, []), + providerOptions: ProviderTransform.providerOptions(small, options), messages: [ ...SystemPrompt.summarize(small.providerID).map( (x): ModelMessage => ({