diff --git a/packages/ai/src/providers/cloudflare-ai-gateway.ts b/packages/ai/src/providers/cloudflare-ai-gateway.ts index d516440b7c3..a122e98da56 100644 --- a/packages/ai/src/providers/cloudflare-ai-gateway.ts +++ b/packages/ai/src/providers/cloudflare-ai-gateway.ts @@ -42,14 +42,24 @@ export type Settings = ProviderPackage.Settings & export const baseURL = (input: GatewayURL) => { if (input.baseURL) return input.baseURL - if (!input.accountId) - throw new ProviderConfigurationError({ - provider: id, - message: "CloudflareAIGateway.configure requires accountId unless baseURL is supplied", - }) - return `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(input.accountId)}/ai/v1` + return `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(requireAccountId(input))}/ai/v1` } +// Cloudflare's REST API rejects parts of the Anthropic Messages and OpenAI Responses request shapes, such as +// system prompt blocks and `minimal` reasoning effort, so these models use the provider-native gateway endpoints. +const passthroughURL = (input: GatewayURL & GatewayOptions, provider: "anthropic/v1" | "openai") => + `https://gateway.ai.cloudflare.com/v1/${encodeURIComponent(requireAccountId(input))}/${encodeURIComponent(gatewayId(input))}/${provider}` + +const requireAccountId = (input: GatewayURL) => { + if (input.accountId) return input.accountId + throw new ProviderConfigurationError({ + provider: id, + message: "CloudflareAIGateway.configure requires accountId unless baseURL is supplied", + }) +} + +const gatewayId = (input: GatewayOptions) => input.gatewayId?.trim() || "default" + export const responsesRoute = OpenAIResponses.route.with({ id: "cloudflare-ai-gateway-responses", provider: id, @@ -70,16 +80,12 @@ export const route = OpenAIChat.route.with({ export const routes = [responsesRoute, messagesRoute, route] -const auth = (input: LanguageModelOptions) => { - if ("auth" in input && input.auth) return input.auth - return Auth.optional(input.gatewayApiKey ?? ("apiKey" in input ? input.apiKey : undefined), "apiKey") +const credential = (input: LanguageModelOptions) => + Auth.optional(input.gatewayApiKey ?? ("apiKey" in input ? input.apiKey : undefined), "apiKey") .orElse(Auth.config(authEnvVars[0])) .orElse(Auth.config(authEnvVars[1])) - .bearer() -} const headers = (input: LanguageModelOptions) => ({ - ...(input.gatewayId === undefined ? {} : { "cf-aig-gateway-id": input.gatewayId.trim() || "default" }), ...(input.metadata === undefined ? {} : { "cf-aig-metadata": Schema.encodeSync(Schema.fromJsonString(Schema.Unknown))(input.metadata) }), @@ -90,31 +96,42 @@ const headers = (input: LanguageModelOptions) => ({ ...input.headers, }) -const modelID = (input: string | ModelID) => { - const value = String(input) - if (value.startsWith("workers-ai/")) return value.slice("workers-ai/".length) - if (value.startsWith("anthropic/")) return `anthropic/${value.slice("anthropic/".length).replaceAll(".", "-")}` - return value -} - export const configure = (input: LanguageModelOptions) => { - const defaults = { + const custom = "auth" in input && input.auth ? input.auth : undefined + const rest = { endpoint: { baseURL: baseURL(input) }, - auth: auth(input), - headers: headers(input), + auth: custom ?? credential(input).bearer(), + headers: { "cf-aig-gateway-id": gatewayId(input), ...headers(input) }, http: input.http, providerOptions: input.providerOptions, } - const responses = responsesRoute.with(defaults) - const messages = messagesRoute.with(defaults) - const chat = route.with(defaults) + // A custom baseURL stands in for the REST API, so every model keeps using it with gateway model IDs. + const native = input.baseURL === undefined + const passthrough = (provider: "anthropic/v1" | "openai") => + native + ? { + ...rest, + endpoint: { baseURL: passthroughURL(input, provider) }, + auth: custom ?? Auth.bearerHeader("cf-aig-authorization", credential(input)), + headers: headers(input), + } + : rest + const responses = responsesRoute.with(passthrough("openai")) + const messages = messagesRoute.with(passthrough("anthropic/v1")) + const chat = route.with(rest) return { id, model: (input: string | ModelID) => { - const wire = modelID(input) - if (String(input).startsWith("openai/")) return responses.model({ id: wire }) - if (String(input).startsWith("anthropic/")) return messages.model({ id: wire }) - return chat.model({ id: wire }) + const value = String(input) + if (value.startsWith("openai/")) + return responses.model({ id: native ? value.slice("openai/".length) : value }) + if (value.startsWith("anthropic/")) + return messages.model({ + id: native ? value.slice("anthropic/".length).replaceAll(".", "-") : value, + }) + if (value.startsWith("workers-ai/")) + return chat.model({ id: value.slice("workers-ai/".length) }) + return chat.model({ id: value }) }, configure, } diff --git a/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-text.json b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-text.json new file mode 100644 index 00000000000..d1e81dc989d --- /dev/null +++ b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-text.json @@ -0,0 +1,38 @@ +{ + "version": 1, + "metadata": { + "provider": "cloudflare-ai-gateway", + "route": "cloudflare-ai-gateway-messages", + "transport": "http", + "model": "claude-haiku-4-5", + "tags": [ + "prefix:cloudflare-ai-gateway", + "provider:cloudflare-ai-gateway", + "text", + "golden" + ], + "name": "cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-text", + "recordedAt": "2026-09-30T04:35:43.657Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/anthropic/v1/messages", + "headers": { + "anthropic-version": "2023-06-01", + "content-type": "application/json" + }, + "body": "{\"model\":\"claude-haiku-4-5\",\"system\":[{\"type\":\"text\",\"text\":\"You are concise.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Reply exactly with: Hello!\"}]}],\"stream\":true,\"max_tokens\":40,\"temperature\":0}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: message_start\ndata: {\"p\": \"6247a7b95b5e3f\", \"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_011CfZ16hXSVRfaXBDtUBKis\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"container\":null,\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":18,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"},\"diagnostics\":null}}\n\nevent: content_block_start\ndata: {\"p\": \"f9b\", \"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"p\": \"cc76aa8\", \"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"p\": \"94f\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello!\"} }\n\nevent: content_block_stop\ndata: {\"p\": \"1a9d8\", \"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"p\": \"0cb3c23\", \"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"container\":null},\"usage\":{\"input_tokens\":18,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":5} }\n\nevent: message_stop\ndata: {\"p\": \"6ef9de29973ed\", \"type\":\"message_stop\" }\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-tool-loop.json b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-tool-loop.json new file mode 100644 index 00000000000..b1605c0dc22 --- /dev/null +++ b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-tool-loop.json @@ -0,0 +1,58 @@ +{ + "version": 1, + "metadata": { + "provider": "cloudflare-ai-gateway", + "route": "cloudflare-ai-gateway-messages", + "transport": "http", + "model": "claude-haiku-4-5", + "tags": [ + "prefix:cloudflare-ai-gateway", + "provider:cloudflare-ai-gateway", + "tool", + "tool-loop", + "golden" + ], + "name": "cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-tool-loop", + "recordedAt": "2026-09-30T04:35:45.222Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/anthropic/v1/messages", + "headers": { + "anthropic-version": "2023-06-01", + "content-type": "application/json" + }, + "body": "{\"model\":\"claude-haiku-4-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\":80,\"temperature\":0}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: message_start\ndata: {\"p\": \"f2f6a212\", \"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_011CfZ16jzmxrCrHLxeCxrL8\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"container\":null,\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":593,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"},\"diagnostics\":null} }\n\nevent: content_block_start\ndata: {\"p\": \"7\", \"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_019hb1DDwKBNMMeaKjZf1CpS\",\"name\":\"get_weather\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: ping\ndata: {\"p\": \"e112b39d9a4869\", \"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"p\": \"12d\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"2\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"city\\\"\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"f6bb01e5ae5c1c7\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\": \\\"Pari\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"c26aa17\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"s\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"p\": \"16f665\", \"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"p\": \"e00cc845875\", \"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null,\"container\":null},\"usage\":{\"input_tokens\":593,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":54} }\n\nevent: message_stop\ndata: {\"p\": \"5082e73ce8055a\", \"type\":\"message_stop\" }\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/anthropic/v1/messages", + "headers": { + "anthropic-version": "2023-06-01", + "content-type": "application/json" + }, + "body": "{\"model\":\"claude-haiku-4-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_019hb1DDwKBNMMeaKjZf1CpS\",\"name\":\"get_weather\",\"input\":{\"city\":\"Paris\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_019hb1DDwKBNMMeaKjZf1CpS\",\"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\":80,\"temperature\":0}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: message_start\ndata: {\"p\": \"ff048fa113d17ffc\", \"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_011CfZ16ooU6seiMzxt69doW\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"container\":null,\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":668,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"},\"diagnostics\":null} }\n\nevent: content_block_start\ndata: {\"p\": \"5172b1\", \"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"p\": \"d\", \"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"p\": \"7f847273c\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Paris\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"1de4\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" is sunny\"}}\n\nevent: content_block_delta\ndata: {\"p\": \"d61\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\"}}\n\nevent: content_block_stop\ndata: {\"p\": \"29638a9b46e21ecd\", \"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"p\": \"8e5f9442b03f76\", \"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"container\":null},\"usage\":{\"input_tokens\":668,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":7} }\n\nevent: message_stop\ndata: {\"p\": \"97d9afcead79c7a\", \"type\":\"message_stop\" }\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-text.json b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-text.json new file mode 100644 index 00000000000..b84a73eda4c --- /dev/null +++ b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-text.json @@ -0,0 +1,37 @@ +{ + "version": 1, + "metadata": { + "provider": "cloudflare-ai-gateway", + "route": "cloudflare-ai-gateway-responses", + "transport": "http", + "model": "gpt-5-nano", + "tags": [ + "prefix:cloudflare-ai-gateway", + "provider:cloudflare-ai-gateway", + "text", + "golden" + ], + "name": "cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-text", + "recordedAt": "2026-09-30T04:36:07.656Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/openai/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"gpt-5-nano\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly with: Hello!\"}]}],\"instructions\":\"You are concise.\",\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"max_output_tokens\":2000,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: response.created\ndata: {\"p\": \"6e564fc3bf3\", \"type\":\"response.created\",\"response\":{\"id\":\"resp_0137cf30a4752bdf016abc91b6263487d19ea9f96c7605459a\",\"object\":\"response\",\"created_at\":1790742966,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"You are concise.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"p\": \"f61c4ed2a9768\", \"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0137cf30a4752bdf016abc91b6263487d19ea9f96c7605459a\",\"object\":\"response\",\"created_at\":1790742966,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"You are concise.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"p\": \"01\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_0137cf30a4752bdf016abc91b69ae887d1af96950defb06f97\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJG2MoCXBYbRlRsokydYprE0lt8iLr1a1iUQbNLeerXyd1MY4Fm9MfjJLFaGzGPJy6wwZUZmm6nsZ7LP1VprwIAnv2kfNTBoO57je5cZ78dkzJzOOLYnQyaiSPrbXTXKG8JQfKrrt0qN7x5-eiXD-SIcQa8E9Or_ZugNqIYn7UfiS3YC5Xupjij5eCgl43kmMzNLNoQgBOv5sTQkj4saT7b0NltrY_coexW7o7HA3YJU02_aKqV6YSKKKMzZkVUw8Qt71Jwbg28x-Udpk9Wk6uzqqMSFRchkQJ4oT8HGsFO6MfHo-eLUNqGI24ZJlYcHI_CVW8IkjcpGWtv7dHzoOIAbQ3tTx09taixBP3PvaIcD9Qn5YsUV7dE3NncXID-eAnjgs0WmP9Tz8vvLXZrYiV4Ry0S_gIAUPFKC-3Z9e7yQ395JWPeWk7K1eT9y0FmTGHNZ6MVfzqBahukSbogL1Q1izblwxu6dTVMfQbo0n7IEi6HhfDYIzsHNbpYDDL54yG0ncHNy4fSu4dnYjuZrokgqThIOCrpeYcJq7xTN9cTU5L-AQz9_EEraVt2swKnl-4sJ1V8k6j0eJIZMK8yTAgOAUiI3jFBLscCRGjmmWLft2UDMt8h6SoKFXyDmTyRZJ2Mbm0wBIvKmH-EZM5-xc2T_57XV9vI-BaPZYrbxT7jtOuK8oqnpZRtLGyDKiUYxibnU1J2AUh3HbXz-nMrnjA6WxU9NXCVf-1WnVQyJfFAIRD0Kpbt6c6rXOJw7aMGLHATOFFG4716vVg79CvT-ll9_AsUNQwyby8sRUYi-Y05x4vxPnkegilDuhnytDt6uYqNRFertTCaSCIg-u96yHrg3r8T9zBpy3UqogKq2pkG-WrkNxIwEa0LMOqlwQwrnIDf72eV9wFpg9M67ezVptNFTkCLi3BOB4Gfi4SFkmOBBE4tfjymktfUWGk9g1uAa3VAsRRSBcbMqwbnSS9s1flwOp7NkHffGHdDP6bo66Y-YcsryokhHWi1s3ikK7cvp5n6BdpypWLRQq4za25PGf3uj7c17XKM6YoiV36wuu4AKp6XAX2iJ1cIDIqLETLmPJowSL7bj-Nodly_VatRK8h4yjbCExGdk6WURCo93KaEc1h9lbFKuRWeYGNPIByblnILU4JSoOm40K1aP0DT_KKJ6Gg==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.output_item.done\ndata: {\"p\": \"2fe6743f311de\", \"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_0137cf30a4752bdf016abc91b69ae887d1af96950defb06f97\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJG3gRV3VHJfHPkeyz8hrmbpzFNdsSUxXsSgIJAc7Z5rVjrK-eBH6uM3cuwf6oFRHDk5gl3tjbkubNIsNuX9QWS5qKZmni4YyzHa00eDWaF0mdEB41BiwcO085Ef-zgU9dsblogPVbaAkCx9AG01rLpeJvmEZrG3ZceeHDer7iebUpvoFSeBLRnuUn192MSKUTF0602CdbtQvzI39HuCijTVGiEh4dWgs5BoAvnD0tfNeWrXZBOueaf_CFInJPuoR4eyAORHV5pTn8fbAEiv8mVqKvHI0OL7GvA0E1Yg7c5rlrLgJj0Ey6P5POAYCL7KOkT3kw1LKbA4glCNOiZC54XWY2V7jI1bgquxOgj0FG3Gstl9B7IMncSTuNgKZONEJ9LWWVWOtLl6kMRq0gLZqhpKUoNqkgKpiS7noUkEQJJ7n-tv1djLIfrHdwWDO2XJhb0ouXC6qgn-TKnu1eBKCaGcWvKO7kanbyWJp1PUdXGqqf394_6TOji_abNkXWzid0r0YxoG4opHQK0RXOpznzQlMQyw5f3Kg8Ql-qSpukfVbdZvNjrFnTmfsscfP7X0ZATGlb-ocFAaRQry316RSvrrHU_GuGcMeNXrk_IFJckdaVYjqv0XbL10gSq7_cSoPFthIznwNcUnqdkJ38GXHLQyPMIbW7c3RB6em7J6cBglpLAr6nBOZ-Vun3ZK8eGnFCmUTEsKFvJ5zxo-pIgS4hKO2ZWvcWjFcBZ5VOFzS5aeAMddpDFnAxjQK3_VvllWZ9uD0FbmNl6XGJC5Zlx6bOxGZXyal9CFFkyoAMyM8LjqMVo7i_smIUX5d9H0nCWy7jta-NT7aFERV0NQ1mjG3nzjuIZo-t1jyd40HEFceMEil5ee9N0KHTiO_17a_13egb6GkQNz77mcuxS2l5dyC9qme-682FMFHYzEz1njTtJbqtX65vh1T8yuiZEFMZKwsxjl-6qVl5Qqf72iG4EidABetFStCrFD5clZVn-BeqtSnj5n-bstkeyuiGAOtPVhr_1-L_UPsNw7LHk98Iaiq2GIHLnfXHxeyS6OCNp8ReEAdJvOp6fi_HRy0GgM_vw5bf-jNUtKBuwJISJnIYCE-egLgqrAI2crHpwi15u9ovlAvDle93hSh9ZVW5wk28R0JDqyDN2rhL7wbvVB7nuD-tYvVPTxcU-nWtOsc9kKVdD5xBDF8FxiEdSuP8xyZYqv7e0tLAyeFwdwevzBTZ8mnrve1lgWjPkXtVJ31ZiigMiI2Ko0cqoNsJ1p6wHjRgQpJZugGNQMkO-rQ6E5VheO6hyAFAe5Lh_CfA376XksVaR-ope-n4H5KDw-MXCWILRj8jV09sw6743nq3cQLXRCpsf5N6r6ptv8hp6J1mq3ohkMqXBlL0BPoaQR9zO6nb168zk5rnvdFx7JTF6NFgtT8GItmEbcuGzALOhb3CZgwGEtqwdZM_cYnoWuLOYnxPq74Nm3LP2B8fowBVlPPS8eYB-DePp3_UwaYnvCH0fiC55fJxlVcQ3P6AsNYqyfVRQyDrjSi3zWbBPdUbrT7O7qyeORfwOmeZvIAgm3uUkhX-0JtC9dS2gPSch0DKqyt45dQbKPpA_iu_F_8uG9CCJirP70zwBn-Ipgkm6LHKwvV1Z_RnmATvFb-DFPlX63RySIAQw9htd8X4vV8-eGjI5xPwSZLsnzXPceJoDknttYtv-Fg3KeIpHd64DALwcXmvsIzYAQUN9v6QDRIfb_UENV2xipIjhgelvzLe-xvtVMSB4yLtSM41-cDn55WrbhqGZVCwQ4z-pGSlH_lyWvOcHIAj71ivYmitVaXsdIxQehonsL5cfFAIWrCWS06Eykd11C3UajQ90lJzsT-XpaAhtGuRueGY-Hc5WqHNJ2zF7WbJOuJ4rdVlJc2fNnotx5kSevaLQ_olV6EBpVkrEAymJyeZRE1jl4cAzFe3Fm_aobDNnED3bTw47RBDS6aMtpn87HDZ-LiBPOJ9FmIVouMBjliP7MglN9-N6yuK2qzldXEx099VRNWkEdoCt2QbzgA8NJbfusvHCcIQ81TtVhD-ViYpoRTh4lEAI4FbkNEtkBBc6mMI1EyFVm2HWKefww_A559_ljhBOPFUtrc2fgLJT8t3ZM_1aGWm8b4kl4Bj1QGZGabZwsLSahsvI8dq2PQhkx-dM_\",\"summary\":[]},\"output_index\":0,\"sequence_number\":3}\n\nevent: response.output_item.added\ndata: {\"p\": \"9354da\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":4}\n\nevent: response.content_part.added\ndata: {\"p\": \"8ad\", \"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":5}\n\nevent: response.output_text.delta\ndata: {\"p\": \"bd38fb7\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Hello\",\"item_id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"logprobs\":[],\"obfuscation\":\"MEc0Nkd9X9b\",\"output_index\":1,\"sequence_number\":6}\n\nevent: response.output_text.delta\ndata: {\"p\": \"5fb78c06d4e4\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"!\",\"item_id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"logprobs\":[],\"obfuscation\":\"gtCmtHD5K1yxKRL\",\"output_index\":1,\"sequence_number\":7}\n\nevent: response.output_text.done\ndata: {\"p\": \"cd4b29\", \"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":8,\"text\":\"Hello!\"}\n\nevent: response.content_part.done\ndata: {\"p\": \"2dccc0229c\", \"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hello!\"},\"sequence_number\":9}\n\nevent: response.output_item.done\ndata: {\"p\": \"343a7f571bac52\", \"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hello!\"}],\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":10}\n\nevent: response.completed\ndata: {\"p\": \"2a2b96d\", \"type\":\"response.completed\",\"response\":{\"id\":\"resp_0137cf30a4752bdf016abc91b6263487d19ea9f96c7605459a\",\"object\":\"response\",\"created_at\":1790742966,\"status\":\"completed\",\"access_programs\":null,\"background\":false,\"completed_at\":1790742967,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"You are concise.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[{\"id\":\"rs_0137cf30a4752bdf016abc91b69ae887d1af96950defb06f97\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJG38TX-aw0RCy87cZ5zFTjCihfK0iZnrrzy6ZvfgWKTAg01ARU-DNx9h7A_5EdiTZ_6LTAZnBzHhkhU6deIHtDYg713DagmNLNFFmnIXhkCUqVXGtZgDyc89Jhhi4xrLC35H_0KtuZ9WGM14nTtDySW2lxb6ndshrz_Qn7f4x4sPdz532PjQikN-kkXKAf6yECQBxOYF3xBlrpdyGMAVEE7te7Yo2b1YeweAhSoP3D7ndU7QPg7lnIFy2VAwStYrRxVb_aIKPCpYNpMa7h_89ceIiZGq_oUCbFScF7OQaqZWVXCcA5gYHOIXYKToi4ukXCmnknjezCQ8cBYJ4ND_QslDfStvZUhzsq2lePa1o8DVO_apqqjgHHps1Ma1CBHOUmFpBAbj88cX9SntkLJS32KfaT_tAAfT9K3lOYd8UPgLBZ9FBjBhN7LdoDyBHp0pfgReOc4yWn7DrwvSmVPFppwmXSsSJ3R9nJBrUYxuYLmnZU2CajrfSUje_AbGEDsPRSwGjmMGu57B3O5ydl9G2frsiakf0n1xC2DL62XHda032rIGfS7PV6cqKjeTWA9iRoJxBuNrOUHwozKP7ZB-wi6ltS3z28Z-jJxT_Dqy5jqLrso5ZJJGzrPey_xA3JdYj0lYGxvbigZykKiav0lhtwv6E7BXXJG2URFDw8HbwxpTTE6PRuxeLn8zftyurYK8kvjyRqaVGoeDI-r6CTh-93ekRpRq5Ylkrv4WAS2l1AfACpTp3Wls_S7lZTHop9fZX-eg1YQOlMGyGR2A63kBQyMxDsYQ8A-P5BtP7XK_2_5tYYlZ_jh9fFzv8ZLRrpikpJjhUZ3k4o8n9QJEYg-cBni7xTrlnwNf9sboSEh_x23joFw1qnjgjjMHxf5rVndwoRqeUhJvFe_Q-4yabW1ntdWEOwnXITZQ1HhEAmxqvd_hndLSldE_GqOkTQDUz_NTViDgoI--BeX7VE-2Q6xCo5h8z1JAbr18cdbIF9nAa9Opm1KQf9zxCs4BK9uMs7guPGLJQKD3tw3Fc3vCT0P0D-z9H6KkF0OozCygS5ZQl6-X8wBzqq147nCPx6sgacinHgxbwbXKwx6bTpHR1-F9m6tyNZcYkyr1XdvTDNgHNvwi6JiW8SCwV4gLrOBoYWwixOWh9EyamX3k4LnY3kjtZoI8UH1aydmm1KmerqbmtahHsrKxes35wiv9P2dHDo8PLjWJ7NV5TjvKdPCvu843A8OStsf9Squj9_z_HgqHLo-7C6vzriboPfJzNXbbXfiF4upaFDsoxQMnhOCVHlahuZnQkZD2Zw5YJOgI5FsQoLIqG1yRzADHcdZeA5IfyylKkcl_BrcdfoxFccGAyIFPPZ8kApXsATT1-rz1HExqQPMeVqFrMGNfOQQcL4F4RR22x_Y2ORPvIvw7G7RMFn8jy2fd5416tuSmXvXvHpyyUgDTkgrrQgRkjo_KmqZH6Yhu-u2XvQMTdmBc7dufEuv1KS3Na8gvSfGSqsoxsgHJ7rtaKPcqo4CA9pAtX1kXsX3AEgn_e55A9tN2v0ljderDxLmnMcscDPgokm-iIoF7iGZLe58wEBX9X7jjKwqWfaAn19tKhvWwv1Vddi6s7ccviUlPy7PLux9_91RiXPtvT4M8GTfywcd2XmvAQLN_vrMB4CCk6CoGewmC6O4OOC7ALo_ZW3nayRDj9ZNX6TVFKbD-rJuvQqjdQ1pylt9uPdNh4gBf-NtO5Fxsk3lY6S_eqMbOax3E0KMkcz75juYQFj82qAUg1uNdJhWl_kr9tuf9I7Dl7aZLbvB9aSW7F3TVCi8vvAxs7HbLIOE2WnhbvnSlPn-rOflGaydWrcJ8cIR62lp0N2wssSt-lal7Aj2Xg5NgWUZ3NdaPn3_uCOaAuB0NRAYJuiwI_O9N0qhVt9agSno3kywiaXU8Cdhu3tQD-wW7tlRZTn3PdU_dL2ntSEShiyeMxGu77doLwI5fAM0hM6iYJEzbYlAWd1DCVXUd79MPq9bGS99BAMcSh14v6csOfp-8JxTnXzAopDGTCa5YahSIig3LS5YW7jbElBfUHypfd6p_4tPTtKWfs0Agqa9gBe799Z_zKz4rbeDVwnn2Tu--hnAWfYMle3dqfR3CrMYge-i-XCTj6geyI1g8ykJ8Ff1F7itLaP7C1oZ30YmqOBs\",\"summary\":[]},{\"id\":\"msg_0137cf30a4752bdf016abc91b78e9c87d18bb405e1bbe219ec\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Hello!\"}],\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":20,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":164,\"output_tokens_details\":{\"reasoning_tokens\":128},\"total_tokens\":184},\"user\":null,\"metadata\":{}},\"sequence_number\":11}\n\n" + } + } + ] +} diff --git a/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-tool-loop.json b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-tool-loop.json new file mode 100644 index 00000000000..559e6fbc186 --- /dev/null +++ b/packages/ai/test/fixtures/recordings/cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-tool-loop.json @@ -0,0 +1,56 @@ +{ + "version": 1, + "metadata": { + "provider": "cloudflare-ai-gateway", + "route": "cloudflare-ai-gateway-responses", + "transport": "http", + "model": "gpt-5-nano", + "tags": [ + "prefix:cloudflare-ai-gateway", + "provider:cloudflare-ai-gateway", + "tool", + "tool-loop", + "golden" + ], + "name": "cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-tool-loop", + "recordedAt": "2026-09-30T04:36:34.984Z" + }, + "interactions": [ + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/openai/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"gpt-5-nano\",\"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\"],\"max_output_tokens\":2000,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: response.created\ndata: {\"p\": \"d82f1d10b60\", \"type\":\"response.created\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91ce40f887d189169a2d89f2ee42\",\"object\":\"response\",\"created_at\":1790742990,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"p\": \"0b0cb44c44\", \"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91ce40f887d189169a2d89f2ee42\",\"object\":\"response\",\"created_at\":1790742990,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"p\": \"608c6368a0c703ea\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_06bc195765a8c9c7016abc91ce881c87d18e71310b9a21b3ca\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJHOLa_CYgj0Ynl-_tBncVpCPOfdueKV7-3xsbVVIXhZFUdhtEfjPYHW85-TecrFUmkr7jMw4OsT2Zckq7xkxVfZ8kXNObzjU6N6t35hp8hIJ9CbGmJBsxZBQceHHtZgQrOVWCKJ7z1B1k5zsJ8lbSRkLVAahsCEwg-tYDzQAkYUcqlK7VS62sKLLfDMYpSMCvv-y-5v9I4S74P0D2Zr7znJBap72RK0tHjN-d3vdjOaxMMkLP8JlK6-HlOWabzVEPWmsdXFPFzjWuRjAOn5ILrcQzu5T8kPLjon2kW4XuwwcXeI_7UvFmt9yRfkTlVFVr-hRcSad9AjBHHYx3nmvlhM2nVuKggGolqpwdKLR87sccTmbo0kzYp8Av1yHPJ6xKxAIJYZO_XDdvXGK1vu6WFqxe37LXpWIAvJ1BbXo7yMdV2tZshqP2WBiYdILkMiZFV4VDuzuOYEx3_56QyL4sdh0p3_mxXfgjEiLNzloBrq9VtPL1YLucqzAZwCDuhtwKLC3zZ1awSC_exx7thmBbULObOBfOhWN9oIG53MrLbpJVN4MlENvtLoq7l_92k0VbPcOGDgJcmJjYOy-p7P_YDQ_Y9WR0oNpCNRFfarqGBOzegO7_UPRVUEKJeb3xx2e6-9HnrjC9jyAFs4MI54uNfKMbF7a6p55NWaEtvbTn3124XDaUD2qeSChCoAv2KFXxTIIyQeOVgVBQbHW8IamL9Eh0RvjEpg5WZqgRK4QdRZ8fiFL3lLV5stsqVvNz_1LAnGs4bnH69pmmlfO0E2C8LLhLhRVh9hcEXnKwpA6URICdn58uEPKVr9tDmTyaJbiZx_4lPbVSQ_T2ncKL8pL8gK4odQDdhe_XjdqF9i5N_t29H8A3XUDkXOgd5zyMepV1GCfKm7DaJHYXIIIjr_q4xheZFzxMl1KMQX_a9xbc-KRvOj2apHphqo5TsmYOFl5QYyt3wWllpmUZ6c9oSVOg159RFyR7UUdjGJDN8aN9pXlDjxYjR76Q9Xvmun2duw37Abi7TXl2BzJ2Bq9VFLGW_7CB_kv0vxNijP2K2XuD0Ge8tfM7HfU_gFXN-WXym5GdN6DMCxAg7whaBAj9su1wEaI11VQBTF0s41glo-V4DTcLyKTNFjScck_6a_vEnmFE57IEToGK_akhdx_SzKS9rHrA==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.output_item.done\ndata: {\"p\": \"79994\", \"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_06bc195765a8c9c7016abc91ce881c87d18e71310b9a21b3ca\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJHSN68IWwhWq9YTPxs-e-OeLdOqfdkkIEP_1hc9f7k6yH9tGZKLUC2OPF0avZ7xaJQp8iOR_gPt3z0TGQ1T0e4M9OR6boMFOA_C4TwinAF1POgbZLI-0SUDkAm1PZSaO0Ic7DBh1rRT6biled_-iWs0ke3FP3nFZit5Ve16AI1TdL4c9QERNCcZFsD5neV8eBeABWarZ8SMFfxcYJ5y8QkpDOL1ShqenD22Df7fDy8cdkYWybEEZcAfgXWSfQK2hRwSNYN_BDV9CfKj-cUTePwf0D2vLm-2xd9LGt-KiLPbsp9EpGXP41ra6-qHRLeu7MO64XWIu2vsf9qAflOjp7MOSuTJ3ZvDQoTNRs-pEam8-kRYRC07Hj9IzC0sjreE1NPVzz-Yuw79gqaQokfVmuznok8XoBFKLjQ1Sa9-i8Ano4lXOKz6APnXXBdbbSUBvNV3jlmqvtxhyZ2u901SuIxZcSmPY5aAEsaHiSm2lTIuHXr6f1WZcKOsR88U4ymlI-wSGKMKJT5hh9xxvSw4VQxcbjfMBllDpH6dg3X_AUwmN6JFVWZsl1QFyaTAt3LsbUY6xfuh_IbuO30tPnG5tlUrluJdXGloRCH11NFB7wjLQ3aR1DTSxjlfce9xxfX0OdZsrIeL0Z03kveHI5ZkIjp4xQo_VAGkU-tiQ8Dyx5DYiAR-sCYaOqNwzxKWVc5YYohZKpHjpCBel0pzDgUADDWyube45tOIDVyeefNjIfmRuDnJ9ZZhi45JY4E8kRviu00sFlfVxDdKLwnykuYBEeU6RCQSx6yVr5zxErvtqEbcrz3XvNJhOiNL6n_I8D6dd4muE7j43BrQgI_ae5dakGyvy83JLyeylqZKak5GIwq7KjymQQIWMVdQIfTuF4n6HbaYLcoYZthe9WdGjo3nnBkyn08Voor8OFycbUxS6qm-siAYU_0CgG29fDgorAb9F4DmFEM_Lg0ymYyBAEXsnm9EilD0rndz-tKthJN1e53CvZZlCfhbBKxjU-hTeiBeE49YIsM7KueWdCTNetU_gSeGipB-UU-sDJMvHUR0sclIenUIMC0MSNPuUBGdzTDjQBVKmJZvKUy8NJBghXHYEzCxZRdO_qnn-FIq731ZhqA-G58_48T-tovQF3JlygWW8d-XH4PzjgSR0YK4Ucpo-d8AcY0u-2HsEv6TaxgUvBnot_kPBxFhCpdLj6nbesLCAZkvmM1erBl-ew6ztD1FSt2MIg2Nr9vqOL-cQeH9zGX0OqXrbWWqCrOJkLSoq7ST-Fq6cQhvM_6KUw_mhgWHHuYy-68EgzyzZ7K7lQAx379F2Fiv2U3SR9JCWvtja5YigRBqM7PwxIyYs_o0y4p19UZg5jTx0TyR-pVXw1_CcknqmrGginPv2zKvbRlnRm-cIEO0mWsfwUgN7DzGSKa06zoEStd-aVprHolQ4_l3oFtTUVhymo4SO5VMAWVZLrGuZNn2PRifFZlvYl00Dt1RAMG0FguNGMcLn0-0hzxio-5psReiIguJfurRHnsk3tPyYwYqILmCng1OW5ytJvRUkWYgZ_esuD_9gnHyL58mYyW5MsqFmWeMoOGeDPde9ggHqf1uAZ6NxGbpco7njgxOhPKEKkr0bMXWmn31C1wh7VM2TkwhD2M9apFLQ1eX4wHl6hZsbknl1LdZrY3pLIL_SkAL6xnfxfCcA7hGli6fnlOVhLQC4WEIR-h7zJqeK60SF9-AZt6l3LwnQru5e5aIwKWv0xqS5VLf6mOmCRC0HVAJBdYRKU2CImoih7pey6HV7-JP1nxOTiK2teo5uI2PUrMLTV_oAh-O3SFkvX8eZ0LgGDF3s-AgEq3G-Hs5qNEeX7rcyUWeTZ63HZ2w7e4R1STF0pulnJW91So2vjF9aEBFY2AkpM-rSx2kKl-xYG3UMX-57Gbw0PPTNgnhwbP1AjAlPfG3TAt6Df8ouxUYsCsnA5tZna5e4QOQW1ZcEoesgJxy24QeaJN4OUHRBlmzEQ4d20rtNJL2wsed0HXdLSU427NicPdioL1lYVHXxlnU0tQEbQbLFQzIsh2WJaFqkDsBOTrN6HXTgUfY85cDo39Rm5u6gAEnJpef8NXpeqTim5oBASsqra5hZEjYi5PkFHNcLI663eNzFLlYix73_pVIcMch6YsHg6Z8JWLo7Xhz5vN4IqxEH8Qya_odYAkNpYN_kT1oTeo4MDqs0IpvPmj6tIylYRoW3txAnatsxge0l6HGa4_tG4dP_t91rsTJTzR8wsOsQZpnrKtuYEv5WlfuvpgeITrloDcWQOxq9VeqdYFdNEJPevIwvmEIA-udNoo-Ho31ZxhbODdI9p9nOLQ9lRxBVA__VOCNJisDFVLt_PyDevENtme1qJTe-RvsCuQBhTVffUPTpc4ez7V9V8CiTN37yb2VkMtHcmxcyNYiGdwIaMe3WKS_W9bM7Fqil1LX6mVu-49CnWp7SNRHXlvCeNfFWo_DvKiu8g6aMy-th-kdRSrMjKJ_6R_5ph0GKyGkF5sirhUf6W-S5s2WnV-3yj9Y0IbNWUr8xHK6SXgOXhSLfdLcJbedHNdIXIggd-jzLcAi5F_qkRV67g1W9IKQGwTzIFvoJrlq3gChxHzyrD3gOPYiAirj_6Kh2tO8iyEtEzXPNaIWHqxPnoHC2f79AA6S1rzzt1e0IPLyo0KrCeNWPb1lUsKDKOuxPB8_v6mp1LbSxAw-qWDcgHpHrM6Hve90fw-xSyRY1ZOJ14NsJE85vw2rqSo7AJtBQC2SrIsyKy5Q7rKC2lkQeyWdpCJOEslvHPTbNTftWXs9qZKSGhHXhY2_MCR5_jetBl33KKpIqOvJZs4nkaz2tmyCbFAmfQgcreiOYA1qGNWvPNJWUifd8GZHvDiemO37ZvLew3WCloB2bsn1urkN8OGmK2-NcL3jtVr0T7fRSvGG8BETDPUDtLvg2wAQeV0kGfM6dH37ED8auPcWxygz8cDD1ni8QoPpdlHsH55kL38rEerrgI_fEcAsAGqXacW8-07vKLDt_dJ4cmSVq-DVTIw-EQIrhBOf3gW8W5LP2_sVA2mjchwWP2mZ-v-zEeYVtkzCWznJCqFbeatyxHv5Q2phnWkxj2d-lsELVDHHRXPlB-MqUBsYLAhhPJzRC9n76EoOVdgr5bsfP1XQlYj_WdPdZ13HpGZyj7ZfIPRazmYlw-EBURIYzOHlilo0Hk7pLZ5Im7E5XtLXjb0r4MaUZltViFS7Za7lm1yeVmrA0k-X4T8lgTqaBpr5BnnVVTPUNB2701N4_Z3tPxFxO3Bi_wGVVqB19D1hZGyNCXHqxlvvvPozHi3gV6Oh5ElPb4_wWTJeXJTqPI_gcnExs8Z-AnwTST6Dbw1u4EBUapdXDNcC3PD6YHlQ2Cn0lUD3TMFjGZiTL3UQADwKHEtH8R45QIEadT7O8p1pVWhzFsdOZbNdc160iViD3A920MHSRcJ_pLhxwuyndMEfzJEInv3s7lIxcJtc_VVruYr-ce24eD57jf23TsXgBEkFBsGFvORSUtds_eviuK4_wjijz33Aeydm3SqMi_DfFoy0CDSLe7qnvWBda9IF4MRlvcR-N8qgZW_vjx3OkfiUUF2PDlw1tmXhSuiF1IoPusUWZQ7QD2aEEBD_RXaipdCy_Lh7yDEw9CtdEb332MLvzFgUPBLSWWyrA6gYQwpEiJHFrQMDetsaFFL-A5188dSPLjtXi9KkxGsA5wEbA1FcvABrHOBo_Ws8V3wzIuHl7uo7PO6FGIZB-d0RZiAw7AP5cVjG0udCWmQPpS0WVF5slSNmKgZjr9WD20w9yaliZOiPN_2A2w9Zx_ca4YBceORZ69irPy0xCKQJBlq0HMeZV_WzQATzbV0n8oyfx3CB12oF9JYn_4C5vqC_QnlGmUMFno3F1ZP94Tv7XqyDTt_JTAK_xppfVJEX7018apcF2w40aM5kp6_Clv0lb7AOkGWFKOAI7qOoraTldZh6ik_1DuJqKno7k0Qtc8RnRdQjNRhXJDfb1oqRBYvBfNQkROULiOoNx2yJdp7wvMjqah7WYE1wnKbftTPLjPDQDoc=\",\"summary\":[]},\"output_index\":0,\"sequence_number\":3}\n\nevent: response.output_item.added\ndata: {\"p\": \"1d6168135144b\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_RN4tTgQaJ61I5FoTePK9mpHu\",\"name\":\"get_weather\"},\"output_index\":1,\"sequence_number\":4}\n\nevent: response.function_call_arguments.delta\ndata: {\"p\": \"7cea337f3f3\", \"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"obfuscation\":\"EH2nAu56oVY3i3\",\"output_index\":1,\"sequence_number\":5}\n\nevent: response.function_call_arguments.delta\ndata: {\"p\": \"97c3b3cea4e5\", \"type\":\"response.function_call_arguments.delta\",\"delta\":\"city\",\"item_id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"obfuscation\":\"Z6rgHbATKgFH\",\"output_index\":1,\"sequence_number\":6}\n\nevent: response.function_call_arguments.delta\ndata: {\"p\": \"8\", \"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"obfuscation\":\"AMuVtxHLunbC3\",\"output_index\":1,\"sequence_number\":7}\n\nevent: response.function_call_arguments.delta\ndata: {\"p\": \"0f8287d55651\", \"type\":\"response.function_call_arguments.delta\",\"delta\":\"Paris\",\"item_id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"obfuscation\":\"bGFqKOKpEeC\",\"output_index\":1,\"sequence_number\":8}\n\nevent: response.function_call_arguments.delta\ndata: {\"p\": \"3d0a821b79a2d\", \"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"obfuscation\":\"BDjZwVOmzchB8t\",\"output_index\":1,\"sequence_number\":9}\n\nevent: response.function_call_arguments.done\ndata: {\"p\": \"feda3dd2\", \"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"item_id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"output_index\":1,\"sequence_number\":10}\n\nevent: response.output_item.done\ndata: {\"p\": \"9\", \"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"call_id\":\"call_RN4tTgQaJ61I5FoTePK9mpHu\",\"name\":\"get_weather\"},\"output_index\":1,\"sequence_number\":11}\n\nevent: response.completed\ndata: {\"p\": \"5bccd4d8c7a625\", \"type\":\"response.completed\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91ce40f887d189169a2d89f2ee42\",\"object\":\"response\",\"created_at\":1790742990,\"status\":\"completed\",\"access_programs\":null,\"background\":false,\"completed_at\":1790742994,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[{\"id\":\"rs_06bc195765a8c9c7016abc91ce881c87d18e71310b9a21b3ca\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJHSiszE3t4K7jn9bHy8s0SW2UP903LbXwxOwSb6GZw4IqOz4zxJgv6LvrbcXrF57uuMt_i6u8YBCg61rtpMTxxK9Uj1S3Lp_D2Wpb7hKX__6ofb2YgaLuJOL3zR8DJdW3LMEj70R6tc4fNrYqWmpK9BbP-rDuViz9BzJUHVWfJOGFcygoG9TUggNxDxn81T9i71Dj6fc0frLEE68ROHYip_6CYqriPTOgapXgpeN1F21h88jzzcyhOIn5stNkRCdug3UGBqDz2PAJspPKKY9f_sJDFIq-pq2IJZPbAx-Lv7K9wNiaxy3e29b5tb3_biJLL11VBW7qLijWH-nnJe9tXgIPqcwWod9FfFGSp7x4sB5y7RWKQIQwsthe7zE-KIDxghYKG_wtwv5gppScZhhuHZfbzS2kOCAEqWUkIZo59P-O7biozPes9IbTbrVIg7N_hCeUGy1TbAAXxtaiLNyd6JdOx-2sJVuRq8t7kDKDxX6oEybu7hYy2q4xZUG1JBvY7D472Rv3UKTwGuDQYoEq4B7CQgVeif_HLWPUyIY47G0q3QYlb2M0TAqlcIRUaiTozYCYFp3aBW0hu6FtswsQZW05_pd-vMRzfWEqm2rBa1i36q3DKgCuGDxSmPuTKjCREt6KbnRJoZ1Qvy0Og8_BqTDo5LMJKxZFDuwastdmp0-KTur-Ez38uQxHOVIJ-BKZZbxMLLZFRER5pTi6IgzSEnoLZ9LenoDnH2AmjID67Aez1fo1A_g6Wc12DrDICXyn5jZ5W7CNvieFJvBx-d6rgbhsFIrRwGUK6Kg_JGszFRRdZ_9Isl8aeiRtaQgD2s4W2B31xQrM3oe_PG6QtmXOIIe1dHVd6TEkmukV-WPE7AImgy3usmZXu8u0hz9yRSdBbaSZN-2OpHcvuQ9mWr-697IfhfloKVuy-fHzXD5zf6PL_p7x0CSzrIA7LkNWq-V1K0ql93D-qWLBhUtiV-QgizsX3BYnsWUtHK_lVSPKyA7hOFAZnVpFaeIXidux1a7imzzATmJxl6rqwKgDdsTaQqWMFiM5lfrmIa51g4CksB44ZZNeh1xPQr5DJcrfv8L5noesU_l7_2XQTzqGeNr8NRXN91XhbxtyCXfgObYTPSKOkHKTC9R7wyThAvf_rDRbjsNzMfuovbwxckzuakEw2RmaZxkQXiDe2mkSM9CeQ3rUWnE6kqat08qtjoBhWiqiV5UXP0heYtQ85G7D-W6AKg2ZyMP9nH99q-TM2kJQ2wfu0hI0Fkfha1RcK-kZhlGFcbTr0WKI_xT7AxvIzmvsLxx8t4xwuzUly1UAEJzOrOd1Joct2CVLtg85t0Rw-SIvYpJLw9GBxdJWhA2brvVQOlppVqOaimDet1Nzg4tsUqtS1YRT5JW6hiRBSgOYN7BoqIinVxHknBdJPaSSOOXdFvUkrMwUitPf96ZZ_6ZLLkbasfXJu4_O13p3XREYtYEMeBRkLJCi2C0onVkrKIHwP_kNRrx7FxCbMbBz6hKiqIsU11Rg7giWhLwqKw4uBDa23Q_9Jfktc6s1-ADaZ3yL_-FgZXxLOHi8fEp9whGYVHYY4Pup3L8exYWHGodMGah7u5caeYfNHO8-TgeGXghwbZh9qrvKfPCEnxJuK0Zw531YI3kISLxYfr2b-uAEMnnM3jYAkoR6x_Y4Ony7o2vsi1df7bvMCsIIUOFEoEmRNyJziYT-usHNrKBDD5pJ5NvUFaQvdhJ9mIldFE9XOfZ0qMWzLYBRVQQIwXxBhTTkUNQ6mMTvnzUkpipp9juxM4NWOOd_nxXSR7GxbmVvWUzHwRuk0bDWCGR83OOsxVrk882YLpiHn2diRUV-qn2TH9QcvimImne6FJZYwGZ1N2kyTX4u00W59YRgY0aA4dxqV3ZU5y0khc24xiFdyUUCyWu5gen5eZkPHhBPQmqzIKlR36UBSJFyhsN5FdeNLYqoApXfb9NsuDsmOUEolwE1PlySeqv3CexRs935A3iJ2r6anKWKGi3jMKYtrnf9asfqV919R4wTZBKnsAVDtM1mH4Tkf4X4yVPbittb2Sx8d58R8jAMr_lGz_CbaGAen2P1sWpFOpcOA9hO7Enf9cNiTxUfVw-8SRU2A8DHF3vF0TKW6-DeNsXKaSHKbQR27rweoKRJb2CCaSKoUQpQcwoqpIkVYtUxOEa5p5t5pvyZNdSaKp0t7_UPU64ggHH7LGcpHEYmiOVv-K12o60OjvtriZG4b5sxCbTP260OAoxvIJuhnGRrgAUIlmW8v1CWFvIwenVX8uGNAZ7lsFWK1GDVGGFvBKboY2-qF1RkpjBgIZukV3Ig0LGSOC2q3tsi_9xY7_0IizaZZ2tv7TJgcSUeOHqF7mSW8FCYk_PiWYT9lC-LudIRa-k4D36iTQTXfGld44EgdP8jZ2ocrAHJG9tnwpVOpYS5HOaF56O-qCXwQEt6bvAlviXo1VMF334X5IJRhn1jpTNHWGRYEnMlr78HjEi84vTUnyKhtH6KHBn9RPmmC9jIjDmDI7Mohok-K0RtrDhBkbqNSp9PVCGg5t8KvYF0RTEYviiYVxTIct3A1-qzxZgCGVPrl2UNVXYEAl-xITc9fACYEJxa1gIonRaiWtYzoWKtBGrT7QGnVViBs6BL_83xjJJyPUyyoTzs28PxFWt5R59jCfK8hbyqfYmnGTp5Gkaq9PI0nkiLvrD8uQ4FgFBM_of9jUyjUbZbtMX5GstsvPdE6A_wjkG86d_WFVZa8_CELf-jGCfRYE1sssRQdMyjJj5jGOXU2sbFOrOVrNMFGeGK7CbklUHGICsSpbDNI-IR5uBFADH1PVilZu61YsAvDq1XVp7msm-drNiLJpWokUYNYpBg8wibJZr4-KJR0bGqz5ROvnz7fJh9Pjh1ntwHv9fQrB727Pwn9nF8O6KHxx7XvIfRhNLdvpQVPkQKN-Yi89h4DFOGYJzNOPZD-IHqUC0CepAlJlS_p-A7Se744WKnfATDtoU-CGC3sZI5XpJxM2FmOj-kLMN7-smh3fJ4Wd2m8KmY91R5a7b2OEm4RICMCbiAz3NBJKZNDPDFuBguFkaQUrqYq7St9_k1TwQamtRyvt2FFDcBfAECgQBJ711WsotvhEQVulbrJBynPU5Q3TILnPv9zRyaAz95XCSMbyl_qFSvOGBWOD2yA82h0pvCIwIc7R2nnqMRO9UGdqCU4SzIJC-0avonxUGralYGXy2LSAkFLxY7E53S9zuab-Qvk7-wvz0mHgm8Dai0xdq_0P58G8RHd-kj2imCrRhVbuZC7uDZRN6HtESGzx-ZkUJkHpGqPS16u3qhbiEajnwXb0tBqA-KlAAQAtRtiWw1lmtSujYNWvhBbza_Z9H-d96MunNBCGQmtvFBsMix8PfAGM4QEZgg1etoGPbcBdjOoOti_Y1wKjbDUMqW1Tth5QKvGQNiskUhwi9IA3MXv3-m2y0py_AJNvOUHp4-qAnGHpZSUVJ_m6dWF3Q3LtyhNWn1el4bx5cUTf35YUcr6XcjvFi3_t4Is0E7I4jjR_4CD-gAXUrKny52DykYyA3LeW5Y9FiM7b-ZQN-TGjWdofzZluXq_ri0DLXJuAK0I59wDqqaNVsYGOivGUWTnwHk_NmhsQHDLZb7yDrl44ASMaTTaPuJ0MsVJREI0mpUYptU3nG_RG2K-DpcUzt_caa_wEM66ajC1EpZTnS9NClaIhaJGClEkLdYuCK6OStm9OE9ovY43XQR06NkTGWLjsLQebiiZI1eXzoEFcHrHLoqkbE67XKshtK58W4MlMdt7z5kebbrUO_l1RHBwf1fQRCLcYsDUOW86XAS-eOLVjqcB1VcE2mFA9uM9Rm_PsmhSjCPZI0zC3W1FrYXhh4X3yeT-d3T6zqtCgR64J7t1Ulk9jd736cPxcBPYDmEHFhoF06k3n06Cyb68Mn9zhadFaLu6XjN4Rl_sSwpDDax7JDAfRFmjqLwhYnUemOUvdc3F_XuXzrE3fzOl1p9CEYwnbEd0rTWmZY2eWk_Bu_9xfZLgPvRlGpfSHC_zB7U0-rN7XFb2OOhJ57rSZk13KeSfWgXE=\",\"summary\":[]},{\"id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"call_id\":\"call_RN4tTgQaJ61I5FoTePK9mpHu\",\"name\":\"get_weather\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":74,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":473,\"output_tokens_details\":{\"reasoning_tokens\":448},\"total_tokens\":547},\"user\":null,\"metadata\":{}},\"sequence_number\":12}\n\n" + } + }, + { + "transport": "http", + "request": { + "method": "POST", + "url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/openai/responses", + "headers": { + "content-type": "application/json" + }, + "body": "{\"model\":\"gpt-5-nano\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"reasoning\",\"id\":\"rs_06bc195765a8c9c7016abc91ce881c87d18e71310b9a21b3ca\",\"summary\":[],\"encrypted_content\":\"gAAAAABqvJHSN68IWwhWq9YTPxs-e-OeLdOqfdkkIEP_1hc9f7k6yH9tGZKLUC2OPF0avZ7xaJQp8iOR_gPt3z0TGQ1T0e4M9OR6boMFOA_C4TwinAF1POgbZLI-0SUDkAm1PZSaO0Ic7DBh1rRT6biled_-iWs0ke3FP3nFZit5Ve16AI1TdL4c9QERNCcZFsD5neV8eBeABWarZ8SMFfxcYJ5y8QkpDOL1ShqenD22Df7fDy8cdkYWybEEZcAfgXWSfQK2hRwSNYN_BDV9CfKj-cUTePwf0D2vLm-2xd9LGt-KiLPbsp9EpGXP41ra6-qHRLeu7MO64XWIu2vsf9qAflOjp7MOSuTJ3ZvDQoTNRs-pEam8-kRYRC07Hj9IzC0sjreE1NPVzz-Yuw79gqaQokfVmuznok8XoBFKLjQ1Sa9-i8Ano4lXOKz6APnXXBdbbSUBvNV3jlmqvtxhyZ2u901SuIxZcSmPY5aAEsaHiSm2lTIuHXr6f1WZcKOsR88U4ymlI-wSGKMKJT5hh9xxvSw4VQxcbjfMBllDpH6dg3X_AUwmN6JFVWZsl1QFyaTAt3LsbUY6xfuh_IbuO30tPnG5tlUrluJdXGloRCH11NFB7wjLQ3aR1DTSxjlfce9xxfX0OdZsrIeL0Z03kveHI5ZkIjp4xQo_VAGkU-tiQ8Dyx5DYiAR-sCYaOqNwzxKWVc5YYohZKpHjpCBel0pzDgUADDWyube45tOIDVyeefNjIfmRuDnJ9ZZhi45JY4E8kRviu00sFlfVxDdKLwnykuYBEeU6RCQSx6yVr5zxErvtqEbcrz3XvNJhOiNL6n_I8D6dd4muE7j43BrQgI_ae5dakGyvy83JLyeylqZKak5GIwq7KjymQQIWMVdQIfTuF4n6HbaYLcoYZthe9WdGjo3nnBkyn08Voor8OFycbUxS6qm-siAYU_0CgG29fDgorAb9F4DmFEM_Lg0ymYyBAEXsnm9EilD0rndz-tKthJN1e53CvZZlCfhbBKxjU-hTeiBeE49YIsM7KueWdCTNetU_gSeGipB-UU-sDJMvHUR0sclIenUIMC0MSNPuUBGdzTDjQBVKmJZvKUy8NJBghXHYEzCxZRdO_qnn-FIq731ZhqA-G58_48T-tovQF3JlygWW8d-XH4PzjgSR0YK4Ucpo-d8AcY0u-2HsEv6TaxgUvBnot_kPBxFhCpdLj6nbesLCAZkvmM1erBl-ew6ztD1FSt2MIg2Nr9vqOL-cQeH9zGX0OqXrbWWqCrOJkLSoq7ST-Fq6cQhvM_6KUw_mhgWHHuYy-68EgzyzZ7K7lQAx379F2Fiv2U3SR9JCWvtja5YigRBqM7PwxIyYs_o0y4p19UZg5jTx0TyR-pVXw1_CcknqmrGginPv2zKvbRlnRm-cIEO0mWsfwUgN7DzGSKa06zoEStd-aVprHolQ4_l3oFtTUVhymo4SO5VMAWVZLrGuZNn2PRifFZlvYl00Dt1RAMG0FguNGMcLn0-0hzxio-5psReiIguJfurRHnsk3tPyYwYqILmCng1OW5ytJvRUkWYgZ_esuD_9gnHyL58mYyW5MsqFmWeMoOGeDPde9ggHqf1uAZ6NxGbpco7njgxOhPKEKkr0bMXWmn31C1wh7VM2TkwhD2M9apFLQ1eX4wHl6hZsbknl1LdZrY3pLIL_SkAL6xnfxfCcA7hGli6fnlOVhLQC4WEIR-h7zJqeK60SF9-AZt6l3LwnQru5e5aIwKWv0xqS5VLf6mOmCRC0HVAJBdYRKU2CImoih7pey6HV7-JP1nxOTiK2teo5uI2PUrMLTV_oAh-O3SFkvX8eZ0LgGDF3s-AgEq3G-Hs5qNEeX7rcyUWeTZ63HZ2w7e4R1STF0pulnJW91So2vjF9aEBFY2AkpM-rSx2kKl-xYG3UMX-57Gbw0PPTNgnhwbP1AjAlPfG3TAt6Df8ouxUYsCsnA5tZna5e4QOQW1ZcEoesgJxy24QeaJN4OUHRBlmzEQ4d20rtNJL2wsed0HXdLSU427NicPdioL1lYVHXxlnU0tQEbQbLFQzIsh2WJaFqkDsBOTrN6HXTgUfY85cDo39Rm5u6gAEnJpef8NXpeqTim5oBASsqra5hZEjYi5PkFHNcLI663eNzFLlYix73_pVIcMch6YsHg6Z8JWLo7Xhz5vN4IqxEH8Qya_odYAkNpYN_kT1oTeo4MDqs0IpvPmj6tIylYRoW3txAnatsxge0l6HGa4_tG4dP_t91rsTJTzR8wsOsQZpnrKtuYEv5WlfuvpgeITrloDcWQOxq9VeqdYFdNEJPevIwvmEIA-udNoo-Ho31ZxhbODdI9p9nOLQ9lRxBVA__VOCNJisDFVLt_PyDevENtme1qJTe-RvsCuQBhTVffUPTpc4ez7V9V8CiTN37yb2VkMtHcmxcyNYiGdwIaMe3WKS_W9bM7Fqil1LX6mVu-49CnWp7SNRHXlvCeNfFWo_DvKiu8g6aMy-th-kdRSrMjKJ_6R_5ph0GKyGkF5sirhUf6W-S5s2WnV-3yj9Y0IbNWUr8xHK6SXgOXhSLfdLcJbedHNdIXIggd-jzLcAi5F_qkRV67g1W9IKQGwTzIFvoJrlq3gChxHzyrD3gOPYiAirj_6Kh2tO8iyEtEzXPNaIWHqxPnoHC2f79AA6S1rzzt1e0IPLyo0KrCeNWPb1lUsKDKOuxPB8_v6mp1LbSxAw-qWDcgHpHrM6Hve90fw-xSyRY1ZOJ14NsJE85vw2rqSo7AJtBQC2SrIsyKy5Q7rKC2lkQeyWdpCJOEslvHPTbNTftWXs9qZKSGhHXhY2_MCR5_jetBl33KKpIqOvJZs4nkaz2tmyCbFAmfQgcreiOYA1qGNWvPNJWUifd8GZHvDiemO37ZvLew3WCloB2bsn1urkN8OGmK2-NcL3jtVr0T7fRSvGG8BETDPUDtLvg2wAQeV0kGfM6dH37ED8auPcWxygz8cDD1ni8QoPpdlHsH55kL38rEerrgI_fEcAsAGqXacW8-07vKLDt_dJ4cmSVq-DVTIw-EQIrhBOf3gW8W5LP2_sVA2mjchwWP2mZ-v-zEeYVtkzCWznJCqFbeatyxHv5Q2phnWkxj2d-lsELVDHHRXPlB-MqUBsYLAhhPJzRC9n76EoOVdgr5bsfP1XQlYj_WdPdZ13HpGZyj7ZfIPRazmYlw-EBURIYzOHlilo0Hk7pLZ5Im7E5XtLXjb0r4MaUZltViFS7Za7lm1yeVmrA0k-X4T8lgTqaBpr5BnnVVTPUNB2701N4_Z3tPxFxO3Bi_wGVVqB19D1hZGyNCXHqxlvvvPozHi3gV6Oh5ElPb4_wWTJeXJTqPI_gcnExs8Z-AnwTST6Dbw1u4EBUapdXDNcC3PD6YHlQ2Cn0lUD3TMFjGZiTL3UQADwKHEtH8R45QIEadT7O8p1pVWhzFsdOZbNdc160iViD3A920MHSRcJ_pLhxwuyndMEfzJEInv3s7lIxcJtc_VVruYr-ce24eD57jf23TsXgBEkFBsGFvORSUtds_eviuK4_wjijz33Aeydm3SqMi_DfFoy0CDSLe7qnvWBda9IF4MRlvcR-N8qgZW_vjx3OkfiUUF2PDlw1tmXhSuiF1IoPusUWZQ7QD2aEEBD_RXaipdCy_Lh7yDEw9CtdEb332MLvzFgUPBLSWWyrA6gYQwpEiJHFrQMDetsaFFL-A5188dSPLjtXi9KkxGsA5wEbA1FcvABrHOBo_Ws8V3wzIuHl7uo7PO6FGIZB-d0RZiAw7AP5cVjG0udCWmQPpS0WVF5slSNmKgZjr9WD20w9yaliZOiPN_2A2w9Zx_ca4YBceORZ69irPy0xCKQJBlq0HMeZV_WzQATzbV0n8oyfx3CB12oF9JYn_4C5vqC_QnlGmUMFno3F1ZP94Tv7XqyDTt_JTAK_xppfVJEX7018apcF2w40aM5kp6_Clv0lb7AOkGWFKOAI7qOoraTldZh6ik_1DuJqKno7k0Qtc8RnRdQjNRhXJDfb1oqRBYvBfNQkROULiOoNx2yJdp7wvMjqah7WYE1wnKbftTPLjPDQDoc=\"},{\"type\":\"function_call\",\"id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"call_id\":\"call_RN4tTgQaJ61I5FoTePK9mpHu\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_RN4tTgQaJ61I5FoTePK9mpHu\",\"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\"],\"max_output_tokens\":2000,\"stream\":true}" + }, + "response": { + "status": 200, + "headers": { + "content-type": "text/event-stream; charset=utf-8" + }, + "body": "event: response.created\ndata: {\"p\": \"aa3530\", \"type\":\"response.created\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91d296d887d1bb460e087f742d90\",\"object\":\"response\",\"created_at\":1790742994,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"p\": \"a6b66d35bc20b2b\", \"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91d296d887d1bb460e087f742d90\",\"object\":\"response\",\"created_at\":1790742994,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"p\": \"169cd8bd840\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.content_part.added\ndata: {\"p\": \"045e323978cf3\", \"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}\n\nevent: response.output_text.delta\ndata: {\"p\": \"42af845ab126d6\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Paris\",\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"obfuscation\":\"WAn5iJo53hF\",\"output_index\":0,\"sequence_number\":4}\n\nevent: response.output_text.delta\ndata: {\"p\": \"aa473d2c7c859\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" is\",\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"obfuscation\":\"uH1AD585J0MVU\",\"output_index\":0,\"sequence_number\":5}\n\nevent: response.output_text.delta\ndata: {\"p\": \"8\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" sunny\",\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"obfuscation\":\"URq9B4uxqg\",\"output_index\":0,\"sequence_number\":6}\n\nevent: response.output_text.delta\ndata: {\"p\": \"ee15ea4be3b06e\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"obfuscation\":\"uCqR0gUCE0vGumP\",\"output_index\":0,\"sequence_number\":7}\n\nevent: response.output_text.done\ndata: {\"p\": \"ed1\", \"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":8,\"text\":\"Paris is sunny.\"}\n\nevent: response.content_part.done\ndata: {\"p\": \"3e34d7f983fd5\", \"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Paris is sunny.\"},\"sequence_number\":9}\n\nevent: response.output_item.done\ndata: {\"p\": \"0c\", \"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Paris is sunny.\"}],\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":10}\n\nevent: response.completed\ndata: {\"p\": \"07\", \"type\":\"response.completed\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91d296d887d1bb460e087f742d90\",\"object\":\"response\",\"created_at\":1790742994,\"status\":\"completed\",\"access_programs\":null,\"background\":false,\"completed_at\":1790742994,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[{\"id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Paris is sunny.\"}],\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":565,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":8,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":573},\"user\":null,\"metadata\":{}},\"sequence_number\":11}\n\n" + } + } + ] +} diff --git a/packages/ai/test/provider-package.test.ts b/packages/ai/test/provider-package.test.ts index a75d8061863..e5b0859f2dd 100644 --- a/packages/ai/test/provider-package.test.ts +++ b/packages/ai/test/provider-package.test.ts @@ -214,7 +214,7 @@ describe("provider package entrypoints", () => { }) expect(selected.provider).toBe(provider.id) expect(selected.route.endpoint.baseURL).toBe(provider.baseURL({ accountId: "account" })) - expect(selected.route.defaults.headers).toEqual({ "x-application": "opencode" }) + expect(selected.route.defaults.headers).toMatchObject({ "x-application": "opencode" }) expect(selected.route.defaults.http?.body).toEqual({ custom: true }) expect(selected.route.defaults.providerOptions).toEqual({ reasoningEffort: "high" }) } diff --git a/packages/ai/test/provider/cloudflare.test.ts b/packages/ai/test/provider/cloudflare.test.ts index 89fcb7607fc..6f77d902694 100644 --- a/packages/ai/test/provider/cloudflare.test.ts +++ b/packages/ai/test/provider/cloudflare.test.ts @@ -39,9 +39,16 @@ describe("Cloudflare", () => { ) expect(responses.route).toBe("cloudflare-ai-gateway-responses") - expect(responses.body).toMatchObject({ model: "openai/gpt-5.4", stream: true }) + expect(responses.model.route.endpoint.baseURL).toBe( + "https://gateway.ai.cloudflare.com/v1/test-account/test-gateway/openai", + ) + expect(responses.body).toMatchObject({ model: "gpt-5.4", stream: true }) expect(messages.route).toBe("cloudflare-ai-gateway-messages") - expect(messages.body).toMatchObject({ model: "anthropic/claude-haiku-4-5" }) + expect(messages.model.route.endpoint.baseURL).toBe( + "https://gateway.ai.cloudflare.com/v1/test-account/test-gateway/anthropic/v1", + ) + expect(messages.body).toMatchObject({ model: "claude-haiku-4-5" }) + expect(chat.model.route.endpoint.baseURL).toBe("https://api.cloudflare.com/client/v4/accounts/test-account/ai/v1") expect(chat.route).toBe("cloudflare-ai-gateway-chat") expect(chat.body).toMatchObject({ model: "xai/grok-4.6", stream: true }) expect(workers.route).toBe("cloudflare-ai-gateway-chat") @@ -100,6 +107,54 @@ describe("Cloudflare", () => { }), ) + it.effect("posts Claude and OpenAI models to the provider-native gateway endpoints", () => + Effect.gen(function* () { + const gateway = CloudflareAIGateway.configure({ accountId: "test-account", apiKey: "test-token", cacheTtl: 300 }) + const requests: Array = [] + const capture = dynamicResponse((input) => + Effect.gen(function* () { + requests.push(yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)) + return input.respond("", { status: 500 }) + }), + ) + yield* LLM.generate(LLM.request({ model: gateway.model("anthropic/claude-sonnet-4.6"), prompt: "Hi" })).pipe( + Effect.provide(capture), + Effect.ignore, + ) + yield* LLM.generate(LLM.request({ model: gateway.model("openai/gpt-5-nano"), prompt: "Hi" })).pipe( + Effect.provide(capture), + Effect.ignore, + ) + + expect(requests.map((request) => request.url)).toEqual([ + "https://gateway.ai.cloudflare.com/v1/test-account/default/anthropic/v1/messages", + "https://gateway.ai.cloudflare.com/v1/test-account/default/openai/responses", + ]) + requests.forEach((request) => { + expect(request.headers.get("cf-aig-authorization")).toBe("Bearer test-token") + expect(request.headers.get("authorization")).toBeNull() + expect(request.headers.get("x-api-key")).toBeNull() + expect(request.headers.get("cf-aig-gateway-id")).toBeNull() + expect(request.headers.get("cf-aig-cache-ttl")).toBe("300") + }) + }), + ) + + it.effect("sends the default gateway ID to the REST API", () => + Effect.gen(function* () { + const prepared = yield* compileRequest( + LLM.request({ + model: CloudflareAIGateway.configure({ accountId: "test-account", apiKey: "test-token" }).model( + "workers-ai/@cf/meta/llama-3.3-70b-instruct", + ), + prompt: "Say hello.", + }), + ) + + expect(prepared.model.route.defaults.headers).toMatchObject({ "cf-aig-gateway-id": "default" }) + }), + ) + it.effect("preserves reasoning details for AI Gateway continuation", () => Effect.gen(function* () { const model = CloudflareAIGateway.configure({ @@ -160,12 +215,13 @@ describe("Cloudflare", () => { model: CloudflareAIGateway.configure({ baseURL: "https://gateway.proxy.test/v1", apiKey: "test-token", - }).model("xai/grok-4.6"), + }).model("anthropic/claude-sonnet-4.6"), prompt: "Say hello.", }), ) expect(prepared.model.route.endpoint.baseURL).toBe("https://gateway.proxy.test/v1") + expect(prepared.body).toMatchObject({ model: "anthropic/claude-sonnet-4.6" }) }), ) diff --git a/packages/ai/test/provider/golden.recorded.test.ts b/packages/ai/test/provider/golden.recorded.test.ts index fa5323e3499..7c8c56116ae 100644 --- a/packages/ai/test/provider/golden.recorded.test.ts +++ b/packages/ai/test/provider/golden.recorded.test.ts @@ -49,6 +49,8 @@ const cloudflareWorkers = CloudflareWorkersAI.configure({ }) const cloudflareAIGatewayWorkers = cloudflareAIGateway.model("workers-ai/@cf/meta/llama-3.1-8b-instruct") const cloudflareAIGatewayWorkersTools = cloudflareAIGateway.model("workers-ai/@cf/openai/gpt-oss-20b") +const cloudflareAIGatewayClaude = cloudflareAIGateway.model("anthropic/claude-haiku-4.5") +const cloudflareAIGatewayOpenAI = cloudflareAIGateway.model("openai/gpt-5-nano") const cloudflareWorkersAI = cloudflareWorkers.model("@cf/meta/llama-3.1-8b-instruct") const cloudflareWorkersAITools = cloudflareWorkers.model("@cf/openai/gpt-oss-20b") const deepseek = DeepSeek.configure({ apiKey: process.env.DEEPSEEK_API_KEY ?? "fixture" }).model("deepseek-chat") @@ -76,7 +78,7 @@ const openrouterOpus = OpenRouter.configure({ const redactCloudflareURL = (url: string) => url .replace(/\/client\/v4\/accounts\/[^/]+\/ai\/v1\//, "/client/v4/accounts/{account}/ai/v1/") - .replace(/\/v1\/[^/]+\/[^/]+\/compat\//, "/v1/{account}/{gateway}/compat/") + .replace(/\/v1\/[^/]+\/[^/]+\/(anthropic|openai)\//, "/v1/{account}/{gateway}/$1/") const cloudflareOptions = { redact: { url: redactCloudflareURL }, @@ -177,6 +179,25 @@ describeRecordedGoldenScenarios([ options: cloudflareOptions, scenarios: [{ id: "tool-call", maxTokens: 120 }], }, + { + name: "Cloudflare AI Gateway Claude Haiku 4.5", + prefix: "cloudflare-ai-gateway", + model: cloudflareAIGatewayClaude, + requires: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"], + options: { redact: { url: redactCloudflareURL, allowRequestHeaders: ["anthropic-version"] } }, + scenarios: ["text", "tool-loop"], + }, + { + name: "Cloudflare AI Gateway OpenAI GPT-5 Nano", + prefix: "cloudflare-ai-gateway", + model: cloudflareAIGatewayOpenAI, + requires: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"], + options: cloudflareOptions, + scenarios: [ + { id: "text", temperature: false, maxTokens: 2000 }, + { id: "tool-loop", temperature: false, maxTokens: 2000, timeout: 30_000 }, + ], + }, { name: "Cloudflare Workers AI Llama 3.1 8B", prefix: "cloudflare-workers-ai",