diff --git a/ai b/ai new file mode 160000 index 0000000000..6414c01078 --- /dev/null +++ b/ai @@ -0,0 +1 @@ +Subproject commit 6414c01078f4a33ec3b5da539d437dd05c181d60 diff --git a/codex b/codex new file mode 160000 index 0000000000..c26fe64539 --- /dev/null +++ b/codex @@ -0,0 +1 @@ +Subproject commit c26fe645393534c47b8ae5e03b6037c1dfc0d504 diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 9e2dd0ba0b..c538e4bdcd 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1006,6 +1006,26 @@ export namespace Provider { }) } + // Wrap fetch for @ai-sdk/openai to strip "id" from reasoning blocks + // Strip openai itemId metadata following what codex does + if (model.api.npm === "@ai-sdk/openai") { + const wrappedFetch = options["fetch"] + options["fetch"] = async (url: any, init?: BunFetchRequestInit) => { + if (init?.body && init.method === "POST") { + const body = JSON.parse(init.body as string) + if (Array.isArray(body.input)) { + for (const item of body.input) { + if (item.type === "reasoning" && "id" in item) { + delete item.id + } + } + } + init = { ...init, body: JSON.stringify(body) } + } + return wrappedFetch(url, init) + } + } + // Special case: google-vertex-anthropic uses a subpath import const bundledKey = model.providerID === "google-vertex-anthropic" ? "@ai-sdk/google-vertex/anthropic" : model.api.npm diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 023b00dd30..043b4d9000 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -21,28 +21,27 @@ export namespace ProviderTransform { model: Provider.Model, options: Record, ): ModelMessage[] { - // Strip openai itemId metadata following what codex does - if (model.api.npm === "@ai-sdk/openai" || options.store === false) { - msgs = msgs.map((msg) => { - if (msg.providerOptions) { - for (const options of Object.values(msg.providerOptions)) { - delete options["itemId"] - } - } - if (!Array.isArray(msg.content)) { - return msg - } - const content = msg.content.map((part) => { - if (part.providerOptions) { - for (const options of Object.values(part.providerOptions)) { - delete options["itemId"] - } - } - return part - }) - return { ...msg, content } as typeof msg - }) - } + // if (model.api.npm === "@ai-sdk/openai" || options.store === false) { + // msgs = msgs.map((msg) => { + // if (msg.providerOptions) { + // for (const options of Object.values(msg.providerOptions)) { + // delete options["itemId"] + // } + // } + // if (!Array.isArray(msg.content)) { + // return msg + // } + // const content = msg.content.map((part) => { + // if (part.providerOptions) { + // for (const options of Object.values(part.providerOptions)) { + // delete options["itemId"] + // } + // } + // return part + // }) + // return { ...msg, content } as typeof msg + // }) + // } // Anthropic rejects messages with empty content - filter out empty string messages // and remove empty text/reasoning parts from array content diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index d326976f1a..4ec45db41a 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -478,10 +478,7 @@ export namespace MessageV2 { if (msg.info.role === "assistant") { if ( msg.info.error && - !( - MessageV2.AbortedError.isInstance(msg.info.error) && - msg.parts.some((part) => part.type !== "step-start" && part.type !== "reasoning") - ) + !(MessageV2.AbortedError.isInstance(msg.info.error) && msg.parts.some((part) => part.type !== "step-start")) ) { continue }