fix(bedrock): send inference config (max_tokens, temperature) on Converse - #9889
Conversation
…erse The Bedrock provider built Converse and ConverseStream requests without calling .inference_config(...), so the configured max_tokens and temperature in ModelConfig were silently dropped on every call. Bedrock then applied its per-model server defaults, truncating responses to a small default max_tokens. The sibling Anthropic provider already sends these fields: max_tokens always (via ModelConfig::max_output_tokens) and temperature when the model supports it. This change adds a bedrock_inference_config helper that mirrors that behaviour and threads the result into both Converse and ConverseStream. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3249db6d37
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| let mut builder = | ||
| bedrock::InferenceConfiguration::builder().max_tokens(model_config.max_output_tokens()); |
There was a problem hiding this comment.
Normalize Bedrock Claude IDs before choosing maxTokens
For Bedrock Claude IDs that are in this provider but not present in the Bedrock canonical catalog, such as us.anthropic.claude-3-7-sonnet-20250219-v1:0, ModelConfig::max_output_tokens() falls back to 4096 unless the user explicitly set GOOSE_MAX_TOKENS. This new line then sends that fallback as maxTokens on every Converse request, so those known Claude models remain capped at 4k despite their Anthropic canonical output limit being much higher; the helper should use the same Bedrock-to-Anthropic normalization used below for temperature/thinking before defaulting the token cap.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
this seems worth fixing -- can we get them from the canonical registry?
Per review feedback (@DOsinga, codex): forwarding ModelConfig::max_output_tokens() pinned every model without a canonical-catalog entry (e.g. cross-region ids like us.anthropic.claude-...) to the generic 4096 fallback, capping models whose real output limit is much higher. Now max_tokens is sent only when the user explicitly sets it, mirroring how temperature is handled — unset values keep Bedrock's per-model server default. Updates tests accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the review @DOsinga, and good catch 🙏 — you and the codex bot are right. The problem: I considered resolving the real per-model output limit from the canonical registry (the same Instead I went with the minimal, correct fix that matches how |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ddc4485563
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if let Some(max_tokens) = model_config.max_tokens { | ||
| builder = builder.max_tokens(max_tokens); |
There was a problem hiding this comment.
Clamp thinking budget before sending maxTokens
When a Bedrock Anthropic model has thinking enabled and an explicit max_tokens is below the selected thinking budget (for example GOOSE_MAX_TOKENS=4096 with thinking_effort=high, whose budget is 16000), this newly sent maxTokens conflicts with the unchanged budget_tokens emitted by bedrock_anthropic_thinking_fields. The Anthropic formatter clamps the thinking budget against max_tokens because thinking tokens count against that cap; the Bedrock path should apply the same clamp before adding maxTokens so these requests do not fail validation or leave no room for an answer.
Useful? React with 👍 / 👎.
Per codex review on this PR: when an explicit max_tokens is below the selected thinking budget (e.g. GOOSE_MAX_TOKENS=4096 with thinking_effort=high, budget 16000), the budget_tokens emitted by bedrock_anthropic_thinking_fields would conflict with the maxTokens now sent by bedrock_inference_config. Thinking tokens count against the cap, so mirror the Anthropic formatter: clamp the budget to leave MIN_ANSWER_TOKENS of room, and drop thinking when even a minimal budget wouldn't fit. Only clamps when max_tokens is explicitly set (otherwise Bedrock applies its per-model default). Shares MIN_ANSWER_TOKENS with the Anthropic formatter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed Now that @DOsinga — to follow up on your earlier question about pulling the limit "from the canonical registry": you're right that's the cleaner direction, and I dug into it. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d5d9ad986
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| } else { | ||
| true |
There was a problem hiding this comment.
Respect Bedrock registry temperature support
When GOOSE_TEMPERATURE is set for the Bedrock OpenAI models advertised by this provider (openai.gpt-5.4 / openai.gpt-5.5), this fallback returns true and the new inference config forwards temperature. The bundled Bedrock canonical entries for those exact models mark temperature: false, so these reasoning-model requests can now be rejected instead of using the server default; the helper should consult the Bedrock canonical registry for non-Anthropic ids rather than allowing all of them.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 350bba9, @DOsinga.
bedrock_model_supports_temperature now keeps the existing Anthropic normalization path, and for non-Anthropic Bedrock ids consults the Bedrock canonical registry via maybe_get_canonical_model(BEDROCK_PROVIDER_NAME, &model_config.model_name).and_then(|model| model.temperature) before falling back to true only for unknown/missing registry data. I also added regression coverage for openai.gpt-5.4 with temperature set, asserting bedrock_inference_config omits it.
|
Thanks for the thorough work here, and for the detailed reply on the canonical-registry question — that explanation makes sense, a focused follow-up for the Bedrock token-limit normalization sounds right. One codex comment landed just after your last push and looks unaddressed: "Respect Bedrock registry temperature support". For |
DOsinga
left a comment
There was a problem hiding this comment.
Thanks for the careful, focused work here — and for addressing every codex point with a matching regression test. The final commit (350bba9) honors the Bedrock canonical registry for non-Anthropic temperature support exactly as discussed, with a test covering the openai.gpt-5.4 case. The fix is small, well-scoped, mirrors the existing Anthropic formatter, and the thinking-budget clamp against an explicit max_tokens is a nice touch. Approving.
Minor non-blocking nit for next time: test_bedrock_inference_config_omits_temperature_for_unsupported_model branches on if supported { ... } else { ... }, so it passes regardless of the actual outcome — the registry-backed test already covers the real behavior, so this one isn't adding much.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6948009662
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| if let Some(temperature) = model_config.temperature { | ||
| if bedrock_model_supports_temperature(model_config) { | ||
| builder = builder.temperature(temperature); |
There was a problem hiding this comment.
Omit temperature when Bedrock thinking is enabled
When a Bedrock Anthropic request has thinking enabled (for example Claude Sonnet 4.5 with GOOSE_THINKING_EFFORT=low) and GOOSE_TEMPERATURE is also set, this branch still adds temperature while bedrock_anthropic_thinking_fields() adds the thinking block. AWS documents Bedrock Claude thinking as incompatible with temperature modifications, so this turns requests that previously succeeded by omitting temperature into ValidationExceptions; skip temperature whenever Bedrock thinking fields will be emitted.
Useful? React with 👍 / 👎.
* main: (26 commits) Fix MCP app sandbox bridge lifecycle (#10064) fix(bedrock): send inference config (max_tokens, temperature) on Converse (#9889) feat(providers): support OpenRouter request parameters (#9276) Migrate local inference model management to ACP (#10124) (attempt to) fix disk space errors in linux release builds (#10024) feat: add --edit session flag to edit conversation before forking (#9799) feat: add iFlytek Spark and Astron MaaS providers (#9837) fix(desktop): dedupe Nostr session deep link imports (#9918) [codex] Add SessionStart hook parity outside CLI (#9970) feat(providers): add Fireworks AI declarative provider (#9990) fix(providers): don't retry deterministically-permanent 400s (thinking-block immutability) (#10005) fix(deps): downgrade pkcs8 to v0.10 to match sec1/pkcs1 v0.7 (#10119) chore(deps): bump actions/cache from 5.0.2 to 6.0.0 (#10051) Make OpenAI Responses API store param configurable (#10040) remove unsupported model (#10121) chore(release): bump version to 1.40.0 (minor) (#10099) move ollama provider into goose-providers (#9986) UI acp migratoin: Decouple desktop UI types from generated OpenAPI types (#10109) fix(otel): use async reqwest client so OTLP export works in `goose serve` mode (#10100) feat (acp): exposed available tools in acp schema (#10097) ...
* main: (42 commits) Fix MCP app sandbox bridge lifecycle (#10064) fix(bedrock): send inference config (max_tokens, temperature) on Converse (#9889) feat(providers): support OpenRouter request parameters (#9276) Migrate local inference model management to ACP (#10124) (attempt to) fix disk space errors in linux release builds (#10024) feat: add --edit session flag to edit conversation before forking (#9799) feat: add iFlytek Spark and Astron MaaS providers (#9837) fix(desktop): dedupe Nostr session deep link imports (#9918) [codex] Add SessionStart hook parity outside CLI (#9970) feat(providers): add Fireworks AI declarative provider (#9990) fix(providers): don't retry deterministically-permanent 400s (thinking-block immutability) (#10005) fix(deps): downgrade pkcs8 to v0.10 to match sec1/pkcs1 v0.7 (#10119) chore(deps): bump actions/cache from 5.0.2 to 6.0.0 (#10051) Make OpenAI Responses API store param configurable (#10040) remove unsupported model (#10121) chore(release): bump version to 1.40.0 (minor) (#10099) move ollama provider into goose-providers (#9986) UI acp migratoin: Decouple desktop UI types from generated OpenAPI types (#10109) fix(otel): use async reqwest client so OTLP export works in `goose serve` mode (#10100) feat (acp): exposed available tools in acp schema (#10097) ...
* main: (31 commits) test: generic validator for declarative providers (#10010) UI acp migratoin: Decouple desktop UI types from generated OpenAPI types (Part 2) (#10149) Remove MCP sampling support (#10087) Support TLS for ACP serve (#10088) feat (ui): Migrate dictation local model manager to ACP (#10131) Fix MCP app sandbox bridge lifecycle (#10064) fix(bedrock): send inference config (max_tokens, temperature) on Converse (#9889) feat(providers): support OpenRouter request parameters (#9276) Migrate local inference model management to ACP (#10124) (attempt to) fix disk space errors in linux release builds (#10024) feat: add --edit session flag to edit conversation before forking (#9799) feat: add iFlytek Spark and Astron MaaS providers (#9837) fix(desktop): dedupe Nostr session deep link imports (#9918) [codex] Add SessionStart hook parity outside CLI (#9970) feat(providers): add Fireworks AI declarative provider (#9990) fix(providers): don't retry deterministically-permanent 400s (thinking-block immutability) (#10005) fix(deps): downgrade pkcs8 to v0.10 to match sec1/pkcs1 v0.7 (#10119) chore(deps): bump actions/cache from 5.0.2 to 6.0.0 (#10051) Make OpenAI Responses API store param configurable (#10040) remove unsupported model (#10121) ...
Thanks for goose, and for the AWS Bedrock provider in particular — it is a pleasure to use. This is a small, focused fix for a bug I hit while using Bedrock.
Closes #9888
Problem
The Bedrock provider builds its
ConverseandConverseStreamrequests without aninferenceConfig, so themax_tokensandtemperaturefromModelConfigare silently dropped on every Bedrock call. Bedrock then applies its per-model server defaults, truncating responses to a small defaultmaxTokens.crates/goose/src/providers/bedrock.rs— bothconverse(...)andconverse_stream(...)setsystem/model_id/messages(+ thinking fields, tool config) but never call.inference_config(...).Cause (sibling parity)
The Anthropic provider already sends these fields (
crates/goose/src/providers/formats/anthropic.rs):max_tokensalways (viaModelConfig::max_output_tokens()) andtemperaturewhenmodel_supports_temperature(...). The Bedrock provider was simply missing the equivalentInferenceConfiguration. This PR fills in that one missing piece rather than introducing new behaviour.Change
bedrock_inference_config(&ModelConfig)inproviders/formats/bedrock.rsthat buildsInferenceConfiguration:max_tokensis always set viamax_output_tokens()(matches the Anthropic provider; prevents silent truncation).temperatureis set only when configured and the model supports it. Support is resolved against the Anthropic canonical registry foranthropic.*ids using the same model-name mapping already used by the thinking logic (strip_bedrock_version_suffix+ Anthropic-mappedModelConfig); other models default to allowing it, matchingmodel_supports_temperature.ConverseRequestPartsand call.inference_config(...)on both theConverseandConverseStreambuilders.Minimal diff: +97 / -6 across 2 files.
Before / After
max_tokenssent to BedrockConverse/ConverseStreammax_output_tokens())temperaturesent to Bedrocktemperaturefor a model that rejects itinference_configadded)ConverseStream)BEDROCK_DISABLE_STREAMINGescape hatch (stream_via_converse)converse)Tests
Added 3 unit tests in
providers/formats/bedrock.rs(no AWS credentials needed — they assert on the builtInferenceConfiguration).Red/green proof that the tests actually catch the bug. Simulating the old behaviour (helper returns an empty
InferenceConfiguration, i.e. nomax_tokens):With the fix in place:
Local gates (feature
aws-providers, toolchain 1.92):Notes / scope
additional_model_request_fieldsand is being addressed elsewhere (e.g. fix: pass thinking config to Bedrock Anthropic models #9794, fix(anthropic): use adaptive thinking for Claude 4.6+ served outside the canonical registry #9840). This PR is scoped to the missinginferenceConfigonly.This contribution was prepared with the help of an AI agent (Claude Code); I reviewed the change, the reasoning, and the test results myself before submitting.