Skip to content

adaptive thinking support for passthrough mode - #5934

Merged
akshaydeo merged 2 commits into
devfrom
08-06-adaptive_thinking_support_for_passthrough_mode
Aug 7, 2026
Merged

adaptive thinking support for passthrough mode#5934
akshaydeo merged 2 commits into
devfrom
08-06-adaptive_thinking_support_for_passthrough_mode

Conversation

@akshaydeo

@akshaydeo akshaydeo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adaptive-only Anthropic models (Opus 4.7+, Opus 4.8, Opus 5, Sonnet 5, Fable 5, Mythos 5) no longer support the legacy thinking.type: "enabled" shape and reject it with a 400 upstream. When a caller sends a native Anthropic request body through Bifrost with {"type":"enabled","budget_tokens":N}, it was being forwarded verbatim, causing failures. This PR rewrites thinking.type from "enabled" to "adaptive" and strips budget_tokens for these models on both the typed and raw passthrough paths, matching the behavior already implemented in the converted chat/responses paths.

Additionally, stripUnsupportedAnthropicFields in both the responses and chat builders is now gated on capModel (the canonical Anthropic model name) rather than request.Model, so Bifrost aliases correctly resolve to a model the capability predicates can match.

Changes

  • In stripUnsupportedAnthropicFields, when thinking.type == "enabled" and the model is adaptive-only, the type is rewritten to "adaptive" and budget_tokens is set to nil. The field is rewritten rather than deleted because Opus 4.7/4.8 default thinking to Off, so dropping it would silently disable thinking for callers who explicitly requested it.
  • In StripUnsupportedFieldsFromRawBody, the same rewrite is applied to the raw JSON body path using SetJSONField and DeleteJSONField.
  • Both the responses and chat request builders now pass capModel instead of request.Model to stripUnsupportedAnthropicFields so that Bifrost aliases do not bypass model-level capability checks.
  • Tests covering both the raw body and typed request paths verify that adaptive-only models get the rewrite and that older models (Opus 4.6, Sonnet 4.5, Haiku 4.5, etc.) have their legacy shape preserved unchanged.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./core/providers/anthropic/... -run TestAdaptiveOnlyThinkingStrip -v
go test ./...

Expected: all four sub-tests pass — adaptive-only models have thinking.type rewritten to "adaptive" with budget_tokens removed, and legacy models retain thinking.type: "enabled" with budget_tokens: 10000 intact.

Screenshots/Recordings

N/A

Breaking changes

  • Yes
  • No

Related issues

Security considerations

No auth, secrets, or PII implications. The change only rewrites a field in outbound request bodies before they are forwarded to the Anthropic API.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility with Anthropic adaptive-thinking models.
    • Automatically converts legacy thinking settings to supported formats.
    • Removes unsupported thinking budget fields where required.
    • Adjusts disabled-thinking settings based on model and effort level.
    • Preserves supported extended-thinking behavior for compatible models.
    • Applies these corrections consistently across typed and raw requests.

Walkthrough

Anthropic typed and raw request paths now normalize unsupported thinking configurations. The logic uses canonical model identifiers, model families, and effort conditions. Tests cover adaptive-only models, disabled-thinking rejection, Mythos Preview behavior, raw requests, typed requests, and supported-field preservation.

Changes

Anthropic thinking normalization

Layer / File(s) Summary
Thinking normalization rules
core/providers/anthropic/utils.go
Typed and raw requests convert unsupported thinking.type: "enabled" configurations to "adaptive" and remove incompatible budget_tokens. Rejected disabled thinking also converts to "adaptive". Mythos Preview preserves enabled thinking.
Canonical model wiring
core/providers/anthropic/requestbuilder.go
Typed Responses and Chat request builders pass the canonical resolved model to unsupported-field filtering.
Normalization validation
core/providers/anthropic/adaptivethinkingstrip_test.go
Tests cover typed and raw requests, adaptive-only models, disabled-thinking rejection, effort conditions, Mythos Preview, output effort, display fields, and legacy-model preservation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RequestBuilder
  participant stripUnsupportedAnthropicFields
  participant ThinkingCapabilityRules
  RequestBuilder->>stripUnsupportedAnthropicFields: pass request and canonical model
  stripUnsupportedAnthropicFields->>ThinkingCapabilityRules: evaluate model and effort
  ThinkingCapabilityRules-->>stripUnsupportedAnthropicFields: return normalization decision
  stripUnsupportedAnthropicFields-->>RequestBuilder: return normalized request or error
Loading

Possibly related issues

  • maximhq/bifrost#4828: Both changes address Anthropic model-specific thinking sanitization. This PR covers adaptive-only model detection and legacy fallback behavior.

Suggested reviewers: tejasghatte, pratham-mishra04

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title clearly and concisely describes the primary change: adding adaptive thinking support for Anthropic passthrough requests.
Description check ✅ Passed The description includes the required sections, explains the change, identifies affected areas, and provides test commands and expected results.
✨ 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 08-06-adaptive_thinking_support_for_passthrough_mode

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

@akshaydeo
akshaydeo marked this pull request as ready for review August 7, 2026 06:26

akshaydeo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@akshaydeo akshaydeo mentioned this pull request Aug 7, 2026
18 tasks

@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

🤖 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 `@core/providers/anthropic/utils.go`:
- Around line 323-325: The adaptive-thinking normalization in
core/providers/anthropic/utils.go:323-325 and the raw-path handling at
core/providers/anthropic/utils.go:662-674 must clear BudgetTokens whenever the
final thinking type is "adaptive", including requests already marked adaptive,
while preserving budget_tokens for legacy models. Update both typed and raw
paths accordingly, and add regression coverage in
core/providers/anthropic/adaptivethinkingstrip_test.go:45-124 for typed and raw
adaptive requests.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 717971b7-c85a-4cbb-9ea3-6dac70395d43

📥 Commits

Reviewing files that changed from the base of the PR and between dca169b and 8e0c1b1.

📒 Files selected for processing (3)
  • core/providers/anthropic/adaptivethinkingstrip_test.go
  • core/providers/anthropic/requestbuilder.go
  • core/providers/anthropic/utils.go

Comment thread core/providers/anthropic/utils.go Outdated
@akshaydeo
akshaydeo force-pushed the 08-06-adds_harness_run_test branch from dca169b to 9c046db Compare August 7, 2026 06:49
@akshaydeo
akshaydeo force-pushed the 08-06-adaptive_thinking_support_for_passthrough_mode branch from 8e0c1b1 to bdec000 Compare August 7, 2026 06:49
@coderabbitai

coderabbitai Bot commented Aug 7, 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.

@akshaydeo
akshaydeo force-pushed the 08-06-adaptive_thinking_support_for_passthrough_mode branch from bdec000 to bf3ad45 Compare August 7, 2026 07:01
@coderabbitai

coderabbitai Bot commented Aug 7, 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.

@akshaydeo
akshaydeo force-pushed the 08-06-adaptive_thinking_support_for_passthrough_mode branch from bf3ad45 to 4e8cdb2 Compare August 7, 2026 07:09
@coderabbitai

coderabbitai Bot commented Aug 7, 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.

@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

🤖 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 `@core/providers/anthropic/adaptivethinkingstrip_test.go`:
- Around line 238-240: Strengthen the assertions in the adaptive-thinking
rewrite tests at each referenced site to require that the resulting req.Thinking
object is present and its Type equals "adaptive", rather than only checking that
"disabled" is absent. Preserve the separate budget_tokens removal assertion
while also verifying that an already-adaptive request retains thinking.Type as
"adaptive".
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4cfc3740-4aca-44a3-b352-2afeea866a80

📥 Commits

Reviewing files that changed from the base of the PR and between 9c046db and 4e8cdb2.

📒 Files selected for processing (3)
  • core/providers/anthropic/adaptivethinkingstrip_test.go
  • core/providers/anthropic/requestbuilder.go
  • core/providers/anthropic/utils.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/providers/anthropic/utils.go
  • core/providers/anthropic/requestbuilder.go

Comment thread core/providers/anthropic/adaptivethinkingstrip_test.go
@akshaydeo
akshaydeo force-pushed the 08-06-adds_harness_run_test branch from 9c046db to ddbb11d Compare August 7, 2026 07:28
@akshaydeo
akshaydeo force-pushed the 08-06-adaptive_thinking_support_for_passthrough_mode branch from 4e8cdb2 to 926c7bd Compare August 7, 2026 07:28
@coderabbitai

coderabbitai Bot commented Aug 7, 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.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 7, 2026
@akshaydeo
akshaydeo force-pushed the 08-06-adds_harness_run_test branch from ddbb11d to 6e6112d Compare August 7, 2026 07:40
@akshaydeo
akshaydeo force-pushed the 08-06-adaptive_thinking_support_for_passthrough_mode branch from 926c7bd to ec02f23 Compare August 7, 2026 07:40
@coderabbitai

coderabbitai Bot commented Aug 7, 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.

@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)
core/providers/anthropic/adaptivethinkingstrip_test.go (2)

264-282: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a mixed-case effort case.

RejectsDisabledThinking lowercases the effort value before comparison (core/providers/anthropic/utils.go Lines 1003-1014). The tests only send lowercase values, so the lowercasing branch is untested.

Add "XHigh" or "MAX" to the effort slice at Line 265 to lock that behavior.

🤖 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 `@core/providers/anthropic/adaptivethinkingstrip_test.go` around lines 264 -
282, Extend the effort table in
typed_request_rewrites_disabled_on_opus5_at_high_effort to include one
mixed-case value such as "XHigh" or "MAX", ensuring RejectsDisabledThinking’s
case-insensitive comparison is covered while preserving the existing assertions.

185-199: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that thinking.type is untouched on legacy models.

The comment at Lines 182-184 states the sanitizer must not rewrite legacy models. This subtest only checks budget_tokens. A regression that rewrites thinking.type on claude-opus-4-5 or claude-sonnet-4-5 still passes.

Add a thinking.type assertion to cover the stated guarantee.

✅ Proposed assertion
 			if got := providerUtils.GetJSONField(result, "thinking.budget_tokens").Int(); got != 10000 {
 				t.Errorf("%s: budget_tokens = %d, want 10000 preserved; body: %s",
 					model, got, string(result))
 			}
+			if got := providerUtils.GetJSONField(result, "thinking.type").String(); got != "adaptive" {
+				t.Errorf("%s: thinking.type = %q, want \"adaptive\" untouched; body: %s",
+					model, got, string(result))
+			}
🤖 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 `@core/providers/anthropic/adaptivethinkingstrip_test.go` around lines 185 -
199, Add an assertion in the raw_body_preserves_budget_tokens_on_legacy_models
subtest, alongside the existing budget_tokens check, that verifies thinking.type
remains "adaptive" for every legacy model. Preserve the current error handling
and iteration over legacyOK.
🤖 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 `@core/providers/anthropic/adaptivethinkingstrip_test.go`:
- Around line 264-282: Extend the effort table in
typed_request_rewrites_disabled_on_opus5_at_high_effort to include one
mixed-case value such as "XHigh" or "MAX", ensuring RejectsDisabledThinking’s
case-insensitive comparison is covered while preserving the existing assertions.
- Around line 185-199: Add an assertion in the
raw_body_preserves_budget_tokens_on_legacy_models subtest, alongside the
existing budget_tokens check, that verifies thinking.type remains "adaptive" for
every legacy model. Preserve the current error handling and iteration over
legacyOK.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c2594b8-d884-4b43-8b6f-3af2afe6923d

📥 Commits

Reviewing files that changed from the base of the PR and between 6e6112d and ec02f23.

📒 Files selected for processing (3)
  • core/providers/anthropic/adaptivethinkingstrip_test.go
  • core/providers/anthropic/requestbuilder.go
  • core/providers/anthropic/utils.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • core/providers/anthropic/requestbuilder.go
  • core/providers/anthropic/utils.go

akshaydeo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Aug 7, 7:50 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 7, 7:51 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo changed the base branch from 08-06-adds_harness_run_test to graphite-base/5934 August 7, 2026 07:50
@akshaydeo
akshaydeo changed the base branch from graphite-base/5934 to dev August 7, 2026 07:51
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review August 7, 2026 07:51

The base branch was changed.

@akshaydeo
akshaydeo requested a review from a team as a code owner August 7, 2026 07:51
@akshaydeo
akshaydeo merged commit 4742c6c into dev Aug 7, 2026
11 checks passed
@akshaydeo
akshaydeo deleted the 08-06-adaptive_thinking_support_for_passthrough_mode branch August 7, 2026 07:51
akshaydeo added a commit that referenced this pull request Aug 7, 2026
## Summary

Adaptive-only Anthropic models (Opus 4.7+, Opus 4.8, Opus 5, Sonnet 5, Fable 5, Mythos 5) no longer support the legacy `thinking.type: "enabled"` shape and reject it with a 400 upstream. When a caller sends a native Anthropic request body through Bifrost with `{"type":"enabled","budget_tokens":N}`, it was being forwarded verbatim, causing failures. This PR rewrites `thinking.type` from `"enabled"` to `"adaptive"` and strips `budget_tokens` for these models on both the typed and raw passthrough paths, matching the behavior already implemented in the converted chat/responses paths.

Additionally, `stripUnsupportedAnthropicFields` in both the responses and chat builders is now gated on `capModel` (the canonical Anthropic model name) rather than `request.Model`, so Bifrost aliases correctly resolve to a model the capability predicates can match.

## Changes

- In `stripUnsupportedAnthropicFields`, when `thinking.type == "enabled"` and the model is adaptive-only, the type is rewritten to `"adaptive"` and `budget_tokens` is set to `nil`. The field is rewritten rather than deleted because Opus 4.7/4.8 default thinking to Off, so dropping it would silently disable thinking for callers who explicitly requested it.
- In `StripUnsupportedFieldsFromRawBody`, the same rewrite is applied to the raw JSON body path using `SetJSONField` and `DeleteJSONField`.
- Both the responses and chat request builders now pass `capModel` instead of `request.Model` to `stripUnsupportedAnthropicFields` so that Bifrost aliases do not bypass model-level capability checks.
- Tests covering both the raw body and typed request paths verify that adaptive-only models get the rewrite and that older models (Opus 4.6, Sonnet 4.5, Haiku 4.5, etc.) have their legacy shape preserved unchanged.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/anthropic/... -run TestAdaptiveOnlyThinkingStrip -v
go test ./...
```

Expected: all four sub-tests pass — adaptive-only models have `thinking.type` rewritten to `"adaptive"` with `budget_tokens` removed, and legacy models retain `thinking.type: "enabled"` with `budget_tokens: 10000` intact.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No auth, secrets, or PII implications. The change only rewrites a field in outbound request bodies before they are forwarded to the Anthropic API.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
@akshaydeo akshaydeo mentioned this pull request Aug 7, 2026
18 tasks
atharvamhaske pushed a commit to atharvamhaske/bifrost that referenced this pull request Aug 13, 2026
## Summary

Adaptive-only Anthropic models (Opus 4.7+, Opus 4.8, Opus 5, Sonnet 5, Fable 5, Mythos 5) no longer support the legacy `thinking.type: "enabled"` shape and reject it with a 400 upstream. When a caller sends a native Anthropic request body through Bifrost with `{"type":"enabled","budget_tokens":N}`, it was being forwarded verbatim, causing failures. This PR rewrites `thinking.type` from `"enabled"` to `"adaptive"` and strips `budget_tokens` for these models on both the typed and raw passthrough paths, matching the behavior already implemented in the converted chat/responses paths.

Additionally, `stripUnsupportedAnthropicFields` in both the responses and chat builders is now gated on `capModel` (the canonical Anthropic model name) rather than `request.Model`, so Bifrost aliases correctly resolve to a model the capability predicates can match.

## Changes

- In `stripUnsupportedAnthropicFields`, when `thinking.type == "enabled"` and the model is adaptive-only, the type is rewritten to `"adaptive"` and `budget_tokens` is set to `nil`. The field is rewritten rather than deleted because Opus 4.7/4.8 default thinking to Off, so dropping it would silently disable thinking for callers who explicitly requested it.
- In `StripUnsupportedFieldsFromRawBody`, the same rewrite is applied to the raw JSON body path using `SetJSONField` and `DeleteJSONField`.
- Both the responses and chat request builders now pass `capModel` instead of `request.Model` to `stripUnsupportedAnthropicFields` so that Bifrost aliases do not bypass model-level capability checks.
- Tests covering both the raw body and typed request paths verify that adaptive-only models get the rewrite and that older models (Opus 4.6, Sonnet 4.5, Haiku 4.5, etc.) have their legacy shape preserved unchanged.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/anthropic/... -run TestAdaptiveOnlyThinkingStrip -v
go test ./...
```

Expected: all four sub-tests pass — adaptive-only models have `thinking.type` rewritten to `"adaptive"` with `budget_tokens` removed, and legacy models retain `thinking.type: "enabled"` with `budget_tokens: 10000` intact.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No auth, secrets, or PII implications. The change only rewrites a field in outbound request bodies before they are forwarded to the Anthropic API.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
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.

1 participant