summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-12-10 11:29:26 -0800
committerGitHub <[email protected]>2025-12-10 13:29:26 -0600
commit18d3c054a36933e9800fd423e39953abe99b1289 (patch)
tree1555a5f8ac3d639a5f6dd4d621832f2f7d32a630
parent59c5da9b6c0503bb157de32ee0e8091afaaed399 (diff)
downloadopencode-18d3c054a36933e9800fd423e39953abe99b1289.tar.gz
opencode-18d3c054a36933e9800fd423e39953abe99b1289.zip
more interleaved thinking fixes (#5334)
-rw-r--r--packages/opencode/src/provider/transform.ts49
-rw-r--r--packages/opencode/src/session/compaction.ts1
-rw-r--r--packages/opencode/src/session/prompt.ts4
-rw-r--r--packages/opencode/src/session/summary.ts4
4 files changed, 22 insertions, 36 deletions
diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts
index 6ad033a2c..2ae31acf8 100644
--- a/packages/opencode/src/provider/transform.ts
+++ b/packages/opencode/src/provider/transform.ts
@@ -74,23 +74,23 @@ export namespace ProviderTransform {
return result
}
- // 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)
- // See: https://api-docs.deepseek.com/guides/thinking_mode
- if (model.providerID === "deepseek" || model.api.id.toLowerCase().includes("deepseek")) {
+ if (
+ model.providerID === "deepseek" ||
+ model.api.id.toLowerCase().includes("deepseek") ||
+ (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 hasToolCalls = msg.content.some((part: any) => part.type === "tool-call")
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")
- // If this message has tool calls and reasoning, include reasoning_content
- // so DeepSeek can continue reasoning after tool execution
- if (hasToolCalls && reasoningText) {
+ // Include reasoning_content directly on the message for all assistant messages
+ if (reasoningText) {
return {
...msg,
content: filteredContent,
@@ -104,12 +104,12 @@ export namespace ProviderTransform {
}
}
- // For final answers (no tool calls), just strip reasoning
return {
...msg,
content: filteredContent,
}
}
+
return msg
})
}
@@ -212,20 +212,23 @@ export namespace ProviderTransform {
): Record<string, any> {
const result: Record<string, any> = {}
- // switch to providerID later, for now use this
if (model.api.npm === "@openrouter/ai-sdk-provider") {
result["usage"] = {
include: true,
}
+ if (model.api.id.includes("gemini-3")) {
+ result["reasoning"] = { effort: "high" }
+ }
+ }
+
+ if (model.providerID === "baseten") {
+ result["chat_template_args"] = { enable_thinking: true }
}
if (model.providerID === "openai" || providerOptions?.setCacheKey) {
result["promptCacheKey"] = sessionID
}
- if (model.api.npm === "@openrouter/ai-sdk-provider" && model.api.id.includes("gemini-3")) {
- result["reasoning"] = { effort: "high" }
- }
if (model.api.npm === "@ai-sdk/google" || model.api.npm === "@ai-sdk/google-vertex") {
result["thinkingConfig"] = {
thinkingLevel: "high",
@@ -274,23 +277,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 c25e16dc5..45bab9ae6 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 15e934962..71b99ab0d 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 09cdeb23a..ab6a98686 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 => ({