From 51566b90f1c78548206ae0f966797a474a5b4820 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Wed, 29 Jul 2026 18:36:12 +0000 Subject: [PATCH 1/2] fix(provider): widen interleaved reasoning fields --- packages/core/src/models-dev.ts | 10 ++++++++-- packages/core/src/v1/config/provider.ts | 10 ++++++++-- packages/opencode/src/provider/provider.ts | 11 ++++++++--- packages/opencode/test/provider/provider.test.ts | 6 ++++++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/core/src/models-dev.ts b/packages/core/src/models-dev.ts index 602133d9047a..44fa9832dde3 100644 --- a/packages/core/src/models-dev.ts +++ b/packages/core/src/models-dev.ts @@ -15,6 +15,11 @@ import { httpClient } from "./effect/app-node-platform" export const CatalogModelStatus = Schema.Literals(["alpha", "beta", "deprecated"]) export type CatalogModelStatus = typeof CatalogModelStatus.Type +const InterleavedField = Schema.Union([ + Schema.Literals(["reasoning", "reasoning_content", "reasoning_text"]), + Schema.String, +]) + const USER_AGENT = `opencode/${InstallationChannel}/${InstallationVersion}/${Flag.OPENCODE_CLIENT}` const CostTier = Schema.Struct({ @@ -71,9 +76,10 @@ export const Model = Schema.Struct({ reasoning_options: Schema.optional(Schema.Array(ReasoningOption)), interleaved: Schema.optional( Schema.Union([ - Schema.Literal(true), + Schema.Boolean, + InterleavedField, Schema.Struct({ - field: Schema.Literals(["reasoning", "reasoning_content", "reasoning_details"]), + field: InterleavedField, }), ]), ), diff --git a/packages/core/src/v1/config/provider.ts b/packages/core/src/v1/config/provider.ts index d54a3f08f926..f860a2b4ac7e 100644 --- a/packages/core/src/v1/config/provider.ts +++ b/packages/core/src/v1/config/provider.ts @@ -5,6 +5,11 @@ import { PositiveInt } from "../../schema" export const ModelStatus = Schema.Literals(["alpha", "beta", "deprecated", "active"]) +const InterleavedField = Schema.Union([ + Schema.Literals(["reasoning", "reasoning_content", "reasoning_text"]), + Schema.String, +]) + export const Model = Schema.Struct({ id: Schema.optional(Schema.String), name: Schema.optional(Schema.String), @@ -16,9 +21,10 @@ export const Model = Schema.Struct({ tool_call: Schema.optional(Schema.Boolean), interleaved: Schema.optional( Schema.Union([ - Schema.Literal(true), + Schema.Boolean, + InterleavedField, Schema.Struct({ - field: Schema.Literals(["reasoning", "reasoning_content", "reasoning_details"]), + field: InterleavedField, }), ]), ), diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index a0ab5eb3474d..85b7fd3978e8 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -976,10 +976,15 @@ const ProviderModalities = Schema.Struct({ pdf: Schema.Boolean, }) +const ProviderInterleavedField = Schema.Union([ + Schema.Literals(["reasoning", "reasoning_content", "reasoning_text"]), + Schema.String, +]) + const ProviderInterleaved = Schema.Union([ Schema.Boolean, Schema.Struct({ - field: Schema.Literals(["reasoning", "reasoning_content", "reasoning_details"]), + field: ProviderInterleavedField, }), ]) @@ -1243,7 +1248,7 @@ function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model video: model.modalities?.output?.includes("video") ?? false, pdf: model.modalities?.output?.includes("pdf") ?? false, }, - interleaved: model.interleaved ?? false, + interleaved: typeof model.interleaved === "string" ? { field: model.interleaved } : (model.interleaved ?? false), }, release_date: model.release_date ?? "", variants: {}, @@ -1475,7 +1480,7 @@ const layer = Layer.effect( pdf: model.modalities?.output?.includes("pdf") ?? existingModel?.capabilities.output.pdf ?? false, }, interleaved: - model.interleaved ?? + (typeof model.interleaved === "string" ? { field: model.interleaved } : model.interleaved) ?? existingModel?.capabilities.interleaved ?? (!existingModel && apiNpm === "@ai-sdk/openai-compatible" && apiID.includes("deepseek") ? { field: "reasoning_content" } diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index 058bad23a1c6..44e82c7e1f44 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -252,6 +252,8 @@ it.instance( const provider = providers[ProviderV2.ID.make("custom-provider")] expect(provider.models["deepseek-r1"].capabilities.interleaved).toEqual({ field: "reasoning_content" }) expect(provider.models["deepseek-details"].capabilities.interleaved).toEqual({ field: "reasoning_details" }) + expect(provider.models["deepseek-text"].capabilities.interleaved).toEqual({ field: "reasoning_text" }) + expect(provider.models["custom-reasoning"].capabilities.interleaved).toEqual({ field: "vendor_reasoning" }) expect(provider.models["custom-model"].capabilities.interleaved).toBe(false) expect( providers[ProviderV2.ID.make("custom-anthropic-provider")].models["deepseek-r1"].capabilities.interleaved, @@ -267,6 +269,8 @@ it.instance( models: { "deepseek-r1": { name: "DeepSeek R1" }, "deepseek-details": { name: "DeepSeek Details", interleaved: { field: "reasoning_details" } }, + "deepseek-text": { name: "DeepSeek Text", interleaved: "reasoning_text" }, + "custom-reasoning": { name: "Custom Reasoning", interleaved: { field: "vendor_reasoning" } }, "custom-model": { name: "Custom Model" }, }, options: { apiKey: "custom-key" }, @@ -1462,6 +1466,7 @@ test("models.dev normalization fills required response fields", () => { id: "gpt-5.4", name: "GPT-5.4", family: "gpt", + interleaved: "reasoning_text", cost: { input: 2.5, output: 15 }, limit: { context: 1_050_000, input: 922_000, output: 128_000 }, }, @@ -1474,6 +1479,7 @@ test("models.dev normalization fills required response fields", () => { expect(model.capabilities.reasoning).toBe(false) expect(model.capabilities.attachment).toBe(false) expect(model.capabilities.toolcall).toBe(true) + expect(model.capabilities.interleaved).toEqual({ field: "reasoning_text" }) expect(model.release_date).toBe("") }) From 8b6fde65eb3effffbdc5c95b2e76cf0346104f2a Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Wed, 29 Jul 2026 18:52:07 +0000 Subject: [PATCH 2/2] chore(sdk): regenerate interleaved types --- packages/sdk/js/src/v2/gen/types.gen.ts | 10 +++++--- packages/sdk/openapi.json | 32 ++++++++++++++++++++----- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 42d224780d32..90c91e9158cc 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1768,9 +1768,13 @@ export type ProviderConfig = { temperature?: boolean tool_call?: boolean interleaved?: - | true + | boolean + | "reasoning" + | "reasoning_content" + | "reasoning_text" + | string | { - field: "reasoning" | "reasoning_content" | "reasoning_details" + field: "reasoning" | "reasoning_content" | "reasoning_text" | string } cost?: { input: number @@ -2057,7 +2061,7 @@ export type Model = { interleaved: | boolean | { - field: "reasoning" | "reasoning_content" | "reasoning_details" + field: "reasoning" | "reasoning_content" | "reasoning_text" | string } } cost: { diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index b300754bc859..afe14bb604cc 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -20820,15 +20820,28 @@ "interleaved": { "anyOf": [ { - "type": "boolean", - "enum": [true] + "type": "boolean" + }, + { + "type": "string", + "enum": ["reasoning", "reasoning_content", "reasoning_text"] + }, + { + "type": "string" }, { "type": "object", "properties": { "field": { - "type": "string", - "enum": ["reasoning", "reasoning_content", "reasoning_details"] + "anyOf": [ + { + "type": "string", + "enum": ["reasoning", "reasoning_content", "reasoning_text"] + }, + { + "type": "string" + } + ] } }, "required": ["field"], @@ -21641,8 +21654,15 @@ "type": "object", "properties": { "field": { - "type": "string", - "enum": ["reasoning", "reasoning_content", "reasoning_details"] + "anyOf": [ + { + "type": "string", + "enum": ["reasoning", "reasoning_content", "reasoning_text"] + }, + { + "type": "string" + } + ] } }, "required": ["field"],