Skip to content

fix: port 3 upstream DeepSeek transform fixes#852

Open
anandgupta42 wants to merge 1 commit into
mainfrom
fix/upstream-deepseek-fixes
Open

fix: port 3 upstream DeepSeek transform fixes#852
anandgupta42 wants to merge 1 commit into
mainfrom
fix/upstream-deepseek-fixes

Conversation

@anandgupta42
Copy link
Copy Markdown
Contributor

@anandgupta42 anandgupta42 commented May 30, 2026

What does this PR do?

Cherry-picks (manual port — no merge base with upstream-opencode) 3 DeepSeek-specific transform.ts fixes that landed in upstream-opencode but never made it into altimate-code's main:

  1. DeepSeek assistant messages always carry a reasoning part — upstream 86715fecc4 / #24180. DeepSeek's OpenAI-compatible protocol rejects multi-turn assistant messages that lack a reasoning part; the fix inserts an empty one before the interleaved-field normalization.

  2. Always emit the interleaved reasoning field, even when empty — upstream 923af96d26 / #24146. DeepSeek V4 thinking mode may emit empty reasoning_content; the field must still be present in subsequent requests or the API rejects the message. Removes the if (reasoningText) guard around the providerOptions set so the field is always written.

  3. DeepSeek-v4 supports max reasoning effort — upstream f8e939d96f / #24163. Adds "max" to the @ai-sdk/openai-compatible variant cascade when the model id contains deepseek-v4.

A fourth upstream fix (9d6718131e — remove deepseek-chat/deepseek-reasoner from a variants include list) is not ported because our equivalent block already excludes all deepseek model ids from variant calculation via the broader id.includes("deepseek") check at transform.ts:380.

Also wraps the altimate-specific altimate-backend / google-vertex-anthropic providerID entries in altimate_change markers (pre-existing oversight from PR #850 that the marker guard now flags).

All three ported changes are marked with the upstream_fix: tag so they can be dropped when upstream finally ships the same shape via a normal merge.

Type of change

fix: (provider compat with DeepSeek V4 / Reasoner endpoints).

Issue for this PR

No tracking issue — these are upstream fixes that fell off the bridge during our diverged history. Investigated as a follow-up to #849 review.

How did you verify your code works?

  • bun test test/provider/ — 387 pass / 0 fail, exercises the variant cascade and normalizeMessages
  • bun run typecheck — clean
  • bun run script/upstream/analyze.ts --markers --base main --strict — clean (new code wrapped in altimate_change blocks)
  • Spot-checked against a DGX DeepSeek-V4 benchmark trace at ~/ade-bench-openrouter/experiments/duckdb_deepseek_v4pro_run1__none/ to confirm the model id pattern matches (alibaba-cn/deepseek-v4-pro and azure-deepseek/deepseek-v4-flash both contain deepseek-v4).

Checklist

  • Tests pass locally
  • No new lint errors
  • Marker guard clean
  • Self-reviewed the diff
  • All ported changes carry upstream_fix: so they auto-surface during the next upstream merge audit

Summary by cubic

Ports three upstream DeepSeek transform fixes to restore API compatibility and prevent 400 errors in multi-turn chats. Ensures reasoning fields are always present and enables max reasoning effort for deepseek-v4.

  • Bug Fixes

    • Always add an empty reasoning part to DeepSeek assistant messages.
    • Always set the interleaved reasoning field on assistant messages, even when empty.
    • Add "max" to reasoning effort variants for deepseek-v4 under @ai-sdk/openai-compatible.
  • Refactors

    • Wrap altimate-backend and google-vertex-anthropic provider IDs in altimate_change guards.

Written for commit dc995c5. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Improvements
    • Enhanced support for AI reasoning capabilities across models
    • Expanded compatibility with additional AI provider backends
    • Added maximum reasoning effort level option for DeepSeek models
    • Improved message transformation consistency for multi-provider support

Review Change Stack

… providerID

Cherry-picks (manual port — no merge base with upstream-opencode) 3
DeepSeek-specific transform.ts fixes that landed in upstream-opencode but
never made it into altimate-code's main:

- **deepseek assistant messages always carry a reasoning part** (upstream
  86715fecc4 / #24180). DeepSeek's OpenAI-compatible protocol rejects
  multi-turn assistant messages that lack a `reasoning` part. Inserts
  an empty one before the interleaved-field normalization.

- **always emit the interleaved reasoning field, even when empty**
  (upstream 923af96d26 / #24146). DeepSeek V4 thinking mode may emit
  empty reasoning_content; the field must still be present in
  subsequent requests or the API rejects the message. Removes the
  `if (reasoningText)` guard around the providerOptions set.

- **deepseek-v4 supports `max` reasoning effort** (upstream f8e939d96f
  / #24163). Adds "max" to the OpenAI-compatible variant cascade when
  the model id contains `deepseek-v4`.

A fourth upstream fix (9d6718131e — remove deepseek-chat/deepseek-reasoner
from a variants include list) is NOT ported because our equivalent block
already excludes ALL `deepseek` model ids from variant calculation via
the broader `id.includes("deepseek")` check at transform.ts:380.

Also wraps the altimate-specific `altimate-backend`/
`google-vertex-anthropic` providerID entries in altimate_change markers
(pre-existing oversight from PR #850 that the marker guard now flags).

All marked with `upstream_fix:` so they can be dropped when upstream
finally ships the same shape via a normal merge.

Tests: 387 pass / 0 fail in test/provider/; typecheck + marker guard
clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

📝 Walkthrough

Walkthrough

A single file receives four enhancements: DeepSeek messages are normalized to always contain a reasoning content part, interleaved reasoning fields are unconditionally persisted, Anthropic-compatible provider IDs are expanded for caching eligibility, and DeepSeek v4 reasoning effort variants include a new max option.

Changes

Provider Transform Updates

Layer / File(s) Summary
DeepSeek reasoning structure normalization
packages/opencode/src/provider/transform.ts
normalizeMessages ensures every DeepSeek assistant message includes a reasoning content part by appending an empty one if missing, or wrapping non-array content as a text part before adding reasoning. Interleaved reasoning is always written to providerOptions.openaiCompatible[field] including when the computed reasoning text is empty, while reasoning parts are removed from the final content array.
Anthropic-compatible provider caching expansion
packages/opencode/src/provider/transform.ts
The message function's caching decision expands to recognize additional Anthropic-family provider IDs (google-vertex-anthropic, altimate-backend) alongside existing ones, enabling cache-control headers for a wider set of compatible backends.
DeepSeek v4 reasoning effort variants
packages/opencode/src/provider/transform.ts
The variants section for deepseek-v4 models adds max as a supported reasoning effort value alongside the previously supported standard efforts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AltimateAI/altimate-code#850: Both PRs modify packages/opencode/src/provider/transform.ts in ProviderTransform.message() to treat altimate-backend (Anthropic-family) the same way for applyCaching/cache_control gating, so the caching-related change in the main PR extends the exact condition added in #850.

Suggested labels

contributor

Poem

🐰 DeepSeek thoughts now flow so clear,
With reasoning parts that always appear,
More caching friends join the Anthropic crew,
And v4's efforts reach further—max too! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is comprehensive and well-structured, covering what changed, why, verification methods, and checklist items, but lacks the required 'PINEAPPLE' marker specified in the template for AI-generated contributions. Add 'PINEAPPLE' at the very top of the PR description before any other content as required by the template for AI-generated contributions.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: porting 3 upstream DeepSeek fixes to transform.ts for provider compatibility.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/upstream-deepseek-fixes

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/provider/transform.ts`:
- Around line 305-309: The Anthropic-family provider IDs added to the model
gating (e.g., "google-vertex-anthropic" and "altimate-backend") must also be
included in the caching logic inside applyCaching() so they follow the same
Anthropic message-level handling; update the conditional in applyCaching() that
currently checks providerID === "anthropic" (and the bedrock branch) to OR in
the new IDs and ensure that when messages are an array the cache metadata is
attached via the Anthropic path instead of only on the last content part—search
for the applyCaching function and the providerID === "anthropic" check and
extend that predicate to include "google-vertex-anthropic" and
"altimate-backend".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3ff4cfac-ed1a-4943-9792-3b6496c1145a

📥 Commits

Reviewing files that changed from the base of the PR and between a490bd4 and dc995c5.

📒 Files selected for processing (1)
  • packages/opencode/src/provider/transform.ts

Comment on lines 305 to +309
(model.providerID === "anthropic" ||
// altimate_change start — altimate-specific Anthropic provider IDs
model.providerID === "google-vertex-anthropic" ||
model.providerID === "altimate-backend" ||
// altimate_change end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Carry the Anthropic-family expansion into applyCaching().

This widens the outer gate, but applyCaching() still only treats providerID === "anthropic" / bedrock as message-level on Line 235. For google-vertex-anthropic and altimate-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
+    const isAnthropicLikeProvider =
+      model.providerID === "anthropic" ||
+      model.providerID === "google-vertex-anthropic" ||
+      model.providerID === "altimate-backend"
+
     for (const msg of unique([...system, ...final])) {
       const useMessageLevelOptions =
-        model.providerID === "anthropic" ||
+        isAnthropicLikeProvider ||
         model.providerID.includes("bedrock") ||
         model.api.npm === "`@ai-sdk/amazon-bedrock`"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(model.providerID === "anthropic" ||
// altimate_change start — altimate-specific Anthropic provider IDs
model.providerID === "google-vertex-anthropic" ||
model.providerID === "altimate-backend" ||
// altimate_change end
const isAnthropicLikeProvider =
model.providerID === "anthropic" ||
model.providerID === "google-vertex-anthropic" ||
model.providerID === "altimate-backend"
for (const msg of unique([...system, ...final])) {
const useMessageLevelOptions =
isAnthropicLikeProvider ||
model.providerID.includes("bedrock") ||
model.api.npm === "`@ai-sdk/amazon-bedrock`"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/provider/transform.ts` around lines 305 - 309, The
Anthropic-family provider IDs added to the model gating (e.g.,
"google-vertex-anthropic" and "altimate-backend") must also be included in the
caching logic inside applyCaching() so they follow the same Anthropic
message-level handling; update the conditional in applyCaching() that currently
checks providerID === "anthropic" (and the bedrock branch) to OR in the new IDs
and ensure that when messages are an array the cache metadata is attached via
the Anthropic path instead of only on the last content part—search for the
applyCaching function and the providerID === "anthropic" check and extend that
predicate to include "google-vertex-anthropic" and "altimate-backend".

Comment on lines +528 to +538
// 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"] : []),
].map((effort) => [effort, { reasoningEffort: effort }]),
)
// altimate_change end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

deepseek-v4 still never reaches this branch.

Line 402 returns {} for every model.id containing deepseek, so this new @ai-sdk/openai-compatible case is dead for the models it is meant to unlock. As written, deepseek-v4 still exposes no reasoning-effort variants.

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 {}

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/provider/transform.ts">

<violation number="1" location="packages/opencode/src/provider/transform.ts:535">
P1: The `model.api.id.includes("deepseek-v4")` check here is dead code for the models it's meant to support. An earlier guard in this function returns `{}` for any model whose `id` includes `"deepseek"`, which means `deepseek-v4` models never reach this `@ai-sdk/openai-compatible` case. You need to exclude `deepseek-v4` from that early return (e.g., `id.includes("deepseek") && !id.includes("deepseek-v4")`) for this branch to be reachable.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

return Object.fromEntries(
[
...WIDELY_SUPPORTED_EFFORTS,
...(model.api.id.includes("deepseek-v4") ? ["max"] : []),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The model.api.id.includes("deepseek-v4") check here is dead code for the models it's meant to support. An earlier guard in this function returns {} for any model whose id includes "deepseek", which means deepseek-v4 models never reach this @ai-sdk/openai-compatible case. You need to exclude deepseek-v4 from that early return (e.g., id.includes("deepseek") && !id.includes("deepseek-v4")) for this branch to be reachable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/provider/transform.ts, line 535:

<comment>The `model.api.id.includes("deepseek-v4")` check here is dead code for the models it's meant to support. An earlier guard in this function returns `{}` for any model whose `id` includes `"deepseek"`, which means `deepseek-v4` models never reach this `@ai-sdk/openai-compatible` case. You need to exclude `deepseek-v4` from that early return (e.g., `id.includes("deepseek") && !id.includes("deepseek-v4")`) for this branch to be reachable.</comment>

<file context>
@@ -503,7 +525,17 @@ export namespace ProviderTransform {
+        return Object.fromEntries(
+          [
+            ...WIDELY_SUPPORTED_EFFORTS,
+            ...(model.api.id.includes("deepseek-v4") ? ["max"] : []),
+          ].map((effort) => [effort, { reasoningEffort: effort }]),
+        )
</file context>

Copy link
Copy Markdown

@dev-punia-altimate dev-punia-altimate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-Persona Review — Verdict: skipped

Multi-persona review completed.

0/0 agents completed · 2s · 0 findings (0 critical, 0 high, 0 medium)


Multi-Persona Review · vllm:qwen3-next-80b (waves) + vllm-fallback (synth) ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants