Skip to content

fix(actions): forward declared reasoning none - #3427

Merged
steebchen merged 3 commits into
theopenco:mainfrom
andyst-dev:fix/reasoning-effort-none
Aug 9, 2026
Merged

steebchen merged 3 commits into
theopenco:mainfrom
andyst-dev:fix/reasoning-effort-none

Conversation

@andyst-dev

@andyst-dev andyst-dev commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3423

What & why

8 mappings across 5 providers (canopywave, runware, deepinfra, novita, xai) publish "none" in their catalog reasoningEfforts, but none of those providers are in the handlesNoneNatively allowlist in prepare-request-body.ts — so a user requesting reasoning_effort: "none" gets the value silently stripped before the request is built, regardless of what the mapping declares. Same bug shape as #3365 (bytedance/glm-5-2), but spanning 8 mappings.

For a mapping that publishes none in reasoningEfforts, that catalog entry is authoritative: it documents that the provider accepts the value. The fix lets the catalog speak for the 8 mappings instead of hardcoding each provider into the allowlist.

Changes

  • packages/actions/src/prepare-request-body.ts: add providerMappingForOptions?.reasoningEfforts?.includes("none") to handlesNoneNatively. The existing provider allowlist is untouched (still authoritative for providers that accept none without declaring it); the catalog entry now also forwards the value when declared. Applied with parentheses to keep ||/?? mixing valid.
  • packages/actions/src/prepare-request-body.spec.ts: regression test forwards none to xAI Grok when the mapping declares it (xai/grok-4-3 publishes none in reasoningEfforts).

How I tested it

  • New test fails on main (requestBody.reasoning_effort is undefined — the value was stripped); passes after the fix.
  • vitest run packages/actions/src/prepare-request-body.spec.ts — 233 passed.
  • Full packages/actions suite: 531 passed; the 4 failures in env-inventory.spec.ts are Redis-dependent (verified failing identically on a clean main checkout without this change — no Redis/Docker available here).
  • pnpm format + lint-staged (eslint/prettier) pass on the changed files.

Summary by CodeRabbit

  • Bug Fixes
    • Preserved the reasoning_effort: "none" setting for supported provider configurations.
    • Added regression coverage to ensure the setting is forwarded correctly.
    • Continued filtering the setting when the provider does not support it.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The request preparation logic preserves reasoning_effort: "none" when the selected provider mapping declares support. Tests cover xAI, DeepInfra, Novita, Runware, CanopyWave, and an Azure AI Foundry mapping without support.

Changes

Reasoning effort forwarding

Layer / File(s) Summary
Mapping-aware normalization and regression coverage
packages/actions/src/prepare-request-body.ts, packages/actions/src/prepare-request-body.spec.ts
The normalization logic preserves "none" when the selected mapping declares it in reasoningEfforts. Tests cover supported mappings and removal for an unsupported Azure AI Foundry mapping.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address all eight affected mappings, use mapping-level declarations, and add positive and negative coverage required by issue #3423.
Out of Scope Changes check ✅ Passed The implementation and tests remain focused on mapping-scoped forwarding and removal of unsupported "none" values.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes forwarding declared reasoning_effort: "none" values in actions request preparation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/actions/src/prepare-request-body.spec.ts (1)

981-990: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Expand coverage for the catalog contract.

This test covers only xai/grok-4-3, while the PR targets eight mappings. Add table-driven cases for the other affected mappings.

Also add a mapping that uses openai-chat-completions but does not declare "none". Assert that normalization still removes "none" for that mapping.

🤖 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/actions/src/prepare-request-body.spec.ts` around lines 981 - 990,
Expand the test around prepare in prepare-request-body.spec.ts into table-driven
cases covering all eight affected mappings, including the existing xai/grok-4-3
case and the seven other mappings targeted by the PR. Add a separate
openai-chat-completions mapping that does not declare "none", and assert
normalization removes "none" for it while preserving "none" for mappings that
declare it.
🤖 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/actions/src/prepare-request-body.ts`:
- Around line 1173-1176: Update the “none” reasoning-effort handling near the
apiFormat check to remove the broad OpenAI-compatible mapping fallback. Preserve
the existing provider allowlist, and allow mapping-specific forwarding only when
the mapping’s reasoningEfforts includes "none".

---

Nitpick comments:
In `@packages/actions/src/prepare-request-body.spec.ts`:
- Around line 981-990: Expand the test around prepare in
prepare-request-body.spec.ts into table-driven cases covering all eight affected
mappings, including the existing xai/grok-4-3 case and the seven other mappings
targeted by the PR. Add a separate openai-chat-completions mapping that does not
declare "none", and assert normalization removes "none" for it while preserving
"none" for mappings that declare it.
🪄 Autofix

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 Plus

Run ID: afd284d5-7289-4848-b00c-e857199764eb

📥 Commits

Reviewing files that changed from the base of the PR and between c626619 and cda1fd6.

📒 Files selected for processing (2)
  • packages/actions/src/prepare-request-body.spec.ts
  • packages/actions/src/prepare-request-body.ts

Comment thread packages/actions/src/prepare-request-body.ts
@andyst-dev
andyst-dev force-pushed the fix/reasoning-effort-none branch from cda1fd6 to 4f60e34 Compare August 6, 2026 06:34
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

andyst-dev added a commit to andyst-dev/llmgateway that referenced this pull request Aug 6, 2026
…none

CodeRabbit review (theopenco#3427): the apiFormat === openai-chat-completions
fallback preserved 'none' for any OpenAI-compatible mapping even when its
catalog entry omits 'none' from reasoningEfforts, risking a provider 4xx
from an unsupported enum. The mapping's reasoningEfforts is authoritative
(matching the PR objective theopenco#3423); the provider allowlist still handles
native cases. Add table-driven coverage for non-allowlisted mappings that
declare 'none' (deepinfra, novita, runware) plus a regression test proving
a non-allowlisted OpenAI-compatible mapping without 'none' gets the value
stripped.
@andyst-dev

Copy link
Copy Markdown
Contributor Author

Good point — the apiFormat === "openai-chat-completions" fallback preserved none for any OpenAI-compatible mapping even when its catalog entry omits none from reasoningEfforts, which could forward an unsupported enum and get a provider 4xx. The mapping's reasoningEfforts should be the authority, as the PR objective states.

Fix (2f9ca3c): removed the broad apiFormat fallback from handlesNoneNatively; only the provider allowlist and reasoningEfforts?.includes("none") keep none now.

Tests added (all 238 pass in prepare-request-body.spec.ts):

  • Table-driven: non-allowlisted mappings that do declare none forward it — deepinfra/deepseek-v4-pro, deepinfra/hy3, novita/hy3, runware/deepseek-v4-flash (+ existing xai/grok-4-3 case)
  • Regression: a non-allowlisted OpenAI-compatible mapping that does not declare none (grok-4-3 via azure-ai-foundry) now gets the value stripped (reasoning_effort undefined) instead of forwarded

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/actions/src/prepare-request-body.spec.ts (2)

992-1006: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the remaining affected mappings.

The table does not exercise canopywave/kimi-k3 or runware/gemma-4-31b-it, although both are listed among the eight affected mappings. Add rows for both mappings. Otherwise, a future catalog regression for either mapping can pass without a failing test.

Suggested additions
 	test.each([
+		["canopywave", "kimi-k3"],
 		["deepinfra", "deepseek-v4-pro"],
 		["deepinfra", "hy3"],
 		["novita", "hy3"],
 		["runware", "deepseek-v4-flash"],
+		["runware", "gemma-4-31b-it"],
 	])(
🤖 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/actions/src/prepare-request-body.spec.ts` around lines 992 - 1006,
Add test cases for the missing affected mappings, canopywave/kimi-k3 and
runware/gemma-4-31b-it, to the parameter table in the “forwards none to %s when
the mapping declares it” test. Use each provider/model pair and preserve the
existing expectation that reasoning_effort equals “none”.

1000-1002: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Correct the allowlist description.

The table values are deepinfra, novita, and runware, but the handlesNoneNatively allowlist shown in packages/actions/src/prepare-request-body.ts:1155-1205 lists none of those provider IDs. These cases exercise the mapping catalog path. Update the comment so it does not claim that the provider allowlist is also covered.

🤖 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/actions/src/prepare-request-body.spec.ts` around lines 1000 - 1002,
Update the comment above these provider cases in prepare-request-body.spec.ts to
state that their catalog entries publish `none` and exercise the mapping catalog
path, without claiming they are included in the handlesNoneNatively allowlist.
Keep the note that both relevant paths must agree where applicable.
🤖 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.

Nitpick comments:
In `@packages/actions/src/prepare-request-body.spec.ts`:
- Around line 992-1006: Add test cases for the missing affected mappings,
canopywave/kimi-k3 and runware/gemma-4-31b-it, to the parameter table in the
“forwards none to %s when the mapping declares it” test. Use each provider/model
pair and preserve the existing expectation that reasoning_effort equals “none”.
- Around line 1000-1002: Update the comment above these provider cases in
prepare-request-body.spec.ts to state that their catalog entries publish `none`
and exercise the mapping catalog path, without claiming they are included in the
handlesNoneNatively allowlist. Keep the note that both relevant paths must agree
where applicable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b69da22-7850-4e4f-b00f-8e3aac97338c

📥 Commits

Reviewing files that changed from the base of the PR and between 4f60e34 and 2f9ca3c.

📒 Files selected for processing (2)
  • packages/actions/src/prepare-request-body.spec.ts
  • packages/actions/src/prepare-request-body.ts
💤 Files with no reviewable changes (1)
  • packages/actions/src/prepare-request-body.ts

@AmineAce

AmineAce commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

While auditing aws-bedrock today, found something that directly supports this fix approach: xai/grok-4.3 on aws-bedrock already has an apiFormat: "openai-chat-completions" exemption that bypasses both the handlesNoneNatively gate and the supportedParameters check — so it already forwards reasoning_effort: "none" correctly today, unlike the 8 mappings this PR fixes. Same model, same value, working on one host and broken on another purely due to this per-mapping wiring gap. Confirms the pattern this PR addresses is real and the fix (catalog-driven, per-mapping) is the right shape — aws-bedrock is basically already doing what this PR makes possible everywhere else.

@AmineAce

AmineAce commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Confirmed this resolves the root cause described in #3423. One thing worth adding to the test table, tying back to the original scope: #3423 named 8 mappings total, and this diff's tests cover 5 of them (grok-4.3/xai, deepinfra/deepseek-v4-pro, deepinfra/hy3, novita/hy3, runware/deepseek-v4-flash). The 3 not yet covered — canopywave/kimi-k3, runware/deepseek-v4-pro, runware/gemma-4-31b-it — were explicitly part of the original finding, so adding them would close the loop on the full reported scope, not just a subset.

…none

CodeRabbit review (theopenco#3427): the apiFormat === openai-chat-completions
fallback preserved 'none' for any OpenAI-compatible mapping even when its
catalog entry omits 'none' from reasoningEfforts, risking a provider 4xx
from an unsupported enum. The mapping's reasoningEfforts is authoritative
(matching the PR objective theopenco#3423); the provider allowlist still handles
native cases. Add table-driven coverage for non-allowlisted mappings that
declare 'none' (deepinfra, novita, runware) plus a regression test proving
a non-allowlisted OpenAI-compatible mapping without 'none' gets the value
stripped.
…ng_effort none table

Adds canopywave/kimi-k3, runware/deepseek-v4-pro and
runware/gemma-4-31b-it to the table-driven cases — the three mappings
named in the original issue that were still untested. All declare
"none" in reasoningEfforts.
@andyst-dev
andyst-dev force-pushed the fix/reasoning-effort-none branch from 2f9ca3c to 224c62e Compare August 8, 2026 09:47
@andyst-dev

Copy link
Copy Markdown
Contributor Author

Thanks for the precise call-out — all 8 mappings from #3423 are now covered. Added the 3 missing cases to the table-driven test, rebased the branch onto current main:

  • canopywave/kimi-k3 (moonshot.ts, declares none in reasoningEfforts)
  • runware/deepseek-v4-pro (deepseek.ts, reasoningEfforts: ["none", "high", "xhigh", "max"])
  • runware/gemma-4-31b-it (google.ts, same none-declaring runware block)

Each entry was verified against the model catalog before adding (grep -n "reasoningEfforts" + the exact model id), so the cases exercise the mapping-declared opt-in path, not the allowlist.

Validation:

pnpm exec vitest run --config vitest.config.mts packages/actions/src/prepare-request-body.spec.ts
Test Files  1 passed (1)
Tests       252 passed (252)

Branch rebased on main (was behind by ~20 commits). New head: 224c62e.

@steebchen steebchen self-assigned this Aug 8, 2026
@steebchen
steebchen enabled auto-merge August 9, 2026 12:04
@steebchen steebchen changed the title fix(actions): forward reasoning_effort none when the mapping declares it fix(actions): forward declared reasoning none Aug 9, 2026
@steebchen
steebchen added this pull request to the merge queue Aug 9, 2026
Merged via the queue into theopenco:main with commit 5423cdf Aug 9, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: reasoning_effort "none" is unreachable for 8 mappings across 5 providers — declared in catalog but stripped before request-building

3 participants