fix(opencode): route SAP AI Core reasoning variants through modelParams#30482
Conversation
Top-level reasoningEffort, thinking and thinkingConfig keys were silently stripped by the SAP provider's Zod schema. Move them under modelParams (catchall) using each upstream backend's REST field names: reasoning_effort for Azure OpenAI, thinking + output_config for Bedrock-routed Claude, thinkingConfig for Vertex-routed Gemini. Reusing openaiReasoningEfforts lets SAP inherit gpt-5.x sub-family coverage automatically.
| return 24_576 | ||
| } | ||
|
|
||
| // SAP's Zod schema drops unknown top-level keys; reasoning controls survive |
There was a problem hiding this comment.
can we follow style guide here prefer inlining things that arent used multiple times
There was a problem hiding this comment.
Thanks — addressed in ab87638bd. Inlined the last SAP-specific single-use helper (sapAnthropicReasoningParams).
Recap of the multi-use helpers I kept:
wrapInSapModelParams— 3 call sites in the SAP case (otherwiseObject.fromEntries(Object.entries(...).map(...))would repeat 3×)googleThinkingVariants— 2 call sites, shared between@ai-sdk/google[/-vertex]and SAP, so SAP automatically inherits future Gemini versions handled bygoogleThinkingLevelEfforts/googleThinkingBudgetMax.
Other single-use helpers from the original commit (sapGeminiReasoningParams, sapOpenaiReasoningParams) were removed in e97a3d2a4 (folded into the shared helper / inlined respectively).
… SAP - Extract googleThinkingVariants(model) from @ai-sdk/google[/-vertex] and reuse it in the SAP case. SAP now inherits Gemini 3.x thinkingLevel tiers via googleThinkingLevelEfforts, matching the forward-compat coverage already in place for GPT (openaiReasoningEfforts) and Anthropic (anthropicAdaptiveEfforts). - Drop sapGeminiReasoningParams and inline sapOpenaiReasoningParams per AGENTS.md single-use rule. - Fix @ai-sdk/gateway Google 2.5 max budget hardcoded to 24576 \u2014 now uses googleThinkingBudgetMax (32k for 2.5 Pro, 24k for Flash). - Brace SAP case if-bodies for consistency with the rest of the switch. - Add Gemini 3 SAP test (gemini-3.1-flash-lite) and missing result.high assertion on gemini-2.5-flash.
Addresses review feedback on PR anomalyco#30482: AGENTS.md prefers inlining single-use helpers. The prior commit (e97a3d2) already removed sapGeminiReasoningParams (folded into the shared googleThinkingVariants helper) and sapOpenaiReasoningParams (inlined). This drops the last SAP-specific single-use helper. Multi-use helpers are kept: wrapInSapModelParams (3 call sites) and googleThinkingVariants (2 call sites, shared between native and SAP).
…r SAP models - Gate Gemini branch on `2.5` so non-2.5 (Gemini 3+) falls through to the harmonized fallback. SAP vertexai harmonizer drops unknown thinkingLevel; harmonized reasoning_effort is mapped server-side. - New harmonized fallback at the end of the SAP case using `reasoning_effort: low|medium|high` for any reasoning-capable model not handled by the major-provider branches (cohere reasoning, sonar-deep-research, future providers). - Update tests: gemini-3.1-flash-lite now asserts harmonized fallback; add cohere-command-a-reasoning and sonar-deep-research fallback tests; llama opus-substring test asserts harmonized fallback (previously asserted empty).
Consolidate Gemini 2.5 pro/flash tests into one loop parametrized on maxBudget. Consolidate gpt-5/-mini/-nano + gpt-5.4 + o3-mini into one loop parametrized on apiId+releaseDate+efforts. Consolidate the four harmonized-fallback tests (gemini-3.1-flash-lite + cohere + sonar + llama opus-substring) into one loop. Net -16 lines, same coverage.
- Drop redundant .toLowerCase() on the `2.5` check in the Gemini guard; matches the case-sensitive substring style used by the surrounding anthropic/gpt branches (SAP API ids are already lowercase). - Parametrize the non-adaptive Anthropic test over claude-sonnet-4 and claude-4.5-opus to lock in that pre-4.6 reasoning models still take the budget_tokens path.
Drops the local lowercased id alias. The googleThinkingBudgetMax and googleThinkingLevelEfforts helpers already lowercase their input, and the inline "2.5" substring check matches against lowercase Gemini ids by convention. Single-source naming aligns with the surrounding switch which uses model.api.id directly.
Aligns the googleThinkingBudgetMax call with the surrounding
`id.includes("2.5")` check (outer-scope `id = model.id.toLowerCase()`)
introduced by the prior commit's drive-by fix.
Preserves the defensive .toLowerCase() the original @ai-sdk/google[/-vertex] branch had on the outer scope id (= model.id.toLowerCase()). The helper now declares const id = model.api.id.toLowerCase() locally and reuses it for every check and every helper call inside googleThinkingVariants.
Replaces model.api.id with the outer-scope id (= model.id.toLowerCase() declared at L643) in the SAP case, matching the majority pattern used by other case branches (gateway, anthropic, copilot, etc.). The helpers GPT5_FAMILY_RE and openaiReasoningEfforts are anchored on (?:^|/) so they match equally well against the prefixed model.id form.
Mirrors the .serena/ pattern for the .omo/ agent runtime directory.
review bot feedback, acccurate? |
SAP AI Core do the sanitization needed at routing the request to a given provider. So I've chosen to expose the largest tunables surface that OpenCode offer for variants and SAP AI Core. |
…ms (anomalyco#30482) (cherry picked from commit 0b796c5)
Issue for this PR
Closes #30481
Type of change
What does this PR do?
The SAP provider's Zod schema strips unknown top-level keys, so opencode's
reasoningEffort/thinking/thinkingConfigvariants for SAP-hosted models never reached the wire. OnlymodelParams(Zod catchall) is forwarded verbatim by the SAP SDKs.Routes reasoning variants through
modelParamsusing upstream-native REST field names where SAP supports granular tiers, with a harmonizedreasoning_effortfallback elsewhere:reasoning_effortviaopenaiReasoningEfforts(inherits gpt-5.x sub-families and date-gatednone/xhigh/minimal).thinking+output_config.effortadaptive (Opus 4.6+ / Sonnet 4.6+, withdisplay: "summarized"for Opus 4.7+);thinking: { type: "enabled", budget_tokens }non-adaptive. Adaptive shape per AWS Bedrock adaptive thinking docs.thinkingConfig: { thinkingBudget, includeThoughts }via a newgoogleThinkingVariantshelper shared with native@ai-sdk/google[/-vertex]. Pro 32k / Flash 24k viagoogleThinkingBudgetMax.reasoning_effort: low|medium|highfallback. SAP orchestration translates server-side; FM proxies pass through. Required for Gemini 3+ because the SAPvertexai.pyharmonizer reads onlythinkingBudgetand dropsthinkingLevel.@ai-sdk/gatewayGoogle 2.5maxbudget unified withgoogleThinkingBudgetMax(was hardcoded24576).Behavioral change: variant overrides under
providerOptions['sap-ai-core']now reach the wire; SAP users previously got no reasoning at all.How did you verify your code works?
bun test packages/opencode/test/provider/transform.test.ts bun typecheck241/241 tests pass (459 expects), typecheck clean. Tests use real SAP fixture IDs (
gpt-5,gpt-5-mini,gpt-5-nano,gpt-5.4,gemini-2.5-pro,gemini-2.5-flash,gemini-3.1-flash-lite,anthropic--claude-4.5-opus,cohere--command-a-reasoning,sonar-deep-research) and assert themodelParamsenvelope on every effort variant. Wire passthrough across both orchestration and foundation-models API modes is validated by@jerome-benoit/sap-ai-provider-v2's parametrized test "should preserve unknown parameters" (sap-ai-language-model.test.ts:4912-4979).Screenshots / recordings
N/A.
Checklist