diff --git a/packages/ai/src/protocols/anthropic-messages.ts b/packages/ai/src/protocols/anthropic-messages.ts index 2584a473afc..fd46b61118b 100644 --- a/packages/ai/src/protocols/anthropic-messages.ts +++ b/packages/ai/src/protocols/anthropic-messages.ts @@ -422,57 +422,74 @@ const AnthropicUsage = Schema.StructWithRest( ) type AnthropicUsage = Schema.Schema.Type -const AnthropicStreamBlock = Schema.Struct({ - type: Schema.String, - id: Schema.optional(Schema.String), - name: Schema.optional(Schema.String), - text: Schema.optional(Schema.String), - thinking: Schema.optional(Schema.String), - signature: Schema.optional(Schema.String), - // redacted_thinking blocks arrive whole in content_block_start with the - // encrypted payload in `data`; there is no streaming delta sequence. - data: Schema.optional(Schema.String), - input: Schema.optional(Schema.Unknown), - // *_tool_result blocks arrive whole as content_block_start (no streaming - // delta) with the structured payload in `content` and the originating - // server_tool_use id in `tool_use_id`. - tool_use_id: Schema.optional(Schema.String), - content: Schema.optional(Schema.Unknown), -}) +const AnthropicStreamBlock = Schema.StructWithRest( + Schema.Struct({ + type: Schema.String, + id: Schema.optional(Schema.String), + name: Schema.optional(Schema.String), + text: Schema.optional(Schema.String), + thinking: Schema.optional(Schema.String), + signature: Schema.optional(Schema.String), + // redacted_thinking blocks arrive whole in content_block_start with the + // encrypted payload in `data`; there is no streaming delta sequence. + data: Schema.optional(Schema.String), + input: Schema.optional(Schema.Unknown), + // *_tool_result blocks arrive whole as content_block_start (no streaming + // delta) with the structured payload in `content` and the originating + // server_tool_use id in `tool_use_id`. + tool_use_id: Schema.optional(Schema.String), + content: Schema.optional(Schema.Unknown), + }), + [JsonObject], +) type AnthropicStreamBlock = Schema.Schema.Type const decodeAnthropicStreamBlock = Schema.decodeUnknownOption(AnthropicStreamBlock) -const AnthropicStreamDelta = Schema.Struct({ - content: optionalNull(Schema.String), - type: Schema.optional(Schema.String), - text: Schema.optional(Schema.String), - thinking: Schema.optional(Schema.String), - partial_json: Schema.optional(Schema.String), - signature: Schema.optional(Schema.String), - stop_reason: optionalNull(Schema.String), - stop_sequence: optionalNull(Schema.String), - stop_details: optionalNull( - Schema.Struct({ category: optionalNull(Schema.String), explanation: optionalNull(Schema.String) }), - ), -}) +const AnthropicStreamDelta = Schema.StructWithRest( + Schema.Struct({ + content: optionalNull(Schema.String), + type: Schema.optional(Schema.String), + text: Schema.optional(Schema.String), + thinking: Schema.optional(Schema.String), + partial_json: Schema.optional(Schema.String), + signature: Schema.optional(Schema.String), + stop_reason: optionalNull(Schema.String), + stop_sequence: optionalNull(Schema.String), + stop_details: optionalNull( + Schema.StructWithRest( + Schema.Struct({ category: optionalNull(Schema.String), explanation: optionalNull(Schema.String) }), + [JsonObject], + ), + ), + }), + [JsonObject], +) type AnthropicStreamDelta = Schema.Schema.Type const decodeAnthropicStreamDelta = Schema.decodeUnknownOption(AnthropicStreamDelta) -const AnthropicEvent = Schema.Struct({ - type: Schema.String, - index: Schema.optional(Schema.Number), - message: Schema.optional(Schema.Struct({ usage: Schema.optional(AnthropicUsage) })), - content_block: Schema.optional(Schema.Unknown), - delta: Schema.optional(Schema.Unknown), - usage: Schema.optional(AnthropicUsage), - // `type` and `message` are both required per Anthropic's spec, but - // OpenAI-compatible proxies and gateway translations occasionally drop one - // or the other; mark them optional so a partial payload still parses and - // the parser can fall back to whichever field is populated. - error: Schema.optional( - Schema.Struct({ type: Schema.optional(Schema.String), message: Schema.optional(Schema.String) }), - ), -}) +const AnthropicEvent = Schema.StructWithRest( + Schema.Struct({ + type: Schema.String, + index: Schema.optional(Schema.Number), + message: Schema.optional( + Schema.StructWithRest(Schema.Struct({ usage: Schema.optional(AnthropicUsage) }), [JsonObject]), + ), + content_block: Schema.optional(Schema.Unknown), + delta: Schema.optional(Schema.Unknown), + usage: Schema.optional(AnthropicUsage), + // `type` and `message` are both required per Anthropic's spec, but + // OpenAI-compatible proxies and gateway translations occasionally drop one + // or the other; mark them optional so a partial payload still parses and + // the parser can fall back to whichever field is populated. + error: Schema.optional( + Schema.StructWithRest( + Schema.Struct({ type: Schema.optional(Schema.String), message: Schema.optional(Schema.String) }), + [JsonObject], + ), + ), + }), + [JsonObject], +) type AnthropicEvent = Schema.Schema.Type interface ParserState { diff --git a/packages/ai/src/protocols/open-responses.ts b/packages/ai/src/protocols/open-responses.ts index 0e3f1e60006..1672f4b7ce6 100644 --- a/packages/ai/src/protocols/open-responses.ts +++ b/packages/ai/src/protocols/open-responses.ts @@ -301,18 +301,26 @@ export const OpenResponsesBody = Schema.Struct({ }) export type OpenResponsesBody = Schema.Schema.Type -export const OpenResponsesUsage = Schema.Struct({ - input_tokens: Schema.optional(Schema.Number), - input_tokens_details: optionalNull( - Schema.Struct({ - cached_tokens: Schema.optional(Schema.Number), - cache_write_tokens: Schema.optional(Schema.Number), - }), - ), - output_tokens: Schema.optional(Schema.Number), - output_tokens_details: optionalNull(Schema.Struct({ reasoning_tokens: Schema.optional(Schema.Number) })), - total_tokens: Schema.optional(Schema.Number), -}) +export const OpenResponsesUsage = Schema.StructWithRest( + Schema.Struct({ + input_tokens: Schema.optional(Schema.Number), + input_tokens_details: optionalNull( + Schema.StructWithRest( + Schema.Struct({ + cached_tokens: Schema.optional(Schema.Number), + cache_write_tokens: Schema.optional(Schema.Number), + }), + [JsonObject], + ), + ), + output_tokens: Schema.optional(Schema.Number), + output_tokens_details: optionalNull( + Schema.StructWithRest(Schema.Struct({ reasoning_tokens: Schema.optional(Schema.Number) }), [JsonObject]), + ), + total_tokens: Schema.optional(Schema.Number), + }), + [JsonObject], +) type OpenResponsesUsage = Schema.Schema.Type // The spec requires `id` on every output item, but some gateways drop it from