fixed message start frame - #5907
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe Anthropic Responses and Chat Completions converters now emit valid ChangesAnthropic message_start framing
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
413be75 to
266a3c4
Compare
9dae7ab to
064bc48
Compare
|
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. |
266a3c4 to
a8ee2d2
Compare
064bc48 to
598cabc
Compare
|
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. |
a8ee2d2 to
c8bbad0
Compare
598cabc to
64542fe
Compare
|
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. |
64542fe to
a8c74a6
Compare
c8bbad0 to
0e91527
Compare
|
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. |
Merge activity
|
The base branch was changed.
a8c74a6 to
ddc2a3c
Compare
## Summary Strict Anthropic-dialect clients (e.g. `@ai-sdk/anthropic`) validate the very first SSE frame and abort the stream if `message_start` is missing a `message` object, `message.usage`, or `message.content`. This was causing streams to fail when Bifrost served requests backed by Bedrock Converse or any provider routed through `SendCreatedEventResponsesChunk`, which have no usage figures at `message_start` time. Additionally, the Chat Completions → Anthropic converter could panic on a content-less assistant message. ## Changes - **Responses converter (`responses.go`):** The `message` object is now built unconditionally on `message_start`, even when the upstream `created` event carries no `BifrostResponsesResponse` payload. Usage defaults to all-zeros (neutral under both additive and max-merge accounting) when real counts are not yet available. Nil guards were added for `Response.ID`, `Response.Usage`, and `Response.Diagnostics`. - **Chat converter (`chat.go`):** `message_start` frames now always include an empty `Usage` struct and an initialized (non-nil) `content` array. The role is read defensively from the message when present, falling back to `assistant`. The model is only attached to the `message` object when one already exists (i.e. on `message_start`), preventing stub objects from being emitted on delta frames. Usage is placed under `message.usage` on `message_start` and at the top level on `message_delta`, matching the Anthropic wire protocol. - **Tests (`messagestartusage_test.go`):** Consolidated and extended into a shared `requireValidMessageStart` helper that asserts the marshalled wire shape (type, message object, usage object with both token fields, content array) for both the Responses and Chat Completions converters. Added a case for a `created` event with no `Response` payload at all. ## Type of change - [x] Bug fix ## Affected areas - [x] Core (Go) - [x] Providers/Integrations ## How to test ```sh go test ./core/providers/anthropic/... -run TestResponsesMessageStartAlwaysCarriesUsage go test ./core/providers/anthropic/... -run TestChatMessageStartAlwaysCarriesUsage go test ./... ``` Send a streaming `/v1/messages` request routed through a Bedrock Converse model using an `@ai-sdk/anthropic` client and confirm the stream completes without a `Type validation failed ... path: [message, usage]` error on the first frame. ## Breaking changes - [x] No ## Related issues Closes #5885 ## Security considerations None. ## Checklist - [x] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [x] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [x] I verified the CI pipeline passes locally if applicable
## Summary Strict Anthropic-dialect clients (e.g. `@ai-sdk/anthropic`) validate the very first SSE frame and abort the stream if `message_start` is missing a `message` object, `message.usage`, or `message.content`. This was causing streams to fail when Bifrost served requests backed by Bedrock Converse or any provider routed through `SendCreatedEventResponsesChunk`, which have no usage figures at `message_start` time. Additionally, the Chat Completions → Anthropic converter could panic on a content-less assistant message. ## Changes - **Responses converter (`responses.go`):** The `message` object is now built unconditionally on `message_start`, even when the upstream `created` event carries no `BifrostResponsesResponse` payload. Usage defaults to all-zeros (neutral under both additive and max-merge accounting) when real counts are not yet available. Nil guards were added for `Response.ID`, `Response.Usage`, and `Response.Diagnostics`. - **Chat converter (`chat.go`):** `message_start` frames now always include an empty `Usage` struct and an initialized (non-nil) `content` array. The role is read defensively from the message when present, falling back to `assistant`. The model is only attached to the `message` object when one already exists (i.e. on `message_start`), preventing stub objects from being emitted on delta frames. Usage is placed under `message.usage` on `message_start` and at the top level on `message_delta`, matching the Anthropic wire protocol. - **Tests (`messagestartusage_test.go`):** Consolidated and extended into a shared `requireValidMessageStart` helper that asserts the marshalled wire shape (type, message object, usage object with both token fields, content array) for both the Responses and Chat Completions converters. Added a case for a `created` event with no `Response` payload at all. ## Type of change - [x] Bug fix ## Affected areas - [x] Core (Go) - [x] Providers/Integrations ## How to test ```sh go test ./core/providers/anthropic/... -run TestResponsesMessageStartAlwaysCarriesUsage go test ./core/providers/anthropic/... -run TestChatMessageStartAlwaysCarriesUsage go test ./... ``` Send a streaming `/v1/messages` request routed through a Bedrock Converse model using an `@ai-sdk/anthropic` client and confirm the stream completes without a `Type validation failed ... path: [message, usage]` error on the first frame. ## Breaking changes - [x] No ## Related issues Closes maximhq#5885 ## Security considerations None. ## Checklist - [x] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [x] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [x] I verified the CI pipeline passes locally if applicable
## Summary Adds E2E regression test coverage for issue #5885, where strict Anthropic-dialect clients (`@ai-sdk/anthropic`, the official Anthropic Python SDK, and Claude Code subagents) crash with `"undefined is not an object (evaluating 'o.input_tokens')"` when `message_start.message.usage` is absent from the first SSE frame. The fix (PR #5907, `db388c35c`) ensured a zero-value usage placeholder is always serialized instead of being silently dropped by Go's `omitempty` JSON tag. ## Changes - Adds test group **55** to the provider harness collection covering three distinct paths that previously omitted `message_start.message.usage`: - **Case 1** — OpenAI Responses API path (`openai/gpt-4o-mini` → `/anthropic/v1/messages` streaming), the exact route reported in #5885 - **Case 2** — xAI Responses-API-compatible path (`xai/grok-4-0709` → `/anthropic/v1/messages` streaming) - **Case 3** — Bedrock Converse `invoke-with-response-stream` path (`claude-haiku-4-5`), the route that broke Claude Code's Bedrock-backed subagents - Each test asserts that the `message_start` frame contains a `usage` object with numeric `input_tokens` and `output_tokens` fields, not merely that the stream completes successfully. ## Type of change - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [ ] Core (Go) - [ ] Transports (HTTP) - [x] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test Run the provider harness E2E collection against a live Bifrost instance and confirm test group 55 passes for all three cases: ```sh # Ensure BIFROST_BASE_URL / baseUrl is pointed at a running Bifrost instance # with OpenAI, xAI, and Bedrock credentials configured, then execute the # Postman/Newman collection: newman run tests/e2e/api/collections/provider-harness.json \ --env-var baseUrl=<your-bifrost-base-url> ``` Expected outcome: all three tests in group 55 report `message_start.message.usage` as an object with numeric `input_tokens` and `output_tokens`. ## Breaking changes - [ ] Yes - [x] No ## Related issues Closes #5885 (regression coverage); references PR #5907 (`db388c35c`). ## Security considerations None — test-only change with no auth, secrets, or PII implications. ## Checklist - [x] 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) - [x] I verified the CI pipeline passes locally if applicable

Summary
Strict Anthropic-dialect clients (e.g.
@ai-sdk/anthropic) validate the very first SSE frame and abort the stream ifmessage_startis missing amessageobject,message.usage, ormessage.content. This was causing streams to fail when Bifrost served requests backed by Bedrock Converse or any provider routed throughSendCreatedEventResponsesChunk, which have no usage figures atmessage_starttime. Additionally, the Chat Completions → Anthropic converter could panic on a content-less assistant message.Changes
responses.go): Themessageobject is now built unconditionally onmessage_start, even when the upstreamcreatedevent carries noBifrostResponsesResponsepayload. Usage defaults to all-zeros (neutral under both additive and max-merge accounting) when real counts are not yet available. Nil guards were added forResponse.ID,Response.Usage, andResponse.Diagnostics.chat.go):message_startframes now always include an emptyUsagestruct and an initialized (non-nil)contentarray. The role is read defensively from the message when present, falling back toassistant. The model is only attached to themessageobject when one already exists (i.e. onmessage_start), preventing stub objects from being emitted on delta frames. Usage is placed undermessage.usageonmessage_startand at the top level onmessage_delta, matching the Anthropic wire protocol.messagestartusage_test.go): Consolidated and extended into a sharedrequireValidMessageStarthelper that asserts the marshalled wire shape (type, message object, usage object with both token fields, content array) for both the Responses and Chat Completions converters. Added a case for acreatedevent with noResponsepayload at all.Type of change
Affected areas
How to test
Send a streaming
/v1/messagesrequest routed through a Bedrock Converse model using an@ai-sdk/anthropicclient and confirm the stream completes without aType validation failed ... path: [message, usage]error on the first frame.Breaking changes
Related issues
Closes #5885
Security considerations
None.
Checklist
docs/contributing/README.mdand followed the guidelines