bedrock + anthropic patches for adaptive thinking - #5821
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (18)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR updates Anthropic reasoning validation to wrap errors with a sentinel for HTTP 400 classification. It separates tool-search from advanced tool-use support with separate beta headers and gating. It adds Bedrock Nova tool conversion, tracks dropped tools in request context, and exposes them in responses. It also preserves Bedrock reasoning content-block signatures and emits streaming signatures as separate events. ChangesAnthropic Reasoning Error Handling and Streaming Fixes
Anthropic Tool-Search Beta Header Split
Bedrock Nova Tool Support and Dropped-Tools Tracking
Bedrock Reasoning Signature Handling
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant AnthropicRequestBuilder
participant AnthropicConverter
participant BifrostResponse
Client->>AnthropicRequestBuilder: Submit request
AnthropicRequestBuilder->>AnthropicConverter: Convert request
AnthropicConverter-->>AnthropicRequestBuilder: Return wrapped reasoning budget error
AnthropicRequestBuilder->>BifrostResponse: Create HTTP 400 error
BifrostResponse-->>Client: Return validation error
sequenceDiagram
participant Client
participant BedrockProvider
participant ToolConverter
participant BifrostContext
participant BifrostResponse
Client->>BedrockProvider: Submit request with tools
BedrockProvider->>ToolConverter: Convert and filter tools
ToolConverter->>BifrostContext: Store dropped tool names
BedrockProvider->>BifrostContext: Read dropped tool names
BedrockProvider->>BifrostResponse: Add dropped tools to response
BifrostResponse-->>Client: Return response metadata
Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/bedrock/invoke.go`:
- Around line 1230-1255: Update the reasoning conversion around the
Content.ContentBlocks loop to track whether a non-empty reasoning block was
emitted. If no thinking block was appended, fall back to iterating
item.ResponsesReasoning.Summary and preserve its non-empty entries; retain the
existing block output when usable reasoning content exists. Add a regression
case covering non-reasoning or empty content blocks with a populated summary.
In `@core/providers/bedrock/responses.go`:
- Around line 2512-2526: Update the tool-type condition in the Nova conversion
loop around convertToNovaSystemTool to handle both ResponsesToolTypeWebSearch
and ResponsesToolTypeWebSearchPreview, mapping either to
BedrockSystemToolNovaGrounding. Preserve the existing code-interpreter mapping
and modelDroppedTools behavior, and add Nova2 and non-Nova2 coverage for
web_search_preview.
In `@core/providers/bedrock/utils.go`:
- Line 264: Use the resolved canonical model variable capModel, rather than
bifrostReq.Model, when calling convertToolConfigFromFiltered in the Chat path so
Nova2 aliases trigger the correct capability handling; update the related
IsNova2Model check context as needed and add a regression test covering an alias
that resolves to Nova2 and preserves the required web_search or code_execution
system tool.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: b636d8b9-d59b-4ce0-a56a-5da785fb55a6
📒 Files selected for processing (18)
core/providers/anthropic/chat.gocore/providers/anthropic/messagestartusage_test.gocore/providers/anthropic/requestbuilder.gocore/providers/anthropic/requestbuilder_test.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/providers/anthropic/utils_test.gocore/providers/anthropic/validatechattools_test.gocore/providers/bedrock/bedrock.gocore/providers/bedrock/convert_tool_config_test.gocore/providers/bedrock/invoke.gocore/providers/bedrock/invoke_test.gocore/providers/bedrock/responses.gocore/providers/bedrock/types.gocore/providers/bedrock/utils.gocore/providers/utils/utils.gocore/schemas/bifrost.go
efd2b41 to
bce1722
Compare
bce1722 to
d9e928e
Compare
55d2790 to
d506a89
Compare
d9e928e to
e2a7d8b
Compare
d506a89 to
2bfd9ce
Compare
2bfd9ce to
709c216
Compare
e2a7d8b to
c97b720
Compare
Merge activity
|
The base branch was changed.
c97b720 to
e58ed94
Compare
## Summary Fixes #5885. `@ai-sdk/anthropic` (versions 4.0.6–4.0.32) marks `message.usage` and `message.usage.input_tokens` as required fields in its `message_start` schema, while `id`, `model`, and `role` are nullable. When Bifrost omitted the `usage` key entirely on `message_start` frames — which happened for every non-Anthropic provider, including Bedrock Converse, because usage figures aren't available until the terminal event — the SDK aborted the stream before the first token reached the client. The fix emits an all-zero `usage` object on `message_start` when no real figures are available. Zeros are chosen over Anthropic's own `output_tokens: 1` placeholder because Bedrock's `message_delta` carries absolute totals rather than cumulative deltas; a client summing both frames would over-count by one. Zero is neutral under both interpretations, and Bifrost's passthrough accumulator uses max-merge, so a zero can never displace a real figure later in the stream. ## Changes - `responses.go`: The `else` branch that previously left `messageUsage` nil now populates an all-zero `AnthropicUsage` struct, ensuring the `usage` key is always present on the wire regardless of `omitempty`. - `invoke.go`: `message_start` frames built for the Bedrock `/invoke-with-response-stream` path now include a `usage` map with zero placeholders by default. When `resp.Response.Usage` is non-nil, real figures replace the zeros and cache token fields are conditionally included. - Tests updated to assert that `usage` is present and zero when unknown, and that real figures are preferred when available. A second test marshals the struct to raw JSON and validates the wire representation directly, since the original `omitempty` bug was invisible to struct-level nil checks. ## 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/... ./core/providers/bedrock/... ``` Validate that streaming through a Bedrock-backed model with an `@ai-sdk/anthropic` client no longer aborts on the first frame. The `message_start` event should contain `message.usage.input_tokens = 0` and `message.usage.output_tokens = 0`, with authoritative totals arriving on `message_delta`. ## Breaking changes - [x] No ## Related issues Closes #5885 See also #5821 (the prior fix that introduced the omission) ## Security considerations None. ## 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
## Summary Fixes #5885. `@ai-sdk/anthropic` (versions 4.0.6–4.0.32) marks `message.usage` and `message.usage.input_tokens` as required fields in its `message_start` schema, while `id`, `model`, and `role` are nullable. When Bifrost omitted the `usage` key entirely on `message_start` frames — which happened for every non-Anthropic provider, including Bedrock Converse, because usage figures aren't available until the terminal event — the SDK aborted the stream before the first token reached the client. The fix emits an all-zero `usage` object on `message_start` when no real figures are available. Zeros are chosen over Anthropic's own `output_tokens: 1` placeholder because Bedrock's `message_delta` carries absolute totals rather than cumulative deltas; a client summing both frames would over-count by one. Zero is neutral under both interpretations, and Bifrost's passthrough accumulator uses max-merge, so a zero can never displace a real figure later in the stream. ## Changes - `responses.go`: The `else` branch that previously left `messageUsage` nil now populates an all-zero `AnthropicUsage` struct, ensuring the `usage` key is always present on the wire regardless of `omitempty`. - `invoke.go`: `message_start` frames built for the Bedrock `/invoke-with-response-stream` path now include a `usage` map with zero placeholders by default. When `resp.Response.Usage` is non-nil, real figures replace the zeros and cache token fields are conditionally included. - Tests updated to assert that `usage` is present and zero when unknown, and that real figures are preferred when available. A second test marshals the struct to raw JSON and validates the wire representation directly, since the original `omitempty` bug was invisible to struct-level nil checks. ## 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/... ./core/providers/bedrock/... ``` Validate that streaming through a Bedrock-backed model with an `@ai-sdk/anthropic` client no longer aborts on the first frame. The `message_start` event should contain `message.usage.input_tokens = 0` and `message.usage.output_tokens = 0`, with authoritative totals arriving on `message_delta`. ## Breaking changes - [x] No ## Related issues Closes #5885 See also #5821 (the prior fix that introduced the omission) ## Security considerations None. ## 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
## Summary This PR fixes several correctness bugs across the Anthropic and Bedrock providers: reasoning/thinking `max_tokens` validation errors now surface as HTTP 400 instead of 500; `tool.defer_loading` is re-gated on its own `tool-search-tool-2025-10-19` beta header rather than the `advanced-tool-use` bundle; Bedrock's classic Converse path no longer claims to support `tool_search_tool_*` (which AWS restricts to InvokeModel only); Nova2 web search and code execution tools are now correctly handled on the chat completions path (not just the Responses path); reasoning signatures are preserved through the `/invoke` egress path; and `message_start` streaming events no longer fabricate all-zero usage when the upstream hasn't reported it yet. Closes maximhq#5638 ## Changes - **Reasoning** **`max_tokens`** **→ 400 not 500**: Introduced `ErrReasoningMaxTokensTooLow` sentinel error, wrapped at every reasoning-budget validation site in `chat.go` and `responses.go`. `requestbuilder.go` now detects it via `errors.Is` and calls `NewBifrostBadRequestError` (new helper, sets `StatusCode=400`) instead of the generic operation error that defaulted to 500. - **`defer_loading`** **beta header split**: `tool.defer_loading` now requires `tool-search-tool-2025-10-19` (`AnthropicToolSearchBetaHeader`), not the `advanced-tool-use-2025-11-20` bundle. `ProviderFeatureSupport.ToolSearch` gates it; `AdvancedToolUse` is narrowed to `allowed_callers` only. `stripUnsupportedAnthropicFields`, `StripUnsupportedFieldsFromRawBody`, `AddMissingBetaHeadersToContext`, and `betaHeaderPrefixToFeature` are all updated consistently. - **Bedrock drops** **`ToolSearch`**: `ProviderFeatures[schemas.Bedrock].ToolSearch` is set to `false` because AWS restricts `tool-search-tool-2025-10-19` to InvokeModel/InvokeModelWithResponseStream; Bifrost's Bedrock provider always dispatches tool-bearing requests via Converse. `convertAnthropicTools` in `invoke.go` now explicitly skips `tool_search_tool_*` entries rather than building a broken schema-less function tool. - **Nova2 web search/code execution on chat completions path**: `ValidateChatToolsForProvider`'s prefix map now ORs in `WebSearchNova`/`CodeExecNova` for `web_search_`/`code_execution_` prefixes, matching the Responses-path validator. `convertToolConfigFromFiltered` gains a new branch that converts these to `nova_grounding`/`nova_code_interpreter` system tools for Nova2 models and records them as model-dropped for non-Nova2 models. A shared `convertToNovaSystemTool` helper is extracted so both the Chat and Responses builders use one rule. - **Dropped tools surfaced on response**: `BifrostContextKeyDroppedUnsupportedTools` context key and `BifrostResponseExtraFields.DroppedUnsupportedTools` field are added. Both `ChatCompletion` and `Responses` in `bedrock.go` call a new `applyDroppedUnsupportedTools` helper that reads the context value and logs a warning. - **Reasoning signature through** **`/invoke`**: `toBedrockInvokeAnthropicResponse` now reads `item.Content.ContentBlocks` first (where Bedrock-originated reasoning lives) and carries the `Signature` field through to `BedrockInvokeMessagesContentBlock` (new field). The streaming path emits a `signature_delta` event type instead of nesting the signature inside `thinking_delta`, matching Anthropic's documented streaming contract. - **`message_start`** **usage omission**: `ToAnthropicResponsesStreamResponse` no longer fabricates an all-zero `AnthropicUsage` when `bifrostResp.Response.Usage` is nil. The field is omitted entirely, which Anthropic's streaming spec permits and which avoids misrepresenting cost telemetry to clients (e.g. Claude Code) that read `input_tokens` from `message_start`. - **`collectBedrockServerToolsFromFiltered`**: Skips `web_search_`/`code_execution_` prefixes to avoid sending Bedrock two conflicting representations of the same Nova system tool. ## 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/... ./core/providers/bedrock/... ./core/providers/utils/... ``` Key scenarios to validate: - Send a request with `reasoning.effort=high` and `max_tokens=500` to an Anthropic model — expect a `400` response, not `500`. - Send a request with `tool.defer_loading=true` to Vertex — expect `tool-search-tool-2025-10-19` in the beta headers, not `advanced-tool-use-2025-11-20`. - Send a `tool_search_tool_*` tool to classic Bedrock — expect it to be silently dropped, not forwarded as a broken function tool. - Send `web_search`/`code_execution` tools to a Nova2 Bedrock model via chat completions — expect `nova_grounding`/`nova_code_interpreter` system tools in the Converse request. - Verify `ExtraFields.DroppedUnsupportedTools` is populated when tools are dropped on Bedrock. - Stream a reasoning response through `/invoke` — expect a `signature_delta` event carrying the signature, and the thinking text present in the non-streaming response. ## Breaking changes - [x] No `DroppedUnsupportedTools` is a new additive field. The `defer_loading` beta header change is a correctness fix; clients that were relying on `advanced-tool-use` to gate `defer_loading` on Bedrock were already broken (Bedrock doesn't support it). The `message_start` usage omission is spec-compliant and only removes fabricated zeros. ## Security considerations None. No auth, secrets, PII, or sandboxing changes. ## 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
…mhq#5886) ## Summary Fixes maximhq#5885. `@ai-sdk/anthropic` (versions 4.0.6–4.0.32) marks `message.usage` and `message.usage.input_tokens` as required fields in its `message_start` schema, while `id`, `model`, and `role` are nullable. When Bifrost omitted the `usage` key entirely on `message_start` frames — which happened for every non-Anthropic provider, including Bedrock Converse, because usage figures aren't available until the terminal event — the SDK aborted the stream before the first token reached the client. The fix emits an all-zero `usage` object on `message_start` when no real figures are available. Zeros are chosen over Anthropic's own `output_tokens: 1` placeholder because Bedrock's `message_delta` carries absolute totals rather than cumulative deltas; a client summing both frames would over-count by one. Zero is neutral under both interpretations, and Bifrost's passthrough accumulator uses max-merge, so a zero can never displace a real figure later in the stream. ## Changes - `responses.go`: The `else` branch that previously left `messageUsage` nil now populates an all-zero `AnthropicUsage` struct, ensuring the `usage` key is always present on the wire regardless of `omitempty`. - `invoke.go`: `message_start` frames built for the Bedrock `/invoke-with-response-stream` path now include a `usage` map with zero placeholders by default. When `resp.Response.Usage` is non-nil, real figures replace the zeros and cache token fields are conditionally included. - Tests updated to assert that `usage` is present and zero when unknown, and that real figures are preferred when available. A second test marshals the struct to raw JSON and validates the wire representation directly, since the original `omitempty` bug was invisible to struct-level nil checks. ## 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/... ./core/providers/bedrock/... ``` Validate that streaming through a Bedrock-backed model with an `@ai-sdk/anthropic` client no longer aborts on the first frame. The `message_start` event should contain `message.usage.input_tokens = 0` and `message.usage.output_tokens = 0`, with authoritative totals arriving on `message_delta`. ## Breaking changes - [x] No ## Related issues Closes maximhq#5885 See also maximhq#5821 (the prior fix that introduced the omission) ## Security considerations None. ## 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
This PR fixes several correctness bugs across the Anthropic and Bedrock providers: reasoning/thinking `max_tokens` validation errors now surface as HTTP 400 instead of 500; `tool.defer_loading` is re-gated on its own `tool-search-tool-2025-10-19` beta header rather than the `advanced-tool-use` bundle; Bedrock's classic Converse path no longer claims to support `tool_search_tool_*` (which AWS restricts to InvokeModel only); Nova2 web search and code execution tools are now correctly handled on the chat completions path (not just the Responses path); reasoning signatures are preserved through the `/invoke` egress path; and `message_start` streaming events no longer fabricate all-zero usage when the upstream hasn't reported it yet. Closes maximhq#5638 - **Reasoning** **`max_tokens`** **→ 400 not 500**: Introduced `ErrReasoningMaxTokensTooLow` sentinel error, wrapped at every reasoning-budget validation site in `chat.go` and `responses.go`. `requestbuilder.go` now detects it via `errors.Is` and calls `NewBifrostBadRequestError` (new helper, sets `StatusCode=400`) instead of the generic operation error that defaulted to 500. - **`defer_loading`** **beta header split**: `tool.defer_loading` now requires `tool-search-tool-2025-10-19` (`AnthropicToolSearchBetaHeader`), not the `advanced-tool-use-2025-11-20` bundle. `ProviderFeatureSupport.ToolSearch` gates it; `AdvancedToolUse` is narrowed to `allowed_callers` only. `stripUnsupportedAnthropicFields`, `StripUnsupportedFieldsFromRawBody`, `AddMissingBetaHeadersToContext`, and `betaHeaderPrefixToFeature` are all updated consistently. - **Bedrock drops** **`ToolSearch`**: `ProviderFeatures[schemas.Bedrock].ToolSearch` is set to `false` because AWS restricts `tool-search-tool-2025-10-19` to InvokeModel/InvokeModelWithResponseStream; Bifrost's Bedrock provider always dispatches tool-bearing requests via Converse. `convertAnthropicTools` in `invoke.go` now explicitly skips `tool_search_tool_*` entries rather than building a broken schema-less function tool. - **Nova2 web search/code execution on chat completions path**: `ValidateChatToolsForProvider`'s prefix map now ORs in `WebSearchNova`/`CodeExecNova` for `web_search_`/`code_execution_` prefixes, matching the Responses-path validator. `convertToolConfigFromFiltered` gains a new branch that converts these to `nova_grounding`/`nova_code_interpreter` system tools for Nova2 models and records them as model-dropped for non-Nova2 models. A shared `convertToNovaSystemTool` helper is extracted so both the Chat and Responses builders use one rule. - **Dropped tools surfaced on response**: `BifrostContextKeyDroppedUnsupportedTools` context key and `BifrostResponseExtraFields.DroppedUnsupportedTools` field are added. Both `ChatCompletion` and `Responses` in `bedrock.go` call a new `applyDroppedUnsupportedTools` helper that reads the context value and logs a warning. - **Reasoning signature through** **`/invoke`**: `toBedrockInvokeAnthropicResponse` now reads `item.Content.ContentBlocks` first (where Bedrock-originated reasoning lives) and carries the `Signature` field through to `BedrockInvokeMessagesContentBlock` (new field). The streaming path emits a `signature_delta` event type instead of nesting the signature inside `thinking_delta`, matching Anthropic's documented streaming contract. - **`message_start`** **usage omission**: `ToAnthropicResponsesStreamResponse` no longer fabricates an all-zero `AnthropicUsage` when `bifrostResp.Response.Usage` is nil. The field is omitted entirely, which Anthropic's streaming spec permits and which avoids misrepresenting cost telemetry to clients (e.g. Claude Code) that read `input_tokens` from `message_start`. - **`collectBedrockServerToolsFromFiltered`**: Skips `web_search_`/`code_execution_` prefixes to avoid sending Bedrock two conflicting representations of the same Nova system tool. - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI - [x] Core (Go) - [ ] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ```sh go test ./core/providers/anthropic/... ./core/providers/bedrock/... ./core/providers/utils/... ``` Key scenarios to validate: - Send a request with `reasoning.effort=high` and `max_tokens=500` to an Anthropic model — expect a `400` response, not `500`. - Send a request with `tool.defer_loading=true` to Vertex — expect `tool-search-tool-2025-10-19` in the beta headers, not `advanced-tool-use-2025-11-20`. - Send a `tool_search_tool_*` tool to classic Bedrock — expect it to be silently dropped, not forwarded as a broken function tool. - Send `web_search`/`code_execution` tools to a Nova2 Bedrock model via chat completions — expect `nova_grounding`/`nova_code_interpreter` system tools in the Converse request. - Verify `ExtraFields.DroppedUnsupportedTools` is populated when tools are dropped on Bedrock. - Stream a reasoning response through `/invoke` — expect a `signature_delta` event carrying the signature, and the thinking text present in the non-streaming response. - [x] No `DroppedUnsupportedTools` is a new additive field. The `defer_loading` beta header change is a correctness fix; clients that were relying on `advanced-tool-use` to gate `defer_loading` on Bedrock were already broken (Bedrock doesn't support it). The `message_start` usage omission is spec-compliant and only removes fabricated zeros. None. No auth, secrets, PII, or sandboxing changes. - [ ] 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
This PR fixes several correctness bugs across the Anthropic and Bedrock providers: reasoning/thinking `max_tokens` validation errors now surface as HTTP 400 instead of 500; `tool.defer_loading` is re-gated on its own `tool-search-tool-2025-10-19` beta header rather than the `advanced-tool-use` bundle; Bedrock's classic Converse path no longer claims to support `tool_search_tool_*` (which AWS restricts to InvokeModel only); Nova2 web search and code execution tools are now correctly handled on the chat completions path (not just the Responses path); reasoning signatures are preserved through the `/invoke` egress path; and `message_start` streaming events no longer fabricate all-zero usage when the upstream hasn't reported it yet. Closes maximhq#5638 - **Reasoning** **`max_tokens`** **→ 400 not 500**: Introduced `ErrReasoningMaxTokensTooLow` sentinel error, wrapped at every reasoning-budget validation site in `chat.go` and `responses.go`. `requestbuilder.go` now detects it via `errors.Is` and calls `NewBifrostBadRequestError` (new helper, sets `StatusCode=400`) instead of the generic operation error that defaulted to 500. - **`defer_loading`** **beta header split**: `tool.defer_loading` now requires `tool-search-tool-2025-10-19` (`AnthropicToolSearchBetaHeader`), not the `advanced-tool-use-2025-11-20` bundle. `ProviderFeatureSupport.ToolSearch` gates it; `AdvancedToolUse` is narrowed to `allowed_callers` only. `stripUnsupportedAnthropicFields`, `StripUnsupportedFieldsFromRawBody`, `AddMissingBetaHeadersToContext`, and `betaHeaderPrefixToFeature` are all updated consistently. - **Bedrock drops** **`ToolSearch`**: `ProviderFeatures[schemas.Bedrock].ToolSearch` is set to `false` because AWS restricts `tool-search-tool-2025-10-19` to InvokeModel/InvokeModelWithResponseStream; Bifrost's Bedrock provider always dispatches tool-bearing requests via Converse. `convertAnthropicTools` in `invoke.go` now explicitly skips `tool_search_tool_*` entries rather than building a broken schema-less function tool. - **Nova2 web search/code execution on chat completions path**: `ValidateChatToolsForProvider`'s prefix map now ORs in `WebSearchNova`/`CodeExecNova` for `web_search_`/`code_execution_` prefixes, matching the Responses-path validator. `convertToolConfigFromFiltered` gains a new branch that converts these to `nova_grounding`/`nova_code_interpreter` system tools for Nova2 models and records them as model-dropped for non-Nova2 models. A shared `convertToNovaSystemTool` helper is extracted so both the Chat and Responses builders use one rule. - **Dropped tools surfaced on response**: `BifrostContextKeyDroppedUnsupportedTools` context key and `BifrostResponseExtraFields.DroppedUnsupportedTools` field are added. Both `ChatCompletion` and `Responses` in `bedrock.go` call a new `applyDroppedUnsupportedTools` helper that reads the context value and logs a warning. - **Reasoning signature through** **`/invoke`**: `toBedrockInvokeAnthropicResponse` now reads `item.Content.ContentBlocks` first (where Bedrock-originated reasoning lives) and carries the `Signature` field through to `BedrockInvokeMessagesContentBlock` (new field). The streaming path emits a `signature_delta` event type instead of nesting the signature inside `thinking_delta`, matching Anthropic's documented streaming contract. - **`message_start`** **usage omission**: `ToAnthropicResponsesStreamResponse` no longer fabricates an all-zero `AnthropicUsage` when `bifrostResp.Response.Usage` is nil. The field is omitted entirely, which Anthropic's streaming spec permits and which avoids misrepresenting cost telemetry to clients (e.g. Claude Code) that read `input_tokens` from `message_start`. - **`collectBedrockServerToolsFromFiltered`**: Skips `web_search_`/`code_execution_` prefixes to avoid sending Bedrock two conflicting representations of the same Nova system tool. - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI - [x] Core (Go) - [ ] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ```sh go test ./core/providers/anthropic/... ./core/providers/bedrock/... ./core/providers/utils/... ``` Key scenarios to validate: - Send a request with `reasoning.effort=high` and `max_tokens=500` to an Anthropic model — expect a `400` response, not `500`. - Send a request with `tool.defer_loading=true` to Vertex — expect `tool-search-tool-2025-10-19` in the beta headers, not `advanced-tool-use-2025-11-20`. - Send a `tool_search_tool_*` tool to classic Bedrock — expect it to be silently dropped, not forwarded as a broken function tool. - Send `web_search`/`code_execution` tools to a Nova2 Bedrock model via chat completions — expect `nova_grounding`/`nova_code_interpreter` system tools in the Converse request. - Verify `ExtraFields.DroppedUnsupportedTools` is populated when tools are dropped on Bedrock. - Stream a reasoning response through `/invoke` — expect a `signature_delta` event carrying the signature, and the thinking text present in the non-streaming response. - [x] No `DroppedUnsupportedTools` is a new additive field. The `defer_loading` beta header change is a correctness fix; clients that were relying on `advanced-tool-use` to gate `defer_loading` on Bedrock were already broken (Bedrock doesn't support it). The `message_start` usage omission is spec-compliant and only removes fabricated zeros. None. No auth, secrets, PII, or sandboxing changes. - [ ] 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
## Summary This PR fixes several correctness bugs across the Anthropic and Bedrock providers: reasoning/thinking `max_tokens` validation errors now surface as HTTP 400 instead of 500; `tool.defer_loading` is re-gated on its own `tool-search-tool-2025-10-19` beta header rather than the `advanced-tool-use` bundle; Bedrock's classic Converse path no longer claims to support `tool_search_tool_*` (which AWS restricts to InvokeModel only); Nova2 web search and code execution tools are now correctly handled on the chat completions path (not just the Responses path); reasoning signatures are preserved through the `/invoke` egress path; and `message_start` streaming events no longer fabricate all-zero usage when the upstream hasn't reported it yet. Closes maximhq#5638 ## Changes - **Reasoning** **`max_tokens`** **→ 400 not 500**: Introduced `ErrReasoningMaxTokensTooLow` sentinel error, wrapped at every reasoning-budget validation site in `chat.go` and `responses.go`. `requestbuilder.go` now detects it via `errors.Is` and calls `NewBifrostBadRequestError` (new helper, sets `StatusCode=400`) instead of the generic operation error that defaulted to 500. - **`defer_loading`** **beta header split**: `tool.defer_loading` now requires `tool-search-tool-2025-10-19` (`AnthropicToolSearchBetaHeader`), not the `advanced-tool-use-2025-11-20` bundle. `ProviderFeatureSupport.ToolSearch` gates it; `AdvancedToolUse` is narrowed to `allowed_callers` only. `stripUnsupportedAnthropicFields`, `StripUnsupportedFieldsFromRawBody`, `AddMissingBetaHeadersToContext`, and `betaHeaderPrefixToFeature` are all updated consistently. - **Bedrock drops** **`ToolSearch`**: `ProviderFeatures[schemas.Bedrock].ToolSearch` is set to `false` because AWS restricts `tool-search-tool-2025-10-19` to InvokeModel/InvokeModelWithResponseStream; Bifrost's Bedrock provider always dispatches tool-bearing requests via Converse. `convertAnthropicTools` in `invoke.go` now explicitly skips `tool_search_tool_*` entries rather than building a broken schema-less function tool. - **Nova2 web search/code execution on chat completions path**: `ValidateChatToolsForProvider`'s prefix map now ORs in `WebSearchNova`/`CodeExecNova` for `web_search_`/`code_execution_` prefixes, matching the Responses-path validator. `convertToolConfigFromFiltered` gains a new branch that converts these to `nova_grounding`/`nova_code_interpreter` system tools for Nova2 models and records them as model-dropped for non-Nova2 models. A shared `convertToNovaSystemTool` helper is extracted so both the Chat and Responses builders use one rule. - **Dropped tools surfaced on response**: `BifrostContextKeyDroppedUnsupportedTools` context key and `BifrostResponseExtraFields.DroppedUnsupportedTools` field are added. Both `ChatCompletion` and `Responses` in `bedrock.go` call a new `applyDroppedUnsupportedTools` helper that reads the context value and logs a warning. - **Reasoning signature through** **`/invoke`**: `toBedrockInvokeAnthropicResponse` now reads `item.Content.ContentBlocks` first (where Bedrock-originated reasoning lives) and carries the `Signature` field through to `BedrockInvokeMessagesContentBlock` (new field). The streaming path emits a `signature_delta` event type instead of nesting the signature inside `thinking_delta`, matching Anthropic's documented streaming contract. - **`message_start`** **usage omission**: `ToAnthropicResponsesStreamResponse` no longer fabricates an all-zero `AnthropicUsage` when `bifrostResp.Response.Usage` is nil. The field is omitted entirely, which Anthropic's streaming spec permits and which avoids misrepresenting cost telemetry to clients (e.g. Claude Code) that read `input_tokens` from `message_start`. - **`collectBedrockServerToolsFromFiltered`**: Skips `web_search_`/`code_execution_` prefixes to avoid sending Bedrock two conflicting representations of the same Nova system tool. ## 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/... ./core/providers/bedrock/... ./core/providers/utils/... ``` Key scenarios to validate: - Send a request with `reasoning.effort=high` and `max_tokens=500` to an Anthropic model — expect a `400` response, not `500`. - Send a request with `tool.defer_loading=true` to Vertex — expect `tool-search-tool-2025-10-19` in the beta headers, not `advanced-tool-use-2025-11-20`. - Send a `tool_search_tool_*` tool to classic Bedrock — expect it to be silently dropped, not forwarded as a broken function tool. - Send `web_search`/`code_execution` tools to a Nova2 Bedrock model via chat completions — expect `nova_grounding`/`nova_code_interpreter` system tools in the Converse request. - Verify `ExtraFields.DroppedUnsupportedTools` is populated when tools are dropped on Bedrock. - Stream a reasoning response through `/invoke` — expect a `signature_delta` event carrying the signature, and the thinking text present in the non-streaming response. ## Breaking changes - [x] No `DroppedUnsupportedTools` is a new additive field. The `defer_loading` beta header change is a correctness fix; clients that were relying on `advanced-tool-use` to gate `defer_loading` on Bedrock were already broken (Bedrock doesn't support it). The `message_start` usage omission is spec-compliant and only removes fabricated zeros. ## Security considerations None. No auth, secrets, PII, or sandboxing changes. ## 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
…mhq#5886) ## Summary Fixes maximhq#5885. `@ai-sdk/anthropic` (versions 4.0.6–4.0.32) marks `message.usage` and `message.usage.input_tokens` as required fields in its `message_start` schema, while `id`, `model`, and `role` are nullable. When Bifrost omitted the `usage` key entirely on `message_start` frames — which happened for every non-Anthropic provider, including Bedrock Converse, because usage figures aren't available until the terminal event — the SDK aborted the stream before the first token reached the client. The fix emits an all-zero `usage` object on `message_start` when no real figures are available. Zeros are chosen over Anthropic's own `output_tokens: 1` placeholder because Bedrock's `message_delta` carries absolute totals rather than cumulative deltas; a client summing both frames would over-count by one. Zero is neutral under both interpretations, and Bifrost's passthrough accumulator uses max-merge, so a zero can never displace a real figure later in the stream. ## Changes - `responses.go`: The `else` branch that previously left `messageUsage` nil now populates an all-zero `AnthropicUsage` struct, ensuring the `usage` key is always present on the wire regardless of `omitempty`. - `invoke.go`: `message_start` frames built for the Bedrock `/invoke-with-response-stream` path now include a `usage` map with zero placeholders by default. When `resp.Response.Usage` is non-nil, real figures replace the zeros and cache token fields are conditionally included. - Tests updated to assert that `usage` is present and zero when unknown, and that real figures are preferred when available. A second test marshals the struct to raw JSON and validates the wire representation directly, since the original `omitempty` bug was invisible to struct-level nil checks. ## 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/... ./core/providers/bedrock/... ``` Validate that streaming through a Bedrock-backed model with an `@ai-sdk/anthropic` client no longer aborts on the first frame. The `message_start` event should contain `message.usage.input_tokens = 0` and `message.usage.output_tokens = 0`, with authoritative totals arriving on `message_delta`. ## Breaking changes - [x] No ## Related issues Closes maximhq#5885 See also maximhq#5821 (the prior fix that introduced the omission) ## Security considerations None. ## 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
## Summary This PR fixes several correctness bugs across the Anthropic and Bedrock providers: reasoning/thinking `max_tokens` validation errors now surface as HTTP 400 instead of 500; `tool.defer_loading` is re-gated on its own `tool-search-tool-2025-10-19` beta header rather than the `advanced-tool-use` bundle; Bedrock's classic Converse path no longer claims to support `tool_search_tool_*` (which AWS restricts to InvokeModel only); Nova2 web search and code execution tools are now correctly handled on the chat completions path (not just the Responses path); reasoning signatures are preserved through the `/invoke` egress path; and `message_start` streaming events no longer fabricate all-zero usage when the upstream hasn't reported it yet. Closes maximhq#5638 ## Changes - **Reasoning** **`max_tokens`** **→ 400 not 500**: Introduced `ErrReasoningMaxTokensTooLow` sentinel error, wrapped at every reasoning-budget validation site in `chat.go` and `responses.go`. `requestbuilder.go` now detects it via `errors.Is` and calls `NewBifrostBadRequestError` (new helper, sets `StatusCode=400`) instead of the generic operation error that defaulted to 500. - **`defer_loading`** **beta header split**: `tool.defer_loading` now requires `tool-search-tool-2025-10-19` (`AnthropicToolSearchBetaHeader`), not the `advanced-tool-use-2025-11-20` bundle. `ProviderFeatureSupport.ToolSearch` gates it; `AdvancedToolUse` is narrowed to `allowed_callers` only. `stripUnsupportedAnthropicFields`, `StripUnsupportedFieldsFromRawBody`, `AddMissingBetaHeadersToContext`, and `betaHeaderPrefixToFeature` are all updated consistently. - **Bedrock drops** **`ToolSearch`**: `ProviderFeatures[schemas.Bedrock].ToolSearch` is set to `false` because AWS restricts `tool-search-tool-2025-10-19` to InvokeModel/InvokeModelWithResponseStream; Bifrost's Bedrock provider always dispatches tool-bearing requests via Converse. `convertAnthropicTools` in `invoke.go` now explicitly skips `tool_search_tool_*` entries rather than building a broken schema-less function tool. - **Nova2 web search/code execution on chat completions path**: `ValidateChatToolsForProvider`'s prefix map now ORs in `WebSearchNova`/`CodeExecNova` for `web_search_`/`code_execution_` prefixes, matching the Responses-path validator. `convertToolConfigFromFiltered` gains a new branch that converts these to `nova_grounding`/`nova_code_interpreter` system tools for Nova2 models and records them as model-dropped for non-Nova2 models. A shared `convertToNovaSystemTool` helper is extracted so both the Chat and Responses builders use one rule. - **Dropped tools surfaced on response**: `BifrostContextKeyDroppedUnsupportedTools` context key and `BifrostResponseExtraFields.DroppedUnsupportedTools` field are added. Both `ChatCompletion` and `Responses` in `bedrock.go` call a new `applyDroppedUnsupportedTools` helper that reads the context value and logs a warning. - **Reasoning signature through** **`/invoke`**: `toBedrockInvokeAnthropicResponse` now reads `item.Content.ContentBlocks` first (where Bedrock-originated reasoning lives) and carries the `Signature` field through to `BedrockInvokeMessagesContentBlock` (new field). The streaming path emits a `signature_delta` event type instead of nesting the signature inside `thinking_delta`, matching Anthropic's documented streaming contract. - **`message_start`** **usage omission**: `ToAnthropicResponsesStreamResponse` no longer fabricates an all-zero `AnthropicUsage` when `bifrostResp.Response.Usage` is nil. The field is omitted entirely, which Anthropic's streaming spec permits and which avoids misrepresenting cost telemetry to clients (e.g. Claude Code) that read `input_tokens` from `message_start`. - **`collectBedrockServerToolsFromFiltered`**: Skips `web_search_`/`code_execution_` prefixes to avoid sending Bedrock two conflicting representations of the same Nova system tool. ## 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/... ./core/providers/bedrock/... ./core/providers/utils/... ``` Key scenarios to validate: - Send a request with `reasoning.effort=high` and `max_tokens=500` to an Anthropic model — expect a `400` response, not `500`. - Send a request with `tool.defer_loading=true` to Vertex — expect `tool-search-tool-2025-10-19` in the beta headers, not `advanced-tool-use-2025-11-20`. - Send a `tool_search_tool_*` tool to classic Bedrock — expect it to be silently dropped, not forwarded as a broken function tool. - Send `web_search`/`code_execution` tools to a Nova2 Bedrock model via chat completions — expect `nova_grounding`/`nova_code_interpreter` system tools in the Converse request. - Verify `ExtraFields.DroppedUnsupportedTools` is populated when tools are dropped on Bedrock. - Stream a reasoning response through `/invoke` — expect a `signature_delta` event carrying the signature, and the thinking text present in the non-streaming response. ## Breaking changes - [x] No `DroppedUnsupportedTools` is a new additive field. The `defer_loading` beta header change is a correctness fix; clients that were relying on `advanced-tool-use` to gate `defer_loading` on Bedrock were already broken (Bedrock doesn't support it). The `message_start` usage omission is spec-compliant and only removes fabricated zeros. ## Security considerations None. No auth, secrets, PII, or sandboxing changes. ## 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
…mhq#5886) ## Summary Fixes maximhq#5885. `@ai-sdk/anthropic` (versions 4.0.6–4.0.32) marks `message.usage` and `message.usage.input_tokens` as required fields in its `message_start` schema, while `id`, `model`, and `role` are nullable. When Bifrost omitted the `usage` key entirely on `message_start` frames — which happened for every non-Anthropic provider, including Bedrock Converse, because usage figures aren't available until the terminal event — the SDK aborted the stream before the first token reached the client. The fix emits an all-zero `usage` object on `message_start` when no real figures are available. Zeros are chosen over Anthropic's own `output_tokens: 1` placeholder because Bedrock's `message_delta` carries absolute totals rather than cumulative deltas; a client summing both frames would over-count by one. Zero is neutral under both interpretations, and Bifrost's passthrough accumulator uses max-merge, so a zero can never displace a real figure later in the stream. ## Changes - `responses.go`: The `else` branch that previously left `messageUsage` nil now populates an all-zero `AnthropicUsage` struct, ensuring the `usage` key is always present on the wire regardless of `omitempty`. - `invoke.go`: `message_start` frames built for the Bedrock `/invoke-with-response-stream` path now include a `usage` map with zero placeholders by default. When `resp.Response.Usage` is non-nil, real figures replace the zeros and cache token fields are conditionally included. - Tests updated to assert that `usage` is present and zero when unknown, and that real figures are preferred when available. A second test marshals the struct to raw JSON and validates the wire representation directly, since the original `omitempty` bug was invisible to struct-level nil checks. ## 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/... ./core/providers/bedrock/... ``` Validate that streaming through a Bedrock-backed model with an `@ai-sdk/anthropic` client no longer aborts on the first frame. The `message_start` event should contain `message.usage.input_tokens = 0` and `message.usage.output_tokens = 0`, with authoritative totals arriving on `message_delta`. ## Breaking changes - [x] No ## Related issues Closes maximhq#5885 See also maximhq#5821 (the prior fix that introduced the omission) ## Security considerations None. ## 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

Summary
This PR fixes several correctness bugs across the Anthropic and Bedrock providers: reasoning/thinking
max_tokensvalidation errors now surface as HTTP 400 instead of 500;tool.defer_loadingis re-gated on its owntool-search-tool-2025-10-19beta header rather than theadvanced-tool-usebundle; Bedrock's classic Converse path no longer claims to supporttool_search_tool_*(which AWS restricts to InvokeModel only); Nova2 web search and code execution tools are now correctly handled on the chat completions path (not just the Responses path); reasoning signatures are preserved through the/invokeegress path; andmessage_startstreaming events no longer fabricate all-zero usage when the upstream hasn't reported it yet.Closes #5638
Changes
max_tokens→ 400 not 500: IntroducedErrReasoningMaxTokensTooLowsentinel error, wrapped at every reasoning-budget validation site inchat.goandresponses.go.requestbuilder.gonow detects it viaerrors.Isand callsNewBifrostBadRequestError(new helper, setsStatusCode=400) instead of the generic operation error that defaulted to 500.defer_loadingbeta header split:tool.defer_loadingnow requirestool-search-tool-2025-10-19(AnthropicToolSearchBetaHeader), not theadvanced-tool-use-2025-11-20bundle.ProviderFeatureSupport.ToolSearchgates it;AdvancedToolUseis narrowed toallowed_callersonly.stripUnsupportedAnthropicFields,StripUnsupportedFieldsFromRawBody,AddMissingBetaHeadersToContext, andbetaHeaderPrefixToFeatureare all updated consistently.ToolSearch:ProviderFeatures[schemas.Bedrock].ToolSearchis set tofalsebecause AWS restrictstool-search-tool-2025-10-19to InvokeModel/InvokeModelWithResponseStream; Bifrost's Bedrock provider always dispatches tool-bearing requests via Converse.convertAnthropicToolsininvoke.gonow explicitly skipstool_search_tool_*entries rather than building a broken schema-less function tool.ValidateChatToolsForProvider's prefix map now ORs inWebSearchNova/CodeExecNovaforweb_search_/code_execution_prefixes, matching the Responses-path validator.convertToolConfigFromFilteredgains a new branch that converts these tonova_grounding/nova_code_interpretersystem tools for Nova2 models and records them as model-dropped for non-Nova2 models. A sharedconvertToNovaSystemToolhelper is extracted so both the Chat and Responses builders use one rule.BifrostContextKeyDroppedUnsupportedToolscontext key andBifrostResponseExtraFields.DroppedUnsupportedToolsfield are added. BothChatCompletionandResponsesinbedrock.gocall a newapplyDroppedUnsupportedToolshelper that reads the context value and logs a warning./invoke:toBedrockInvokeAnthropicResponsenow readsitem.Content.ContentBlocksfirst (where Bedrock-originated reasoning lives) and carries theSignaturefield through toBedrockInvokeMessagesContentBlock(new field). The streaming path emits asignature_deltaevent type instead of nesting the signature insidethinking_delta, matching Anthropic's documented streaming contract.message_startusage omission:ToAnthropicResponsesStreamResponseno longer fabricates an all-zeroAnthropicUsagewhenbifrostResp.Response.Usageis nil. The field is omitted entirely, which Anthropic's streaming spec permits and which avoids misrepresenting cost telemetry to clients (e.g. Claude Code) that readinput_tokensfrommessage_start.collectBedrockServerToolsFromFiltered: Skipsweb_search_/code_execution_prefixes to avoid sending Bedrock two conflicting representations of the same Nova system tool.Type of change
Affected areas
How to test
go test ./core/providers/anthropic/... ./core/providers/bedrock/... ./core/providers/utils/...Key scenarios to validate:
reasoning.effort=highandmax_tokens=500to an Anthropic model — expect a400response, not500.tool.defer_loading=trueto Vertex — expecttool-search-tool-2025-10-19in the beta headers, notadvanced-tool-use-2025-11-20.tool_search_tool_*tool to classic Bedrock — expect it to be silently dropped, not forwarded as a broken function tool.web_search/code_executiontools to a Nova2 Bedrock model via chat completions — expectnova_grounding/nova_code_interpretersystem tools in the Converse request.ExtraFields.DroppedUnsupportedToolsis populated when tools are dropped on Bedrock./invoke— expect asignature_deltaevent carrying the signature, and the thinking text present in the non-streaming response.Breaking changes
DroppedUnsupportedToolsis a new additive field. Thedefer_loadingbeta header change is a correctness fix; clients that were relying onadvanced-tool-useto gatedefer_loadingon Bedrock were already broken (Bedrock doesn't support it). Themessage_startusage omission is spec-compliant and only removes fabricated zeros.Security considerations
None. No auth, secrets, PII, or sandboxing changes.
Checklist
docs/contributing/README.mdand followed the guidelines