diff --git a/packages/ai/README.md b/packages/ai/README.md index cf8b6bcd360..7ff40c3dff7 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -1283,7 +1283,7 @@ const gateway = CloudflareAIGateway.configure({ }).model("workers-ai/@cf/meta/llama-3.1-8b-instruct") ``` -Included LLM providers: OpenAI, Anthropic, Google (Gemini), Google Vertex, Amazon Bedrock, Azure OpenAI, Baseten, Cerebras, Cohere, Cloudflare AI Gateway, Cloudflare Workers AI, DeepInfra, DeepSeek, Fireworks, Groq, Mistral, OpenRouter, TogetherAI, and xAI. Z.ai currently exposes image generation. Generic Chat Completions, Responses, and Anthropic Messages-compatible entrypoints support custom endpoints. +Included LLM providers: OpenAI, Anthropic, Google (Gemini), Google Vertex, Amazon Bedrock, Azure OpenAI, Baseten, Cerebras, Cohere, Cloudflare AI Gateway, Cloudflare Workers AI, DeepInfra, DeepSeek, Fireworks, Groq, Mistral, OpenRouter, TogetherAI, Vercel AI Gateway, and xAI. Z.ai currently exposes image generation. Generic Chat Completions, Responses, and Anthropic Messages-compatible entrypoints support custom endpoints. Each named provider owns its module, endpoint, authentication, and route setup. Providers with the same wire format compose the shared protocol directly: diff --git a/packages/ai/src/cache-policy.ts b/packages/ai/src/cache-policy.ts index dc2340559ca..8ddde620693 100644 --- a/packages/ai/src/cache-policy.ts +++ b/packages/ai/src/cache-policy.ts @@ -48,21 +48,22 @@ const RESPECTS_INLINE_HINTS = new Set([ "meta-messages", "minimax-messages", "moonshot-messages", + "vercel-ai-gateway-messages", "zai-coding-messages", "bedrock-converse", "openrouter", "digitalocean", ]) -// OpenRouter upstreams other than Anthropic and Alibaba Qwen cache without breakpoints. Gemini uses only the last -// breakpoint, so a conversation-tail breakpoint writes a new cache every step and costs more than none. Qwen ignores -// breakpoints on tool definitions and caches tools with the system prompt. +// OpenRouter and Vercel AI Gateway upstreams other than Anthropic and Alibaba Qwen cache without breakpoints. +// Gemini uses only the last breakpoint, so a conversation-tail breakpoint writes a new cache every step and costs +// more than none. Qwen ignores breakpoints on tool definitions and caches tools with the system prompt. const QWEN: CachePolicyObject = { system: true, messages: { tail: 1 } } -const openRouterPolicy = (modelID: string): CachePolicyObject => { +const gatewayPolicy = (modelID: string): CachePolicyObject => { // `~anthropic/claude-sonnet-latest` style IDs are OpenRouter aliases for the latest model in a family. const id = modelID.replace(/^~/, "") if (id.startsWith("anthropic/")) return AUTO - if (id.startsWith("qwen/")) return QWEN + if (id.startsWith("qwen/") || id.startsWith("alibaba/qwen")) return QWEN return NONE } @@ -170,11 +171,13 @@ const countHints = (request: LLMRequest) => ) export const applyCachePolicy = (request: LLMRequest): LLMRequest => { - if (!RESPECTS_INLINE_HINTS.has(request.model.route.id)) return request + const route = request.model.route.id + if (!RESPECTS_INLINE_HINTS.has(route)) return request const policy = - request.model.route.id === "openrouter" && (request.cache === undefined || request.cache === "auto") - ? openRouterPolicy(request.model.id) - : request.model.route.id === "alibaba-chat" && (request.cache === undefined || request.cache === "auto") + (route === "openrouter" || route === "vercel-ai-gateway-messages") && + (request.cache === undefined || request.cache === "auto") + ? gatewayPolicy(request.model.id) + : route === "alibaba-chat" && (request.cache === undefined || request.cache === "auto") ? request.model.id.toLowerCase().startsWith("qwen") ? QWEN : NONE diff --git a/packages/ai/src/protocols/utils/gateway-protocol.ts b/packages/ai/src/protocols/utils/gateway-protocol.ts new file mode 100644 index 00000000000..8fe8651dd54 --- /dev/null +++ b/packages/ai/src/protocols/utils/gateway-protocol.ts @@ -0,0 +1,90 @@ +import { Effect, Option, Schema } from "effect" +import { Protocol } from "../../route/protocol.js" +import { LLMEvent, mergeJsonRecords, type AIError, type LLMRequest } from "../../schema/index.js" +import { JsonObject, lenient } from "../shared.js" + +interface ParserState { + readonly inner: Inner + readonly gateway?: Record +} + +const GatewayHolder = Schema.Struct({ + provider_metadata: lenient( + Schema.Struct({ + gateway: lenient(JsonObject), + }), + ), +}) + +const GatewayEvent = Schema.Struct({ + ...GatewayHolder.fields, + response: lenient(GatewayHolder), + choices: lenient( + Schema.Array( + Schema.Struct({ + delta: lenient(GatewayHolder), + }), + ), + ), +}) +const decodeGatewayEvent = Schema.decodeUnknownOption(GatewayEvent) + +function attachGatewayMetadata( + events: ReadonlyArray, + gateway: Record | undefined, +): ReadonlyArray { + if (!gateway || !events.some(LLMEvent.is.finish)) return events + return events.map((event) => + LLMEvent.is.finish(event) ? { ...event, providerMetadata: { ...event.providerMetadata, gateway } } : event, + ) +} + +export function gatewayProtocol( + protocol: Protocol, + input: { + readonly id: string + readonly prepare: ( + request: LLMRequest, + ) => Effect.Effect<{ readonly request: LLMRequest; readonly body: Record }, AIError> + }, +) { + const initial = (request: LLMRequest): ParserState => ({ + inner: protocol.stream.initial(request), + }) + const onHalt = protocol.stream.onHalt + + return Protocol.make({ + id: input.id, + body: { + schema: JsonObject, + from: Effect.fnUntraced(function* (request: LLMRequest) { + const prepared = yield* input.prepare(request) + const body = yield* protocol.body.from(prepared.request) + return { ...body, ...prepared.body } + }), + }, + supportsEffortUpdates: protocol.supportsEffortUpdates, + sanitizer: protocol.sanitizer, + stream: { + event: protocol.stream.event, + initial, + step: Effect.fnUntraced(function* (state: ParserState, event: Event) { + const decoded = Option.getOrUndefined(decodeGatewayEvent(event)) + const gateway = decoded + ? mergeJsonRecords( + state.gateway, + decoded.provider_metadata?.gateway, + decoded.response?.provider_metadata?.gateway, + ...(decoded.choices ?? []).map((choice) => choice.delta?.provider_metadata?.gateway), + ) + : state.gateway + const [inner, events] = yield* protocol.stream.step(state.inner, event) + return [{ inner, gateway }, attachGatewayMetadata(events, gateway)] as const + }), + terminal: protocol.stream.terminal, + onHalt: onHalt + ? (state) => onHalt(state.inner).pipe(Effect.map((events) => attachGatewayMetadata(events, state.gateway))) + : undefined, + }, + }) +} diff --git a/packages/ai/src/providers/vercel-ai-gateway.ts b/packages/ai/src/providers/vercel-ai-gateway.ts index c435c8e1295..fc0f2e171ac 100644 --- a/packages/ai/src/providers/vercel-ai-gateway.ts +++ b/packages/ai/src/providers/vercel-ai-gateway.ts @@ -8,38 +8,226 @@ import { EvaluationResponse, EvaluationRounding, } from "../experimental/evaluation.js" +import { AnthropicMessages } from "../protocols/anthropic-messages.js" +import { OpenAIChat } from "../protocols/openai-chat.js" +import { OpenResponses } from "../protocols/open-responses.js" +import { optionalNull, ProviderShared } from "../protocols/shared.js" +import { gatewayProtocol } from "../protocols/utils/gateway-protocol.js" +import type { ProviderPackage } from "../provider-package.js" import { Auth } from "../route/auth.js" import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js" +import { Route, type RouteDefaultsInput } from "../route/client.js" +import { Endpoint } from "../route/endpoint.js" +import { Framing } from "../route/framing.js" +import type { Protocol } from "../route/protocol.js" import { AIError, HttpContext, HttpOptions, InvalidProviderOutputError, InvalidRequestError, + LLMRequest, ModelID, ProviderID, ProviderMetadata, + ReasoningEffort, Usage, } from "../schema/index.js" +import type { OpenResponsesProviderOptionsInput } from "./open-responses-options.js" export const id = ProviderID.make("vercel-ai-gateway") const baseURL = "https://ai-gateway.vercel.sh/v1" -export interface EvaluationOptions { +export interface GatewayOptions { + /** Service-owned options added by the Gateway without requiring an SDK update. */ readonly [key: string]: unknown - readonly gateway?: Readonly<{ + /** Enables Gateway automatic prompt-cache breakpoint injection (`"auto"`). */ + readonly caching?: "auto" | (string & {}) + /** Provider slugs that are the only ones allowed to serve the request (e.g. `["anthropic", "vertex"]`). */ + readonly only?: ReadonlyArray + /** Provider slugs specifying the order in which providers are tried (e.g. `["bedrock", "anthropic"]`). */ + readonly order?: ReadonlyArray + /** Sort candidate providers by cost (`"cost"`), throughput (`"tps"`), or time-to-first-token (`"ttft"`). */ + readonly sort?: "cost" | "tps" | "ttft" | (string & {}) + /** Fallback models to try in order, or a conditional `{ model, when }` entry on evaluation requests. */ + readonly models?: ReadonlyArray>> + /** Restrict routing to providers with zero data retention agreements. */ + readonly zeroDataRetention?: boolean + /** Restrict routing to providers that do not train on prompt data. */ + readonly disallowPromptTraining?: boolean + /** + * Restrict routing to provider models that satisfy every entry: capability + * tags (`"implicit-caching"`, `"reasoning"`, `"structured-output"`, + * `"tool-use"`, `"vision"`) or weight-format filters (`"quantization:fp8"`, + * `"!quantization:fp8"`). + */ + readonly has?: ReadonlyArray< + | "implicit-caching" + | "reasoning" + | "structured-output" + | "tool-use" + | "vision" + | `quantization:${string}` + | `!quantization:${string}` + | (string & {}) + > + /** Entity identifier against which Gateway quota is tracked. */ + readonly quotaEntityId?: string + /** Unified service tier intent (`"flex"` or `"priority"`). */ + readonly serviceTier?: "flex" | "priority" | (string & {}) + /** End-user identifier for spend tracking and attribution. */ + readonly user?: string + /** User-specified tags for reporting and filtering usage. */ + readonly tags?: ReadonlyArray + /** Request-scoped BYOK credentials keyed by provider slug, used instead of cached workspace credentials. */ + readonly byok?: Readonly>>>> + /** Preferred inference region for upstream provider routing. */ + readonly inferenceRegion?: string + /** Per-provider timeouts in milliseconds (e.g. `{ byok: { anthropic: 3000 } }`). */ + readonly providerTimeouts?: { readonly [key: string]: unknown - readonly zeroDataRetention?: boolean - readonly only?: ReadonlyArray - }> + readonly byok?: Readonly> + } } -export type Options = ProviderAuthOption<"optional"> & { - readonly baseURL?: string - readonly headers?: Record - readonly http?: HttpOptions.Input +export type ProviderOptionsInput = OpenResponsesProviderOptionsInput & + Omit & { + /** Reasoning configuration for Messages (`thinking`) or Chat (`reasoning.enabled` + `reasoning.max_tokens`). */ + readonly thinking?: + | AnthropicMessages.OptionsInput["thinking"] + | { + readonly type: "enabled" | "adaptive" | "disabled" | (string & {}) + readonly budgetTokens?: number + readonly budget_tokens?: number + } + /** Gateway routing, fallback, BYOK, compliance, and attribution options sent under `body.providerOptions.gateway`. */ + readonly gateway?: GatewayOptions + /** Provider-specific options forwarded under their upstream namespace in `body.providerOptions` (e.g. `{ anthropic: { ... } }`). */ + readonly upstream?: Readonly>>> + /** Responses API automatic-cache lifetime (`"5m"` or `"1h"`), sent as top-level `cache_ttl`. */ + readonly cacheTTL?: "5m" | "1h" | (string & {}) + /** Responses API count of stable input items to anchor for caching, sent as top-level `cache_anchor_items`. */ + readonly cacheAnchorItems?: number + } + +export interface EvaluationOptions { + readonly [key: string]: unknown + readonly gateway?: GatewayOptions } +export type Options = Omit & + ProviderAuthOption<"optional"> & { + readonly baseURL?: string + readonly providerOptions?: ProviderOptionsInput + } + +export type Settings = ProviderPackage.Settings & ProviderOptionsInput & { readonly apiKey?: string } + +const GatewayOptionsSchema = Schema.Struct({ + gateway: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)), + upstream: Schema.optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown))), + reasoningEffort: Schema.optional(ReasoningEffort), + thinking: Schema.optional( + Schema.Struct({ + type: Schema.String, + budgetTokens: Schema.optional(Schema.Number), + budget_tokens: Schema.optional(Schema.Number), + }), + ), + cacheTTL: Schema.optional(Schema.String), + cacheAnchorItems: Schema.optional(Schema.Number), +}) +const decodeOptions = ProviderShared.validateWith(Schema.decodeUnknownEffect(GatewayOptionsSchema)) + +const prepare = (api: "messages" | "responses" | "chat") => + Effect.fnUntraced(function* (request: LLMRequest) { + const options = yield* decodeOptions(request.providerOptions ?? {}) + const providerOptions = (() => { + if (!options.upstream && !options.gateway) return undefined + if (!options.gateway) return options.upstream + return { ...options.upstream, gateway: options.gateway } + })() + switch (api) { + case "messages": { + const effort = options.reasoningEffort + if (effort === undefined) return { request, body: { providerOptions } } + const enabled = effort !== "none" + const thinking = request.providerOptions?.thinking ?? { type: enabled ? "adaptive" : "disabled" } + const next = LLMRequest.update(request, { + providerOptions: { + ...request.providerOptions, + effort: enabled ? effort : undefined, + thinking, + }, + }) + return { request: next, body: { providerOptions } } + } + case "responses": + return { + request, + body: { + providerOptions, + cache_ttl: options.cacheTTL, + cache_anchor_items: options.cacheAnchorItems, + }, + } + case "chat": { + if (!options.thinking) return { request, body: { providerOptions } } + const reasoning = { + enabled: options.thinking.type !== "disabled", + max_tokens: options.thinking.budgetTokens ?? options.thinking.budget_tokens, + } + return { request, body: { providerOptions, reasoning } } + } + } + }) + +const route = (input: { + readonly id: string + readonly protocol: Protocol + readonly api: "messages" | "responses" | "chat" + readonly path: string + readonly framing: Framing.Definition + readonly defaults?: RouteDefaultsInput +}) => + Route.make({ + id: input.id, + provider: id, + providerMetadataKey: id, + protocol: gatewayProtocol(input.protocol, { id: input.id, prepare: prepare(input.api) }), + endpoint: Endpoint.path(input.path, { baseURL }), + framing: input.framing, + headers: ({ request }): Record => + request.promptCacheKey ? { "x-session-affinity": request.promptCacheKey } : {}, + defaults: input.defaults, + }) + +const messagesRoute = route({ + id: "vercel-ai-gateway-messages", + protocol: AnthropicMessages.protocol, + api: "messages", + path: "/messages", + framing: AnthropicMessages.framing, + defaults: { headers: { "anthropic-version": "2023-06-01" } }, +}) +const responsesRoute = route({ + id: "vercel-ai-gateway-responses", + protocol: OpenResponses.protocol, + api: "responses", + path: "/responses", + framing: Framing.sse, + defaults: { providerOptions: { store: false, include: ["reasoning.encrypted_content"] } }, +}) +const chatRoute = route({ + id: "vercel-ai-gateway-chat", + protocol: OpenAIChat.protocol, + api: "chat", + path: "/chat/completions", + framing: OpenAIChat.framing, +}) + +export const routes = [messagesRoute, responsesRoute, chatRoute] + const Request = Schema.StructWithRest( Schema.Struct({ model: Schema.String, @@ -50,19 +238,43 @@ const Request = Schema.StructWithRest( [Schema.Record(Schema.String, Schema.Any)], ) const Response = Schema.Struct({ - model: Schema.optional(Schema.String), + model: optionalNull(Schema.String), answers: Schema.Record(Schema.String, EvaluationAnswer), - usage: Schema.optional( + usage: optionalNull( Schema.Struct({ - inputTokens: Schema.optional(Schema.Number), - outputTokens: Schema.optional(Schema.Number), + inputTokens: optionalNull(Schema.Number), + outputTokens: optionalNull(Schema.Number), }), ), - rounding: Schema.optional(EvaluationRounding), - providerMetadata: Schema.optional(ProviderMetadata), + rounding: optionalNull(EvaluationRounding), + providerMetadata: optionalNull(ProviderMetadata), }) export const configure = (input: Options = {}) => { + const { apiKey: _apiKey, auth: _auth, baseURL: endpoint, ...defaults } = input + const configured = { + ...defaults, + endpoint: { baseURL: endpoint ?? baseURL }, + auth: AuthOptions.bearer(input, ["AI_GATEWAY_API_KEY", "VERCEL_OIDC_TOKEN"]), + } + const messages = (modelID: string | ModelID) => + messagesRoute.with(configured).model({ + id: modelID, + // Recorded Gateway translations for non-Claude models return thinking with empty signatures. + compatibility: { requireSignature: modelID.startsWith("anthropic/") }, + }) + const responses = (modelID: string | ModelID) => + responsesRoute.with(configured).model({ id: modelID }) + const chat = (modelID: string | ModelID) => + chatRoute + .with(configured) + .model({ id: modelID, compatibility: { reasoningField: "reasoning" } }) + // Each family uses the API whose Gateway translation carries its reasoning state across turns. + const model = (modelID: string | ModelID) => { + if (/^(openai\/gpt-|spacexai\/grok-)/.test(modelID)) return responses(modelID) + if (modelID.startsWith("meta/muse-")) return chat(modelID) + return messages(modelID) + } const evaluation = (modelID: string | ModelID) => EvaluationModel.make({ id: modelID, @@ -85,9 +297,7 @@ export const configure = (input: Options = {}) => { (cause) => new AIError({ reason: new InvalidRequestError({ message: cause.message, cause }) }), ), ) - const headers = yield* Auth.toEffect( - AuthOptions.bearer(input, ["AI_GATEWAY_API_KEY", "VERCEL_OIDC_TOKEN"]), - )({ + const headers = yield* Auth.toEffect(configured.auth)({ request: req, method: "POST", url: url.toString(), @@ -119,30 +329,54 @@ export const configure = (input: Options = {}) => { fail("Vercel AI Gateway returned an invalid evaluation response", cause, text), ), ) + const inputTokens = data.usage?.inputTokens ?? undefined + const outputTokens = data.usage?.outputTokens ?? undefined + const usage = data.usage + ? new Usage({ + inputTokens, + outputTokens, + totalTokens: ProviderShared.totalTokens(inputTokens, outputTokens, undefined), + providerMetadata: { gateway: data.usage }, + }) + : undefined return new EvaluationResponse({ model: ModelID.make(data.model ?? req.model.id), answers: data.answers, - usage: data.usage - ? new Usage({ - inputTokens: data.usage.inputTokens, - outputTokens: data.usage.outputTokens, - totalTokens: - data.usage.inputTokens === undefined && data.usage.outputTokens === undefined - ? undefined - : (data.usage.inputTokens ?? 0) + (data.usage.outputTokens ?? 0), - providerMetadata: { gateway: data.usage }, - }) - : undefined, - rounding: data.rounding, - providerMetadata: data.providerMetadata, + usage, + rounding: data.rounding ?? undefined, + providerMetadata: data.providerMetadata ?? undefined, }) }), }, }) - return { id, experimental: { evaluation }, configure } + return { id, model, messages, responses, chat, experimental: { evaluation }, configure } } export const provider = configure() export const experimental = provider.experimental +export const messages = provider.messages +export const responses = provider.responses +export const chat = provider.chat + +export const model: ProviderPackage.Definition["model"] = (modelID, settings) => + fromSettings(settings).model(modelID) +export const messagesModel: ProviderPackage.Definition["model"] = (modelID, settings) => + fromSettings(settings).messages(modelID) +export const responsesModel: ProviderPackage.Definition["model"] = ( + modelID, + settings, +) => fromSettings(settings).responses(modelID) +export const chatModel: ProviderPackage.Definition["model"] = (modelID, settings) => + fromSettings(settings).chat(modelID) + +function fromSettings({ apiKey, baseURL, headers, body, ...providerOptions }: Settings) { + return configure({ + apiKey, + baseURL, + headers, + http: body === undefined ? undefined : { body: { ...body } }, + providerOptions, + }) +} export * as VercelAIGateway from "./vercel-ai-gateway.js" diff --git a/packages/ai/src/providers/vercel-ai-gateway/chat.ts b/packages/ai/src/providers/vercel-ai-gateway/chat.ts new file mode 100644 index 00000000000..d2dab164d30 --- /dev/null +++ b/packages/ai/src/providers/vercel-ai-gateway/chat.ts @@ -0,0 +1,2 @@ +export { chatModel as model } from "../vercel-ai-gateway.js" +export type { Settings } from "../vercel-ai-gateway.js" diff --git a/packages/ai/src/providers/vercel-ai-gateway/messages.ts b/packages/ai/src/providers/vercel-ai-gateway/messages.ts new file mode 100644 index 00000000000..13341ff11bd --- /dev/null +++ b/packages/ai/src/providers/vercel-ai-gateway/messages.ts @@ -0,0 +1,2 @@ +export { messagesModel as model } from "../vercel-ai-gateway.js" +export type { Settings } from "../vercel-ai-gateway.js" diff --git a/packages/ai/src/providers/vercel-ai-gateway/responses.ts b/packages/ai/src/providers/vercel-ai-gateway/responses.ts new file mode 100644 index 00000000000..7e03a291099 --- /dev/null +++ b/packages/ai/src/providers/vercel-ai-gateway/responses.ts @@ -0,0 +1,2 @@ +export { responsesModel as model } from "../vercel-ai-gateway.js" +export type { Settings } from "../vercel-ai-gateway.js" diff --git a/packages/ai/test/fixtures/recordings/vercel-native/automatically-caches-distinct-claude-system-blocks.json b/packages/ai/test/fixtures/recordings/vercel-native/automatically-caches-distinct-claude-system-blocks.json new file mode 100644 index 00000000000..6ed6f55990f --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/automatically-caches-distinct-claude-system-blocks.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/automatically-caches-distinct-claude-system-blocks", + "recordedAt": "2026-10-05T05:01:46.082Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"anthropic/claude-sonnet-5.5\",\"system\":[{\"type\":\"text\",\"text\":\"Rule 0: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 1: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 2: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 3: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 4: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 5: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 6: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 7: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 8: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 9: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 10: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 11: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 12: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 13: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 14: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 15: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 16: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 17: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 18: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 19: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 20: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 21: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 22: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 23: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 24: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 25: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 26: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 27: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 28: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 29: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 30: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 31: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 32: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 33: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 34: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 35: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 36: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 37: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 38: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 39: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 40: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 41: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 42: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 43: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 44: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 45: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 46: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 47: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 48: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 49: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 50: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 51: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 52: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 53: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 54: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 55: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 56: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 57: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 58: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 59: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 60: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 61: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 62: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 63: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 64: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 65: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 66: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 67: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 68: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 69: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 70: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 71: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 72: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 73: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 74: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 75: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 76: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 77: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 78: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 79: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 80: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 81: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 82: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 83: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 84: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 85: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 86: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 87: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 88: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 89: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 90: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 91: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 92: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 93: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 94: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 95: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 96: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 97: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 98: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 99: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 100: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 101: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 102: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 103: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 104: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 105: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 106: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 107: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 108: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 109: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 110: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 111: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 112: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 113: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 114: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 115: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 116: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 117: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 118: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 119: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 120: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 121: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 122: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 123: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 124: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 125: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 126: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 127: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 128: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 129: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 130: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 131: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 132: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 133: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 134: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 135: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 136: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 137: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 138: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 139: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 140: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 141: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 142: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 143: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 144: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 145: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 146: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 147: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 148: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 149: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 150: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 151: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 152: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 153: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 154: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 155: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 156: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 157: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 158: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 159: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 160: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 161: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 162: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 163: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 164: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 165: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 166: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 167: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 168: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 169: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 170: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 171: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 172: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 173: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 174: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 175: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 176: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 177: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 178: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 179: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 180: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 181: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 182: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 183: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 184: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 185: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 186: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 187: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 188: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 189: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 190: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 191: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 192: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 193: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 194: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 195: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 196: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 197: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 198: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 199: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 200: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 201: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 202: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 203: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 204: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 205: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 206: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 207: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 208: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 209: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 210: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 211: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 212: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 213: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 214: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 215: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 216: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 217: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 218: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 219: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 220: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 221: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 222: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 223: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 224: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 225: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 226: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 227: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 228: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 229: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 230: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 231: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 232: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 233: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 234: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 235: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 236: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 237: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 238: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 239: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 240: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 241: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 242: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 243: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 244: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 245: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 246: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 247: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 248: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 249: Preserve provider reasoning state and keep stable prompt prefixes intact.\",\"cache_control\":{\"type\":\"ephemeral\"}},{\"type\":\"text\",\"text\":\"Reply with OK only.\",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Confirm.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"max_tokens\":32,\"thinking\":{\"type\":\"adaptive\",\"block_binding\":{\"prefix_mismatch_behavior\":\"drop_block\"}},\"providerOptions\":{\"gateway\":{\"only\":[\"anthropic\"]}}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M4570W9EPZN6C4RCWA9BS27G\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"anthropic/claude-sonnet-5.5\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"OK\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"output_tokens\":4,\"cache_creation_input_tokens\":8267,\"cache_creation\":{\"ephemeral_5m_input_tokens\":8267,\"ephemeral_1h_input_tokens\":0}},\"input_transformations\":[],\"provider_metadata\":{\"anthropic\":{\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":8267,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":8267,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":4,\"service_tier\":\"standard\",\"inference_geo\":\"global\",\"output_tokens_details\":{\"thinking_tokens\":0}},\"cacheCreationInputTokens\":8267,\"stopSequence\":null,\"inputTransformations\":[],\"iterations\":null,\"container\":null,\"contextManagement\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"anthropic/claude-sonnet-5.5\",\"resolvedProvider\":\"anthropic\",\"fallbacksAvailable\":[],\"planningReasoning\":\"Provider set restricted to: anthropic. System credentials planned for: anthropic. Total execution order: anthropic(system)\",\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"finalProvider\":\"anthropic\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"anthropic\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791176503639,\"endTime\":1791176504703,\"providerRequestId\":\"req_011CfiW8HmuUPgY1T2zujPjJ\",\"statusCode\":200,\"providerResponseId\":\"msg_011CfiW8J7FTDATHvjsDxLMz\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"no_pin\"},\"clientSessionId\":\"c07bb6f3d42a3239255c5e93ab22568e\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.0207155\",\"marketCost\":\"0.0207155\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.0207155\",\"inferenceCost\":\"0.0207155\",\"inputInferenceCost\":\"0.0206755\",\"outputInferenceCost\":\"0.00004\",\"generationId\":\"gen_01M4570W9EPZN6C4RCWA9BS27G\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"anthropic/claude-sonnet-5.5\",\"system\":[{\"type\":\"text\",\"text\":\"Rule 0: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 1: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 2: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 3: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 4: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 5: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 6: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 7: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 8: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 9: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 10: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 11: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 12: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 13: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 14: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 15: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 16: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 17: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 18: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 19: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 20: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 21: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 22: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 23: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 24: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 25: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 26: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 27: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 28: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 29: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 30: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 31: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 32: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 33: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 34: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 35: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 36: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 37: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 38: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 39: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 40: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 41: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 42: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 43: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 44: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 45: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 46: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 47: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 48: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 49: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 50: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 51: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 52: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 53: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 54: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 55: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 56: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 57: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 58: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 59: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 60: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 61: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 62: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 63: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 64: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 65: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 66: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 67: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 68: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 69: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 70: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 71: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 72: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 73: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 74: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 75: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 76: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 77: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 78: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 79: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 80: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 81: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 82: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 83: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 84: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 85: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 86: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 87: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 88: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 89: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 90: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 91: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 92: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 93: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 94: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 95: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 96: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 97: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 98: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 99: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 100: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 101: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 102: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 103: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 104: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 105: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 106: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 107: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 108: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 109: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 110: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 111: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 112: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 113: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 114: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 115: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 116: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 117: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 118: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 119: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 120: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 121: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 122: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 123: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 124: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 125: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 126: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 127: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 128: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 129: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 130: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 131: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 132: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 133: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 134: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 135: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 136: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 137: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 138: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 139: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 140: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 141: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 142: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 143: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 144: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 145: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 146: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 147: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 148: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 149: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 150: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 151: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 152: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 153: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 154: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 155: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 156: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 157: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 158: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 159: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 160: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 161: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 162: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 163: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 164: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 165: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 166: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 167: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 168: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 169: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 170: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 171: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 172: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 173: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 174: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 175: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 176: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 177: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 178: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 179: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 180: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 181: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 182: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 183: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 184: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 185: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 186: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 187: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 188: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 189: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 190: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 191: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 192: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 193: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 194: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 195: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 196: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 197: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 198: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 199: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 200: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 201: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 202: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 203: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 204: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 205: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 206: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 207: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 208: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 209: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 210: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 211: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 212: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 213: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 214: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 215: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 216: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 217: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 218: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 219: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 220: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 221: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 222: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 223: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 224: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 225: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 226: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 227: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 228: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 229: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 230: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 231: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 232: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 233: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 234: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 235: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 236: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 237: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 238: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 239: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 240: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 241: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 242: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 243: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 244: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 245: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 246: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 247: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 248: Preserve provider reasoning state and keep stable prompt prefixes intact.\\nRule 249: Preserve provider reasoning state and keep stable prompt prefixes intact.\",\"cache_control\":{\"type\":\"ephemeral\"}},{\"type\":\"text\",\"text\":\"Reply with OK only.\",\"cache_control\":{\"type\":\"ephemeral\"}}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Confirm.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"max_tokens\":32,\"thinking\":{\"type\":\"adaptive\",\"block_binding\":{\"prefix_mismatch_behavior\":\"drop_block\"}},\"providerOptions\":{\"gateway\":{\"only\":[\"anthropic\"]}}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M4570XG28GG4V70MVE5DPKPG\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"anthropic/claude-sonnet-5.5\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"OK\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":4,\"output_tokens\":4,\"cache_read_input_tokens\":8267,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0}},\"input_transformations\":[],\"provider_metadata\":{\"anthropic\":{\"usage\":{\"input_tokens\":4,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":8267,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":4,\"service_tier\":\"standard\",\"inference_geo\":\"global\",\"output_tokens_details\":{\"thinking_tokens\":0}},\"cacheCreationInputTokens\":0,\"stopSequence\":null,\"inputTransformations\":[],\"iterations\":null,\"container\":null,\"contextManagement\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"anthropic/claude-sonnet-5.5\",\"resolvedProvider\":\"anthropic\",\"fallbacksAvailable\":[],\"planningReasoning\":\"Provider set restricted to: anthropic. System credentials planned for: anthropic. Total execution order: anthropic(system)\",\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"finalProvider\":\"anthropic\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"anthropic\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791176504863,\"endTime\":1791176506119,\"providerRequestId\":\"req_011CfiW8NxhdWhraDtd72Lhp\",\"statusCode\":200,\"providerResponseId\":\"msg_011CfiW8PNzxLgGo4XCy8euc\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"confirmed\",\"pinnedProvider\":\"anthropic\"},\"clientSessionId\":\"c07bb6f3d42a3239255c5e93ab22568e\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.0017014\",\"marketCost\":\"0.0017014\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.0017014\",\"inferenceCost\":\"0.0017014\",\"inputInferenceCost\":\"0.0016614\",\"outputInferenceCost\":\"0.00004\",\"generationId\":\"gen_01M4570XG28GG4V70MVE5DPKPG\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/continues-chat-claude-reasoning-and-tools.json b/packages/ai/test/fixtures/recordings/vercel-native/continues-chat-claude-reasoning-and-tools.json new file mode 100644 index 00000000000..a8878ac063e --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/continues-chat-claude-reasoning-and-tools.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/continues-chat-claude-reasoning-and-tools", + "recordedAt": "2026-10-02T03:35:31.909Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/chat/completions", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"anthropic/claude-sonnet-5.5\",\"messages\":[{\"role\":\"system\",\"content\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":\"What is the weather in Paris?\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"low\",\"max_completion_tokens\":2048}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "data: {\"id\":\"gen_01M3XAWTT89S61AZ5BSE3W3SA9\",\"object\":\"chat.completion.chunk\",\"created\":1790912130,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\"},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_9rbv9d39c7\"}\n\ndata: {\"id\":\"gen_01M3XAWTT89S61AZ5BSE3W3SA9\",\"object\":\"chat.completion.chunk\",\"created\":1790912130,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"toolu_01GV2kwNUpDVDUXK3nekcJGf\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"\"}}]},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_9rbv9d39c7\"}\n\ndata: {\"id\":\"gen_01M3XAWTT89S61AZ5BSE3W3SA9\",\"object\":\"chat.completion.chunk\",\"created\":1790912130,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"{\\\"city\\\": \\\"Paris\"}}]},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_9rbv9d39c7\"}\n\ndata: {\"id\":\"gen_01M3XAWTT89S61AZ5BSE3W3SA9\",\"object\":\"chat.completion.chunk\",\"created\":1790912130,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"\\\"}\"}}]},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_9rbv9d39c7\"}\n\ndata: {\"id\":\"gen_01M3XAWTT89S61AZ5BSE3W3SA9\",\"object\":\"chat.completion.chunk\",\"created\":1790912130,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"provider_metadata\":{\"anthropic\":{\"usage\":{\"input_tokens\":420,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":50,\"service_tier\":\"standard\",\"inference_geo\":\"global\",\"output_tokens_details\":{\"thinking_tokens\":0}},\"cacheCreationInputTokens\":0,\"stopSequence\":null,\"iterations\":null,\"container\":null,\"contextManagement\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"anthropic/claude-sonnet-5.5\",\"resolvedProvider\":\"anthropic\",\"fallbacksAvailable\":[\"claudeaws\",\"bedrock\",\"vertexAnthropic\"],\"planningReasoning\":\"Weighted tie pick: anthropic won tier 0 over claudeaws. System credentials planned for: anthropic, claudeaws, bedrock, vertexAnthropic. Total execution order: anthropic(system) → claudeaws(system) → bedrock(system) → vertexAnthropic(system)\",\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"finalProvider\":\"anthropic\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"anthropic\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912129901,\"endTime\":1790912130999,\"providerRequestId\":\"req_011Cfci8AT17gzqZn2jJEXeT\",\"statusCode\":200,\"providerResponseId\":\"msg_011Cfci8AtJRw1vue5qnmohn\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.00134\",\"marketCost\":\"0.00134\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.00134\",\"inferenceCost\":\"0.00134\",\"inputInferenceCost\":\"0.00084\",\"outputInferenceCost\":\"0.0005\",\"generationId\":\"gen_01M3XAWTT89S61AZ5BSE3W3SA9\"}}},\"logprobs\":null,\"finish_reason\":\"tool_calls\"}],\"usage\":{\"prompt_tokens\":420,\"completion_tokens\":50,\"total_tokens\":470,\"cost\":0.00134,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0,\"video_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0,\"upstream_inference_completions_cost\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0},\"cache_creation_input_tokens\":0,\"market_cost\":0.00134,\"gateway_cost\":0.00134},\"system_fingerprint\":\"fp_9rbv9d39c7\",\"generationId\":\"gen_01M3XAWTT89S61AZ5BSE3W3SA9\"}\n\ndata: [DONE]\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/chat/completions", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"anthropic/claude-sonnet-5.5\",\"messages\":[{\"role\":\"system\",\"content\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":\"What is the weather in Paris?\"},{\"role\":\"assistant\",\"content\":null,\"tool_calls\":[{\"id\":\"toolu_01GV2kwNUpDVDUXK3nekcJGf\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}],\"reasoning\":\"\"},{\"role\":\"tool\",\"tool_call_id\":\"toolu_01GV2kwNUpDVDUXK3nekcJGf\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"low\",\"max_completion_tokens\":2048}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "data: {\"id\":\"gen_01M3XAWW1EQYN8E229MXPGQ69V\",\"object\":\"chat.completion.chunk\",\"created\":1790912131,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\"},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_deco7uk6x4\"}\n\ndata: {\"id\":\"gen_01M3XAWW1EQYN8E229MXPGQ69V\",\"object\":\"chat.completion.chunk\",\"created\":1790912131,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"Paris is sunny.\"},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_deco7uk6x4\"}\n\ndata: {\"id\":\"gen_01M3XAWW1EQYN8E229MXPGQ69V\",\"object\":\"chat.completion.chunk\",\"created\":1790912131,\"model\":\"anthropic/claude-sonnet-5.5\",\"choices\":[{\"index\":0,\"delta\":{\"provider_metadata\":{\"anthropic\":{\"usage\":{\"input_tokens\":493,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":10,\"service_tier\":\"standard\",\"inference_geo\":\"global\",\"output_tokens_details\":{\"thinking_tokens\":0}},\"cacheCreationInputTokens\":0,\"stopSequence\":null,\"iterations\":null,\"container\":null,\"contextManagement\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"anthropic/claude-sonnet-5.5\",\"resolvedProvider\":\"anthropic\",\"fallbacksAvailable\":[\"claudeaws\",\"bedrock\",\"vertexAnthropic\"],\"planningReasoning\":\"Weighted tie pick: anthropic won tier 0 over claudeaws. System credentials planned for: anthropic, claudeaws, bedrock, vertexAnthropic. Total execution order: anthropic(system) → claudeaws(system) → bedrock(system) → vertexAnthropic(system)\",\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"finalProvider\":\"anthropic\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"anthropic\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912131152,\"endTime\":1790912131960,\"providerRequestId\":\"req_011Cfci8FjVzcog3yJtTFw81\",\"statusCode\":200,\"providerResponseId\":\"msg_011Cfci8G5pr3sikiEmpeSSo\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.001086\",\"marketCost\":\"0.001086\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.001086\",\"inferenceCost\":\"0.001086\",\"inputInferenceCost\":\"0.000986\",\"outputInferenceCost\":\"0.0001\",\"generationId\":\"gen_01M3XAWW1EQYN8E229MXPGQ69V\"}}},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":493,\"completion_tokens\":10,\"total_tokens\":503,\"cost\":0.001086,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0,\"video_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0,\"upstream_inference_completions_cost\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0},\"cache_creation_input_tokens\":0,\"market_cost\":0.001086,\"gateway_cost\":0.001086},\"system_fingerprint\":\"fp_deco7uk6x4\",\"generationId\":\"gen_01M3XAWW1EQYN8E229MXPGQ69V\"}\n\ndata: [DONE]\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/continues-chat-muse-reasoning-and-tools.json b/packages/ai/test/fixtures/recordings/vercel-native/continues-chat-muse-reasoning-and-tools.json new file mode 100644 index 00000000000..c5907e5d5f9 --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/continues-chat-muse-reasoning-and-tools.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/continues-chat-muse-reasoning-and-tools", + "recordedAt": "2026-10-05T02:56:27.988Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/chat/completions", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"meta/muse-spark-1.3\",\"messages\":[{\"role\":\"system\",\"content\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":\"What is the weather in Paris?\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"low\",\"max_completion_tokens\":2048}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "data: {\"id\":\"gen_01M44ZVD02HF12MJ8X2S7SK1E6\",\"object\":\"chat.completion.chunk\",\"created\":1791168985,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\"},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_h4uvdop29h\"}\n\ndata: {\"id\":\"gen_01M44ZVD02HF12MJ8X2S7SK1E6\",\"object\":\"chat.completion.chunk\",\"created\":1791168985,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"I'll look up the weather in Paris for you.\",\"phase\":\"commentary\"},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_h4uvdop29h\"}\n\ndata: {\"id\":\"gen_01M44ZVD02HF12MJ8X2S7SK1E6\",\"object\":\"chat.completion.chunk\",\"created\":1791168985,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_01a109fdbbdd7347a6918c3662c8128e\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"\"}}]},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_h4uvdop29h\"}\n\ndata: {\"id\":\"gen_01M44ZVD02HF12MJ8X2S7SK1E6\",\"object\":\"chat.completion.chunk\",\"created\":1791168985,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}]},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_h4uvdop29h\"}\n\ndata: {\"id\":\"gen_01M44ZVD02HF12MJ8X2S7SK1E6\",\"object\":\"chat.completion.chunk\",\"created\":1791168985,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"provider_metadata\":{\"openai\":{\"responseId\":\"resp_6ac311d9306b8844d6104957\",\"serviceTier\":\"auto\"},\"gateway\":{\"routing\":{\"originalModelId\":\"meta/muse-spark-1.3\",\"resolvedProvider\":\"meta\",\"fallbacksAvailable\":[],\"planningReasoning\":\"System credentials planned for: meta. Total execution order: meta(system)\",\"canonicalSlug\":\"meta/muse-spark-1.3\",\"finalProvider\":\"meta\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"meta/muse-spark-1.3\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"meta\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791168984107,\"endTime\":1791168986296,\"providerRequestId\":\"f93f8fd8-1b40-49b2-8a0c-222d6e70af95\",\"statusCode\":200,\"providerResponseId\":\"resp_6ac311d9306b8844d6104957\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.0012462\",\"marketCost\":\"0.0012462\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.0012462\",\"inferenceCost\":\"0.0012462\",\"inputInferenceCost\":\"0.0005832\",\"outputInferenceCost\":\"0.000663\",\"generationId\":\"gen_01M44ZVD02HF12MJ8X2S7SK1E6\"}}},\"logprobs\":null,\"finish_reason\":\"tool_calls\"}],\"usage\":{\"prompt_tokens\":566,\"completion_tokens\":156,\"total_tokens\":722,\"cost\":0.0012462,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":113,\"audio_tokens\":0,\"video_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0,\"upstream_inference_completions_cost\":0},\"completion_tokens_details\":{\"reasoning_tokens\":87,\"image_tokens\":0},\"cache_creation_input_tokens\":0,\"market_cost\":0.0012462,\"gateway_cost\":0.0012462},\"system_fingerprint\":\"fp_h4uvdop29h\",\"service_tier\":\"auto\",\"generationId\":\"gen_01M44ZVD02HF12MJ8X2S7SK1E6\"}\n\ndata: [DONE]\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/chat/completions", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"meta/muse-spark-1.3\",\"messages\":[{\"role\":\"system\",\"content\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":\"What is the weather in Paris?\"},{\"role\":\"assistant\",\"content\":\"I'll look up the weather in Paris for you.\",\"tool_calls\":[{\"id\":\"call_01a109fdbbdd7347a6918c3662c8128e\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}],\"reasoning\":\"\"},{\"role\":\"tool\",\"tool_call_id\":\"call_01a109fdbbdd7347a6918c3662c8128e\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"low\",\"max_completion_tokens\":2048}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "data: {\"id\":\"gen_01M44ZVF8Y196MQE5QNXBYK63X\",\"object\":\"chat.completion.chunk\",\"created\":1791168987,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\"},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_t8q87incvs\"}\n\ndata: {\"id\":\"gen_01M44ZVF8Y196MQE5QNXBYK63X\",\"object\":\"chat.completion.chunk\",\"created\":1791168987,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"Paris is sunny.\"},\"logprobs\":null,\"finish_reason\":null}],\"system_fingerprint\":\"fp_t8q87incvs\"}\n\ndata: {\"id\":\"gen_01M44ZVF8Y196MQE5QNXBYK63X\",\"object\":\"chat.completion.chunk\",\"created\":1791168987,\"model\":\"meta/muse-spark-1.3\",\"choices\":[{\"index\":0,\"delta\":{\"provider_metadata\":{\"openai\":{\"responseId\":\"resp_6ac311db491fe86e00954e5c\",\"serviceTier\":\"auto\"},\"gateway\":{\"routing\":{\"originalModelId\":\"meta/muse-spark-1.3\",\"resolvedProvider\":\"meta\",\"fallbacksAvailable\":[],\"planningReasoning\":\"System credentials planned for: meta. Total execution order: meta(system)\",\"canonicalSlug\":\"meta/muse-spark-1.3\",\"finalProvider\":\"meta\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"meta/muse-spark-1.3\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"meta\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791168986439,\"endTime\":1791168988032,\"providerRequestId\":\"88abdbee-80b6-444b-a295-16cd5b25801f\",\"statusCode\":200,\"providerResponseId\":\"resp_6ac311db491fe86e00954e5c\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.0011\",\"marketCost\":\"0.0011\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.0011\",\"inferenceCost\":\"0.0011\",\"inputInferenceCost\":\"0.00082375\",\"outputInferenceCost\":\"0.00027625\",\"generationId\":\"gen_01M44ZVF8Y196MQE5QNXBYK63X\"}}},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":659,\"completion_tokens\":65,\"total_tokens\":724,\"cost\":0.0011,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0,\"video_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0,\"upstream_inference_completions_cost\":0},\"completion_tokens_details\":{\"reasoning_tokens\":51,\"image_tokens\":0},\"cache_creation_input_tokens\":0,\"market_cost\":0.0011,\"gateway_cost\":0.0011},\"system_fingerprint\":\"fp_t8q87incvs\",\"service_tier\":\"auto\",\"generationId\":\"gen_01M44ZVF8Y196MQE5QNXBYK63X\"}\n\ndata: [DONE]\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-claude-reasoning-and-tools.json b/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-claude-reasoning-and-tools.json new file mode 100644 index 00000000000..626489537c6 --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-claude-reasoning-and-tools.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/continues-messages-claude-reasoning-and-tools", + "recordedAt": "2026-10-05T05:01:43.431Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"anthropic/claude-sonnet-5.5\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":2048,\"thinking\":{\"type\":\"adaptive\",\"block_binding\":{\"prefix_mismatch_behavior\":\"drop_block\"}},\"output_config\":{\"effort\":\"low\"}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M4570TCVSBA1XA414GAQ4CXD\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"anthropic/claude-sonnet-5.5\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01HUuDeJSxrGVzp36mnVyXyv\",\"name\":\"get_weather\",\"input\":{}}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"city\\\": \\\"Paris\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"}\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":420,\"output_tokens\":50,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0}},\"input_transformations\":[],\"provider_metadata\":{\"anthropic\":{\"usage\":{\"input_tokens\":420,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":50,\"service_tier\":\"standard\",\"inference_geo\":\"global\",\"output_tokens_details\":{\"thinking_tokens\":0}},\"cacheCreationInputTokens\":0,\"stopSequence\":null,\"inputTransformations\":[],\"iterations\":null,\"container\":null,\"contextManagement\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"anthropic/claude-sonnet-5.5\",\"resolvedProvider\":\"anthropic\",\"fallbacksAvailable\":[\"bedrock\",\"vertexAnthropic\",\"claudeaws\"],\"planningReasoning\":\"System credentials planned for: anthropic, bedrock, vertexAnthropic, claudeaws. Total execution order: anthropic(system) → bedrock(system) → vertexAnthropic(system) → claudeaws(system)\",\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"finalProvider\":\"anthropic\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"anthropic\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791176501800,\"endTime\":1791176502677,\"providerRequestId\":\"req_011CfiW89nSemMDq17221pkX\",\"statusCode\":200,\"providerResponseId\":\"msg_011CfiW8ATdTzcemBmD1hcuT\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.00134\",\"marketCost\":\"0.00134\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.00134\",\"inferenceCost\":\"0.00134\",\"inputInferenceCost\":\"0.00084\",\"outputInferenceCost\":\"0.0005\",\"generationId\":\"gen_01M4570TCVSBA1XA414GAQ4CXD\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"anthropic/claude-sonnet-5.5\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01HUuDeJSxrGVzp36mnVyXyv\",\"name\":\"get_weather\",\"input\":{\"city\":\"Paris\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_01HUuDeJSxrGVzp36mnVyXyv\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":2048,\"thinking\":{\"type\":\"adaptive\",\"block_binding\":{\"prefix_mismatch_behavior\":\"drop_block\"}},\"output_config\":{\"effort\":\"low\"}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M4570VFYNAEGVS60AYMPC6NG\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"anthropic/claude-sonnet-5.5\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Paris is sunny.\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":493,\"output_tokens\":10,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0}},\"input_transformations\":[],\"provider_metadata\":{\"anthropic\":{\"usage\":{\"input_tokens\":493,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":10,\"service_tier\":\"standard\",\"inference_geo\":\"global\",\"output_tokens_details\":{\"thinking_tokens\":0}},\"cacheCreationInputTokens\":0,\"stopSequence\":null,\"inputTransformations\":[],\"iterations\":null,\"container\":null,\"contextManagement\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"anthropic/claude-sonnet-5.5\",\"resolvedProvider\":\"anthropic\",\"fallbacksAvailable\":[\"bedrock\",\"vertexAnthropic\",\"claudeaws\"],\"planningReasoning\":\"System credentials planned for: anthropic, bedrock, vertexAnthropic, claudeaws. Total execution order: anthropic(system) → bedrock(system) → vertexAnthropic(system) → claudeaws(system)\",\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"finalProvider\":\"anthropic\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"anthropic/claude-sonnet-5.5\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"anthropic\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791176502820,\"endTime\":1791176503470,\"providerRequestId\":\"req_011CfiW8EBMRJC1mJAyNfuhc\",\"statusCode\":200,\"providerResponseId\":\"msg_011CfiW8EYw9svWDkh3UxuCK\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.001086\",\"marketCost\":\"0.001086\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.001086\",\"inferenceCost\":\"0.001086\",\"inputInferenceCost\":\"0.000986\",\"outputInferenceCost\":\"0.0001\",\"generationId\":\"gen_01M4570VFYNAEGVS60AYMPC6NG\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-deepseek-reasoning-and-tools.json b/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-deepseek-reasoning-and-tools.json new file mode 100644 index 00000000000..d6e123e6dbb --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-deepseek-reasoning-and-tools.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/continues-messages-deepseek-reasoning-and-tools", + "recordedAt": "2026-10-05T02:56:29.224Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"deepseek/deepseek-v4.1-flash\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":2048,\"thinking\":{\"type\":\"adaptive\"},\"output_config\":{\"effort\":\"high\"}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M44ZVH1C4678Q0TYP7DRJ7J2\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"deepseek/deepseek-v4.1-flash\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\",\"thinking\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\"The\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" user wants\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" me to call\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" get_weather once\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" for Paris, then\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" reply exactly \\\"Paris\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"thinking_delta\",\"thinking\":\" is sunny.\\\"\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\n\"}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":2,\"content_block\":{\"type\":\"tool_use\",\"id\":\"chatcmpl-tool-51e80fcbd5064b6e8bc9b8d0fa1d4e1d\",\"name\":\"get_weather\",\"input\":{}}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":2,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"city\\\": \\\"Paris\\\"}\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":2}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":324,\"output_tokens\":61},\"provider_metadata\":{\"baseten\":{\"acceptedPredictionTokens\":44,\"rejectedPredictionTokens\":4},\"gateway\":{\"routing\":{\"originalModelId\":\"deepseek/deepseek-v4.1-flash\",\"resolvedProvider\":\"baseten\",\"fallbacksAvailable\":[\"fireworks\",\"alibaba\",\"runware\",\"relace\",\"particle\",\"novita\",\"togetherai\",\"deepinfra\",\"wafer\",\"parasail\",\"gmicloud\",\"modal\",\"morph\",\"deepseek\",\"runinfra\",\"boundless\",\"inference-net\"],\"planningReasoning\":\"System credentials planned for: baseten, fireworks, alibaba, runware, relace, particle, novita, togetherai, deepinfra, wafer, parasail, gmicloud, modal, morph, deepseek, runinfra, boundless, inference-net. Total execution order: baseten(system) → fireworks(system) → alibaba(system) → runware(system) → relace(system) → particle(system) → novita(system) → togetherai(system) → deepinfra(system) → wafer(system) → parasail(system) → gmicloud(system) → modal(system) → morph(system) → deepseek(system) → runinfra(system) → boundless(system) → inference-net(system)\",\"canonicalSlug\":\"deepseek/deepseek-v4.1-flash\",\"finalProvider\":\"baseten\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"deepseek/deepseek-v4.1-flash\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"baseten\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791168988247,\"endTime\":1791168988517,\"providerRequestId\":\"54ca701a109fdc46b7da8b6ab43570cc519dd\",\"statusCode\":200,\"providerResponseId\":\"chatcmpl-4234517cc89342a79b53a68ac41601eb\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.0001704\",\"marketCost\":\"0.0001704\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.0001704\",\"inferenceCost\":\"0.0001704\",\"inputInferenceCost\":\"0.0000972\",\"outputInferenceCost\":\"0.0000732\",\"generationId\":\"gen_01M44ZVH1C4678Q0TYP7DRJ7J2\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"deepseek/deepseek-v4.1-flash\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"The user wants me to call get_weather once for Paris, then reply exactly \\\"Paris is sunny.\\\"\",\"signature\":\"\"},{\"type\":\"tool_use\",\"id\":\"chatcmpl-tool-51e80fcbd5064b6e8bc9b8d0fa1d4e1d\",\"name\":\"get_weather\",\"input\":{\"city\":\"Paris\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"chatcmpl-tool-51e80fcbd5064b6e8bc9b8d0fa1d4e1d\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":2048,\"thinking\":{\"type\":\"adaptive\"},\"output_config\":{\"effort\":\"high\"}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M44ZVHFBW1M2N3JSPQG4PXFG\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"deepseek/deepseek-v4.1-flash\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Paris is sunny.\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":406,\"output_tokens\":6},\"provider_metadata\":{\"baseten\":{\"acceptedPredictionTokens\":4,\"rejectedPredictionTokens\":2},\"gateway\":{\"routing\":{\"originalModelId\":\"deepseek/deepseek-v4.1-flash\",\"resolvedProvider\":\"baseten\",\"fallbacksAvailable\":[\"fireworks\",\"alibaba\",\"runware\",\"relace\",\"particle\",\"novita\",\"togetherai\",\"deepinfra\",\"wafer\",\"parasail\",\"gmicloud\",\"modal\",\"morph\",\"deepseek\",\"runinfra\",\"boundless\",\"inference-net\"],\"planningReasoning\":\"System credentials planned for: baseten, fireworks, alibaba, runware, relace, particle, novita, togetherai, deepinfra, wafer, parasail, gmicloud, modal, morph, deepseek, runinfra, boundless, inference-net. Total execution order: baseten(system) → fireworks(system) → alibaba(system) → runware(system) → relace(system) → particle(system) → novita(system) → togetherai(system) → deepinfra(system) → wafer(system) → parasail(system) → gmicloud(system) → modal(system) → morph(system) → deepseek(system) → runinfra(system) → boundless(system) → inference-net(system)\",\"canonicalSlug\":\"deepseek/deepseek-v4.1-flash\",\"finalProvider\":\"baseten\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"deepseek/deepseek-v4.1-flash\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"baseten\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1791168989072,\"endTime\":1791168989270,\"providerRequestId\":\"54ca701a109fdc7a977f6a444f59bbed7ee04\",\"statusCode\":200,\"providerResponseId\":\"chatcmpl-01cf067760f44cc1bca4b7a01722e964\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"no_pin\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.000129\",\"marketCost\":\"0.000129\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.000129\",\"inferenceCost\":\"0.000129\",\"inputInferenceCost\":\"0.0001218\",\"outputInferenceCost\":\"0.0000072\",\"generationId\":\"gen_01M44ZVHFBW1M2N3JSPQG4PXFG\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-gemini-reasoning-and-tools.json b/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-gemini-reasoning-and-tools.json new file mode 100644 index 00000000000..f94da6afe94 --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/continues-messages-gemini-reasoning-and-tools.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/continues-messages-gemini-reasoning-and-tools", + "recordedAt": "2026-10-02T03:35:15.999Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"google/gemini-3.8-flash\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":2048,\"thinking\":{\"type\":\"adaptive\"},\"output_config\":{\"effort\":\"low\"}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M3XAWAZ6J0VZ2XBQD2GHA031\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"google/gemini-3.8-flash\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"thinking\",\"thinking\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"signature_delta\",\"signature\":\"AY89a19bLXkNRJcCTLXJhlklpdU5hxo3vEit19RMmh8bC8Jm2SmGq6E5NZdq177AdJfOKRYxZkFuEaJ/ngpxoqLeB47ZMMK4VSZD2lhRbfFR6S8bITZ1\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":1,\"content_block\":{\"type\":\"tool_use\",\"id\":\"call_355115\",\"name\":\"get_weather\",\"input\":{}}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":1,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":1}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":52,\"output_tokens\":16},\"provider_metadata\":{\"vertex\":{\"promptFeedback\":null,\"groundingMetadata\":null,\"urlContextMetadata\":null,\"safetyRatings\":null,\"usageMetadata\":{\"promptTokenCount\":52,\"candidatesTokenCount\":16,\"totalTokenCount\":68,\"trafficType\":\"ON_DEMAND\",\"promptTokensDetails\":[{\"modality\":\"TEXT\",\"tokenCount\":52}],\"candidatesTokensDetails\":[{\"modality\":\"TEXT\",\"tokenCount\":16}]},\"finishMessage\":null,\"serviceTier\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"google/gemini-3.8-flash\",\"resolvedProvider\":\"vertex\",\"fallbacksAvailable\":[\"google\"],\"planningReasoning\":\"System credentials planned for: vertex, google. Total execution order: vertex(system) → google(system)\",\"canonicalSlug\":\"google/gemini-3.8-flash\",\"finalProvider\":\"vertex\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"google/gemini-3.8-flash\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"vertex\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912113658,\"endTime\":1790912114756,\"statusCode\":200,\"providerResponseId\":\"cSa_avLEL-zFiLsP5L_a4Ak\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.000099\",\"marketCost\":\"0.000099\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.000099\",\"inferenceCost\":\"0.000099\",\"inputInferenceCost\":\"0.000039\",\"outputInferenceCost\":\"0.00006\",\"generationId\":\"gen_01M3XAWAZ6J0VZ2XBQD2GHA031\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"google/gemini-3.8-flash\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"thinking\",\"thinking\":\"\",\"signature\":\"AY89a19bLXkNRJcCTLXJhlklpdU5hxo3vEit19RMmh8bC8Jm2SmGq6E5NZdq177AdJfOKRYxZkFuEaJ/ngpxoqLeB47ZMMK4VSZD2lhRbfFR6S8bITZ1\"},{\"type\":\"tool_use\",\"id\":\"call_355115\",\"name\":\"get_weather\",\"input\":{\"city\":\"Paris\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"call_355115\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":2048,\"thinking\":{\"type\":\"adaptive\"},\"output_config\":{\"effort\":\"low\"}}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"gen_01M3XAWC57YKVKCYGSHBPW86GH\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"model\":\"google/gemini-3.8-flash\",\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":0,\"output_tokens\":0}}}\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Paris is\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" sunny.\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\"}}\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":83,\"output_tokens\":4},\"provider_metadata\":{\"vertex\":{\"promptFeedback\":null,\"groundingMetadata\":null,\"urlContextMetadata\":null,\"safetyRatings\":null,\"usageMetadata\":{\"promptTokenCount\":83,\"candidatesTokenCount\":4,\"totalTokenCount\":87,\"trafficType\":\"ON_DEMAND\",\"promptTokensDetails\":[{\"modality\":\"TEXT\",\"tokenCount\":83}],\"candidatesTokensDetails\":[{\"modality\":\"TEXT\",\"tokenCount\":4}]},\"finishMessage\":null,\"serviceTier\":null},\"gateway\":{\"routing\":{\"originalModelId\":\"google/gemini-3.8-flash\",\"resolvedProvider\":\"vertex\",\"fallbacksAvailable\":[\"google\"],\"planningReasoning\":\"System credentials planned for: vertex, google. Total execution order: vertex(system) → google(system)\",\"canonicalSlug\":\"google/gemini-3.8-flash\",\"finalProvider\":\"vertex\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"google/gemini-3.8-flash\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"vertex\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912114880,\"endTime\":1790912116051,\"statusCode\":200,\"providerResponseId\":\"cia_auGLOt3Mw8cPopm3wAY\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.00007725\",\"marketCost\":\"0.00007725\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.00007725\",\"inferenceCost\":\"0.00007725\",\"inputInferenceCost\":\"0.00006225\",\"outputInferenceCost\":\"0.000015\",\"generationId\":\"gen_01M3XAWC57YKVKCYGSHBPW86GH\"}}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/continues-responses-gpt-reasoning-and-tools.json b/packages/ai/test/fixtures/recordings/vercel-native/continues-responses-gpt-reasoning-and-tools.json new file mode 100644 index 00000000000..d44938154aa --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/continues-responses-gpt-reasoning-and-tools.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/continues-responses-gpt-reasoning-and-tools", + "recordedAt": "2026-10-02T03:35:17.693Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"openai/gpt-6-luna\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":2048,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"gen_01M3XAWDCYY5RT4HEEH7PRA60S\",\"object\":\"response\",\"created_at\":1790912116,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"openai/gpt-6-luna\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"sequence_number\":1,\"response\":{\"id\":\"gen_01M3XAWDCYY5RT4HEEH7PRA60S\",\"object\":\"response\",\"created_at\":1790912116,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"openai/gpt-6-luna\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":2,\"output_index\":0,\"item\":{\"type\":\"function_call\",\"id\":\"fc_1790912116805_gjpiv08fad6\",\"call_id\":\"call_iUMyf0iR7KJvUJl7O30wRmGi\",\"name\":\"get_weather\",\"arguments\":\"\",\"status\":\"in_progress\"}}\n\nevent: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"sequence_number\":3,\"item_id\":\"fc_1790912116805_gjpiv08fad6\",\"output_index\":0,\"delta\":\"{\\\"\"}\n\nevent: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"sequence_number\":4,\"item_id\":\"fc_1790912116805_gjpiv08fad6\",\"output_index\":0,\"delta\":\"city\"}\n\nevent: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"sequence_number\":5,\"item_id\":\"fc_1790912116805_gjpiv08fad6\",\"output_index\":0,\"delta\":\"\\\":\\\"\"}\n\nevent: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"sequence_number\":6,\"item_id\":\"fc_1790912116805_gjpiv08fad6\",\"output_index\":0,\"delta\":\"Paris\"}\n\nevent: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"sequence_number\":7,\"item_id\":\"fc_1790912116805_gjpiv08fad6\",\"output_index\":0,\"delta\":\"\\\"}\"}\n\nevent: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"sequence_number\":8,\"item_id\":\"fc_1790912116805_gjpiv08fad6\",\"output_index\":0,\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":9,\"output_index\":0,\"item\":{\"type\":\"function_call\",\"id\":\"fc_1790912116805_gjpiv08fad6\",\"call_id\":\"call_iUMyf0iR7KJvUJl7O30wRmGi\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"status\":\"completed\"}}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":10,\"response\":{\"id\":\"gen_01M3XAWDCYY5RT4HEEH7PRA60S\",\"object\":\"response\",\"created_at\":1790912116,\"status\":\"completed\",\"completed_at\":1790912116,\"incomplete_details\":null,\"error\":null,\"output\":[{\"type\":\"function_call\",\"id\":\"fc_1790912116805_gjpiv08fad6\",\"call_id\":\"call_iUMyf0iR7KJvUJl7O30wRmGi\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"status\":\"completed\"}],\"usage\":{\"input_tokens\":74,\"output_tokens\":18,\"total_tokens\":92,\"input_tokens_details\":{\"cached_tokens\":0,\"cache_write_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"gpt-6-luna\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"default\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null,\"provider_metadata\":{\"openai\":{\"responseId\":\"resp_00f283291bb6009d016abf26743f0c87d1a9732d3e4a334cd2\",\"serviceTier\":\"default\",\"reasoningContext\":\"all_turns\"},\"gateway\":{\"routing\":{\"originalModelId\":\"openai/gpt-6-luna\",\"resolvedProvider\":\"openai\",\"fallbacksAvailable\":[\"bedrock\",\"azure\"],\"planningReasoning\":\"System credentials planned for: openai, bedrock, azure. Total execution order: openai(system) → bedrock(system) → azure(system)\",\"canonicalSlug\":\"openai/gpt-6-luna\",\"finalProvider\":\"openai\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"openai/gpt-6-luna\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"openai\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912116162,\"endTime\":1790912116955,\"providerRequestId\":\"req_354dcfb7edf042beab5d73d445d4f82d\",\"statusCode\":200,\"providerResponseId\":\"resp_00f283291bb6009d016abf26743f0c87d1a9732d3e4a334cd2\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.0000164\",\"marketCost\":\"0.0000164\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.0000164\",\"inferenceCost\":\"0.0000164\",\"inputInferenceCost\":\"0.0000074\",\"outputInferenceCost\":\"0.000009\",\"generationId\":\"gen_01M3XAWDCYY5RT4HEEH7PRA60S\"}}}}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"openai/gpt-6-luna\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"function_call\",\"id\":\"fc_1790912116805_gjpiv08fad6\",\"call_id\":\"call_iUMyf0iR7KJvUJl7O30wRmGi\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_iUMyf0iR7KJvUJl7O30wRmGi\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":2048,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"gen_01M3XAWEAE4DFVEN1PK4X2HPDN\",\"object\":\"response\",\"created_at\":1790912117,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"openai/gpt-6-luna\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"sequence_number\":1,\"response\":{\"id\":\"gen_01M3XAWEAE4DFVEN1PK4X2HPDN\",\"object\":\"response\",\"created_at\":1790912117,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"openai/gpt-6-luna\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":2,\"output_index\":0,\"item\":{\"type\":\"message\",\"id\":\"msg_1790912117592_ym79h2ia3op\",\"status\":\"in_progress\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"\",\"annotations\":[],\"logprobs\":[]}],\"phase\":\"final_answer\"}}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"sequence_number\":3,\"item_id\":\"msg_1790912117592_ym79h2ia3op\",\"output_index\":0,\"content_index\":0,\"part\":{\"type\":\"output_text\",\"text\":\"\",\"annotations\":[],\"logprobs\":[]}}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":4,\"item_id\":\"msg_1790912117592_ym79h2ia3op\",\"output_index\":0,\"content_index\":0,\"delta\":\"Paris\",\"logprobs\":[]}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":5,\"item_id\":\"msg_1790912117592_ym79h2ia3op\",\"output_index\":0,\"content_index\":0,\"delta\":\" is\",\"logprobs\":[]}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":6,\"item_id\":\"msg_1790912117592_ym79h2ia3op\",\"output_index\":0,\"content_index\":0,\"delta\":\" sunny\",\"logprobs\":[]}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":7,\"item_id\":\"msg_1790912117592_ym79h2ia3op\",\"output_index\":0,\"content_index\":0,\"delta\":\".\",\"logprobs\":[]}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"sequence_number\":8,\"item_id\":\"msg_1790912117592_ym79h2ia3op\",\"output_index\":0,\"content_index\":0,\"text\":\"Paris is sunny.\",\"logprobs\":[]}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"sequence_number\":9,\"item_id\":\"msg_1790912117592_ym79h2ia3op\",\"output_index\":0,\"content_index\":0,\"part\":{\"type\":\"output_text\",\"text\":\"Paris is sunny.\",\"annotations\":[],\"logprobs\":[]}}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":10,\"output_index\":0,\"item\":{\"type\":\"message\",\"id\":\"msg_1790912117592_ym79h2ia3op\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Paris is sunny.\",\"annotations\":[],\"logprobs\":[]}],\"phase\":\"final_answer\"}}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":11,\"response\":{\"id\":\"gen_01M3XAWEAE4DFVEN1PK4X2HPDN\",\"object\":\"response\",\"created_at\":1790912117,\"status\":\"completed\",\"completed_at\":1790912117,\"incomplete_details\":null,\"error\":null,\"output\":[{\"type\":\"message\",\"id\":\"msg_1790912117592_ym79h2ia3op\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Paris is sunny.\",\"annotations\":[],\"logprobs\":[]}],\"phase\":\"final_answer\"}],\"usage\":{\"input_tokens\":113,\"output_tokens\":8,\"total_tokens\":121,\"input_tokens_details\":{\"cached_tokens\":0,\"cache_write_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"gpt-6-luna\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"default\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null,\"provider_metadata\":{\"openai\":{\"responseId\":\"resp_052bed03e239f42b016abf26752c0087d1aef1c048d528c92e\",\"serviceTier\":\"default\",\"reasoningContext\":\"all_turns\"},\"gateway\":{\"routing\":{\"originalModelId\":\"openai/gpt-6-luna\",\"resolvedProvider\":\"openai\",\"fallbacksAvailable\":[\"bedrock\",\"azure\"],\"planningReasoning\":\"System credentials planned for: openai, bedrock, azure. Total execution order: openai(system) → bedrock(system) → azure(system)\",\"canonicalSlug\":\"openai/gpt-6-luna\",\"finalProvider\":\"openai\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"openai/gpt-6-luna\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"openai\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912117099,\"endTime\":1790912117745,\"providerRequestId\":\"req_4db547dd2dbc497491922d9bbb28c228\",\"statusCode\":200,\"providerResponseId\":\"resp_052bed03e239f42b016abf26752c0087d1aef1c048d528c92e\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.0000153\",\"marketCost\":\"0.0000153\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.0000153\",\"inferenceCost\":\"0.0000153\",\"inputInferenceCost\":\"0.0000113\",\"outputInferenceCost\":\"0.000004\",\"generationId\":\"gen_01M3XAWEAE4DFVEN1PK4X2HPDN\"}}}}\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/continues-responses-grok-reasoning-and-tools.json b/packages/ai/test/fixtures/recordings/vercel-native/continues-responses-grok-reasoning-and-tools.json new file mode 100644 index 00000000000..f67f3cdf055 --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/continues-responses-grok-reasoning-and-tools.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/continues-responses-grok-reasoning-and-tools", + "recordedAt": "2026-10-02T03:35:29.729Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"spacexai/grok-4.7\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":2048,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"gen_01M3XAWJENM4P0F2WHEDRHDGW0\",\"object\":\"response\",\"created_at\":1790912122,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"xai/grok-4.7\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"sequence_number\":1,\"response\":{\"id\":\"gen_01M3XAWJENM4P0F2WHEDRHDGW0\",\"object\":\"response\",\"created_at\":1790912122,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"xai/grok-4.7\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":2,\"output_index\":0,\"item\":{\"type\":\"reasoning\",\"id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"summary\":[]}}\n\nevent: response.reasoning_summary_part.added\ndata: {\"type\":\"response.reasoning_summary_part.added\",\"sequence_number\":3,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"\"}}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":4,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\"The\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":5,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" user\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":6,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" wants\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":7,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" me\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":8,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" to\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":9,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" use\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":10,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" the\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":11,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" get\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":12,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\"_\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":13,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\"weather\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":14,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" tool\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":15,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" exactly\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":16,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" once\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":17,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\",\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":18,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" then\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":19,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" reply\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":20,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" exactly\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":21,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" \\\"\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":22,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\"Paris\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":23,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" is\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":24,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" sunny\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":25,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\".\\\"\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":26,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" after\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":27,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" the\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":28,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" tool\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":29,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\" result\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":30,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"delta\":\".\\n\"}\n\nevent: response.reasoning_summary_text.done\ndata: {\"type\":\"response.reasoning_summary_text.done\",\"sequence_number\":31,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"text\":\"The user wants me to use the get_weather tool exactly once, then reply exactly \\\"Paris is sunny.\\\" after the tool result.\\n\"}\n\nevent: response.reasoning_summary_part.done\ndata: {\"type\":\"response.reasoning_summary_part.done\",\"sequence_number\":32,\"item_id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"output_index\":0,\"summary_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"The user wants me to use the get_weather tool exactly once, then reply exactly \\\"Paris is sunny.\\\" after the tool result.\\n\"}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":33,\"output_index\":1,\"item\":{\"type\":\"function_call\",\"id\":\"fc_1790912123226_i789hz8nb4\",\"call_id\":\"call-37a29db2-38ae-4f89-95ef-321aba43ee4e-0\",\"name\":\"get_weather\",\"arguments\":\"\",\"status\":\"in_progress\"}}\n\nevent: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"sequence_number\":34,\"item_id\":\"fc_1790912123226_i789hz8nb4\",\"output_index\":1,\"delta\":\"{\\\"city\\\":\\\"Paris\\\"}\"}\n\nevent: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"sequence_number\":35,\"item_id\":\"fc_1790912123226_i789hz8nb4\",\"output_index\":1,\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":36,\"output_index\":1,\"item\":{\"type\":\"function_call\",\"id\":\"fc_1790912123226_i789hz8nb4\",\"call_id\":\"call-37a29db2-38ae-4f89-95ef-321aba43ee4e-0\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"status\":\"completed\"}}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":37,\"output_index\":0,\"item\":{\"type\":\"reasoning\",\"id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"The user wants me to use the get_weather tool exactly once, then reply exactly \\\"Paris is sunny.\\\" after the tool result.\\n\"}],\"encrypted_content\":\"gwenc1.eyJvIjp7ImNvbnRlbnQiOiJmQmZoSmxEbkhOUXVzcDdFeDl1TTB6RGVPNExoVlVmK21BdldMZGwwSkk2bCtURjhwbXcxb091emVvVFRSUlBHLzJGd3JCVy9hM1g3NCtTRTAwSFlkd2I1Z1hnTXFTZHVNZ1cyZlJsVE12S3VEaGE1TWlEODRIYy8wSjZFdnRmS1JkcjU5TGpZL29mK01Qbk9RWi9Od0phOEFKb1pHZmVnUkx6cUVudlQ1eUM3dTNDYWsvZzFrWWNmR2xkNDNzVmJMVGZiSjJIaDh5SWlPNlEyOExGVUNBN3FmRzQzV2VqM1BDSzkvZnk1U29tTVN2NUk5ZkoxeU9jc3IwY2ZSUGNmeStBIiwib3JpZ2luIjoieGFpIn19\"}}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":38,\"response\":{\"id\":\"gen_01M3XAWJENM4P0F2WHEDRHDGW0\",\"object\":\"response\",\"created_at\":1790912122,\"status\":\"completed\",\"completed_at\":1790912123,\"incomplete_details\":null,\"error\":null,\"output\":[{\"type\":\"reasoning\",\"id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"The user wants me to use the get_weather tool exactly once, then reply exactly \\\"Paris is sunny.\\\" after the tool result.\\n\"}],\"encrypted_content\":\"gwenc1.eyJvIjp7ImNvbnRlbnQiOiJmQmZoSmxEbkhOUXVzcDdFeDl1TTB6RGVPNExoVlVmK21BdldMZGwwSkk2bCtURjhwbXcxb091emVvVFRSUlBHLzJGd3JCVy9hM1g3NCtTRTAwSFlkd2I1Z1hnTXFTZHVNZ1cyZlJsVE12S3VEaGE1TWlEODRIYy8wSjZFdnRmS1JkcjU5TGpZL29mK01Qbk9RWi9Od0phOEFKb1pHZmVnUkx6cUVudlQ1eUM3dTNDYWsvZzFrWWNmR2xkNDNzVmJMVGZiSjJIaDh5SWlPNlEyOExGVUNBN3FmRzQzV2VqM1BDSzkvZnk1U29tTVN2NUk5ZkoxeU9jc3IwY2ZSUGNmeStBIiwib3JpZ2luIjoieGFpIn19\"},{\"type\":\"function_call\",\"id\":\"fc_1790912123226_i789hz8nb4\",\"call_id\":\"call-37a29db2-38ae-4f89-95ef-321aba43ee4e-0\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"status\":\"completed\"}],\"usage\":{\"input_tokens\":1356,\"output_tokens\":49,\"total_tokens\":1405,\"input_tokens_details\":{\"cached_tokens\":1152},\"output_tokens_details\":{\"reasoning_tokens\":38}},\"model\":\"grok-4.7\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"default\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null,\"provider_metadata\":{\"xai\":{\"serviceTier\":\"default\"},\"gateway\":{\"routing\":{\"originalModelId\":\"spacexai/grok-4.7\",\"resolvedProvider\":\"xai\",\"fallbacksAvailable\":[],\"planningReasoning\":\"System credentials planned for: xai. Total execution order: xai(system)\",\"canonicalSlug\":\"xai/grok-4.7\",\"finalProvider\":\"xai\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"xai/grok-4.7\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"xai\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912121335,\"endTime\":1790912123229,\"providerRequestId\":\"49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"statusCode\":200,\"providerResponseId\":\"49e377e8-18fe-9c93-bc42-e78bc06ef8c8\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.001278\",\"marketCost\":\"0.001278\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.001278\",\"inferenceCost\":\"0.001278\",\"inputInferenceCost\":\"0.000984\",\"outputInferenceCost\":\"0.000294\",\"generationId\":\"gen_01M3XAWJENM4P0F2WHEDRHDGW0\"}}}}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"spacexai/grok-4.7\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"reasoning\",\"id\":\"rs_49e377e8-18fe-9c93-bc42-e78bc06ef8c8\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"The user wants me to use the get_weather tool exactly once, then reply exactly \\\"Paris is sunny.\\\" after the tool result.\\n\"}],\"encrypted_content\":\"gwenc1.eyJvIjp7ImNvbnRlbnQiOiJmQmZoSmxEbkhOUXVzcDdFeDl1TTB6RGVPNExoVlVmK21BdldMZGwwSkk2bCtURjhwbXcxb091emVvVFRSUlBHLzJGd3JCVy9hM1g3NCtTRTAwSFlkd2I1Z1hnTXFTZHVNZ1cyZlJsVE12S3VEaGE1TWlEODRIYy8wSjZFdnRmS1JkcjU5TGpZL29mK01Qbk9RWi9Od0phOEFKb1pHZmVnUkx6cUVudlQ1eUM3dTNDYWsvZzFrWWNmR2xkNDNzVmJMVGZiSjJIaDh5SWlPNlEyOExGVUNBN3FmRzQzV2VqM1BDSzkvZnk1U29tTVN2NUk5ZkoxeU9jc3IwY2ZSUGNmeStBIiwib3JpZ2luIjoieGFpIn19\"},{\"type\":\"function_call\",\"id\":\"fc_1790912123226_i789hz8nb4\",\"call_id\":\"call-37a29db2-38ae-4f89-95ef-321aba43ee4e-0\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"},{\"type\":\"function_call_output\",\"call_id\":\"call-37a29db2-38ae-4f89-95ef-321aba43ee4e-0\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":2048,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream" + }, + "body": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"gen_01M3XAWMDMFZPD7JDZ30E5D2VS\",\"object\":\"response\",\"created_at\":1790912128,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"xai/grok-4.7\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"sequence_number\":1,\"response\":{\"id\":\"gen_01M3XAWMDMFZPD7JDZ30E5D2VS\",\"object\":\"response\",\"created_at\":1790912128,\"status\":\"in_progress\",\"completed_at\":null,\"incomplete_details\":null,\"error\":null,\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"model\":\"xai/grok-4.7\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"auto\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":2,\"output_index\":0,\"item\":{\"type\":\"reasoning\",\"id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"summary\":[]}}\n\nevent: response.reasoning_summary_part.added\ndata: {\"type\":\"response.reasoning_summary_part.added\",\"sequence_number\":3,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"\"}}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":4,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\"The\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":5,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" user\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":6,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" asked\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":7,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" me\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":8,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" to\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":9,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" reply\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":10,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" exactly\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":11,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" \\\"\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":12,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\"Paris\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":13,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" is\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":14,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" sunny\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":15,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\".\\\"\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":16,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" after\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":17,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" the\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":18,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" tool\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":19,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" result\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":20,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\".\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":21,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" I\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":22,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" should\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":23,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" do\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":24,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\" that\"}\n\nevent: response.reasoning_summary_text.delta\ndata: {\"type\":\"response.reasoning_summary_text.delta\",\"sequence_number\":25,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"delta\":\".\"}\n\nevent: response.reasoning_summary_text.done\ndata: {\"type\":\"response.reasoning_summary_text.done\",\"sequence_number\":26,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"text\":\"The user asked me to reply exactly \\\"Paris is sunny.\\\" after the tool result. I should do that.\"}\n\nevent: response.reasoning_summary_part.done\ndata: {\"type\":\"response.reasoning_summary_part.done\",\"sequence_number\":27,\"item_id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"output_index\":0,\"summary_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"The user asked me to reply exactly \\\"Paris is sunny.\\\" after the tool result. I should do that.\"}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":28,\"output_index\":1,\"item\":{\"type\":\"message\",\"id\":\"msg_1790912129613_05dewmuzes0m\",\"status\":\"in_progress\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"\",\"annotations\":[],\"logprobs\":[]}]}}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"sequence_number\":29,\"item_id\":\"msg_1790912129613_05dewmuzes0m\",\"output_index\":1,\"content_index\":0,\"part\":{\"type\":\"output_text\",\"text\":\"\",\"annotations\":[],\"logprobs\":[]}}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":30,\"item_id\":\"msg_1790912129613_05dewmuzes0m\",\"output_index\":1,\"content_index\":0,\"delta\":\"Paris\",\"logprobs\":[]}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":31,\"item_id\":\"msg_1790912129613_05dewmuzes0m\",\"output_index\":1,\"content_index\":0,\"delta\":\" is\",\"logprobs\":[]}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":32,\"item_id\":\"msg_1790912129613_05dewmuzes0m\",\"output_index\":1,\"content_index\":0,\"delta\":\" sunny\",\"logprobs\":[]}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":33,\"item_id\":\"msg_1790912129613_05dewmuzes0m\",\"output_index\":1,\"content_index\":0,\"delta\":\".\",\"logprobs\":[]}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"sequence_number\":34,\"item_id\":\"msg_1790912129613_05dewmuzes0m\",\"output_index\":1,\"content_index\":0,\"text\":\"Paris is sunny.\",\"logprobs\":[]}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"sequence_number\":35,\"item_id\":\"msg_1790912129613_05dewmuzes0m\",\"output_index\":1,\"content_index\":0,\"part\":{\"type\":\"output_text\",\"text\":\"Paris is sunny.\",\"annotations\":[],\"logprobs\":[]}}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":36,\"output_index\":1,\"item\":{\"type\":\"message\",\"id\":\"msg_1790912129613_05dewmuzes0m\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Paris is sunny.\",\"annotations\":[],\"logprobs\":[]}]}}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":37,\"output_index\":0,\"item\":{\"type\":\"reasoning\",\"id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"The user asked me to reply exactly \\\"Paris is sunny.\\\" after the tool result. I should do that.\"}],\"encrypted_content\":\"gwenc1.eyJvIjp7ImNvbnRlbnQiOiI0Z3pHK3I4cS8vdStsVS90bVordkp2Mi9JMkZBdGFEdHUvOUJ3a2R3cUNBeU5hdGlZQ0RNQmxQaFRpSTdpbTgxanpwVXI2c3BZRXpDNm9LUTV0NTljVG9NRlQwVG80MnZrakRjdFh6QUkxVDRocHZkc1k2ckRleGp4K0tMRXFSZ2ZvazNuRGp1Y1loZTBsZllMN3Z6bFh6ZHpqMW9DUWg4Z3ciLCJvcmlnaW4iOiJ4YWkifX0\"}}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":38,\"response\":{\"id\":\"gen_01M3XAWMDMFZPD7JDZ30E5D2VS\",\"object\":\"response\",\"created_at\":1790912128,\"status\":\"completed\",\"completed_at\":1790912129,\"incomplete_details\":null,\"error\":null,\"output\":[{\"type\":\"reasoning\",\"id\":\"rs_f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"The user asked me to reply exactly \\\"Paris is sunny.\\\" after the tool result. I should do that.\"}],\"encrypted_content\":\"gwenc1.eyJvIjp7ImNvbnRlbnQiOiI0Z3pHK3I4cS8vdStsVS90bVordkp2Mi9JMkZBdGFEdHUvOUJ3a2R3cUNBeU5hdGlZQ0RNQmxQaFRpSTdpbTgxanpwVXI2c3BZRXpDNm9LUTV0NTljVG9NRlQwVG80MnZrakRjdFh6QUkxVDRocHZkc1k2ckRleGp4K0tMRXFSZ2ZvazNuRGp1Y1loZTBsZllMN3Z6bFh6ZHpqMW9DUWg4Z3ciLCJvcmlnaW4iOiJ4YWkifX0\"},{\"type\":\"message\",\"id\":\"msg_1790912129613_05dewmuzes0m\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Paris is sunny.\",\"annotations\":[],\"logprobs\":[]}]}],\"usage\":{\"input_tokens\":1423,\"output_tokens\":26,\"total_tokens\":1449,\"input_tokens_details\":{\"cached_tokens\":1280},\"output_tokens_details\":{\"reasoning_tokens\":22}},\"model\":\"grok-4.7\",\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"previous_response_id\":null,\"temperature\":1,\"top_p\":1,\"max_output_tokens\":2048,\"presence_penalty\":0,\"frequency_penalty\":0,\"top_logprobs\":0,\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":true,\"max_tool_calls\":null,\"text\":{\"format\":{\"type\":\"text\"}},\"truncation\":\"disabled\",\"reasoning\":{\"effort\":\"low\"},\"store\":false,\"background\":false,\"service_tier\":\"default\",\"metadata\":{},\"safety_identifier\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":null,\"provider_metadata\":{\"xai\":{\"serviceTier\":\"default\"},\"gateway\":{\"routing\":{\"originalModelId\":\"spacexai/grok-4.7\",\"resolvedProvider\":\"xai\",\"fallbacksAvailable\":[],\"planningReasoning\":\"System credentials planned for: xai. Total execution order: xai(system)\",\"canonicalSlug\":\"xai/grok-4.7\",\"finalProvider\":\"xai\",\"modelAttemptCount\":1,\"modelAttempts\":[{\"canonicalSlug\":\"xai/grok-4.7\",\"success\":true,\"providerAttemptCount\":1,\"providerAttempts\":[{\"provider\":\"xai\",\"credentialType\":\"system\",\"success\":true,\"startTime\":1790912123342,\"endTime\":1790912129779,\"providerRequestId\":\"f77452f9-e227-9c49-9ba4-68ca0a11e020\",\"statusCode\":200,\"providerResponseId\":\"f77452f9-e227-9c49-9ba4-68ca0a11e020\"}]}],\"totalProviderAttemptCount\":1,\"affinity\":{\"outcome\":\"skipped_below_min_prefix\"},\"clientSessionId\":\"a47cd177650917b88c4d443d7bd8be68\",\"clientSessionIdSource\":\"fingerprint\"},\"cost\":\"0.001082\",\"marketCost\":\"0.001082\",\"surchargeCost\":\"0\",\"gatewayCost\":\"0.001082\",\"inferenceCost\":\"0.001082\",\"inputInferenceCost\":\"0.000926\",\"outputInferenceCost\":\"0.000156\",\"generationId\":\"gen_01M3XAWMDMFZPD7JDZ30E5D2VS\"}}}}\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/vercel-native/rejects-invalid-credentials-without-retryable-classification.json b/packages/ai/test/fixtures/recordings/vercel-native/rejects-invalid-credentials-without-retryable-classification.json new file mode 100644 index 00000000000..bee5e34fd69 --- /dev/null +++ b/packages/ai/test/fixtures/recordings/vercel-native/rejects-invalid-credentials-without-retryable-classification.json @@ -0,0 +1,31 @@ +{ + "version": 1, + "metadata": { + "tags": [ + "prefix:vercel-native", + "provider:vercel-ai-gateway" + ], + "name": "vercel-native/rejects-invalid-credentials-without-retryable-classification", + "recordedAt": "2026-10-02T03:35:40.803Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://ai-gateway.vercel.sh/v1/messages", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"anthropic/claude-sonnet-5.5\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Hello\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"max_tokens\":32,\"thinking\":{\"type\":\"adaptive\",\"block_binding\":{\"prefix_mismatch_behavior\":\"drop_block\"}}}" + }, + "response": { + "status": 401, + "headers": { + "content-type": "application/json" + }, + "body": "{\"error\":{\"message\":\"Authentication failed. Create an API key and set in AI_GATEWAY_API_KEY environment variable: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai-gateway%2Fapi-keys%3FshowCreateKeyModal\",\"type\":\"authentication_error\"}}" + } + } + ] +} diff --git a/packages/ai/test/provider-options/vercel-ai-gateway.types.ts b/packages/ai/test/provider-options/vercel-ai-gateway.types.ts new file mode 100644 index 00000000000..3dff5d8fe38 --- /dev/null +++ b/packages/ai/test/provider-options/vercel-ai-gateway.types.ts @@ -0,0 +1,30 @@ +import { LLM } from "../../src/index.js" +import { VercelAIGateway } from "../../src/providers/vercel-ai-gateway.js" + +const gateway = VercelAIGateway.configure({ + apiKey: "fixture", + providerOptions: { gateway: { only: ["anthropic", "bedrock"], caching: "auto" }, reasoningEffort: "high" }, +}) +LLM.request({ + model: gateway.model("meta/muse-spark-1.3"), + prompt: "Hello", + providerOptions: { reasoningEffort: "max" }, +}) +LLM.request({ + model: gateway.messages("anthropic/claude-sonnet-5.5"), + prompt: "Hello", + providerOptions: { thinking: { type: "enabled", budgetTokens: 2048 } }, +}) +LLM.request({ + model: gateway.responses("openai/gpt-6-luna"), + prompt: "Hello", + providerOptions: { reasoningSummary: "detailed", cacheTTL: "1h" }, +}) +VercelAIGateway.configure({ + providerOptions: { + gateway: { byok: { anthropic: [{ apiKey: "fixture" }] } }, + upstream: { google: { thinkingConfig: { includeThoughts: true, thinkingBudget: 1024 } } }, + }, +}) +// @ts-expect-error A thinking budget is numeric. +VercelAIGateway.configure({ providerOptions: { thinking: { type: "enabled", budgetTokens: "many" } } }) diff --git a/packages/ai/test/provider/vercel-ai-gateway.recorded.test.ts b/packages/ai/test/provider/vercel-ai-gateway.recorded.test.ts new file mode 100644 index 00000000000..9c4756ec7cc --- /dev/null +++ b/packages/ai/test/provider/vercel-ai-gateway.recorded.test.ts @@ -0,0 +1,127 @@ +import { expect } from "bun:test" +import { Effect } from "effect" +import { LLM, LLMClient, LLMEvent, LLMRequest } from "../../src/index.js" +import { isRetryable } from "../../src/provider-error.js" +import { VercelAIGateway } from "../../src/providers/vercel-ai-gateway.js" +import { recordedTests } from "../recorded-test.js" +import { expectWeatherToolLoop, goldenWeatherToolLoopRequest, runWeatherToolLoop } from "../recorded-scenarios.js" + +const gateway = VercelAIGateway.configure({ apiKey: process.env.AI_GATEWAY_API_KEY ?? "fixture" }) +const recorded = recordedTests({ + prefix: "vercel-native", + provider: "vercel-ai-gateway", + requires: ["AI_GATEWAY_API_KEY"], +}) + +for (const item of [ + { + name: "messages Claude", + model: gateway.messages("anthropic/claude-sonnet-5.5"), + options: { reasoningEffort: "low" }, + reasoning: false, + }, + { + name: "messages Gemini", + model: gateway.messages("google/gemini-3.8-flash"), + options: { reasoningEffort: "low" }, + reasoning: true, + }, + { + name: "responses GPT", + model: gateway.model("openai/gpt-6-luna"), + options: { reasoningEffort: "low" }, + reasoning: false, + }, + { + name: "chat Muse", + model: gateway.model("meta/muse-spark-1.3"), + options: { reasoningEffort: "low" }, + reasoning: false, + }, + { + name: "messages DeepSeek", + model: gateway.model("deepseek/deepseek-v4.1-flash"), + options: { reasoningEffort: "high" }, + reasoning: true, + }, + { + name: "responses Grok", + model: gateway.model("spacexai/grok-4.7"), + options: { reasoningEffort: "low" }, + reasoning: true, + }, + { + name: "chat Claude", + model: gateway.chat("anthropic/claude-sonnet-5.5"), + options: { reasoningEffort: "low" }, + reasoning: false, + }, +] as const) { + recorded.effect( + `continues ${item.name} reasoning and tools`, + () => + Effect.gen(function* () { + const events = yield* runWeatherToolLoop( + LLMRequest.update( + goldenWeatherToolLoopRequest({ + id: item.name, + model: item.model, + maxTokens: 2048, + temperature: false, + }), + { providerOptions: item.options }, + ), + ) + expectWeatherToolLoop(events) + expect(events.filter(LLMEvent.is.finish).every((event) => event.providerMetadata?.gateway !== undefined)).toBe( + true, + ) + if (item.reasoning) expect(events.some(LLMEvent.is.reasoningEnd)).toBe(true) + }), + 120_000, + ) +} + +recorded.effect( + "rejects invalid credentials without retryable classification", + () => + Effect.gen(function* () { + const error = yield* LLMClient.generate( + LLM.request({ + model: VercelAIGateway.configure({ apiKey: "invalid-gateway-key" }).messages("anthropic/claude-sonnet-5.5"), + prompt: "Hello", + generation: { maxTokens: 32 }, + }), + ).pipe(Effect.flip) + expect(error.reason.http?.status).toBe(401) + expect(isRetryable(error)).toBe(false) + }), + 30_000, +) + +recorded.effect( + "automatically caches distinct Claude system blocks", + () => + Effect.gen(function* () { + const request = LLM.request({ + model: gateway.messages("anthropic/claude-sonnet-5.5"), + system: [ + { + type: "text", + text: Array.from( + { length: 250 }, + (_, i) => `Rule ${i}: Preserve provider reasoning state and keep stable prompt prefixes intact.`, + ).join("\n"), + }, + { type: "text", text: "Reply with OK only." }, + ], + prompt: "Confirm.", + generation: { maxTokens: 32 }, + providerOptions: { gateway: { only: ["anthropic"] } }, + }) + yield* LLMClient.generate(request) + const second = yield* LLMClient.generate(request) + expect(second.usage?.cacheReadInputTokens).toBeGreaterThan(0) + }), + 60_000, +) diff --git a/packages/ai/test/provider/vercel-ai-gateway.test.ts b/packages/ai/test/provider/vercel-ai-gateway.test.ts new file mode 100644 index 00000000000..81cf0a24841 --- /dev/null +++ b/packages/ai/test/provider/vercel-ai-gateway.test.ts @@ -0,0 +1,303 @@ +import { expect } from "bun:test" +import { ConfigProvider, Effect } from "effect" +import { LLM, LLMClient, LLMEvent, Message, CacheHint } from "../../src/index.js" +import { VercelAIGateway } from "../../src/providers/vercel-ai-gateway.js" +import { compileRequest } from "../../src/route/client.js" +import { it } from "../lib/effect.js" +import { dynamicResponse } from "../lib/http.js" +import { sseEvents } from "../lib/sse.js" +import { isRetryable } from "../../src/provider-error.js" + +it.effect("Gateway defaults preserve full model IDs and select the requested families", () => + Effect.gen(function* () { + const gateway = VercelAIGateway.configure({ apiKey: "fixture" }) + for (const [id, path] of [ + ["openai/gpt-6.1", "/responses"], + ["meta/muse-spark-1.3", "/chat/completions"], + ["spacexai/grok-4.7", "/responses"], + ["meta/llama-4", "/messages"], + ["openai/gpt-oss-120b", "/responses"], + ["anthropic/claude-sonnet-5.5", "/messages"], + ["google/gemini-3.8-flash", "/messages"], + ["moonshotai/kimi-k3", "/messages"], + ["deepseek/deepseek-v4.1-flash", "/messages"], + ]) { + const model = gateway.model(id) + expect(model.id).toBe(id) + expect(model.provider).toBe("vercel-ai-gateway") + expect(model.route.endpoint.path).toBe(path) + const compiled = yield* compileRequest(LLM.request({ model, prompt: "Hello" })) + expect(compiled.body.model).toBe(id) + expect(compiled.body.providerOptions).toBeUndefined() + } + }), +) + +it.effect("Gateway Messages applies inline cache markers only for Anthropic and Qwen models", () => + Effect.gen(function* () { + const gateway = VercelAIGateway.configure({ apiKey: "fixture" }) + const input = { + system: "System prompt", + tools: [{ name: "lookup", description: "Lookup", inputSchema: { type: "object" } }], + prompt: "Hello", + } + const claude = yield* compileRequest( + LLM.request({ ...input, model: gateway.messages("anthropic/claude-sonnet-5.5") }), + ) + expect(claude.body).toMatchObject({ + tools: [{ name: "lookup", cache_control: { type: "ephemeral" } }], + system: [{ type: "text", text: "System prompt", cache_control: { type: "ephemeral" } }], + messages: [{ role: "user", content: [{ type: "text", text: "Hello", cache_control: { type: "ephemeral" } }] }], + }) + const qwen = yield* compileRequest(LLM.request({ ...input, model: gateway.messages("alibaba/qwen3.8-max") })) + expect(qwen.body).toMatchObject({ + tools: [{ name: "lookup", cache_control: undefined }], + system: [{ type: "text", text: "System prompt", cache_control: { type: "ephemeral" } }], + messages: [{ role: "user", content: [{ type: "text", text: "Hello", cache_control: { type: "ephemeral" } }] }], + }) + const gemini = yield* compileRequest(LLM.request({ ...input, model: gateway.messages("google/gemini-3.8-flash") })) + expect(JSON.stringify(gemini.body)).not.toContain("cache_control") + }), +) + +it.effect("Gateway keeps distinct system blocks, cache hints, and upstream-scoped controls", () => + Effect.gen(function* () { + const compiled = yield* compileRequest( + LLM.request({ + model: VercelAIGateway.configure({ apiKey: "fixture" }).messages("anthropic/claude-sonnet-5.5"), + system: [ + { type: "text", text: "Stable", cache: new CacheHint({ type: "ephemeral" }) }, + { type: "text", text: "Project" }, + ], + prompt: "Hello", + cache: "none", + providerOptions: { + gateway: { only: ["anthropic", "bedrock"], order: ["bedrock"], zeroDataRetention: true }, + upstream: { + anthropic: { thinking: { type: "adaptive" } }, + bedrock: { reasoningConfig: { type: "adaptive" } }, + }, + }, + }), + ) + expect(compiled.body.system).toEqual([ + { type: "text", text: "Stable", cache_control: { type: "ephemeral" } }, + { type: "text", text: "Project" }, + ]) + expect(compiled.body.providerOptions).toEqual({ + gateway: { only: ["anthropic", "bedrock"], order: ["bedrock"], zeroDataRetention: true }, + anthropic: { thinking: { type: "adaptive" } }, + bedrock: { reasoningConfig: { type: "adaptive" } }, + }) + }), +) + +it.effect("Gateway effort settings lower to the selected API", () => + Effect.gen(function* () { + const gateway = VercelAIGateway.configure({ apiKey: "fixture", providerOptions: { reasoningEffort: "high" } }) + const messages = yield* compileRequest( + LLM.request({ model: gateway.messages("anthropic/claude-sonnet-5.5"), prompt: "Hello" }), + ) + expect(messages.body).toMatchObject({ thinking: { type: "adaptive" }, output_config: { effort: "high" } }) + const responses = yield* compileRequest( + LLM.request({ model: gateway.responses("openai/gpt-6-luna"), prompt: "Hello" }), + ) + expect(responses.body).toMatchObject({ + reasoning: { effort: "high" }, + store: false, + include: ["reasoning.encrypted_content"], + }) + const chat = yield* compileRequest(LLM.request({ model: gateway.chat("google/gemini-3.8-flash"), prompt: "Hello" })) + expect(chat.body).toMatchObject({ reasoning_effort: "high" }) + }), +) + +it.effect("Gateway Responses keeps cache-key and cache controls out of upstream namespaces", () => + Effect.gen(function* () { + const compiled = yield* compileRequest( + LLM.request({ + model: VercelAIGateway.configure({ apiKey: "fixture" }).responses("openai/gpt-6-luna"), + prompt: "Hello", + promptCacheKey: "opaque-session", + providerOptions: { cacheTTL: "1h", cacheAnchorItems: 1 }, + }), + ) + expect(compiled.body).toMatchObject({ prompt_cache_key: "opaque-session", cache_ttl: "1h", cache_anchor_items: 1 }) + expect(compiled.body.providerOptions).toBeUndefined() + }), +) + +it.effect("Gateway Messages preserves signatures and billing metadata through tool continuation", () => + Effect.gen(function* () { + const model = VercelAIGateway.configure({ apiKey: "fixture" }).messages("anthropic/claude-sonnet-5.5") + const response = yield* LLMClient.generate(LLM.request({ model, prompt: "Hello" })).pipe( + Effect.provide( + dynamicResponse((input) => + Effect.sync(() => { + expect(input.request.url).toBe("https://ai-gateway.vercel.sh/v1/messages") + expect(input.request.headers.authorization).toBe("Bearer fixture") + return input.respond( + sseEvents( + { + type: "message_start", + message: { usage: { input_tokens: 10, cache_read_input_tokens: 20 } }, + }, + { + type: "content_block_start", + index: 0, + content_block: { type: "thinking", thinking: "Plan", signature: "signed-state" }, + }, + { type: "content_block_stop", index: 0 }, + { + type: "content_block_start", + index: 1, + content_block: { type: "tool_use", id: "tool_1", name: "lookup", input: {} }, + }, + { type: "content_block_stop", index: 1 }, + { + type: "message_delta", + delta: { stop_reason: "tool_use" }, + usage: { output_tokens: 5 }, + provider_metadata: { gateway: { cost: "0.001", routing: { finalProvider: "bedrock" } } }, + }, + { type: "message_stop" }, + ), + { headers: { "content-type": "text/event-stream" } }, + ) + }), + ), + ), + ) + expect(response.events.find(LLMEvent.is.finish)?.providerMetadata?.gateway).toMatchObject({ + cost: "0.001", + routing: { finalProvider: "bedrock" }, + }) + expect(response.usage?.cacheReadInputTokens).toBe(20) + const compiled = yield* compileRequest( + LLM.request({ + model, + messages: [ + Message.user("Hello"), + response.message, + Message.tool({ id: "tool_1", name: "lookup", result: { found: true } }), + ], + }), + ) + expect(compiled.body.messages).toContainEqual({ + role: "assistant", + content: [ + { type: "thinking", thinking: "Plan", signature: "signed-state" }, + { type: "tool_use", id: "tool_1", name: "lookup", input: {} }, + ], + }) + }), +) + +it.effect("Gateway Responses replays encrypted reasoning alongside tool calls", () => + Effect.gen(function* () { + const model = VercelAIGateway.configure({ apiKey: "fixture" }).responses("openai/gpt-6-luna") + const response = yield* LLMClient.generate(LLM.request({ model, prompt: "Hello" })).pipe( + Effect.provide( + dynamicResponse((input) => + Effect.sync(() => + input.respond( + sseEvents( + { + type: "response.output_item.done", + output_index: 0, + item: { type: "reasoning", id: "rs_1", encrypted_content: "opaque-state", summary: [] }, + }, + { + type: "response.output_item.done", + output_index: 1, + item: { type: "function_call", id: "fc_1", call_id: "call_1", name: "lookup", arguments: "{}" }, + }, + { + type: "response.completed", + response: { + id: "resp_1", + output: [], + usage: { input_tokens: 10, output_tokens: 5 }, + provider_metadata: { gateway: { cost: "0.001" } }, + }, + }, + ), + { headers: { "content-type": "text/event-stream" } }, + ), + ), + ), + ), + ) + const compiled = yield* compileRequest(LLM.request({ model, messages: [Message.user("Hello"), response.message] })) + expect(compiled.body.input).toContainEqual({ + type: "reasoning", + id: "rs_1", + encrypted_content: "opaque-state", + summary: [], + }) + expect(compiled.body.input).toContainEqual({ + type: "function_call", + id: "fc_1", + call_id: "call_1", + name: "lookup", + arguments: "{}", + }) + }), +) + +it.effect("Gateway 401 and 403 failures are terminal and retain HTTP context", () => + Effect.gen(function* () { + for (const status of [401, 403]) { + const error = yield* LLMClient.generate( + LLM.request({ + model: VercelAIGateway.configure({ apiKey: "fixture" }).chat("openai/gpt-6-luna"), + prompt: "Hello", + }), + ).pipe( + Effect.provide( + dynamicResponse((input) => + Effect.succeed( + input.respond( + JSON.stringify({ error: { type: "authentication_error", message: "Invalid credentials" } }), + { status, headers: { "content-type": "application/json" } }, + ), + ), + ), + ), + Effect.flip, + ) + expect(error.reason.http?.status).toBe(status) + expect(isRetryable(error)).toBe(false) + } + }), +) + +it.effect("Gateway authentication supports OIDC fallback and affinity without reading session state", () => + Effect.gen(function* () { + for (const api of ["messages", "responses", "chat"] as const) { + yield* LLMClient.generate( + LLM.request({ + model: VercelAIGateway.configure()[api]("openai/gpt-6-luna"), + prompt: "Hello", + promptCacheKey: "opaque-session", + }), + ).pipe( + Effect.result, + Effect.provide( + dynamicResponse((input) => + Effect.sync(() => { + expect(input.request.headers.authorization).toBe("Bearer oidc-fixture") + expect(input.request.headers["x-session-affinity"]).toBe("opaque-session") + return input.respond("{}", { status: 400, headers: { "content-type": "application/json" } }) + }), + ), + ), + ) + } + }).pipe( + Effect.provideService( + ConfigProvider.ConfigProvider, + ConfigProvider.fromUnknown({ VERCEL_OIDC_TOKEN: "oidc-fixture" }), + ), + ), +)