-
Notifications
You must be signed in to change notification settings - Fork 60
fix: port 3 upstream DeepSeek transform fixes #852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -151,6 +151,28 @@ export namespace ProviderTransform { | |
| return result | ||
| } | ||
|
|
||
| // altimate_change start — upstream_fix: ensure deepseek assistant messages always have a reasoning part | ||
| // Ported from upstream-opencode commit 86715fecc4 (#24180). DeepSeek's OpenAI-compatible | ||
| // protocol requires every assistant message to carry a `reasoning` part — without it the API | ||
| // returns 400 on multi-turn sessions. Drop this marker if upstream merges the same shape. | ||
| if (model.api.id.includes("deepseek")) { | ||
| msgs = msgs.map((msg) => { | ||
| if (msg.role !== "assistant") return msg | ||
| if (Array.isArray(msg.content)) { | ||
| if (msg.content.some((part: any) => part.type === "reasoning")) return msg | ||
| return { ...msg, content: [...msg.content, { type: "reasoning", text: "" } as any] } | ||
| } | ||
| return { | ||
| ...msg, | ||
| content: [ | ||
| ...(msg.content ? [{ type: "text" as const, text: msg.content as unknown as string }] : []), | ||
| { type: "reasoning" as const, text: "" } as any, | ||
| ] as any, | ||
| } | ||
| }) | ||
| } | ||
| // altimate_change end | ||
|
|
||
| if (typeof model.capabilities.interleaved === "object" && model.capabilities.interleaved.field) { | ||
| const field = model.capabilities.interleaved.field | ||
| return msgs.map((msg) => { | ||
|
|
@@ -161,25 +183,23 @@ export namespace ProviderTransform { | |
| // Filter out reasoning parts from content | ||
| const filteredContent = msg.content.filter((part: any) => part.type !== "reasoning") | ||
|
|
||
| // Include reasoning_content | reasoning_details directly on the message for all assistant messages | ||
| if (reasoningText) { | ||
| return { | ||
| ...msg, | ||
| content: filteredContent, | ||
| providerOptions: { | ||
| ...msg.providerOptions, | ||
| openaiCompatible: { | ||
| ...(msg.providerOptions as any)?.openaiCompatible, | ||
| [field]: reasoningText, | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| // altimate_change start — upstream_fix: always set the interleaved reasoning field | ||
| // even when empty. Ported from upstream-opencode commit 923af96d26 (#24146). DeepSeek | ||
| // V4 thinking mode may emit empty reasoning_content; the field must still be present | ||
| // on subsequent requests or the API rejects the message. Drop this marker if upstream | ||
| // ships the same shape. | ||
| return { | ||
| ...msg, | ||
| content: filteredContent, | ||
| providerOptions: { | ||
| ...msg.providerOptions, | ||
| openaiCompatible: { | ||
| ...(msg.providerOptions as any)?.openaiCompatible, | ||
| [field]: reasoningText, | ||
| }, | ||
| }, | ||
| } | ||
| // altimate_change end | ||
| } | ||
|
|
||
| return msg | ||
|
|
@@ -283,8 +303,10 @@ export namespace ProviderTransform { | |
| msgs = normalizeMessages(msgs, model, options) | ||
| if ( | ||
| (model.providerID === "anthropic" || | ||
| // altimate_change start — altimate-specific Anthropic provider IDs | ||
| model.providerID === "google-vertex-anthropic" || | ||
| model.providerID === "altimate-backend" || | ||
| // altimate_change end | ||
| model.api.id.includes("anthropic") || | ||
| model.api.id.includes("claude") || | ||
| model.id.includes("anthropic") || | ||
|
|
@@ -503,7 +525,17 @@ export namespace ProviderTransform { | |
| case "venice-ai-sdk-provider": | ||
| // https://docs.venice.ai/overview/guides/reasoning-models#reasoning-effort | ||
| case "@ai-sdk/openai-compatible": | ||
| return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }])) | ||
| // altimate_change start — upstream_fix: add "max" reasoning effort for deepseek-v4 | ||
| // Ported from upstream-opencode commit f8e939d96f (#24163). DeepSeek-v4 advertises a | ||
| // `max` reasoning effort that the default WIDELY_SUPPORTED_EFFORTS list omits. Drop | ||
| // this marker if upstream lifts the cap or ships the same shape. | ||
| return Object.fromEntries( | ||
| [ | ||
| ...WIDELY_SUPPORTED_EFFORTS, | ||
| ...(model.api.id.includes("deepseek-v4") ? ["max"] : []), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: The Prompt for AI agents |
||
| ].map((effort) => [effort, { reasoningEffort: effort }]), | ||
| ) | ||
| // altimate_change end | ||
|
Comment on lines
+528
to
+538
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Line 402 returns Suggested fix+ const isDeepSeekV4 = id.includes("deepseek-v4") || model.api.id.includes("deepseek-v4")
if (
- id.includes("deepseek") ||
+ (id.includes("deepseek") && !isDeepSeekV4) ||
id.includes("minimax") ||
id.includes("glm") ||
id.includes("mistral") ||
id.includes("kimi") ||
id.includes("k2p5") ||
id.includes("qwen")
)
return {} |
||
|
|
||
| case "@ai-sdk/azure": | ||
| // https://v5.ai-sdk.dev/providers/ai-sdk-providers/azure | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Carry the Anthropic-family expansion into
applyCaching().This widens the outer gate, but
applyCaching()still only treatsproviderID === "anthropic"/ bedrock as message-level on Line 235. Forgoogle-vertex-anthropicandaltimate-backend, array-content messages will still get cache metadata on the last content part instead of following the Anthropic path, so the new support stays partial.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents