From 5e88b69d1825d38a00ab3c6982b361c91d9e4ae3 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Tue, 4 Nov 2025 17:02:20 +0100 Subject: [PATCH 1/4] feat(google): support streaming --- deploy/test/index.spec.ts.snap | 8 +- gateway/package.json | 1 + gateway/src/api/google.ts | 26 +- gateway/src/index.ts | 4 +- gateway/src/providers/default.ts | 11 +- gateway/src/providers/google/index.ts | 25 +- gateway/test/env.d.ts | 1 + gateway/test/gateway.spec.ts.snap | 4 +- gateway/test/providers/anthropic.spec.ts.snap | 8 +- gateway/test/providers/google.spec.ts | 87 +- gateway/test/providers/google.spec.ts.snap | 1950 +++++++++++++++++ gateway/test/providers/openai.spec.ts | 10 +- gateway/test/providers/openai.spec.ts.snap | 581 ++++- gateway/test/worker.ts | 6 + gateway/vitest.config.mts | 1 + package-lock.json | 16 + ...49f2d8f685bbd2122866dcb4714cf6c38082c.yaml | 72 + .../cassettes/google-vertex-stream.yaml | 62 + ...0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml | 42 +- ...0bc66d6ed83681bdf01439a369e81765faeef.yaml | 41 +- ...973ab2560c553630e55f5dd956224b9fc39cd.yaml | 34 +- ...21b255ff9f19310f99aba15e749007fc8daaa.yaml | 51 +- ...668144ac84c9ab6552fefa490c87ad778b5b0.yaml | 68 +- proxy-vcr/proxy_vcr/main.py | 29 +- proxy-vcr/pyproject.toml | 1 + pyproject.toml | 2 +- uv.lock | 2 + 27 files changed, 2902 insertions(+), 241 deletions(-) create mode 100644 gateway/test/providers/google.spec.ts.snap create mode 100644 proxy-vcr/proxy_vcr/cassettes/google-vertex-3fc5cd750ad872a644781b8d4a149f2d8f685bbd2122866dcb4714cf6c38082c.yaml create mode 100644 proxy-vcr/proxy_vcr/cassettes/google-vertex-stream.yaml diff --git a/deploy/test/index.spec.ts.snap b/deploy/test/index.spec.ts.snap index 65cf0ad..af7dcb5 100644 --- a/deploy/test/index.spec.ts.snap +++ b/deploy/test/index.spec.ts.snap @@ -14,8 +14,8 @@ exports[`deploy > should call openai via gateway > llm 1`] = ` }, }, ], - "created": 1761828474, - "id": "chatcmpl-CWMMElxV7Z5jV4zs2g2cRQjZTsY8M", + "created": 1762272055, + "id": "chatcmpl-CYDklwaN7x9okuWTnABMCrZykoiRj", "model": "gpt-5-2025-08-07", "object": "chat.completion", "service_tier": "default", @@ -118,7 +118,7 @@ exports[`deploy > should call openai via gateway > span 1`] = ` { "key": "gen_ai.response.id", "value": { - "stringValue": "chatcmpl-CWMMElxV7Z5jV4zs2g2cRQjZTsY8M", + "stringValue": "chatcmpl-CYDklwaN7x9okuWTnABMCrZykoiRj", }, }, { @@ -293,7 +293,7 @@ exports[`deploy > should call openai via gateway > span 1`] = ` { "key": "http.response.body.text", "value": { - "stringValue": "{"id":"chatcmpl-CWMMElxV7Z5jV4zs2g2cRQjZTsY8M","object":"chat.completion","created":1761828474,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":75,"total_tokens":98,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":64,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00077875}},"service_tier":"default","system_fingerprint":null}", + "stringValue": "{"id":"chatcmpl-CYDklwaN7x9okuWTnABMCrZykoiRj","object":"chat.completion","created":1762272055,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":75,"total_tokens":98,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":64,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00077875}},"service_tier":"default","system_fingerprint":null}", }, }, { diff --git a/gateway/package.json b/gateway/package.json index 43291c6..5e846ca 100644 --- a/gateway/package.json +++ b/gateway/package.json @@ -13,6 +13,7 @@ "@opentelemetry/resources": "^2.0.1", "@pydantic/genai-prices": "^0.0.35", "@pydantic/logfire-api": "^0.9.0", + "@streamparser/json-whatwg": "^0.0.22", "eventsource-parser": "^3.0.6", "mime-types": "^3.0.1", "ts-pattern": "^5.8.0" diff --git a/gateway/src/api/google.ts b/gateway/src/api/google.ts index 088094d..7b0f88b 100644 --- a/gateway/src/api/google.ts +++ b/gateway/src/api/google.ts @@ -19,11 +19,11 @@ import type { TextPart, } from '../otel/genai' import { isMapping, type JsonData } from '../providers/default' -import { BaseAPI } from './base' +import { BaseAPI, type ExtractedRequest, type ExtractedResponse, type ExtractorConfig } from './base' export { GenerateContentResponse } from '@google/genai' -export class GoogleAPI extends BaseAPI { +export class GoogleAPI extends BaseAPI { requestStopSequences = (_request: GoogleRequest): string[] | undefined => { return _request.generationConfig?.stopSequences ?? undefined } @@ -67,6 +67,28 @@ export class GoogleAPI extends BaseAPI { systemInstructions = (_request: GoogleRequest): TextPart[] | undefined => { return systemInstructions(_request.systemInstruction) } + + // SafeExtractor implementation + + requestExtractors: ExtractorConfig = { + requestModel: (_request: GoogleRequest) => { + this.extractedRequest.requestModel = this.requestModel + }, + } + + chunkExtractors: ExtractorConfig = { + usage: (chunk: GenerateContentResponse) => { + if (chunk.usageMetadata) { + // TODO(Marcelo): This is likely to be wrong, since we are not summing the usage. + this.extractedResponse.usage = this.extractUsage(chunk) + } + }, + responseModel: (chunk: GenerateContentResponse) => { + if (chunk.modelVersion) { + this.extractedResponse.responseModel = chunk.modelVersion + } + }, + } } function mapContent(content: Content): ChatMessage { diff --git a/gateway/src/index.ts b/gateway/src/index.ts index 6798c26..42d44bd 100644 --- a/gateway/src/index.ts +++ b/gateway/src/index.ts @@ -45,7 +45,7 @@ export async function gatewayFetch( ctx: ExecutionContext, options: GatewayOptions, ): Promise { - let { pathname: proxyPath } = url + let { pathname: proxyPath, search: queryString } = url if (options.proxyPrefixLength) { proxyPath = proxyPath.slice(options.proxyPrefixLength) } @@ -53,7 +53,7 @@ export async function gatewayFetch( if (proxyPath === '/') { return index(request, options) } else { - return await gateway(request, proxyPath, ctx, options) + return await gateway(request, `${proxyPath}${queryString}`, ctx, options) } } catch (error) { if (error instanceof ResponseError) { diff --git a/gateway/src/providers/default.ts b/gateway/src/providers/default.ts index 4a0cacc..eeeb737 100644 --- a/gateway/src/providers/default.ts +++ b/gateway/src/providers/default.ts @@ -321,9 +321,7 @@ export class DefaultProviderProxy { } } - const isStreaming = - responseHeaders.get('content-type')?.startsWith('text/event-stream') || - ('stream' in requestBodyData && requestBodyData.stream === true) + const isStreaming = this.isStreaming(responseHeaders, requestBodyData) if (isStreaming) { return this.dispatchStreaming(prepResult, response, responseHeaders) } @@ -465,6 +463,13 @@ export class DefaultProviderProxy { } } + protected isStreaming(responseHeaders: Headers, requestBodyData: JsonData): boolean { + return ( + responseHeaders.get('content-type')?.toLowerCase().startsWith('text/event-stream') || + ('stream' in requestBodyData && requestBodyData.stream === true) + ) + } + protected isWhitelistedEndpoint(): boolean { return false } diff --git a/gateway/src/providers/google/index.ts b/gateway/src/providers/google/index.ts index a882f6e..d99cc14 100644 --- a/gateway/src/providers/google/index.ts +++ b/gateway/src/providers/google/index.ts @@ -19,7 +19,7 @@ export class GoogleVertexProvider extends DefaultProviderProxy { if (!path) { return { error: 'Unable to parse path' } } - return `${this.providerProxy.baseUrl}${path}` + return `${stripTrailingSlash(this.providerProxy.baseUrl)}/${stripLeadingSlash(path)}` } else { return { error: 'baseUrl is required for the Google Provider' } } @@ -72,7 +72,8 @@ export class GoogleVertexProvider extends DefaultProviderProxy { this.flavor = 'anthropic' } - return `/${version}/projects/${projectId}/locations/${region}/publishers/${publisher}/models/${modelAndApi}` + const path = `/${version}/projects/${projectId}/locations/${region}/publishers/${publisher}/models/${modelAndApi}` + return path } async prepRequest() { @@ -104,10 +105,20 @@ export class GoogleVertexProvider extends DefaultProviderProxy { * @param url - The URL to extract the region from e.g. https://europe-west4-aiplatform.googleapis.com or https://aiplatform.googleapis.com. */ function regionFromUrl(url: string): null | string { - if (url.includes('https://aiplatform.googleapis.com')) { - return 'global' - } - // The group includes regions with hyphen like "europe-west4" const match = url.match(/^https:\/\/([^-]+)-aiplatform\.googleapis\.com$/) - return match?.[1] ?? null + return match?.[1] ?? 'global' +} + +function stripTrailingSlash(url: string): string { + if (url.endsWith('/')) { + return url.slice(0, -1) + } + return url +} + +function stripLeadingSlash(url: string): string { + if (url.startsWith('/')) { + return url.slice(1) + } + return url } diff --git a/gateway/test/env.d.ts b/gateway/test/env.d.ts index ba8995c..0b7b98d 100644 --- a/gateway/test/env.d.ts +++ b/gateway/test/env.d.ts @@ -6,6 +6,7 @@ interface Env { GROQ_API_KEY: string ANTHROPIC_API_KEY: string AWS_BEARER_TOKEN_BEDROCK: string + GOOGLE_SERVICE_ACCOUNT_KEY: string } declare module 'cloudflare:test' { diff --git a/gateway/test/gateway.spec.ts.snap b/gateway/test/gateway.spec.ts.snap index 53d4dc8..a4dd6b8 100644 --- a/gateway/test/gateway.spec.ts.snap +++ b/gateway/test/gateway.spec.ts.snap @@ -14,8 +14,8 @@ exports[`custom proxyPrefixLength > inference > proxyPrefixLength 1`] = ` }, }, ], - "created": 1761823178, - "id": "chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo", + "created": 1762271642, + "id": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", "model": "gpt-5-2025-08-07", "object": "chat.completion", "service_tier": "default", diff --git a/gateway/test/providers/anthropic.spec.ts.snap b/gateway/test/providers/anthropic.spec.ts.snap index f440728..d94df4b 100644 --- a/gateway/test/providers/anthropic.spec.ts.snap +++ b/gateway/test/providers/anthropic.spec.ts.snap @@ -1130,7 +1130,7 @@ exports[`anthropic > should call anthropic via gateway with stream > span 1`] = { "key": "logfire.json_schema", "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.response.model":{"type":"string"},"gen_ai.response.id":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.anthropic-version":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-stainless-arch":{"type":"string"},"http.request.header.x-stainless-lang":{"type":"string"},"http.request.header.x-stainless-os":{"type":"string"},"http.request.header.x-stainless-package-version":{"type":"string"},"http.request.header.x-stainless-retry-count":{"type":"string"},"http.request.header.x-stainless-runtime":{"type":"string"},"http.request.header.x-stainless-runtime-version":{"type":"string"},"http.request.header.x-stainless-timeout":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", + "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.response.model":{"type":"string"},"gen_ai.response.id":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.anthropic-version":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-stainless-arch":{"type":"string"},"http.request.header.x-stainless-lang":{"type":"string"},"http.request.header.x-stainless-os":{"type":"string"},"http.request.header.x-stainless-package-version":{"type":"string"},"http.request.header.x-stainless-retry-count":{"type":"string"},"http.request.header.x-stainless-runtime":{"type":"string"},"http.request.header.x-stainless-runtime-version":{"type":"string"},"http.request.header.x-stainless-timeout":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.content-type":{"type":"string"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", }, }, { @@ -1295,6 +1295,12 @@ exports[`anthropic > should call anthropic via gateway with stream > span 1`] = "intValue": 200, }, }, + { + "key": "http.response.header.content-type", + "value": { + "stringValue": "text/event-stream; charset=utf-8", + }, + }, { "key": "http.response.header.server", "value": { diff --git a/gateway/test/providers/google.spec.ts b/gateway/test/providers/google.spec.ts index 5f28e61..a68f0f9 100644 --- a/gateway/test/providers/google.spec.ts +++ b/gateway/test/providers/google.spec.ts @@ -1,25 +1,78 @@ -import { GoogleGenAI } from '@google/genai' import { describe, expect } from 'vitest' import { test } from '../setup' +const body = JSON.stringify({ + contents: [{ parts: [{ text: "Samuel lived in London and was born on Jan 28th '87" }], role: 'user' }], + systemInstruction: { parts: [{ text: 'Extract information about the person' }], role: 'user' }, + tools: [ + { + functionDeclarations: [ + { + description: 'The final response which ends this conversation', + name: 'final_result', + parameters: { + properties: { + name: { description: 'The name of the person.', type: 'STRING' }, + dob: { + description: 'The date of birth of the person. MUST BE A VALID ISO 8601 date. (format: date)', + type: 'STRING', + }, + city: { description: 'The city where the person lives.', type: 'STRING' }, + }, + required: ['name', 'dob', 'city'], + type: 'OBJECT', + }, + }, + ], + }, + ], + toolConfig: { functionCallingConfig: { mode: 'ANY', allowedFunctionNames: ['final_result'] } }, + generationConfig: { temperature: 0.5, topP: 0.9, stopSequences: ['potato'] }, +}) +const headers = { + Authorization: 'healthy', + 'x-goog-api-client': 'google-genai-sdk/1.36.0 gl-python/3.13.0', + 'x-goog-api-key': 'unset', + accept: '*/*', + 'accept-encoding': 'deflate', + 'content-type': 'application/json', + 'content-length': body.length.toString(), + 'user-agent': + 'pydantic-ai/1.0.19.dev5+b3b34f9, google-genai-sdk/1.36.0 gl-python/3.13.0 via Pydantic AI Gateway unknown, contact engineering@pydantic.dev', + traceparent: '00-019a4effa21047ac31372f093cb8e712-8b60768281864a49-01', +} + describe('google', () => { // TODO(Marcelo): When Google supports `fetch` parameter, we can fix this: https://github.com/googleapis/js-genai/issues/999 - test.fails('google-vertex/default', async ({ gateway }) => { - const { otelBatch } = gateway - - // The `authToken` is passed as `Authorization` header with the anthropic client. - const client = new GoogleGenAI({ - apiKey: 'healthy', - httpOptions: { baseUrl: 'https://example.com/google-vertex' }, - }) - - const response = await client.models.generateContent({ - model: 'gemini-2.5-flash', - contents: 'What is the capital of france?', - config: { maxOutputTokens: 1024, topP: 0.95, topK: 1, temperature: 0.5, stopSequences: ['potato'] }, - }) - - expect(response).toMatchSnapshot('llm') + test('google-vertex/default', async ({ gateway }) => { + const { fetch, otelBatch } = gateway + + const response = await fetch( + 'https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent?alt=sse', + { method: 'POST', headers, body }, + ) + + const content = await response.text() + + expect(content).toMatchSnapshot('llm') + expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) + expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + }) + + test('google-vertex/stream', async ({ gateway }) => { + const { fetch, otelBatch } = gateway + + const response = await fetch( + 'https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse', + { method: 'POST', headers: { ...headers, 'x-vcr-filename': 'stream' }, body }, + ) + + const chunks: object[] = [] + for await (const chunk of response.body!) { + chunks.push(chunk) + } + + expect(chunks).toMatchSnapshot('chunks') expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') }) diff --git a/gateway/test/providers/google.spec.ts.snap b/gateway/test/providers/google.spec.ts.snap new file mode 100644 index 0000000..55576e2 --- /dev/null +++ b/gateway/test/providers/google.spec.ts.snap @@ -0,0 +1,1950 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`google > google-vertex/default > llm 1`] = ` +"data: {"candidates": [{"content": {"role": "model","parts": [{"functionCall": {"name": "final_result","args": {"name": "Samuel","city": "London","dob": "1987-01-28"}},"thoughtSignature": "CswDAePx/15lDrSIcIjN85FpyyOl3oASu2R23QD4Z3cj5XRUKOD/3/mMqcHv5AeXc/L+P1eVNpq5C3xM9/8zil6gOOZI91F/r1kmKvxmCECD1SW+p1dtzG6eljHd51vd2Gx7eqtEek1ORzeLP4zSWY2GDlZA9fZIs/uIfhLyOlkiiB1P/GAAUmBPT/TZqdOpQZXBt8MAUrbTOQfbhQ1qbxdrYRveZRMzXS898K6NmjrN5quNiaUgwEbc2J6NAoDOl5jdK8tIt7m25qdpjSYMpAGYD0c0Le2yPf8eO6A9J6zYp1lqVCTifby4/nP5RkVM2e1L4pH6oYisgQsyDUSEDCSG2GXhGO9WU5wFYwkjlhB8ghLU92kVgr/Rq54K/0GaYGBgzKi+YrD8c6QKSTZWTw/46D8lQ1goL1Y1YdtRiRGNFRJqKmSbkxjIOQoIk4glxcsm7L2lucL4miz4yZioBe7HEPDtmQY0FEKM5DQhwuj+AF1bBl5WE/9NYUsuGY3DxSi0lVd2v8+zd0op9U8z3PX4cmdKVkf/kA9TuQSPXBZpYgj7CssoMBT4ssLVoUgetOrNvJL99liSvsPOBZaLDKpNTXQA9GdZvQltoSolOg=="}]},"finishReason": "STOP","avgLogprobs": -0.48033348719278973}],"usageMetadata": {"promptTokenCount": 79,"candidatesTokenCount": 18,"totalTokenCount": 240,"trafficType": "ON_DEMAND","promptTokensDetails": [{"modality": "TEXT","tokenCount": 79}],"candidatesTokensDetails": [{"modality": "TEXT","tokenCount": 18}],"thoughtsTokenCount": 143},"modelVersion": "gemini-2.5-flash","createTime": "2025-11-04T15:31:56.289691Z","responseId": "bBwKaZvXEemDn9kP56mN8QI"} + +" +`; + +exports[`google > google-vertex/default > span 1`] = ` +[ + { + "key": "logfire.msg", + "value": { + "stringValue": "chat streaming", + }, + }, + { + "key": "logfire.json_schema", + "value": { + "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.accept-encoding":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-length":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.traceparent":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-goog-api-client":{"type":"string"},"http.request.header.x-goog-api-key":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.content-type":{"type":"string"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", + }, + }, + { + "key": "logfire.level_num", + "value": { + "intValue": 9, + }, + }, + { + "key": "gen_ai.system", + "value": { + "stringValue": "google-vertex", + }, + }, + { + "key": "gen_ai.operation.name", + "value": { + "stringValue": "chat", + }, + }, + { + "key": "gen_ai.response.model", + "value": { + "stringValue": "gemini-2.5-flash", + }, + }, + { + "key": "gen_ai.usage.input_tokens", + "value": { + "intValue": 79, + }, + }, + { + "key": "gen_ai.usage.output_tokens", + "value": { + "intValue": 161, + }, + }, + { + "key": "http.request.method", + "value": { + "stringValue": "POST", + }, + }, + { + "key": "url.full", + "value": { + "stringValue": "https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent?alt=sse", + }, + }, + { + "key": "http.request.header.accept", + "value": { + "stringValue": "*/*", + }, + }, + { + "key": "http.request.header.accept-encoding", + "value": { + "stringValue": "deflate", + }, + }, + { + "key": "http.request.header.authorization", + "value": { + "stringValue": "healthy", + }, + }, + { + "key": "http.request.header.content-length", + "value": { + "stringValue": "830", + }, + }, + { + "key": "http.request.header.content-type", + "value": { + "stringValue": "application/json", + }, + }, + { + "key": "http.request.header.traceparent", + "value": { + "stringValue": "00-019a4effa21047ac31372f093cb8e712-8b60768281864a49-01", + }, + }, + { + "key": "http.request.header.user-agent", + "value": { + "stringValue": "pydantic-ai/1.0.19.dev5+b3b34f9, google-genai-sdk/1.36.0 gl-python/3.13.0 via Pydantic AI Gateway unknown, contact engineering@pydantic.dev", + }, + }, + { + "key": "http.request.header.x-goog-api-client", + "value": { + "stringValue": "google-genai-sdk/1.36.0 gl-python/3.13.0", + }, + }, + { + "key": "http.request.header.x-goog-api-key", + "value": { + "stringValue": "unset", + }, + }, + { + "key": "http.response.status_code", + "value": { + "intValue": 200, + }, + }, + { + "key": "http.response.header.content-type", + "value": { + "stringValue": "text/event-stream", + }, + }, + { + "key": "http.response.header.server", + "value": { + "stringValue": "uvicorn", + }, + }, + { + "key": "http.response.header.transfer-encoding", + "value": { + "stringValue": "chunked", + }, + }, +] +`; + +exports[`google > google-vertex/stream > chunks 1`] = ` +[ + Uint8Array [ + 100, + 97, + 116, + 97, + 58, + 32, + 123, + 34, + 99, + 97, + 110, + 100, + 105, + 100, + 97, + 116, + 101, + 115, + 34, + 58, + 32, + 91, + 123, + 34, + 99, + 111, + 110, + 116, + 101, + 110, + 116, + 34, + 58, + 32, + 123, + 34, + 114, + 111, + 108, + 101, + 34, + 58, + 32, + 34, + 109, + 111, + 100, + 101, + 108, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 115, + 34, + 58, + 32, + 91, + 123, + 34, + 102, + 117, + 110, + 99, + 116, + 105, + 111, + 110, + 67, + 97, + 108, + 108, + 34, + 58, + 32, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 32, + 34, + 102, + 105, + 110, + 97, + 108, + 95, + 114, + 101, + 115, + 117, + 108, + 116, + 34, + 44, + 34, + 97, + 114, + 103, + 115, + 34, + 58, + 32, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 32, + 34, + 83, + 97, + 109, + 117, + 101, + 108, + 34, + 44, + 34, + 100, + 111, + 98, + 34, + 58, + 32, + 34, + 49, + 57, + 56, + 55, + 45, + 48, + 49, + 45, + 50, + 56, + 34, + 44, + 34, + 99, + 105, + 116, + 121, + 34, + 58, + 32, + 34, + 76, + 111, + 110, + 100, + 111, + 110, + 34, + 125, + 125, + 44, + 34, + 116, + 104, + 111, + 117, + 103, + 104, + 116, + 83, + 105, + 103, + 110, + 97, + 116, + 117, + 114, + 101, + 34, + 58, + 32, + 34, + 67, + 105, + 119, + 66, + 52, + 47, + 72, + 47, + 88, + 114, + 70, + 72, + 99, + 113, + 74, + 53, + 112, + 119, + 73, + 69, + 77, + 81, + 110, + 66, + 104, + 81, + 66, + 72, + 97, + 122, + 89, + 109, + 85, + 98, + 55, + 88, + 67, + 49, + 99, + 90, + 104, + 76, + 54, + 56, + 109, + 120, + 65, + 83, + 73, + 73, + 98, + 109, + 87, + 121, + 70, + 106, + 117, + 122, + 88, + 120, + 97, + 119, + 113, + 65, + 65, + 81, + 72, + 106, + 56, + 102, + 57, + 101, + 112, + 88, + 120, + 86, + 106, + 67, + 68, + 121, + 48, + 47, + 111, + 71, + 78, + 88, + 78, + 49, + 76, + 70, + 121, + 79, + 116, + 100, + 65, + 97, + 113, + 80, + 106, + 115, + 121, + 99, + 77, + 53, + 66, + 120, + 67, + 89, + 65, + 80, + 69, + 119, + 108, + 71, + 107, + 105, + 84, + 77, + 86, + 85, + 116, + 74, + 97, + 71, + 110, + 90, + 79, + 90, + 86, + 106, + 77, + 67, + 65, + 49, + 69, + 55, + 106, + 106, + 119, + 106, + 121, + 97, + 85, + 110, + 76, + 105, + 48, + 74, + 88, + 85, + 110, + 47, + 107, + 72, + 72, + 112, + 78, + 90, + 73, + 75, + 72, + 72, + 70, + 122, + 50, + 54, + 85, + 111, + 53, + 79, + 85, + 78, + 89, + 116, + 56, + 67, + 97, + 110, + 88, + 69, + 115, + 98, + 102, + 76, + 89, + 54, + 105, + 43, + 53, + 119, + 73, + 77, + 118, + 76, + 89, + 85, + 82, + 83, + 120, + 66, + 101, + 87, + 113, + 56, + 49, + 52, + 117, + 69, + 119, + 86, + 43, + 83, + 105, + 102, + 54, + 122, + 51, + 55, + 87, + 102, + 97, + 78, + 118, + 85, + 53, + 75, + 114, + 82, + 57, + 101, + 107, + 52, + 49, + 114, + 67, + 82, + 67, + 109, + 107, + 66, + 52, + 47, + 72, + 47, + 88, + 108, + 104, + 82, + 49, + 72, + 107, + 72, + 103, + 109, + 75, + 71, + 107, + 78, + 90, + 110, + 67, + 98, + 122, + 76, + 52, + 97, + 76, + 89, + 86, + 110, + 110, + 65, + 51, + 57, + 112, + 53, + 89, + 70, + 97, + 90, + 118, + 115, + 79, + 118, + 118, + 56, + 79, + 84, + 119, + 101, + 100, + 47, + 97, + 114, + 70, + 99, + 86, + 82, + 69, + 86, + 101, + 103, + 84, + 57, + 106, + 73, + 117, + 115, + 67, + 43, + 66, + 72, + 98, + 120, + 81, + 76, + 54, + 106, + 120, + 65, + 105, + 47, + 114, + 43, + 55, + 89, + 120, + 111, + 52, + 68, + 54, + 76, + 70, + 107, + 104, + 54, + 56, + 106, + 47, + 100, + 98, + 71, + 122, + 47, + 89, + 49, + 107, + 86, + 121, + 43, + 100, + 119, + 113, + 114, + 81, + 72, + 74, + 52, + 43, + 99, + 57, + 85, + 117, + 80, + 79, + 90, + 110, + 77, + 75, + 122, + 116, + 115, + 43, + 101, + 73, + 105, + 112, + 54, + 103, + 75, + 101, + 119, + 72, + 106, + 56, + 102, + 57, + 101, + 68, + 65, + 100, + 67, + 66, + 49, + 65, + 99, + 103, + 97, + 88, + 99, + 101, + 81, + 84, + 99, + 116, + 113, + 113, + 48, + 80, + 101, + 121, + 99, + 98, + 72, + 105, + 115, + 83, + 113, + 76, + 75, + 113, + 98, + 48, + 98, + 107, + 116, + 84, + 117, + 100, + 53, + 83, + 49, + 49, + 70, + 50, + 108, + 54, + 105, + 66, + 50, + 97, + 103, + 100, + 87, + 108, + 54, + 74, + 47, + 52, + 76, + 114, + 120, + 55, + 88, + 103, + 48, + 109, + 68, + 112, + 118, + 74, + 109, + 73, + 90, + 98, + 109, + 68, + 71, + 71, + 114, + 57, + 122, + 65, + 121, + 43, + 80, + 121, + 88, + 84, + 68, + 112, + 99, + 97, + 56, + 88, + 119, + 69, + 80, + 68, + 79, + 67, + 98, + 79, + 97, + 111, + 115, + 68, + 71, + 111, + 102, + 113, + 118, + 65, + 99, + 102, + 73, + 108, + 67, + 83, + 112, + 104, + 53, + 109, + 53, + 54, + 118, + 119, + 108, + 73, + 102, + 110, + 56, + 81, + 68, + 54, + 72, + 102, + 76, + 115, + 85, + 67, + 117, + 71, + 56, + 88, + 81, + 90, + 70, + 49, + 51, + 98, + 99, + 51, + 114, + 107, + 65, + 112, + 49, + 65, + 101, + 80, + 120, + 47, + 49, + 55, + 72, + 83, + 65, + 49, + 111, + 77, + 76, + 121, + 119, + 100, + 109, + 81, + 120, + 54, + 106, + 88, + 89, + 53, + 103, + 43, + 51, + 105, + 66, + 86, + 99, + 115, + 107, + 66, + 101, + 80, + 99, + 88, + 97, + 76, + 52, + 74, + 100, + 52, + 70, + 101, + 83, + 85, + 83, + 113, + 68, + 54, + 51, + 122, + 86, + 117, + 86, + 83, + 117, + 111, + 104, + 51, + 81, + 74, + 108, + 85, + 48, + 122, + 79, + 89, + 121, + 114, + 102, + 90, + 76, + 43, + 102, + 51, + 76, + 116, + 53, + 86, + 103, + 85, + 103, + 76, + 71, + 48, + 115, + 105, + 56, + 79, + 118, + 111, + 68, + 88, + 65, + 122, + 80, + 110, + 57, + 112, + 117, + 52, + 88, + 82, + 82, + 111, + 76, + 66, + 75, + 74, + 113, + 103, + 66, + 50, + 54, + 76, + 80, + 87, + 85, + 48, + 88, + 73, + 47, + 118, + 118, + 49, + 49, + 76, + 56, + 101, + 88, + 82, + 69, + 110, + 99, + 103, + 52, + 54, + 90, + 53, + 65, + 85, + 98, + 115, + 50, + 118, + 71, + 74, + 49, + 115, + 81, + 77, + 110, + 67, + 113, + 85, + 66, + 65, + 101, + 80, + 120, + 47, + 49, + 52, + 68, + 105, + 80, + 115, + 101, + 103, + 118, + 56, + 54, + 67, + 72, + 101, + 79, + 84, + 97, + 77, + 103, + 56, + 112, + 55, + 100, + 119, + 122, + 68, + 68, + 83, + 79, + 102, + 68, + 109, + 76, + 88, + 121, + 99, + 106, + 116, + 71, + 113, + 90, + 75, + 77, + 121, + 116, + 88, + 85, + 88, + 105, + 87, + 99, + 66, + 115, + 51, + 109, + 116, + 78, + 105, + 48, + 114, + 120, + 52, + 68, + 52, + 121, + 81, + 105, + 104, + 109, + 80, + 81, + 67, + 108, + 66, + 102, + 84, + 107, + 112, + 80, + 120, + 52, + 112, + 84, + 57, + 43, + 119, + 71, + 105, + 56, + 54, + 78, + 102, + 57, + 55, + 67, + 67, + 75, + 65, + 68, + 119, + 73, + 102, + 68, + 80, + 74, + 66, + 81, + 70, + 52, + 76, + 84, + 85, + 118, + 65, + 50, + 52, + 118, + 49, + 97, + 121, + 57, + 82, + 69, + 112, + 100, + 97, + 73, + 50, + 50, + 106, + 97, + 100, + 99, + 72, + 108, + 69, + 50, + 115, + 84, + 118, + 68, + 50, + 56, + 105, + 109, + 54, + 51, + 72, + 115, + 81, + 53, + 118, + 109, + 111, + 86, + 48, + 89, + 89, + 76, + 105, + 97, + 106, + 56, + 50, + 106, + 100, + 53, + 50, + 118, + 121, + 89, + 76, + 75, + 68, + 122, + 98, + 55, + 43, + 76, + 103, + 107, + 87, + 108, + 50, + 86, + 52, + 85, + 104, + 74, + 66, + 72, + 121, + 84, + 70, + 107, + 43, + 119, + 77, + 103, + 107, + 80, + 105, + 82, + 117, + 102, + 116, + 112, + 103, + 65, + 107, + 106, + 68, + 108, + 109, + 117, + 67, + 107, + 103, + 66, + 52, + 47, + 72, + 47, + 88, + 116, + 75, + 82, + 43, + 114, + 74, + 114, + 98, + 114, + 84, + 80, + 114, + 75, + 65, + 100, + 97, + 68, + 116, + 115, + 56, + 77, + 49, + 87, + 122, + 71, + 69, + 112, + 115, + 99, + 117, + 99, + 110, + 89, + 70, + 112, + 101, + 113, + 73, + 79, + 107, + 81, + 55, + 71, + 101, + 97, + 100, + 117, + 107, + 87, + 81, + 101, + 99, + 53, + 71, + 66, + 115, + 74, + 86, + 84, + 81, + 54, + 114, + 115, + 109, + 97, + 82, + 122, + 112, + 54, + 110, + 52, + 50, + 66, + 65, + 108, + 114, + 76, + 109, + 111, + 73, + 85, + 73, + 97, + 99, + 77, + 116, + 50, + 102, + 69, + 56, + 61, + 34, + 125, + 93, + 125, + 44, + 34, + 102, + 105, + 110, + 105, + 115, + 104, + 82, + 101, + 97, + 115, + 111, + 110, + 34, + 58, + 32, + 34, + 83, + 84, + 79, + 80, + 34, + 125, + 93, + 44, + 34, + 117, + 115, + 97, + 103, + 101, + 77, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 32, + 123, + 34, + 112, + 114, + 111, + 109, + 112, + 116, + 84, + 111, + 107, + 101, + 110, + 67, + 111, + 117, + 110, + 116, + 34, + 58, + 32, + 55, + 57, + 44, + 34, + 99, + 97, + 110, + 100, + 105, + 100, + 97, + 116, + 101, + 115, + 84, + 111, + 107, + 101, + 110, + 67, + 111, + 117, + 110, + 116, + 34, + 58, + 32, + 49, + 56, + 44, + 34, + 116, + 111, + 116, + 97, + 108, + 84, + 111, + 107, + 101, + 110, + 67, + 111, + 117, + 110, + 116, + 34, + 58, + 32, + 50, + 54, + 55, + 44, + 34, + 116, + 114, + 97, + 102, + 102, + 105, + 99, + 84, + 121, + 112, + 101, + 34, + 58, + 32, + 34, + 79, + 78, + 95, + 68, + 69, + 77, + 65, + 78, + 68, + 34, + 44, + 34, + 112, + 114, + 111, + 109, + 112, + 116, + 84, + 111, + 107, + 101, + 110, + 115, + 68, + 101, + 116, + 97, + 105, + 108, + 115, + 34, + 58, + 32, + 91, + 123, + 34, + 109, + 111, + 100, + 97, + 108, + 105, + 116, + 121, + 34, + 58, + 32, + 34, + 84, + 69, + 88, + 84, + 34, + 44, + 34, + 116, + 111, + 107, + 101, + 110, + 67, + 111, + 117, + 110, + 116, + 34, + 58, + 32, + 55, + 57, + 125, + 93, + 44, + 34, + 99, + 97, + 110, + 100, + 105, + 100, + 97, + 116, + 101, + 115, + 84, + 111, + 107, + 101, + 110, + 115, + 68, + 101, + 116, + 97, + 105, + 108, + 115, + 34, + 58, + 32, + 91, + 123, + 34, + 109, + 111, + 100, + 97, + 108, + 105, + 116, + 121, + 34, + 58, + 32, + 34, + 84, + 69, + 88, + 84, + 34, + 44, + 34, + 116, + 111, + 107, + 101, + 110, + 67, + 111, + 117, + 110, + 116, + 34, + 58, + 32, + 49, + 56, + 125, + 93, + 44, + 34, + 116, + 104, + 111, + 117, + 103, + 104, + 116, + 115, + 84, + 111, + 107, + 101, + 110, + 67, + 111, + 117, + 110, + 116, + 34, + 58, + 32, + 49, + 55, + 48, + 125, + 44, + 34, + 109, + 111, + 100, + 101, + 108, + 86, + 101, + 114, + 115, + 105, + 111, + 110, + 34, + 58, + 32, + 34, + 103, + 101, + 109, + 105, + 110, + 105, + 45, + 50, + 46, + 53, + 45, + 102, + 108, + 97, + 115, + 104, + 34, + 44, + 34, + 99, + 114, + 101, + 97, + 116, + 101, + 84, + 105, + 109, + 101, + 34, + 58, + 32, + 34, + 50, + 48, + 50, + 53, + 45, + 49, + 49, + 45, + 48, + 52, + 84, + 49, + 54, + 58, + 48, + 48, + 58, + 48, + 57, + 46, + 49, + 56, + 51, + 53, + 48, + 57, + 90, + 34, + 44, + 34, + 114, + 101, + 115, + 112, + 111, + 110, + 115, + 101, + 73, + 100, + 34, + 58, + 32, + 34, + 67, + 83, + 77, + 75, + 97, + 100, + 87, + 90, + 67, + 55, + 71, + 51, + 54, + 77, + 69, + 80, + 49, + 89, + 102, + 108, + 45, + 65, + 103, + 34, + 125, + 13, + 10, + 13, + 10, + ], +] +`; + +exports[`google > google-vertex/stream > span 1`] = ` +[ + { + "key": "logfire.msg", + "value": { + "stringValue": "chat streaming", + }, + }, + { + "key": "logfire.json_schema", + "value": { + "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.accept-encoding":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-length":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.traceparent":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-goog-api-client":{"type":"string"},"http.request.header.x-goog-api-key":{"type":"string"},"http.request.header.x-vcr-filename":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.content-type":{"type":"string"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", + }, + }, + { + "key": "logfire.level_num", + "value": { + "intValue": 9, + }, + }, + { + "key": "gen_ai.system", + "value": { + "stringValue": "google-vertex", + }, + }, + { + "key": "gen_ai.operation.name", + "value": { + "stringValue": "chat", + }, + }, + { + "key": "gen_ai.response.model", + "value": { + "stringValue": "gemini-2.5-flash", + }, + }, + { + "key": "gen_ai.usage.input_tokens", + "value": { + "intValue": 79, + }, + }, + { + "key": "gen_ai.usage.output_tokens", + "value": { + "intValue": 188, + }, + }, + { + "key": "http.request.method", + "value": { + "stringValue": "POST", + }, + }, + { + "key": "url.full", + "value": { + "stringValue": "https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse", + }, + }, + { + "key": "http.request.header.accept", + "value": { + "stringValue": "*/*", + }, + }, + { + "key": "http.request.header.accept-encoding", + "value": { + "stringValue": "deflate", + }, + }, + { + "key": "http.request.header.authorization", + "value": { + "stringValue": "healthy", + }, + }, + { + "key": "http.request.header.content-length", + "value": { + "stringValue": "830", + }, + }, + { + "key": "http.request.header.content-type", + "value": { + "stringValue": "application/json", + }, + }, + { + "key": "http.request.header.traceparent", + "value": { + "stringValue": "00-019a4effa21047ac31372f093cb8e712-8b60768281864a49-01", + }, + }, + { + "key": "http.request.header.user-agent", + "value": { + "stringValue": "pydantic-ai/1.0.19.dev5+b3b34f9, google-genai-sdk/1.36.0 gl-python/3.13.0 via Pydantic AI Gateway unknown, contact engineering@pydantic.dev", + }, + }, + { + "key": "http.request.header.x-goog-api-client", + "value": { + "stringValue": "google-genai-sdk/1.36.0 gl-python/3.13.0", + }, + }, + { + "key": "http.request.header.x-goog-api-key", + "value": { + "stringValue": "unset", + }, + }, + { + "key": "http.request.header.x-vcr-filename", + "value": { + "stringValue": "stream", + }, + }, + { + "key": "http.response.status_code", + "value": { + "intValue": 200, + }, + }, + { + "key": "http.response.header.content-type", + "value": { + "stringValue": "text/event-stream", + }, + }, + { + "key": "http.response.header.server", + "value": { + "stringValue": "uvicorn", + }, + }, + { + "key": "http.response.header.transfer-encoding", + "value": { + "stringValue": "chunked", + }, + }, +] +`; diff --git a/gateway/test/providers/openai.spec.ts b/gateway/test/providers/openai.spec.ts index 48ad2b6..2fe966f 100644 --- a/gateway/test/providers/openai.spec.ts +++ b/gateway/test/providers/openai.spec.ts @@ -129,18 +129,18 @@ describe('openai', () => { expect(completion).toMatchSnapshot('llm') expect(completion.usage).toMatchInlineSnapshot(` { - "input_tokens": 1139, + "input_tokens": 1808, "input_tokens_details": { "cached_tokens": 0, }, - "output_tokens": 469, + "output_tokens": 1061, "output_tokens_details": { - "reasoning_tokens": 448, + "reasoning_tokens": 1024, }, "pydantic_ai_gateway": { - "cost_estimate": 0.006113749999999999, + "cost_estimate": 0.01287, }, - "total_tokens": 1608, + "total_tokens": 2869, } `) expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) diff --git a/gateway/test/providers/openai.spec.ts.snap b/gateway/test/providers/openai.spec.ts.snap index 05f39f6..790808a 100644 --- a/gateway/test/providers/openai.spec.ts.snap +++ b/gateway/test/providers/openai.spec.ts.snap @@ -14,8 +14,8 @@ exports[`openai > openai chat > llm 1`] = ` }, }, ], - "created": 1761823178, - "id": "chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo", + "created": 1762271642, + "id": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", "model": "gpt-5-2025-08-07", "object": "chat.completion", "service_tier": "default", @@ -120,7 +120,7 @@ exports[`openai > openai chat > span 1`] = ` { "key": "gen_ai.response.id", "value": { - "stringValue": "chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo", + "stringValue": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", }, }, { @@ -296,7 +296,7 @@ exports[`openai > openai chat > span 1`] = ` { "key": "http.response.body.text", "value": { - "stringValue": "{"id":"chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo","object":"chat.completion","created":1761823178,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00013875}},"service_tier":"default","system_fingerprint":null}", + "stringValue": "{"id":"chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV","object":"chat.completion","created":1762271642,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00013875}},"service_tier":"default","system_fingerprint":null}", }, }, { @@ -360,10 +360,10 @@ exports[`openai > openai chat stream > chunks 1`] = ` "index": 0, }, ], - "created": 1761823216, - "id": "chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q", + "created": 1762271713, + "id": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", "model": "gpt-5-2025-08-07", - "obfuscation": "znk17e1VnU", + "obfuscation": "1oeFyzekfM", "object": "chat.completion.chunk", "service_tier": "default", "system_fingerprint": null, @@ -379,10 +379,10 @@ exports[`openai > openai chat stream > chunks 1`] = ` "index": 0, }, ], - "created": 1761823216, - "id": "chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q", + "created": 1762271713, + "id": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", "model": "gpt-5-2025-08-07", - "obfuscation": "9uyCALM", + "obfuscation": "iNYovHl", "object": "chat.completion.chunk", "service_tier": "default", "system_fingerprint": null, @@ -398,10 +398,10 @@ exports[`openai > openai chat stream > chunks 1`] = ` "index": 0, }, ], - "created": 1761823216, - "id": "chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q", + "created": 1762271713, + "id": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", "model": "gpt-5-2025-08-07", - "obfuscation": "DxMvC33A2iV", + "obfuscation": "z2nujSb0c5n", "object": "chat.completion.chunk", "service_tier": "default", "system_fingerprint": null, @@ -415,10 +415,10 @@ exports[`openai > openai chat stream > chunks 1`] = ` "index": 0, }, ], - "created": 1761823216, - "id": "chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q", + "created": 1762271713, + "id": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", "model": "gpt-5-2025-08-07", - "obfuscation": "6BmmA2", + "obfuscation": "OvkX97", "object": "chat.completion.chunk", "service_tier": "default", "system_fingerprint": null, @@ -426,10 +426,10 @@ exports[`openai > openai chat stream > chunks 1`] = ` }, { "choices": [], - "created": 1761823216, - "id": "chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q", + "created": 1762271713, + "id": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", "model": "gpt-5-2025-08-07", - "obfuscation": "8u6q1bA9GJu", + "obfuscation": "d9x8d108MYZ", "object": "chat.completion.chunk", "service_tier": "default", "system_fingerprint": null, @@ -463,7 +463,7 @@ exports[`openai > openai chat stream > span 1`] = ` { "key": "logfire.json_schema", "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.response.model":{"type":"string"},"gen_ai.response.id":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{"type":"number"},"gen_ai.usage.output_audio_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-length":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-stainless-arch":{"type":"string"},"http.request.header.x-stainless-lang":{"type":"string"},"http.request.header.x-stainless-os":{"type":"string"},"http.request.header.x-stainless-package-version":{"type":"string"},"http.request.header.x-stainless-retry-count":{"type":"string"},"http.request.header.x-stainless-runtime":{"type":"string"},"http.request.header.x-stainless-runtime-version":{"type":"string"},"http.request.header.x-stainless-timeout":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", + "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.response.model":{"type":"string"},"gen_ai.response.id":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{"type":"number"},"gen_ai.usage.output_audio_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-length":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-stainless-arch":{"type":"string"},"http.request.header.x-stainless-lang":{"type":"string"},"http.request.header.x-stainless-os":{"type":"string"},"http.request.header.x-stainless-package-version":{"type":"string"},"http.request.header.x-stainless-retry-count":{"type":"string"},"http.request.header.x-stainless-runtime":{"type":"string"},"http.request.header.x-stainless-runtime-version":{"type":"string"},"http.request.header.x-stainless-timeout":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.content-type":{"type":"string"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", }, }, { @@ -505,7 +505,7 @@ exports[`openai > openai chat stream > span 1`] = ` { "key": "gen_ai.response.id", "value": { - "stringValue": "chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q", + "stringValue": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", }, }, { @@ -634,6 +634,12 @@ exports[`openai > openai chat stream > span 1`] = ` "intValue": 200, }, }, + { + "key": "http.response.header.content-type", + "value": { + "stringValue": "text/event-stream; charset=utf-8", + }, + }, { "key": "http.response.header.server", "value": { @@ -655,9 +661,9 @@ exports[`openai > openai responses > llm 1`] = ` "billing": { "payer": "developer", }, - "created_at": 1761823182, + "created_at": 1762271645, "error": null, - "id": "resp_09393ae2ee2c946a00690349ce17e081a1a43492a969b778ed", + "id": "resp_0460eb44ce4bf29800690a219dd0108193abee8e830ce725cb", "incomplete_details": null, "instructions": "reply concisely", "max_output_tokens": null, @@ -667,7 +673,7 @@ exports[`openai > openai responses > llm 1`] = ` "object": "response", "output": [ { - "id": "rs_09393ae2ee2c946a00690349cea39481a199b6fa1ff1db1ccc", + "id": "rs_0460eb44ce4bf29800690a219ea7b08193be21548d7c847f56", "summary": [], "type": "reasoning", }, @@ -676,20 +682,21 @@ exports[`openai > openai responses > llm 1`] = ` { "annotations": [], "logprobs": [], - "text": "Usually blue in daylight. It can appear red/orange at sunrise/sunset, gray when overcast, and dark at night.", + "text": "Usually blue on a clear day (due to Rayleigh scattering), but it can be red/orange at sunrise/sunset, gray when cloudy, and black at night.", "type": "output_text", }, ], - "id": "msg_09393ae2ee2c946a00690349d1570081a1844badf7835697d5", + "id": "msg_0460eb44ce4bf29800690a21a2388c8193b3f20e6d76de9744", "role": "assistant", "status": "completed", "type": "message", }, ], - "output_text": "Usually blue in daylight. It can appear red/orange at sunrise/sunset, gray when overcast, and dark at night.", + "output_text": "Usually blue on a clear day (due to Rayleigh scattering), but it can be red/orange at sunrise/sunset, gray when cloudy, and black at night.", "parallel_tool_calls": true, "previous_response_id": null, "prompt_cache_key": null, + "prompt_cache_retention": null, "reasoning": { "effort": "medium", "summary": null, @@ -715,14 +722,14 @@ exports[`openai > openai responses > llm 1`] = ` "input_tokens_details": { "cached_tokens": 0, }, - "output_tokens": 96, + "output_tokens": 168, "output_tokens_details": { - "reasoning_tokens": 64, + "reasoning_tokens": 128, }, "pydantic_ai_gateway": { - "cost_estimate": 0.000985, + "cost_estimate": 0.0017050000000000001, }, - "total_tokens": 116, + "total_tokens": 188, }, "user": null, } @@ -797,7 +804,7 @@ exports[`openai > openai responses > span 1`] = ` { "key": "gen_ai.response.id", "value": { - "stringValue": "resp_09393ae2ee2c946a00690349ce17e081a1a43492a969b778ed", + "stringValue": "resp_0460eb44ce4bf29800690a219dd0108193abee8e830ce725cb", }, }, { @@ -899,7 +906,7 @@ exports[`openai > openai responses > span 1`] = ` { "key": "content", "value": { - "stringValue": "Usually blue in daylight. It can appear red/orange at sunrise/sunset, gray when overcast, and dark at night.", + "stringValue": "Usually blue on a clear day (due to Rayleigh scattering), but it can be red/orange at sunrise/sunset, gray when cloudy, and black at night.", }, }, ], @@ -939,7 +946,7 @@ exports[`openai > openai responses > span 1`] = ` { "key": "http.response.body.text", "value": { - "stringValue": "{"id":"resp_09393ae2ee2c946a00690349ce17e081a1a43492a969b778ed","object":"response","created_at":1761823182,"status":"completed","background":false,"billing":{"payer":"developer"},"error":null,"incomplete_details":null,"instructions":"reply concisely","max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5-2025-08-07","output":[{"id":"rs_09393ae2ee2c946a00690349cea39481a199b6fa1ff1db1ccc","type":"reasoning","summary":[]},{"id":"msg_09393ae2ee2c946a00690349d1570081a1844badf7835697d5","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"Usually blue in daylight. It can appear red/orange at sunrise/sunset, gray when overcast, and dark at night."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1,"truncation":"disabled","usage":{"input_tokens":20,"input_tokens_details":{"cached_tokens":0},"output_tokens":96,"output_tokens_details":{"reasoning_tokens":64},"total_tokens":116,"pydantic_ai_gateway":{"cost_estimate":0.000985}},"user":null,"metadata":{}}", + "stringValue": "{"id":"resp_0460eb44ce4bf29800690a219dd0108193abee8e830ce725cb","object":"response","created_at":1762271645,"status":"completed","background":false,"billing":{"payer":"developer"},"error":null,"incomplete_details":null,"instructions":"reply concisely","max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5-2025-08-07","output":[{"id":"rs_0460eb44ce4bf29800690a219ea7b08193be21548d7c847f56","type":"reasoning","summary":[]},{"id":"msg_0460eb44ce4bf29800690a21a2388c8193b3f20e6d76de9744","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"Usually blue on a clear day (due to Rayleigh scattering), but it can be red/orange at sunrise/sunset, gray when cloudy, and black at night."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1,"truncation":"disabled","usage":{"input_tokens":20,"input_tokens_details":{"cached_tokens":0},"output_tokens":168,"output_tokens_details":{"reasoning_tokens":128},"total_tokens":188,"pydantic_ai_gateway":{"cost_estimate":0.0017050000000000001}},"user":null,"metadata":{}}", }, }, { @@ -967,7 +974,7 @@ exports[`openai > openai responses > span 1`] = ` { "key": "gen_ai.usage.output_tokens", "value": { - "intValue": 96, + "intValue": 168, }, }, { @@ -991,9 +998,9 @@ exports[`openai > openai responses with builtin tools > llm 1`] = ` "billing": { "payer": "developer", }, - "created_at": 1761823186, + "created_at": 1762271730, "error": null, - "id": "resp_032eeb0f1946b3fc00690349d295108195a02e83631653e35f", + "id": "resp_0dc4a3c582e21a7f00690a21f2f5a881a38c2bc17284ab0bdb", "incomplete_details": null, "instructions": "be precise", "max_output_tokens": null, @@ -1003,39 +1010,94 @@ exports[`openai > openai responses with builtin tools > llm 1`] = ` "object": "response", "output": [ { - "id": "rs_032eeb0f1946b3fc00690349d5b7b88195bce44ca9ceab4941", + "id": "rs_0dc4a3c582e21a7f00690a21f5c01c81a387696a1225044c74", "summary": [], "type": "reasoning", }, { - "code": "import decimal, math -n=123902139123 -math.isqrt(n), math.isqrt(n)**2 == n, math.sqrt(n)", - "container_id": "cntr_690349d4fe9c8190a698334dee208a580180e82f7f5ef49c", - "id": "ci_032eeb0f1946b3fc00690349dd01688195a33f5e06b171c4fe", + "code": "import decimal, math, fractions, sys, statistics, random", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a21f79eb081a385bc9ea91607bb8e", "outputs": null, "status": "completed", "type": "code_interpreter_call", }, { - "id": "rs_032eeb0f1946b3fc00690349dffc9481958d20478e41cd0e6d", + "code": "n = 123_902_139_123 +# Check if perfect square +is_square = int(n**0.5)**2 == n +sqrt_n = n**0.5 +int_part = int(sqrt_n) +is_square, int_part", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a21fb50d081a3839fffe407f88965", + "outputs": null, + "status": "completed", + "type": "code_interpreter_call", + }, + { + "id": "rs_0dc4a3c582e21a7f00690a21fdb20081a38c8d71579c417602", + "summary": [], + "type": "reasoning", + }, + { + "code": "int_part, int_part**2, (int_part+1)**2", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a21fe485c81a3bc4ba9ccc7418d6e", + "outputs": null, + "status": "completed", + "type": "code_interpreter_call", + }, + { + "id": "rs_0dc4a3c582e21a7f00690a220009f081a3ae01ee9362963591", "summary": [], "type": "reasoning", }, { "code": "from decimal import Decimal, getcontext getcontext().prec = 50 -n=Decimal(123902139123) -s = n.sqrt() -s", - "container_id": "cntr_690349d4fe9c8190a698334dee208a580180e82f7f5ef49c", - "id": "ci_032eeb0f1946b3fc00690349e486f081958929018fa377592f", +sqrt_val = Decimal(n).sqrt() +sqrt_val", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a2200de5881a3b0fb27e1e2317788", "outputs": null, "status": "completed", "type": "code_interpreter_call", }, { - "id": "rs_032eeb0f1946b3fc00690349e8e29c81958735b9417d916d13", + "id": "rs_0dc4a3c582e21a7f00690a22031aa881a38cd6660b4d830c50", + "summary": [], + "type": "reasoning", + }, + { + "code": "float_val = float(sqrt_val) +float_val", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a2209f9cc81a39c0e1ddfa029c7a6", + "outputs": null, + "status": "completed", + "type": "code_interpreter_call", + }, + { + "id": "rs_0dc4a3c582e21a7f00690a220baf1481a3bda6abb8b9c09f88", + "summary": [], + "type": "reasoning", + }, + { + "code": "from decimal import ROUND_HALF_UP +def round_str(dec, places): + q = Decimal(10) ** -places + return str(dec.quantize(q, rounding=ROUND_HALF_UP)) +for p in [6, 9, 12, 15]: + print(p, round_str(sqrt_val, p))", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a220c125481a3ad139dde1b01ef42", + "outputs": null, + "status": "completed", + "type": "code_interpreter_call", + }, + { + "id": "rs_0dc4a3c582e21a7f00690a220ee65081a3b4a3c606ed32f88c", "summary": [], "type": "reasoning", }, @@ -1044,20 +1106,21 @@ s", { "annotations": [], "logprobs": [], - "text": "√123902139123 ≈ 351997.35669888204", + "text": "The square root of 123,902,139,123 is approximately 351,997.356698882044. It’s not a perfect square.", "type": "output_text", }, ], - "id": "msg_032eeb0f1946b3fc00690349edec7c819599bc5f0620f08cb7", + "id": "msg_0dc4a3c582e21a7f00690a221614e081a3b9e5621bd0f3ba23", "role": "assistant", "status": "completed", "type": "message", }, ], - "output_text": "√123902139123 ≈ 351997.35669888204", + "output_text": "The square root of 123,902,139,123 is approximately 351,997.356698882044. It’s not a perfect square.", "parallel_tool_calls": true, "previous_response_id": null, "prompt_cache_key": null, + "prompt_cache_retention": null, "reasoning": { "effort": "medium", "summary": null, @@ -1086,18 +1149,18 @@ s", "top_p": 1, "truncation": "disabled", "usage": { - "input_tokens": 1139, + "input_tokens": 1808, "input_tokens_details": { "cached_tokens": 0, }, - "output_tokens": 469, + "output_tokens": 1061, "output_tokens_details": { - "reasoning_tokens": 448, + "reasoning_tokens": 1024, }, "pydantic_ai_gateway": { - "cost_estimate": 0.006113749999999999, + "cost_estimate": 0.01287, }, - "total_tokens": 1608, + "total_tokens": 2869, }, "user": null, } @@ -1172,7 +1235,7 @@ exports[`openai > openai responses with builtin tools > span 1`] = ` { "key": "gen_ai.response.id", "value": { - "stringValue": "resp_032eeb0f1946b3fc00690349d295108195a02e83631653e35f", + "stringValue": "resp_0dc4a3c582e21a7f00690a21f2f5a881a38c2bc17284ab0bdb", }, }, { @@ -1279,7 +1342,184 @@ exports[`openai > openai responses with builtin tools > span 1`] = ` { "key": "id", "value": { - "stringValue": "ci_032eeb0f1946b3fc00690349dd01688195a33f5e06b171c4fe", + "stringValue": "ci_0dc4a3c582e21a7f00690a21f79eb081a385bc9ea91607bb8e", + }, + }, + { + "key": "type", + "value": { + "stringValue": "code_interpreter_call", + }, + }, + { + "key": "status", + "value": { + "stringValue": "completed", + }, + }, + { + "key": "code", + "value": { + "stringValue": "import decimal, math, fractions, sys, statistics, random", + }, + }, + { + "key": "container_id", + "value": { + "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + }, + }, + { + "key": "outputs", + "value": {}, + }, + ], + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }, + { + "kvlistValue": { + "values": [ + { + "key": "role", + "value": { + "stringValue": "assistant", + }, + }, + { + "key": "parts", + "value": { + "arrayValue": { + "values": [ + { + "kvlistValue": { + "values": [ + { + "key": "type", + "value": { + "stringValue": "unknown", + }, + }, + { + "key": "part", + "value": { + "kvlistValue": { + "values": [ + { + "key": "id", + "value": { + "stringValue": "ci_0dc4a3c582e21a7f00690a21fb50d081a3839fffe407f88965", + }, + }, + { + "key": "type", + "value": { + "stringValue": "code_interpreter_call", + }, + }, + { + "key": "status", + "value": { + "stringValue": "completed", + }, + }, + { + "key": "code", + "value": { + "stringValue": "n = 123_902_139_123 +# Check if perfect square +is_square = int(n**0.5)**2 == n +sqrt_n = n**0.5 +int_part = int(sqrt_n) +is_square, int_part", + }, + }, + { + "key": "container_id", + "value": { + "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + }, + }, + { + "key": "outputs", + "value": {}, + }, + ], + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }, + { + "kvlistValue": { + "values": [ + { + "key": "role", + "value": { + "stringValue": "assistant", + }, + }, + { + "key": "parts", + "value": { + "arrayValue": { + "values": [], + }, + }, + }, + ], + }, + }, + { + "kvlistValue": { + "values": [ + { + "key": "role", + "value": { + "stringValue": "assistant", + }, + }, + { + "key": "parts", + "value": { + "arrayValue": { + "values": [ + { + "kvlistValue": { + "values": [ + { + "key": "type", + "value": { + "stringValue": "unknown", + }, + }, + { + "key": "part", + "value": { + "kvlistValue": { + "values": [ + { + "key": "id", + "value": { + "stringValue": "ci_0dc4a3c582e21a7f00690a21fe485c81a3bc4ba9ccc7418d6e", }, }, { @@ -1297,15 +1537,13 @@ exports[`openai > openai responses with builtin tools > span 1`] = ` { "key": "code", "value": { - "stringValue": "import decimal, math -n=123902139123 -math.isqrt(n), math.isqrt(n)**2 == n, math.sqrt(n)", + "stringValue": "int_part, int_part**2, (int_part+1)**2", }, }, { "key": "container_id", "value": { - "stringValue": "cntr_690349d4fe9c8190a698334dee208a580180e82f7f5ef49c", + "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", }, }, { @@ -1377,7 +1615,7 @@ math.isqrt(n), math.isqrt(n)**2 == n, math.sqrt(n)", { "key": "id", "value": { - "stringValue": "ci_032eeb0f1946b3fc00690349e486f081958929018fa377592f", + "stringValue": "ci_0dc4a3c582e21a7f00690a2200de5881a3b0fb27e1e2317788", }, }, { @@ -1397,15 +1635,212 @@ math.isqrt(n), math.isqrt(n)**2 == n, math.sqrt(n)", "value": { "stringValue": "from decimal import Decimal, getcontext getcontext().prec = 50 -n=Decimal(123902139123) -s = n.sqrt() -s", +sqrt_val = Decimal(n).sqrt() +sqrt_val", + }, + }, + { + "key": "container_id", + "value": { + "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + }, + }, + { + "key": "outputs", + "value": {}, + }, + ], + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }, + { + "kvlistValue": { + "values": [ + { + "key": "role", + "value": { + "stringValue": "assistant", + }, + }, + { + "key": "parts", + "value": { + "arrayValue": { + "values": [], + }, + }, + }, + ], + }, + }, + { + "kvlistValue": { + "values": [ + { + "key": "role", + "value": { + "stringValue": "assistant", + }, + }, + { + "key": "parts", + "value": { + "arrayValue": { + "values": [ + { + "kvlistValue": { + "values": [ + { + "key": "type", + "value": { + "stringValue": "unknown", + }, + }, + { + "key": "part", + "value": { + "kvlistValue": { + "values": [ + { + "key": "id", + "value": { + "stringValue": "ci_0dc4a3c582e21a7f00690a2209f9cc81a39c0e1ddfa029c7a6", + }, + }, + { + "key": "type", + "value": { + "stringValue": "code_interpreter_call", + }, + }, + { + "key": "status", + "value": { + "stringValue": "completed", + }, + }, + { + "key": "code", + "value": { + "stringValue": "float_val = float(sqrt_val) +float_val", + }, + }, + { + "key": "container_id", + "value": { + "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + }, + }, + { + "key": "outputs", + "value": {}, + }, + ], + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }, + { + "kvlistValue": { + "values": [ + { + "key": "role", + "value": { + "stringValue": "assistant", + }, + }, + { + "key": "parts", + "value": { + "arrayValue": { + "values": [], + }, + }, + }, + ], + }, + }, + { + "kvlistValue": { + "values": [ + { + "key": "role", + "value": { + "stringValue": "assistant", + }, + }, + { + "key": "parts", + "value": { + "arrayValue": { + "values": [ + { + "kvlistValue": { + "values": [ + { + "key": "type", + "value": { + "stringValue": "unknown", + }, + }, + { + "key": "part", + "value": { + "kvlistValue": { + "values": [ + { + "key": "id", + "value": { + "stringValue": "ci_0dc4a3c582e21a7f00690a220c125481a3ad139dde1b01ef42", + }, + }, + { + "key": "type", + "value": { + "stringValue": "code_interpreter_call", + }, + }, + { + "key": "status", + "value": { + "stringValue": "completed", + }, + }, + { + "key": "code", + "value": { + "stringValue": "from decimal import ROUND_HALF_UP +def round_str(dec, places): + q = Decimal(10) ** -places + return str(dec.quantize(q, rounding=ROUND_HALF_UP)) +for p in [6, 9, 12, 15]: + print(p, round_str(sqrt_val, p))", }, }, { "key": "container_id", "value": { - "stringValue": "cntr_690349d4fe9c8190a698334dee208a580180e82f7f5ef49c", + "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", }, }, { @@ -1472,7 +1907,7 @@ s", { "key": "content", "value": { - "stringValue": "√123902139123 ≈ 351997.35669888204", + "stringValue": "The square root of 123,902,139,123 is approximately 351,997.356698882044. It’s not a perfect square.", }, }, ], @@ -1520,7 +1955,7 @@ s", { "key": "http.response.body.text", "value": { - "stringValue": "{"id":"resp_032eeb0f1946b3fc00690349d295108195a02e83631653e35f","object":"response","created_at":1761823186,"status":"completed","background":false,"billing":{"payer":"developer"},"error":null,"incomplete_details":null,"instructions":"be precise","max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5-2025-08-07","output":[{"id":"rs_032eeb0f1946b3fc00690349d5b7b88195bce44ca9ceab4941","type":"reasoning","summary":[]},{"id":"ci_032eeb0f1946b3fc00690349dd01688195a33f5e06b171c4fe","type":"code_interpreter_call","status":"completed","code":"import decimal, math\\r\\nn=123902139123\\r\\nmath.isqrt(n), math.isqrt(n)**2 == n, math.sqrt(n)","container_id":"cntr_690349d4fe9c8190a698334dee208a580180e82f7f5ef49c","outputs":null},{"id":"rs_032eeb0f1946b3fc00690349dffc9481958d20478e41cd0e6d","type":"reasoning","summary":[]},{"id":"ci_032eeb0f1946b3fc00690349e486f081958929018fa377592f","type":"code_interpreter_call","status":"completed","code":"from decimal import Decimal, getcontext\\r\\ngetcontext().prec = 50\\r\\nn=Decimal(123902139123)\\r\\ns = n.sqrt()\\r\\ns","container_id":"cntr_690349d4fe9c8190a698334dee208a580180e82f7f5ef49c","outputs":null},{"id":"rs_032eeb0f1946b3fc00690349e8e29c81958735b9417d916d13","type":"reasoning","summary":[]},{"id":"msg_032eeb0f1946b3fc00690349edec7c819599bc5f0620f08cb7","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"√123902139123 ≈ 351997.35669888204"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1,"truncation":"disabled","usage":{"input_tokens":1139,"input_tokens_details":{"cached_tokens":0},"output_tokens":469,"output_tokens_details":{"reasoning_tokens":448},"total_tokens":1608,"pydantic_ai_gateway":{"cost_estimate":0.006113749999999999}},"user":null,"metadata":{}}", + "stringValue": "{"id":"resp_0dc4a3c582e21a7f00690a21f2f5a881a38c2bc17284ab0bdb","object":"response","created_at":1762271730,"status":"completed","background":false,"billing":{"payer":"developer"},"error":null,"incomplete_details":null,"instructions":"be precise","max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5-2025-08-07","output":[{"id":"rs_0dc4a3c582e21a7f00690a21f5c01c81a387696a1225044c74","type":"reasoning","summary":[]},{"id":"ci_0dc4a3c582e21a7f00690a21f79eb081a385bc9ea91607bb8e","type":"code_interpreter_call","status":"completed","code":"import decimal, math, fractions, sys, statistics, random","container_id":"cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18","outputs":null},{"id":"ci_0dc4a3c582e21a7f00690a21fb50d081a3839fffe407f88965","type":"code_interpreter_call","status":"completed","code":"n = 123_902_139_123\\n# Check if perfect square\\nis_square = int(n**0.5)**2 == n\\nsqrt_n = n**0.5\\nint_part = int(sqrt_n)\\nis_square, int_part","container_id":"cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18","outputs":null},{"id":"rs_0dc4a3c582e21a7f00690a21fdb20081a38c8d71579c417602","type":"reasoning","summary":[]},{"id":"ci_0dc4a3c582e21a7f00690a21fe485c81a3bc4ba9ccc7418d6e","type":"code_interpreter_call","status":"completed","code":"int_part, int_part**2, (int_part+1)**2","container_id":"cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18","outputs":null},{"id":"rs_0dc4a3c582e21a7f00690a220009f081a3ae01ee9362963591","type":"reasoning","summary":[]},{"id":"ci_0dc4a3c582e21a7f00690a2200de5881a3b0fb27e1e2317788","type":"code_interpreter_call","status":"completed","code":"from decimal import Decimal, getcontext\\ngetcontext().prec = 50\\nsqrt_val = Decimal(n).sqrt()\\nsqrt_val","container_id":"cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18","outputs":null},{"id":"rs_0dc4a3c582e21a7f00690a22031aa881a38cd6660b4d830c50","type":"reasoning","summary":[]},{"id":"ci_0dc4a3c582e21a7f00690a2209f9cc81a39c0e1ddfa029c7a6","type":"code_interpreter_call","status":"completed","code":"float_val = float(sqrt_val)\\nfloat_val","container_id":"cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18","outputs":null},{"id":"rs_0dc4a3c582e21a7f00690a220baf1481a3bda6abb8b9c09f88","type":"reasoning","summary":[]},{"id":"ci_0dc4a3c582e21a7f00690a220c125481a3ad139dde1b01ef42","type":"code_interpreter_call","status":"completed","code":"from decimal import ROUND_HALF_UP\\ndef round_str(dec, places):\\n q = Decimal(10) ** -places\\n return str(dec.quantize(q, rounding=ROUND_HALF_UP))\\nfor p in [6, 9, 12, 15]:\\n print(p, round_str(sqrt_val, p))","container_id":"cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18","outputs":null},{"id":"rs_0dc4a3c582e21a7f00690a220ee65081a3b4a3c606ed32f88c","type":"reasoning","summary":[]},{"id":"msg_0dc4a3c582e21a7f00690a221614e081a3b9e5621bd0f3ba23","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The square root of 123,902,139,123 is approximately 351,997.356698882044. It’s not a perfect square."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"code_interpreter","container":{"type":"auto"}}],"top_logprobs":0,"top_p":1,"truncation":"disabled","usage":{"input_tokens":1808,"input_tokens_details":{"cached_tokens":0},"output_tokens":1061,"output_tokens_details":{"reasoning_tokens":1024},"total_tokens":2869,"pydantic_ai_gateway":{"cost_estimate":0.01287}},"user":null,"metadata":{}}", }, }, { @@ -1532,7 +1967,7 @@ s", { "key": "gen_ai.usage.input_tokens", "value": { - "intValue": 1139, + "intValue": 1808, }, }, { @@ -1548,7 +1983,7 @@ s", { "key": "gen_ai.usage.output_tokens", "value": { - "intValue": 469, + "intValue": 1061, }, }, { diff --git a/gateway/test/worker.ts b/gateway/test/worker.ts index 6b300fb..f8662e9 100644 --- a/gateway/test/worker.ts +++ b/gateway/test/worker.ts @@ -84,6 +84,12 @@ class TestKeysDB extends KeysDbD1 { injectCost: true, credentials: env.AWS_BEARER_TOKEN_BEDROCK, }, + { + baseUrl: 'http://localhost:8005/google-vertex', + providerId: 'google-vertex', + injectCost: true, + credentials: env.GOOGLE_SERVICE_ACCOUNT_KEY, + }, ] } diff --git a/gateway/vitest.config.mts b/gateway/vitest.config.mts index 195806f..6f41a63 100644 --- a/gateway/vitest.config.mts +++ b/gateway/vitest.config.mts @@ -25,6 +25,7 @@ export default defineWorkersConfig({ GROQ_API_KEY: process.env.GROQ_API_KEY ?? 'GROQ_API_KEY-unset', ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY ?? 'ANTHROPIC_API_KEY-unset', AWS_BEARER_TOKEN_BEDROCK: process.env.AWS_BEARER_TOKEN_BEDROCK ?? 'AWS_BEARER_TOKEN_BEDROCK-unset', + GOOGLE_SERVICE_ACCOUNT_KEY: process.env.GOOGLE_SERVICE_ACCOUNT_KEY ?? 'GOOGLE_SERVICE_ACCOUNT_KEY-unset', }, }, }, diff --git a/package-lock.json b/package-lock.json index 9cd4833..b8f8cfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "@opentelemetry/resources": "^2.0.1", "@pydantic/genai-prices": "^0.0.35", "@pydantic/logfire-api": "^0.9.0", + "@streamparser/json-whatwg": "^0.0.22", "eventsource-parser": "^3.0.6", "mime-types": "^3.0.1", "ts-pattern": "^5.8.0" @@ -4370,6 +4371,21 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/@streamparser/json": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.22.tgz", + "integrity": "sha512-b6gTSBjJ8G8SuO3Gbbj+zXbVx8NSs1EbpbMKpzGLWMdkR+98McH9bEjSz3+0mPJf68c5nxa3CrJHp5EQNXM6zQ==", + "license": "MIT" + }, + "node_modules/@streamparser/json-whatwg": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@streamparser/json-whatwg/-/json-whatwg-0.0.22.tgz", + "integrity": "sha512-qRYYbaytZYXfGMFNtPAUrbK2CJ0KmGk2l+uLxW1hKVLOt3aBaWGsskiUCdGoaoSTeJBHswNqi59wSppg+IVWKQ==", + "license": "MIT", + "dependencies": { + "@streamparser/json": "^0.0.22" + } + }, "node_modules/@types/chai": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", diff --git a/proxy-vcr/proxy_vcr/cassettes/google-vertex-3fc5cd750ad872a644781b8d4a149f2d8f685bbd2122866dcb4714cf6c38082c.yaml b/proxy-vcr/proxy_vcr/cassettes/google-vertex-3fc5cd750ad872a644781b8d4a149f2d8f685bbd2122866dcb4714cf6c38082c.yaml new file mode 100644 index 0000000..211c7c2 --- /dev/null +++ b/proxy-vcr/proxy_vcr/cassettes/google-vertex-3fc5cd750ad872a644781b8d4a149f2d8f685bbd2122866dcb4714cf6c38082c.yaml @@ -0,0 +1,72 @@ +interactions: +- request: + body: '{"contents":[{"parts":[{"text":"Samuel lived in London and was born on + Jan 28th ''87"}],"role":"user"}],"systemInstruction":{"parts":[{"text":"Extract + information about the person"}],"role":"user"},"tools":[{"functionDeclarations":[{"description":"The + final response which ends this conversation","name":"final_result","parameters":{"properties":{"name":{"description":"The + name of the person.","type":"STRING"},"dob":{"description":"The date of birth + of the person. MUST BE A VALID ISO 8601 date. (format: date)","type":"STRING"},"city":{"description":"The + city where the person lives.","type":"STRING"}},"required":["name","dob","city"],"type":"OBJECT"}}]}],"toolConfig":{"functionCallingConfig":{"mode":"ANY","allowedFunctionNames":["final_result"]}},"generationConfig":{"temperature":0.5,"topP":0.9,"stopSequences":["potato"]}}' + headers: + accept: + - '*/*' + accept-encoding: + - deflate + connection: + - keep-alive + content-length: + - '830' + content-type: + - application/json + host: + - aiplatform.googleapis.com + traceparent: + - 00-019a4effa21047ac31372f093cb8e712-8b60768281864a49-01 + user-agent: + - pydantic-ai/1.0.19.dev5+b3b34f9, google-genai-sdk/1.36.0 gl-python/3.13.0 + via Pydantic AI Gateway unknown, contact engineering@pydantic.dev via Pydantic + AI Gateway test, contact engineering@pydantic.dev + x-goog-api-client: + - google-genai-sdk/1.36.0 gl-python/3.13.0 + x-goog-api-key: + - unset + method: POST + uri: https://aiplatform.googleapis.com/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent?alt=sse + response: + body: + string: "data: {\"candidates\": [{\"content\": {\"role\": \"model\",\"parts\": + [{\"functionCall\": {\"name\": \"final_result\",\"args\": {\"name\": \"Samuel\",\"city\": + \"London\",\"dob\": \"1987-01-28\"}},\"thoughtSignature\": \"CswDAePx/15lDrSIcIjN85FpyyOl3oASu2R23QD4Z3cj5XRUKOD/3/mMqcHv5AeXc/L+P1eVNpq5C3xM9/8zil6gOOZI91F/r1kmKvxmCECD1SW+p1dtzG6eljHd51vd2Gx7eqtEek1ORzeLP4zSWY2GDlZA9fZIs/uIfhLyOlkiiB1P/GAAUmBPT/TZqdOpQZXBt8MAUrbTOQfbhQ1qbxdrYRveZRMzXS898K6NmjrN5quNiaUgwEbc2J6NAoDOl5jdK8tIt7m25qdpjSYMpAGYD0c0Le2yPf8eO6A9J6zYp1lqVCTifby4/nP5RkVM2e1L4pH6oYisgQsyDUSEDCSG2GXhGO9WU5wFYwkjlhB8ghLU92kVgr/Rq54K/0GaYGBgzKi+YrD8c6QKSTZWTw/46D8lQ1goL1Y1YdtRiRGNFRJqKmSbkxjIOQoIk4glxcsm7L2lucL4miz4yZioBe7HEPDtmQY0FEKM5DQhwuj+AF1bBl5WE/9NYUsuGY3DxSi0lVd2v8+zd0op9U8z3PX4cmdKVkf/kA9TuQSPXBZpYgj7CssoMBT4ssLVoUgetOrNvJL99liSvsPOBZaLDKpNTXQA9GdZvQltoSolOg==\"}]},\"finishReason\": + \"STOP\",\"avgLogprobs\": -0.48033348719278973}],\"usageMetadata\": {\"promptTokenCount\": + 79,\"candidatesTokenCount\": 18,\"totalTokenCount\": 240,\"trafficType\": + \"ON_DEMAND\",\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": + 79}],\"candidatesTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": + 18}],\"thoughtsTokenCount\": 143},\"modelVersion\": \"gemini-2.5-flash\",\"createTime\": + \"2025-11-04T15:31:56.289691Z\",\"responseId\": \"bBwKaZvXEemDn9kP56mN8QI\"}\r\n\r\n" + headers: + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + Content-Disposition: + - attachment + Content-Length: + - '1272' + Content-Type: + - text/event-stream + Date: + - Tue, 04 Nov 2025 15:31:59 GMT + Server: + - scaffolding on HTTPServer2 + Vary: + - Origin + - X-Origin + - Referer + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + status: + code: 200 + message: OK +version: 1 diff --git a/proxy-vcr/proxy_vcr/cassettes/google-vertex-stream.yaml b/proxy-vcr/proxy_vcr/cassettes/google-vertex-stream.yaml new file mode 100644 index 0000000..7ae8a49 --- /dev/null +++ b/proxy-vcr/proxy_vcr/cassettes/google-vertex-stream.yaml @@ -0,0 +1,62 @@ +interactions: +- request: + body: '{"contents":[{"parts":[{"text":"Samuel lived in London and was born on + Jan 28th ''87"}],"role":"user"}],"systemInstruction":{"parts":[{"text":"Extract + information about the person"}],"role":"user"},"tools":[{"functionDeclarations":[{"description":"The + final response which ends this conversation","name":"final_result","parameters":{"properties":{"name":{"description":"The + name of the person.","type":"STRING"},"dob":{"description":"The date of birth + of the person. MUST BE A VALID ISO 8601 date. (format: date)","type":"STRING"},"city":{"description":"The + city where the person lives.","type":"STRING"}},"required":["name","dob","city"],"type":"OBJECT"}}]}],"toolConfig":{"functionCallingConfig":{"mode":"ANY","allowedFunctionNames":["final_result"]}},"generationConfig":{"temperature":0.5,"topP":0.9,"stopSequences":["potato"]}}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '830' + host: + - aiplatform.googleapis.com + user-agent: + - python-httpx/0.28.1 + method: POST + uri: https://aiplatform.googleapis.com/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse + response: + body: + string: "data: {\"candidates\": [{\"content\": {\"role\": \"model\",\"parts\": + [{\"functionCall\": {\"name\": \"final_result\",\"args\": {\"name\": \"Samuel\",\"dob\": + \"1987-01-28\",\"city\": \"London\"}},\"thoughtSignature\": \"CiwB4/H/XrFHcqJ5pwIEMQnBhQBHazYmUb7XC1cZhL68mxASIIbmWyFjuzXxawqAAQHj8f9epXxVjCDy0/oGNXN1LFyOtdAaqPjsycM5BxCYAPEwlGkiTMVUtJaGnZOZVjMCA1E7jjwjyaUnLi0JXUn/kHHpNZIKHHFz26Uo5OUNYt8CanXEsbfLY6i+5wIMvLYURSxBeWq814uEwV+Sif6z37WfaNvU5KrR9ek41rCRCmkB4/H/XlhR1HkHgmKGkNZnCbzL4aLYVnnA39p5YFaZvsOvv8OTwed/arFcVREVegT9jIusC+BHbxQL6jxAi/r+7Yxo4D6LFkh68j/dbGz/Y1kVy+dwqrQHJ4+c9UuPOZnMKzts+eIip6gKewHj8f9eDAdCB1AcgaXceQTctqq0PeycbHisSqLKqb0bktTud5S11F2l6iB2agdWl6J/4Lrx7Xg0mDpvJmIZbmDGGr9zAy+PyXTDpca8XwEPDOCbOaosDGofqvAcfIlCSph5m56vwlIfn8QD6HfLsUCuG8XQZF13bc3rkAp1AePx/17HSA1oMLywdmQx6jXY5g+3iBVcskBePcXaL4Jd4FeSUSqD63zVuVSuoh3QJlU0zOYyrfZL+f3Lt5VgUgLG0si8OvoDXAzPn9pu4XRRoLBKJqgB26LPWU0XI/vv11L8eXREncg46Z5AUbs2vGJ1sQMnCqUBAePx/14DiPsegv86CHeOTaMg8p7dwzDDSOfDmLXycjtGqZKMytXUXiWcBs3mtNi0rx4D4yQihmPQClBfTkpPx4pT9+wGi86Nf97CCKADwIfDPJBQF4LTUvA24v1ay9REpdaI22jadcHlE2sTvD28im63HsQ5vmoV0YYLiaj82jd52vyYLKDzb7+LgkWl2V4UhJBHyTFk+wMgkPiRuftpgAkjDlmuCkgB4/H/XtKR+rJrbrTPrKAdaDts8M1WzGEpscucnYFpeqIOkQ7GeadukWQec5GBsJVTQ6rsmaRzp6n42BAlrLmoIUIacMt2fE8=\"}]},\"finishReason\": + \"STOP\"}],\"usageMetadata\": {\"promptTokenCount\": 79,\"candidatesTokenCount\": + 18,\"totalTokenCount\": 267,\"trafficType\": \"ON_DEMAND\",\"promptTokensDetails\": + [{\"modality\": \"TEXT\",\"tokenCount\": 79}],\"candidatesTokensDetails\": + [{\"modality\": \"TEXT\",\"tokenCount\": 18}],\"thoughtsTokenCount\": 170},\"modelVersion\": + \"gemini-2.5-flash\",\"createTime\": \"2025-11-04T16:00:09.183509Z\",\"responseId\": + \"CSMKadWZC7G36MEP1Yfl-Ag\"}\r\n\r\n" + headers: + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + Content-Disposition: + - attachment + Content-Type: + - text/event-stream + Date: + - Tue, 04 Nov 2025 16:00:11 GMT + Server: + - scaffolding on HTTPServer2 + Transfer-Encoding: + - chunked + Vary: + - Origin + - X-Origin + - Referer + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + status: + code: 200 + message: OK +version: 1 diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml index 0c13a33..49c80ab 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml @@ -15,6 +15,8 @@ interactions: - '248' content-type: - application/json + cookie: + - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000 host: - api.openai.com user-agent: @@ -24,16 +26,16 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA4yST0sDMRDF7/spQs67pdvaP/RWLL0o6EVEpSwxmd2NZpOQzEpL6XeXpLW7xQpe - cpjfzOO9yewTQqgUdEEorxnyxqrs9vluZ+7Xzm3LlXh6fZjWS7ZaztyLHq8NTcOEef8Ajj9TA24a - qwCl0UfMHTCEoJrPpvl8NM5n8wgaI0CFscpiNslGw9EkG86z4ew0VxvJwdMFeUsIIWQf3+BQC9jS - BRmmP5UGvGcV0MW5iRDqjAoVyryXHplGmnaQG42go+lH5qQf9KGDsvUsWNOtUj3AtDbIQrRoa3Mi - h7ORUmrp68IB80YHcY/G0kgPCSGbGKy98EqtM43FAs0nRNnR+ChHu0V2MM9PEA0y1dXHN+kVtUIA - Mql8bzGUM16D6Ca7LbJWSNMDSS/bbzPXtI+5pa7+I98BzsEiiMI6EJJfBu7aHIQz+6vtvONomHpw - X5JDgRJc+AcBJWvV8QSo33mEpiilrsBZJ+MdhK9ODsk3AAAA//8DAD2WcKgEAwAA + H4sIAAAAAAAAA4ySTUsDMRCG7/srQs7d0m6/ZI+20IOCrRRFpCwxmW2j2SQks2KR/ndJ+rFbrOAl + h3lmXt53Mt8JIVQKmhPKtwx5ZVU6fZnB+Hb6/HA3n89Xanlvyt0ymy0Gy9XjE+2ECfP2DhxPU11u + KqsApdEHzB0whKDan4yzbNIfD7MIKiNAhbGNxXSUZr1slPZu0t7kOLc1koOnOXlNCCHkO77BoRbw + RXPS65wqFXjPNkDzcxMh1BkVKpR5Lz0yjbTTQG40go6mF8xJ321DB2XtWbCma6VagGltkIVo0db6 + SPZnI6XU0m8LB8wbHcQ9Gksj3SeErGOw+sIrtc5UFgs0HxBls8FBjjaLbGC/f4RokKmmPhh2rqgV + ApBJ5VuLoZzxLYhmstkiq4U0LZC0sv02c037kFvqzX/kG8A5WARRWAdC8svATZuDcGZ/tZ13HA1T + D+5TcihQggv/IKBktTqcAPU7j1AVpdQbcNbJeAfhq5N98gMAAP//AwDsjmSCBAMAAA== headers: CF-RAY: - - 996a84cfabfa8e65-AMS + - 9995499d7c4bf64d-AMS Connection: - keep-alive Content-Encoding: @@ -41,15 +43,13 @@ interactions: Content-Type: - application/json Date: - - Thu, 30 Oct 2025 11:19:41 GMT + - Tue, 04 Nov 2025 15:54:03 GMT Server: - cloudflare Set-Cookie: - - __cf_bm=MVOz_vf1d.29xNg5eAq9hZGUpEToHZ9rpFhKtO12GDM-1761823181-1.0.1.1-f_gaGN5YtX1DvFTkOFoMkGEiQh3rNh89AjWKvmjk0_.XbDAuRV_tnz21YzeMVcacrAa5yTXsqWXN_9hwojHD6iRS.NTgTTLPQd8AGlqn.vM; - path=/; expires=Thu, 30-Oct-25 11:49:41 GMT; domain=.api.openai.com; HttpOnly; + - __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ; + path=/; expires=Tue, 04-Nov-25 16:24:03 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None - - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000; - path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload Transfer-Encoding: @@ -65,29 +65,29 @@ interactions: openai-organization: - pydantic-28gund openai-processing-ms: - - '2346' + - '1846' openai-project: - proj_dKobscVY9YJxeEaDJen54e3d openai-version: - '2020-10-01' x-envoy-upstream-service-time: - - '2516' + - '2067' x-openai-proxy-wasm: - v0.1 x-ratelimit-limit-requests: - - '10000' + - '15000' x-ratelimit-limit-tokens: - - '4000000' + - '40000000' x-ratelimit-remaining-requests: - - '9999' + - '14999' x-ratelimit-remaining-tokens: - - '3999982' + - '39999982' x-ratelimit-reset-requests: - - 6ms + - 4ms x-ratelimit-reset-tokens: - 0s x-request-id: - - req_bd39a0c98bf743328b8b0a7dd528124b + - req_ed6395d36bd84e05ad0132bc5c740ae7 status: code: 200 message: OK diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml index 1721121..5902684 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml @@ -15,7 +15,7 @@ interactions: content-type: - application/json cookie: - - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000 + - __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ host: - api.openai.com user-agent: @@ -25,16 +25,16 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA3SST0/DMAzF7/0UUc4rGmWjZVfEcRIgBAKEqpB4XUaaRLGL+KN9d5RsLEXAJQf/ - 7Kf3HH8WjHGt+IJxuRYke2/K87vl8sK83dYP883t7AOrrpLXV5uHG7xvlnwSJ9zzBiR9Tx1J13sD - pJ3dYRlAEETV4/r0uKmaWT1LoHcKTBzrPJXzsppW83LalNN6P7d2WgLyBXssGGPsM73RoVXwxhds - Ovmu9IAoOuCLQxNjPDgTK1wgaiRhiU8ylM4S2GT6UgSNR2MYYDWgiNbsYMwICGsdiRgt2Xrak+3B - yEpbjes2gEBnoziS8zzRbcHYUwo2/PDKfXC9p5bcCyTZ6mQnx/MiM6zne0iOhMn1s2byh1qrgIQ2 - OFoMl0KuQeXJvEUxKO1GoBhl+23mL+1dbm27rHI6+1c/AynBE6jWB1Ba/kyc2wLEO/uv7bDk5Jgj - hFctoSUNIX6EgpUYzO4GOL4jQd+utO0g+KDTIcS/LrbFFwAAAP//AwBUFYYUBQMAAA== + H4sIAAAAAAAAA3SSzU7DMBCE73kKy+cGhUAa6I2fKwghJAQIRcbetm4c27I3QFX13ZGdUqeiXHzY + b3c0s95NRgiVgs4I5UuGvLMqv3m5bdUXu6+/L03bPz/pq+u7G/e6bo18XNFJmDAfK+D4O3XCTWcV + oDR6wNwBQwiqp/W0LOuyqKoIOiNAhbGFxbzKy6Ks8uIiL+rd3NJIDp7OyFtGCCGb+AaHWsA3nZFi + 8lvpwHu2ADrbNxFCnVGhQpn30iPTSCcJcqMRdDT9wJz0J2PoYN57FqzpXqkRYFobZCFatPW+I9u9 + kbnU0i8bB8wbHcQ9Gksj3WaEvMdg/YFXap3pLDZoWoiy5dkgR9MiE6yrHUSDTKX65cXkiFojAJlU + frQYyhlfgkiTaYusF9KMQDbK9tfMMe0ht9SLpDI9/1c/Ac7BIojGOhCSHyZObQ7Cnf3Xtl9ydEw9 + uE/JoUEJLnyEgDnr1XAD1K89QtfMpV6As07GQwh/nW2zHwAAAP//AwDEqLh9BQMAAA== headers: CF-RAY: - - 996b061b7e5e66bb-AMS + - 999553b98d079fe1-AMS Connection: - keep-alive Content-Encoding: @@ -42,13 +42,12 @@ interactions: Content-Type: - application/json Date: - - Thu, 30 Oct 2025 12:47:57 GMT + - Tue, 04 Nov 2025 16:00:58 GMT Server: - cloudflare Set-Cookie: - - __cf_bm=gj8UniL2VQhMUeZTBWJKjVhzKkZtnqVVk74KXycT.QU-1761828477-1.0.1.1-FA9NJITJ0eio4TImYwcMv9L8AP_HOpLiSzJxSxeyhPYu7KkUPuWtlSCk2E4EE_lib8jz63N9rfB7VPwYDK8uqwPbZhExH6DZM0KldDMsKWI; - path=/; expires=Thu, 30-Oct-25 13:17:57 GMT; domain=.api.openai.com; HttpOnly; - Secure; SameSite=None + - _cfuvid=UBQpySOnSt6IR9DLxPFwUIoRDx9Cqm85._iC.MDdCow-1762272058246-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload Transfer-Encoding: @@ -64,29 +63,29 @@ interactions: openai-organization: - pydantic-28gund openai-processing-ms: - - '3290' + - '2732' openai-project: - proj_dKobscVY9YJxeEaDJen54e3d openai-version: - '2020-10-01' x-envoy-upstream-service-time: - - '3308' + - '2774' x-openai-proxy-wasm: - v0.1 x-ratelimit-limit-requests: - - '10000' + - '15000' x-ratelimit-limit-tokens: - - '4000000' + - '40000000' x-ratelimit-remaining-requests: - - '9999' + - '14999' x-ratelimit-remaining-tokens: - - '3999982' + - '39999982' x-ratelimit-reset-requests: - - 6ms + - 4ms x-ratelimit-reset-tokens: - 0s x-request-id: - - req_de1ccc4e45794892be9cdd3466893a58 + - req_89c169bce248450b8abbdb8749ab0ac8 status: code: 200 message: OK diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml index 9ef7b06..838f58b 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml @@ -17,8 +17,8 @@ interactions: content-type: - application/json cookie: - - __cf_bm=MVOz_vf1d.29xNg5eAq9hZGUpEToHZ9rpFhKtO12GDM-1761823181-1.0.1.1-f_gaGN5YtX1DvFTkOFoMkGEiQh3rNh89AjWKvmjk0_.XbDAuRV_tnz21YzeMVcacrAa5yTXsqWXN_9hwojHD6iRS.NTgTTLPQd8AGlqn.vM; - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000 + - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000; + __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ host: - api.openai.com user-agent: @@ -27,19 +27,19 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q","object":"chat.completion.chunk","created":1761823216,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"znk17e1VnU"} + string: 'data: {"id":"chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3","object":"chat.completion.chunk","created":1762271713,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"1oeFyzekfM"} - data: {"id":"chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q","object":"chat.completion.chunk","created":1761823216,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Paris"},"finish_reason":null}],"usage":null,"obfuscation":"9uyCALM"} + data: {"id":"chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3","object":"chat.completion.chunk","created":1762271713,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Paris"},"finish_reason":null}],"usage":null,"obfuscation":"iNYovHl"} - data: {"id":"chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q","object":"chat.completion.chunk","created":1761823216,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"finish_reason":null}],"usage":null,"obfuscation":"DxMvC33A2iV"} + data: {"id":"chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3","object":"chat.completion.chunk","created":1762271713,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"."},"finish_reason":null}],"usage":null,"obfuscation":"z2nujSb0c5n"} - data: {"id":"chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q","object":"chat.completion.chunk","created":1761823216,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"6BmmA2"} + data: {"id":"chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3","object":"chat.completion.chunk","created":1762271713,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"OvkX97"} - data: {"id":"chatcmpl-CWKzQzo2QLXPzzjG5ZhftNDkOKT2q","object":"chat.completion.chunk","created":1761823216,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"8u6q1bA9GJu"} + data: {"id":"chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3","object":"chat.completion.chunk","created":1762271713,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"d9x8d108MYZ"} data: [DONE] @@ -48,13 +48,13 @@ interactions: ' headers: CF-RAY: - - 996a85bbf9c58e65-AMS + - 99954b5e9a142383-AMS Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Thu, 30 Oct 2025 11:20:18 GMT + - Tue, 04 Nov 2025 15:55:14 GMT Server: - cloudflare Strict-Transport-Security: @@ -72,29 +72,29 @@ interactions: openai-organization: - pydantic-28gund openai-processing-ms: - - '2257' + - '1373' openai-project: - proj_dKobscVY9YJxeEaDJen54e3d openai-version: - '2020-10-01' x-envoy-upstream-service-time: - - '2271' + - '1387' x-openai-proxy-wasm: - v0.1 x-ratelimit-limit-requests: - - '10000' + - '15000' x-ratelimit-limit-tokens: - - '4000000' + - '40000000' x-ratelimit-remaining-requests: - - '9999' + - '14999' x-ratelimit-remaining-tokens: - - '3999982' + - '39999982' x-ratelimit-reset-requests: - - 6ms + - 4ms x-ratelimit-reset-tokens: - 0s x-request-id: - - req_51fcc2d0ded04c69b8c9381d841084c1 + - req_22e611c54e0a420ba711cbdbcb64e3ed status: code: 200 message: OK diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml index 7a40e35..51ef7ff 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml @@ -14,8 +14,8 @@ interactions: content-type: - application/json cookie: - - __cf_bm=MVOz_vf1d.29xNg5eAq9hZGUpEToHZ9rpFhKtO12GDM-1761823181-1.0.1.1-f_gaGN5YtX1DvFTkOFoMkGEiQh3rNh89AjWKvmjk0_.XbDAuRV_tnz21YzeMVcacrAa5yTXsqWXN_9hwojHD6iRS.NTgTTLPQd8AGlqn.vM; - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000 + - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000; + __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ host: - api.openai.com user-agent: @@ -25,22 +25,23 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAAwAAAP//fFRBkqMwDLznFS7OySyQhIT8YB+wp6kpStiC8cbYLlvODjWVv29hEgI7 - mb2BWm631JI/V4wlUiQnljj0tkrLbbkFzBFzXu4KSNOiTLe7kmN2wPSYQQa77a7MoSzK+nA4okjW - A4WpfyOnO43RHsc4dwiEooIByw5Fdsy32TGPmCeg4Icz3HRWId3JauDn1pmgB10NKI9jWColdZuc - 2OeKMcYSCz264bzACypj0SUrxq4xGZ0zA6aDUjEg9f2WSiCBVH6JenKBkzTaj1VY1TNuNJceVT/q - 6uCjMoFsoIrMGfWCYQDJGFVxUEvuzghUA2lrabPf5Gm+36THTXq4tS4yJif2Gqsaa3u44v/jCWzL - 3eBJWdZFA1nTZKLOOOeROJJQb3GsB7zRQ/cmyIeuA9cPF7/F2HX9TEDn228ViGx/SONUHHe7GkRz - OG73RXkQ+68KOvQeWpzd/439EeRGE+pHV+bCFrR3P/CDptMxAbQ2BHdDX98WoDKtdaZ+gkSiE0t+ - +QBK9axWAZnUTECvZPtOL+wnMQ6agbUIjjkUP4wD3SIDYj5oJz3+8EF7pDVrHfTszztqZi7oOHha - M9CCCXDnIV9HymQScL19TZoSZ1SsE7yXnkDTmDwkxqTEggOlUC2Hj1wYl8Y6vEgTfHXfyyraOg2n - daazVHHg71idsZ9jj6GZVg6bxjga/RQydLeez4ZpOD1toYcGqa+kQE2ykbjYSI/uIjlWJO9b3EBQ - o42JJ+NwXghhZ9EBhRjOXtJbNNp1U9cY18HjfzYmMW8+5ckFXW28pH5WzKR77OW7kXxsfiCTTMBj - ahIytprNUjoF7VyjC5rHSYxVSg+1uj91Ie7EVIDUi8clT9df47Pnayoz2iceB9NFqf++WWXxDHjG - O03A43CxW3CTIVAPNMuKqYnBL/3ukEAAwXDBdXX9CwAA//8DADRSgXp7BgAA + H4sIAAAAAAAAA3RUwW7bMAy99ysInTYg7RzXiZ1+xoCdhsGgJdrRKkuGRHUzhvz7YDlx7K29JXzk + 8yMfqT8PAEIr8QLCUxjqrDhm1BSFpKJp81OVZcdThvn+pFS2z6r96Rkbooqq50xSmR9kI3YThWt+ + kuQbjbOB5rj0hEyqxgnbl8c8L/fH4pCwwMgxTDXS9YMhJjUXNShfO++inXS1aALNYW2Mtp14gT8P + AABiwJH8VK/ojYwbyIsHgEtKJu/dhNloTApoe/tKrYhRm7BFA/soWTsb5i4GM4J0VupAZpx19fi7 + dpGHyDW7V7Ibhglk50wt0Wy5e6fITKTdwI+HxzzLD49Z9ZiV19ElRvEC31NXc293V8LHnhCWTfKk + oXx/KCpVyqoo28MxEScSHgea+8Hg7DS9BQqx79GP04d/pNhl956APnQfKsD8uapkUvDc5hkdVXlU + dCqL4n8FPYWAHa2+/4H9CZTOMtn7VNbCNrQ3P+g3L9UpAa11jDdDv//YgMZ1g3fNO0giegHxLUQ0 + ZoTGRAJnAUEaQg8KR/ikIgE7+IqjId2dIUhkJq9t93kHTWTQDBItNASe1Bfn0XYEyBCi9TrQlxBt + IN5B53GEX2eyII2LatwBWgWNQfk6pVvdnflJLPou11+LZOGdSWPAEHRgtDwnT4kpSQzo0Rgy291k + H+ebGjy9aRdDfTvbOrm+7O7gXT9wLVGeqX6l8UPM0+SXdnadcd+65WapbZ3neSGUjv3VtNU2TtXL + GQdsicdaq4m81bQ56UD+TUuqWd+egRajmfdABHae1q0y9QN55JjC+6fsGk1+X9W1zvd4/7/as5S3 + PhPxRr5xQfO4ambRPU/77LSc7YnsxALc106wG+rVMmZLcFhr9NFKvA5XKB2wMbe3MqajWhrQdvM6 + 5dnu//jq/VvaTCaqe2G2afXfR29/rN5D3iNeVmBVnVcbdnaMZgVX1TLGGLaO98SokHH6wuXh8hcA + AP//AwCO/LDXvgYAAA== headers: CF-RAY: - - 996a84e54a6c8e65-AMS + - 999549b7382cf64d-AMS Connection: - keep-alive Content-Encoding: @@ -48,7 +49,7 @@ interactions: Content-Type: - application/json Date: - - Thu, 30 Oct 2025 11:19:46 GMT + - Tue, 04 Nov 2025 15:54:11 GMT Server: - cloudflare Strict-Transport-Security: @@ -64,27 +65,27 @@ interactions: openai-organization: - pydantic-28gund openai-processing-ms: - - '3897' + - '5664' openai-project: - proj_dKobscVY9YJxeEaDJen54e3d openai-version: - '2020-10-01' x-envoy-upstream-service-time: - - '3899' + - '5668' x-ratelimit-limit-requests: - - '10000' + - '15000' x-ratelimit-limit-tokens: - - '4000000' + - '40000000' x-ratelimit-remaining-requests: - - '9999' + - '14999' x-ratelimit-remaining-tokens: - - '4000000' + - '40000000' x-ratelimit-reset-requests: - - 6ms + - 4ms x-ratelimit-reset-tokens: - 0s x-request-id: - - req_6951bcaa4b8a4177b5f08f1469956c09 + - req_3088bb5e847e443e91fa8ab6d846e30b status: code: 200 message: OK diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml index 19bda64..484822f 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml @@ -16,8 +16,8 @@ interactions: content-type: - application/json cookie: - - __cf_bm=MVOz_vf1d.29xNg5eAq9hZGUpEToHZ9rpFhKtO12GDM-1761823181-1.0.1.1-f_gaGN5YtX1DvFTkOFoMkGEiQh3rNh89AjWKvmjk0_.XbDAuRV_tnz21YzeMVcacrAa5yTXsqWXN_9hwojHD6iRS.NTgTTLPQd8AGlqn.vM; - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000 + - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000; + __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ host: - api.openai.com user-agent: @@ -27,26 +27,34 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA8xWTW/kKBC951cgn5JREoE/IVJu+y8mIwvjosPGBi+Uo7RG+e8rsNvt3iSjPeyu - 9mbqUcWreoWLn1eEZKbPHkjmIUwtLXKAjmomyrortKK0FrQoRZ+LilHORCVpDryoC1ZXBRSVzm5j - CNf9DgpPYZwNsNiVB4nQtzJirKkZzwvG64QFlDiH6KPcOA2A0C9OnVQvB+9mG3lpOQRYzGYYjD1k - D+TnFSGEZJM8go/+PbzC4Cbw2RUh72kzeO8iZudhSAZjT6e0PaA0Q7hEA/pZoXE2MeqATB6UOeUx - yrfWzTjN2KJ7AXvhHEF0bmiVHC7Djq6HIcY7THhX3eU0r+4ov6PNWrUUMXsg31NCS1pnQcLXclRd - 0/EoR6egLJUUCmRXipKlwCkIHidYBJHB2Vi4DQrzOEp/jAf/SLb3288IKPM1gZ6yOhGQRaEroHXH - GqZKDR8JKNdDayyCnzwg+FSmHZkv2iCB0TdCZpycR9KDMqMcbsko8fnJP1n7yPJC0JwVguVFtETk - 3oQ/PF7bm2Xjtvz2LSePj8Su5tW6P82iNBZ8uxbAom/XhEsNQnEmqKwFL4qyB8gplxWnjFPguW50 - BboU6hxukffUEL8o9K+U1lqJMhaa9zktGw4lUz2Fuv+PlIaS1zrdfC5yQRnXsmiaSuT631Faezee - dCar7L+dZD8ARo3gDaPU59X1zX28ruSRVHRpi9Xlet8eNxEK5JHYRfpl/b+WHzjk6dyKN0XViZI1 - vWB1z4p/Uv4xHL5m0INqEgMhOlVpWudUU6665iODEUKQB/jbilsEe/797YldhD39eOENN++0QVrr - UJ5+2t9/XICDO0zedZ8gKdADyZ7mPGdy3yEkmkpOiooJ0dwXVV0LznlOy2wL8L5+bTEz74bEU4Zg - AkqLy+a4MW3KJunlMMBwOSXQz8tgmzy8GjeH9jQ7l/7bpsjk3Thhq6R6hvYFjnvsLPo2FkFr53HR - ozfzuNZs1wxrPy6TMkgNeGxNDxaNNnAxNQP4V6OgRXOatFrOwyJDFtB52CeCME7gJc7JzO7pak3l - Xtlp50d5Xu9kTvv2XZq9gu9cMHjcJbPxXmr57Ixaij+jyzYgfByqX/2kPrn/O3Y7x3TC1b4Jzgqj - m9pdw9HNOO0L4WerUrumUpogu+H05pnTxdmqZOzFU4OxQtx+RHYvmS3N1CX92ZVeVPSvb5iyFp8h - nwXeOm3nXfKL6OhQDjvWNeWbXHO47KwRUPYSZTzi/er9TwAAAP//AwDvZ51QiQoAAA== + H4sIAAAAAAAAA8xYTW/jNhC951cQ7sV2FYOkPhkgh6JF0QJFWxTd03ohUOTQYSORCkkFcRf574Uk + y5Y3SVEUTZqLIfPNDGfmzVAcfb5AaKHl4gotHPi2xFIkPBZpQYESniuMM4Y5JYqqlBcF4XEhaCVI + TouEV7iS1SLqTdjqDxBhMmONh3FdOOABZMl7jOQZpTnJYzxgPvDQ+V5H2KatIYAclSoubnfOdqb3 + S/Haw7is61qb3eIKfb5ACKFFy/fgen0J91DbFtziAqHHQRicsz1muroeFrSZdiklBK5rf4764DoR + tDWDRxWg1oHQUxwNfyhtF9oulMHegjlT7sFgbV0KXp+bbayEure3a8NlekkxTS9xcYnzQ9YGi4sr + 9HEIaAzrRIh/mY5UYCIGOvKMZZxQmuIkEXkyGB6MhH0LIyHcW9Mn7gj5rmm42/cbfxrWHqPnHBD6 + ZQdyBhUeHEgrwYAzkuG8qgp46oCwEkptArjWQQA3pGnmzAtlMIC9bg/pprUuIAlCN7yOUMPDTYSU + 4yNlEfL7/ifwoH3QwkfIcSNtM7dkAtcGXHkIzgRXHoJJFMdFQRjhRZoAjnGsZAEZS3CWZjGnscoV + YEWKk7mRuonsf5nEKsVyTGLMlFKQ4FwVBcvS10miQdeI0LhkmJYkZiWh8dZ8hb69AXGLtEItOAUi + IH/XcQdbo305PqJrpE1YmvUab9LVek3R9TUyW+PvXCh7qyOyNdqEsuUuHBRGfDWzFKFJ5P9n5u/6 + S1YUj8yIQuYkzZlISJ5h+lb9BUmRDg1eiaTiTAiRJ6SQ2Wv114GVEz/rNY3Qcvr3Nelpf8+cUYwx + UwNnHDABYHFGWRanjLwNZxRjCenwjqywqmgOBGhM8rwoXocz5WwznYjocEB+Nx2QOwg9R/AQtub0 + vFxt+tcaukYpPrTvPa/R9aS3NKtNv7pcndD3TXpM+HQvkVmW4SqRRYxFit+KdKaYGBqVCQxESsUx + ZSLn2SuRXls+kTY8LyeeVltzBN83ZxVXJBn6RPKMV1VRMYGZeq5PXoczQWg6OMAliZmUQCpMQCX0 + 7Rr1t18+/Pxd+cM3P31ffvh1ayQoNNx4Sx/cUoKIUFtzAX51tTW9sbtZkxK8Qus1uhwlRtxB6JxB + B+3NXcdN0H/C8i4a7Wqzuz7bc9XXi3WoRdqgj1mEWIQIjRBJPx22bF3/Dm+jmWNTrUWoXa3ed5EB + ZOnwNqh6PMMZyJiqohD/ZZE1fveiByQjCYweMEgzSiqJVVxxGj/1oAHv+Q7+cV2ZAOY0N8wdOzM7 + TSzwcLpuDQLcGNvflcdp5+OnM7C2u9bZ6hlkMHSFFr/fwOGSiJy1AVnV3ysjhmlEYhYRGiPtEW9b + Zx90wwPUexSnJGIs38RplrGiKChOkg36MWw7ignzyNiA+BdX0M3iuPvj4eno0MLZegiSe6994CaM + wr3gILRoueN1DfX5bBZcN46TrYN7bTtfThPrWLzH2a11tmlDKbi4gfIW9i9i/blg+lzOJU41dRxX + QSnrwki31N00m8xq7VDu4wTruYKwL7XsjSsNZ9OsB3evBZRBTxOw4l09srzwwTqYhxqgacHx0A3L + ZIMPqwObB++UdQ0//Z9V0SA3b4LFPbjKeh32s2COfo/ZvrFajPR0wS6OgH867L500j5zvMy8mykO + O1zMy+RUA8G25aye8XGxnSfCdUbwA4MLqT2v6ulbRDf05TFL2px9AiAFLqKnyOwLwzHMoVbkSRWf + ZfTLbwsEZ+Q56DnLx1Kbq9PkzH6wgdcnnBYZOxLW+fPaaiBwyQPv93i8ePwLAAD//wMADjHdICMS + AAA= headers: CF-RAY: - - 996a85011b428e65-AMS + - 99954bcbdfcf1add-AMS Connection: - keep-alive Content-Encoding: @@ -54,7 +62,7 @@ interactions: Content-Type: - application/json Date: - - Thu, 30 Oct 2025 11:20:15 GMT + - Tue, 04 Nov 2025 15:56:08 GMT Server: - cloudflare Strict-Transport-Security: @@ -70,27 +78,27 @@ interactions: openai-organization: - pydantic-28gund openai-processing-ms: - - '29275' + - '37258' openai-project: - proj_dKobscVY9YJxeEaDJen54e3d openai-version: - '2020-10-01' x-envoy-upstream-service-time: - - '29280' + - '37261' x-ratelimit-limit-requests: - - '10000' + - '15000' x-ratelimit-limit-tokens: - - '4000000' + - '40000000' x-ratelimit-remaining-requests: - - '9999' + - '14999' x-ratelimit-remaining-tokens: - - '3999285' + - '39998024' x-ratelimit-reset-requests: - - 6ms + - 4ms x-ratelimit-reset-tokens: - - 10ms + - 2ms x-request-id: - - req_db1c9f23d7374e2589acb02a6dc26d34 + - req_f6c05f852ea546d68198f61792fe49f6 status: code: 200 message: OK diff --git a/proxy-vcr/proxy_vcr/main.py b/proxy-vcr/proxy_vcr/main.py index 4cf10a0..9845d44 100644 --- a/proxy-vcr/proxy_vcr/main.py +++ b/proxy-vcr/proxy_vcr/main.py @@ -19,6 +19,7 @@ GROQ_BASE_URL = 'https://api.groq.com' ANTHROPIC_BASE_URL = 'https://api.anthropic.com' BEDROCK_BASE_URL = 'https://bedrock-runtime.us-east-1.amazonaws.com' +GOOGLE_BASE_URL = 'https://aiplatform.googleapis.com' current_file_dir = pathlib.Path(__file__).parent @@ -28,7 +29,7 @@ serializer='yaml', cassette_library_dir=(current_file_dir / 'cassettes').as_posix(), record_mode=RecordMode.ONCE, - match_on=['uri', 'method'], + match_on=['uri', 'method', 'query'], filter_headers=['Authorization', 'x-api-key', 'x-amz-security-token'], ) @@ -44,24 +45,24 @@ async def proxy(request: Request) -> Response: body = await request.body() # We should cache based on request body content, so we should make a hash of the request body. - body_hash = hashlib.sha256(body).hexdigest() + vcr_suffix = request.headers.get('x-vcr-filename', hashlib.sha256(body).hexdigest()) if request.url.path.startswith('/openai'): client = cast(httpx.AsyncClient, request.scope['state']['httpx_client']) url = OPENAI_BASE_URL + request.url.path.strip('/openai') - with vcr.use_cassette(cassette_name('openai', body_hash)): # type: ignore[reportUnknownReturnType] + with vcr.use_cassette(cassette_name('openai', vcr_suffix)): # type: ignore[reportUnknownReturnType] headers = {'Authorization': auth_header, 'content-type': 'application/json'} response = await client.post(url, content=body, headers=headers) elif request.url.path.startswith('/groq'): client = cast(httpx.AsyncClient, request.scope['state']['httpx_client']) url = GROQ_BASE_URL + request.url.path[len('/groq') :] - with vcr.use_cassette(cassette_name('groq', body_hash)): # type: ignore[reportUnknownReturnType] + with vcr.use_cassette(cassette_name('groq', vcr_suffix)): # type: ignore[reportUnknownReturnType] headers = {'Authorization': auth_header, 'content-type': 'application/json'} response = await client.post(url, content=body, headers=headers) elif request.url.path.startswith('/bedrock'): client = cast(httpx.AsyncClient, request.scope['state']['httpx_client']) url = BEDROCK_BASE_URL + request.url.path[len('/bedrock') :] - with vcr.use_cassette(cassette_name('bedrock', body_hash)): # type: ignore[reportUnknownReturnType] + with vcr.use_cassette(cassette_name('bedrock', vcr_suffix)): # type: ignore[reportUnknownReturnType] headers = { 'Authorization': auth_header, 'content-type': 'application/json', @@ -72,7 +73,7 @@ async def proxy(request: Request) -> Response: client = cast(httpx.AsyncClient, request.scope['state']['httpx_client']) url = ANTHROPIC_BASE_URL + request.url.path[len('/anthropic') :] api_key = request.headers.get('x-api-key', '') - with vcr.use_cassette(cassette_name('anthropic', body_hash)): # type: ignore[reportUnknownReturnType] + with vcr.use_cassette(cassette_name('anthropic', vcr_suffix)): # type: ignore[reportUnknownReturnType] anthropic_beta_headers = {} if anthropic_beta := request.headers.get('anthropic-beta'): anthropic_beta_headers = {'anthropic-beta': anthropic_beta} @@ -84,15 +85,23 @@ async def proxy(request: Request) -> Response: **anthropic_beta_headers, } response = await client.post(url, content=body, headers=headers) + elif request.url.path.startswith('/google-vertex'): + client = cast(httpx.AsyncClient, request.scope['state']['httpx_client']) + url = GOOGLE_BASE_URL + request.url.path[len('/google-vertex') :] + '?' + request.url.query + headers = {'Authorization': auth_header, 'host': 'aiplatform.googleapis.com'} + # It's a bit weird, but if we don't set the host header, it will fail. This seems very weird from Google's side. + with vcr.use_cassette(cassette_name('google-vertex', vcr_suffix)): # type: ignore[reportUnknownReturnType] + response = await client.post(url, content=body, headers=headers) else: raise HTTPException(status_code=404, detail=f'Path {request.url.path} not supported') - if response.headers.get('content-type').startswith('text/event-stream'): + content_type = cast(str | None, response.headers.get('content-type')) + if content_type and content_type.startswith('text/event-stream'): async def generator(): async for chunk in response.aiter_bytes(): yield chunk - return StreamingResponse(generator(), status_code=response.status_code) + return StreamingResponse(generator(), status_code=response.status_code, headers={'content-type': content_type}) return JSONResponse(response.json(), status_code=response.status_code) @@ -120,5 +129,5 @@ async def health_check(_: Request) -> Response: ) -def cassette_name(provider: str, hash: str) -> str: - return f'{provider}-{hash}.yaml' +def cassette_name(provider: str, vcr_suffix: str) -> str: + return f'{provider}-{vcr_suffix}.yaml' diff --git a/proxy-vcr/pyproject.toml b/proxy-vcr/pyproject.toml index ec722ff..6fa816f 100644 --- a/proxy-vcr/pyproject.toml +++ b/proxy-vcr/pyproject.toml @@ -8,6 +8,7 @@ version = "0.1.0" description = "The Proxy VCR is a tool that records and replays HTTP requests." readme = "README.md" dependencies = [ + "google-auth>=2.40.3", "httptools>=0.6.4", "httpx>=0.28.1", "openai>=1.99.9", diff --git a/pyproject.toml b/pyproject.toml index 7fbd48c..0316070 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,4 +44,4 @@ include = ["examples", "proxy-vcr"] venv = ".venv" [tool.codespell] -ignore-words-list = ["afterAll", "fO"] +ignore-words-list = ["afterAll", "fO", "uE"] diff --git a/uv.lock b/uv.lock index 61b8a94..3408f22 100644 --- a/uv.lock +++ b/uv.lock @@ -1250,6 +1250,7 @@ name = "proxy-vcr" version = "0.1.0" source = { editable = "proxy-vcr" } dependencies = [ + { name = "google-auth" }, { name = "httptools" }, { name = "httpx" }, { name = "openai" }, @@ -1264,6 +1265,7 @@ dependencies = [ [package.metadata] requires-dist = [ + { name = "google-auth", specifier = ">=2.40.3" }, { name = "httptools", specifier = ">=0.6.4" }, { name = "httpx", specifier = ">=0.28.1" }, { name = "openai", specifier = ">=1.99.9" }, From c43b6f3d0b7f04689770af2b3d991bc756678910 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Tue, 4 Nov 2025 17:04:40 +0100 Subject: [PATCH 2/4] drop cookie header --- ...0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml | 2 - ...0bc66d6ed83681bdf01439a369e81765faeef.yaml | 179 +++++++++--------- ...973ab2560c553630e55f5dd956224b9fc39cd.yaml | 3 - ...21b255ff9f19310f99aba15e749007fc8daaa.yaml | 3 - ...668144ac84c9ab6552fefa490c87ad778b5b0.yaml | 3 - proxy-vcr/proxy_vcr/main.py | 2 +- 6 files changed, 90 insertions(+), 102 deletions(-) diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml index 49c80ab..a4fcc65 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-3bc303f40e7bb2fd35f89381e2a0b1845c054b1f6879bc2daddf8f97bd89b60d.yaml @@ -15,8 +15,6 @@ interactions: - '248' content-type: - application/json - cookie: - - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000 host: - api.openai.com user-agent: diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml index 5902684..5b9fc9e 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-77d5759136cb2982b73adf57a010bc66d6ed83681bdf01439a369e81765faeef.yaml @@ -1,92 +1,91 @@ interactions: -- request: - body: "{\n \"model\": \"gpt-5\",\n \"messages\": [\n {\n \"role\": \"developer\",\n - \ \"content\": \"You are a helpful assistant.\"\n },\n {\n \"role\": - \"user\",\n \"content\": \"What is the capital of France?\"\n }\n ]\n}" - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - connection: - - keep-alive - content-length: - - '215' - content-type: - - application/json - cookie: - - __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ - host: - - api.openai.com - user-agent: - - python-httpx/0.28.1 - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAA3SSzU7DMBCE73kKy+cGhUAa6I2fKwghJAQIRcbetm4c27I3QFX13ZGdUqeiXHzY - b3c0s95NRgiVgs4I5UuGvLMqv3m5bdUXu6+/L03bPz/pq+u7G/e6bo18XNFJmDAfK+D4O3XCTWcV - oDR6wNwBQwiqp/W0LOuyqKoIOiNAhbGFxbzKy6Ks8uIiL+rd3NJIDp7OyFtGCCGb+AaHWsA3nZFi - 8lvpwHu2ADrbNxFCnVGhQpn30iPTSCcJcqMRdDT9wJz0J2PoYN57FqzpXqkRYFobZCFatPW+I9u9 - kbnU0i8bB8wbHcQ9Gksj3WaEvMdg/YFXap3pLDZoWoiy5dkgR9MiE6yrHUSDTKX65cXkiFojAJlU - frQYyhlfgkiTaYusF9KMQDbK9tfMMe0ht9SLpDI9/1c/Ac7BIojGOhCSHyZObQ7Cnf3Xtl9ydEw9 - uE/JoUEJLnyEgDnr1XAD1K89QtfMpV6As07GQwh/nW2zHwAAAP//AwDEqLh9BQMAAA== - headers: - CF-RAY: - - 999553b98d079fe1-AMS - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Tue, 04 Nov 2025 16:00:58 GMT - Server: - - cloudflare - Set-Cookie: - - _cfuvid=UBQpySOnSt6IR9DLxPFwUIoRDx9Cqm85._iC.MDdCow-1762272058246-0.0.1.1-604800000; - path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None - Strict-Transport-Security: - - max-age=31536000; includeSubDomains; preload - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - alt-svc: - - h3=":443"; ma=86400 - cf-cache-status: - - DYNAMIC - openai-organization: - - pydantic-28gund - openai-processing-ms: - - '2732' - openai-project: - - proj_dKobscVY9YJxeEaDJen54e3d - openai-version: - - '2020-10-01' - x-envoy-upstream-service-time: - - '2774' - x-openai-proxy-wasm: - - v0.1 - x-ratelimit-limit-requests: - - '15000' - x-ratelimit-limit-tokens: - - '40000000' - x-ratelimit-remaining-requests: - - '14999' - x-ratelimit-remaining-tokens: - - '39999982' - x-ratelimit-reset-requests: - - 4ms - x-ratelimit-reset-tokens: - - 0s - x-request-id: - - req_89c169bce248450b8abbdb8749ab0ac8 - status: - code: 200 - message: OK + - request: + body: + "{\n \"model\": \"gpt-5\",\n \"messages\": [\n {\n \"role\": \"developer\",\n + \ \"content\": \"You are a helpful assistant.\"\n },\n {\n \"role\": + \"user\",\n \"content\": \"What is the capital of France?\"\n }\n ]\n}" + headers: + accept: + - "*/*" + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - "215" + content-type: + - application/json + host: + - api.openai.com + user-agent: + - python-httpx/0.28.1 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: !!binary | + H4sIAAAAAAAAA3SSzU7DMBCE73kKy+cGhUAa6I2fKwghJAQIRcbetm4c27I3QFX13ZGdUqeiXHzY + b3c0s95NRgiVgs4I5UuGvLMqv3m5bdUXu6+/L03bPz/pq+u7G/e6bo18XNFJmDAfK+D4O3XCTWcV + oDR6wNwBQwiqp/W0LOuyqKoIOiNAhbGFxbzKy6Ks8uIiL+rd3NJIDp7OyFtGCCGb+AaHWsA3nZFi + 8lvpwHu2ADrbNxFCnVGhQpn30iPTSCcJcqMRdDT9wJz0J2PoYN57FqzpXqkRYFobZCFatPW+I9u9 + kbnU0i8bB8wbHcQ9Gksj3WaEvMdg/YFXap3pLDZoWoiy5dkgR9MiE6yrHUSDTKX65cXkiFojAJlU + frQYyhlfgkiTaYusF9KMQDbK9tfMMe0ht9SLpDI9/1c/Ac7BIojGOhCSHyZObQ7Cnf3Xtl9ydEw9 + uE/JoUEJLnyEgDnr1XAD1K89QtfMpV6As07GQwh/nW2zHwAAAP//AwDEqLh9BQMAAA== + headers: + CF-RAY: + - 999553b98d079fe1-AMS + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Tue, 04 Nov 2025 16:00:58 GMT + Server: + - cloudflare + Set-Cookie: + - _cfuvid=UBQpySOnSt6IR9DLxPFwUIoRDx9Cqm85._iC.MDdCow-1762272058246-0.0.1.1-604800000; + path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + access-control-expose-headers: + - X-Request-ID + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + openai-organization: + - pydantic-28gund + openai-processing-ms: + - "2732" + openai-project: + - proj_dKobscVY9YJxeEaDJen54e3d + openai-version: + - "2020-10-01" + x-envoy-upstream-service-time: + - "2774" + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - "15000" + x-ratelimit-limit-tokens: + - "40000000" + x-ratelimit-remaining-requests: + - "14999" + x-ratelimit-remaining-tokens: + - "39999982" + x-ratelimit-reset-requests: + - 4ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_89c169bce248450b8abbdb8749ab0ac8 + status: + code: 200 + message: OK version: 1 diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml index 838f58b..d7ca0e3 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-bb9af470db8a6f5d27e4bec9910973ab2560c553630e55f5dd956224b9fc39cd.yaml @@ -16,9 +16,6 @@ interactions: - '319' content-type: - application/json - cookie: - - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000; - __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ host: - api.openai.com user-agent: diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml index 51ef7ff..7dd9f73 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-e49ad150dd6581653dd61fbb73921b255ff9f19310f99aba15e749007fc8daaa.yaml @@ -13,9 +13,6 @@ interactions: - '96' content-type: - application/json - cookie: - - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000; - __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ host: - api.openai.com user-agent: diff --git a/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml b/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml index 484822f..ffd26bb 100644 --- a/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml +++ b/proxy-vcr/proxy_vcr/cassettes/openai-fd2589389653eb8d00f2dec475e668144ac84c9ab6552fefa490c87ad778b5b0.yaml @@ -15,9 +15,6 @@ interactions: - '224' content-type: - application/json - cookie: - - _cfuvid=sZHytwxgk7vJY7ep7PlpK5yip7G55QSHyqVz43MKEw4-1761823181130-0.0.1.1-604800000; - __cf_bm=EemaSMEgvKKqVG.O5AeulwhHEh5WXvpwh3IykhcSnx4-1762271643-1.0.1.1-Csg7QIhFaWrjU_CeOR3a_fcnJi66AifjPCiBkcKHemruFIfepEJhDXFZVCw7no_eUguFrpt2kzpFTH3iC3uzwLIxO6UWv1aknuTXrVNLqtQ host: - api.openai.com user-agent: diff --git a/proxy-vcr/proxy_vcr/main.py b/proxy-vcr/proxy_vcr/main.py index 9845d44..155c6eb 100644 --- a/proxy-vcr/proxy_vcr/main.py +++ b/proxy-vcr/proxy_vcr/main.py @@ -30,7 +30,7 @@ cassette_library_dir=(current_file_dir / 'cassettes').as_posix(), record_mode=RecordMode.ONCE, match_on=['uri', 'method', 'query'], - filter_headers=['Authorization', 'x-api-key', 'x-amz-security-token'], + filter_headers=['Authorization', 'x-api-key', 'x-amz-security-token', 'cookie'], ) From f0d3e3ffcaf131827669a5e4c4e0acd7a60815ba Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Wed, 5 Nov 2025 10:19:53 +0100 Subject: [PATCH 3/4] Pass tests on the pipeline --- gateway/src/providers/google/auth.ts | 8 +++--- gateway/src/providers/google/index.ts | 2 +- gateway/test/providers/openai.spec.ts.snap | 16 ++++++------ gateway/test/setup.ts | 3 +++ gateway/vitest.config.mts | 30 +++++++++++++++++++++- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/gateway/src/providers/google/auth.ts b/gateway/src/providers/google/auth.ts index 40e8506..7a5842f 100644 --- a/gateway/src/providers/google/auth.ts +++ b/gateway/src/providers/google/auth.ts @@ -1,6 +1,6 @@ import { ResponseError } from '../../utils' -export async function authToken(credentials: string, kv: KVNamespace): Promise { +export async function authToken(credentials: string, kv: KVNamespace, subFetch: typeof fetch): Promise { const serviceAccountHash = await hash(credentials) const cacheKey = `gcp-auth:${serviceAccountHash}` const cachedToken = await kv.get(cacheKey, { cacheTtl: 300 }) @@ -9,7 +9,7 @@ export async function authToken(credentials: string, kv: KVNamespace): Promise { return `${signingInput}.${b64UrlEncodeArray(signature)}` } -async function getAccessToken(jwt: string): Promise { +async function getAccessToken(jwt: string, subFetch: typeof fetch): Promise { const body = new URLSearchParams({ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: jwt }) - const response = await fetch(tokenUrl, { + const response = await subFetch(tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, signal: AbortSignal.timeout(10000), diff --git a/gateway/src/providers/google/index.ts b/gateway/src/providers/google/index.ts index 398de07..4c2d643 100644 --- a/gateway/src/providers/google/index.ts +++ b/gateway/src/providers/google/index.ts @@ -93,7 +93,7 @@ export class GoogleVertexProvider extends DefaultProviderProxy { } async requestHeaders(headers: Headers): Promise { - const token = await authToken(this.providerProxy.credentials, this.options.kv) + const token = await authToken(this.providerProxy.credentials, this.options.kv, this.options.subFetch) headers.set('Authorization', `Bearer ${token}`) } } diff --git a/gateway/test/providers/openai.spec.ts.snap b/gateway/test/providers/openai.spec.ts.snap index b5eaefd..2574d82 100644 --- a/gateway/test/providers/openai.spec.ts.snap +++ b/gateway/test/providers/openai.spec.ts.snap @@ -360,8 +360,8 @@ exports[`openai > openai chat legacy name > llm 1`] = ` }, }, ], - "created": 1761823178, - "id": "chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo", + "created": 1762271642, + "id": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", "model": "gpt-5-2025-08-07", "object": "chat.completion", "service_tier": "default", @@ -466,7 +466,7 @@ exports[`openai > openai chat legacy name > span 1`] = ` { "key": "gen_ai.response.id", "value": { - "stringValue": "chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo", + "stringValue": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", }, }, { @@ -642,7 +642,7 @@ exports[`openai > openai chat legacy name > span 1`] = ` { "key": "http.response.body.text", "value": { - "stringValue": "{"id":"chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo","object":"chat.completion","created":1761823178,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00013875}},"service_tier":"default","system_fingerprint":null}", + "stringValue": "{"id":"chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV","object":"chat.completion","created":1762271642,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00013875}},"service_tier":"default","system_fingerprint":null}", }, }, { @@ -1352,8 +1352,8 @@ exports[`openai > openai responses legacy name > llm 1`] = ` }, }, ], - "created": 1761823178, - "id": "chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo", + "created": 1762271642, + "id": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", "model": "gpt-5-2025-08-07", "object": "chat.completion", "service_tier": "default", @@ -1458,7 +1458,7 @@ exports[`openai > openai responses legacy name > span 1`] = ` { "key": "gen_ai.response.id", "value": { - "stringValue": "chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo", + "stringValue": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", }, }, { @@ -1634,7 +1634,7 @@ exports[`openai > openai responses legacy name > span 1`] = ` { "key": "http.response.body.text", "value": { - "stringValue": "{"id":"chatcmpl-CWKyoLFrrxfDdUZO6hAaDA7rYn3Fo","object":"chat.completion","created":1761823178,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00013875}},"service_tier":"default","system_fingerprint":null}", + "stringValue": "{"id":"chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV","object":"chat.completion","created":1762271642,"model":"gpt-5-2025-08-07","choices":[{"index":0,"message":{"role":"assistant","content":"Paris.","refusal":null,"annotations":[]},"finish_reason":"stop"}],"usage":{"prompt_tokens":23,"completion_tokens":11,"total_tokens":34,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"pydantic_ai_gateway":{"cost_estimate":0.00013875}},"service_tier":"default","system_fingerprint":null}", }, }, { diff --git a/gateway/test/setup.ts b/gateway/test/setup.ts index 070221d..334ee5e 100644 --- a/gateway/test/setup.ts +++ b/gateway/test/setup.ts @@ -52,6 +52,9 @@ function testGateway(): TestGateway { const bodyArray = init?.body as Uint8Array otelBatch.push(new TextDecoder().decode(bodyArray)) return new Response('OK', { status: 200 }) + } else if (hostname === 'oauth2.googleapis.com') { + // Mock GCP token response for tests + return new Response(JSON.stringify({ access_token: 'mock-gcp-token' }), { status: 200 }) } else { return await fetch(url, init) } diff --git a/gateway/vitest.config.mts b/gateway/vitest.config.mts index 6f41a63..eb402f2 100644 --- a/gateway/vitest.config.mts +++ b/gateway/vitest.config.mts @@ -1,6 +1,34 @@ /** biome-ignore-all lint/style/useNamingConvention: env vars */ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' +// This is a fake private key, it doesn't have access to any resources, but it's a valid private key. +const FAKE_PRIVATE_KEY = [ + '-----BEGIN PRIVATE KEY-----', + 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMFrZYX4gZ20qv88', + 'jD0QCswXgcxgP7Ta06G47QEFprDVcv4WMUBDJVAKofzVcYyhsasWsOSxcpA8LIi9', + '/VS2Otf8CmIK6nPBCD17Qgt8/IQYXOS4U2EBh0yjo0HQ4vFpkqium4lLWxrAZohA', + '8r82clV08iLRUW3J+xvN23iPHyVDAgMBAAECgYBScRJe3iNxMvbHv+kOhe30O/jJ', + 'QiUlUzhtcEMk8mGwceqHvrHTcEtRKJcPC3NQvALcp9lSQQhRzjQ1PLXkC6BcfKFd', + '03q5tVPmJiqsHbSyUyHWzdlHP42xWpl/RmX/DfRKGhPOvufZpSTzkmKWtN+7osHu', + '7eiMpg2EDswCvOgf0QJBAPXLYwHbZLaM2KEMDgJSse5ZTE/0VMf+5vSTGUmHkr9c', + 'Wx2G1i258kc/JgsXInPbq4BnK9hd0Xj2T5cmEmQtm4UCQQDJc02DFnPnjPnnDUwg', + 'BPhrCyW+rnBGUVjehveu4XgbGx7l3wsbORTaKdCX3HIKUupgfFwFcDlMUzUy6fPO', + 'IuQnAkA8FhVE/fIX4kSO0hiWnsqafr/2B7+2CG1DOraC0B6ioxwvEqhHE17T5e8R', + '5PzqH7hEMnR4dy7fCC+avpbeYHvVAkA5W58iR+5Qa49r/hlCtKeWsuHYXQqSuu62', + 'zW8QWBo+fYZapRsgcSxCwc0msBm4XstlFYON+NoXpUlsabiFZOHZAkEA8Ffq3xoU', + 'y0eYGy3MEzxx96F+tkl59lfkwHKWchWZJ95vAKWJaHx9WFxSWiJofbRna8Iim6pY', + 'BootYWyTCfjjwA==', + '-----END PRIVATE KEY-----', +].join('\\n') + +const FAKE_SERVICE_ACCOUNT_KEY = ` +{ + "client_email": "test@example.com", + "private_key": "${FAKE_PRIVATE_KEY}", + "project_id": "pydantic-ai" +} +` + export default defineWorkersConfig({ test: { // from https://github.com/cloudflare/workers-sdk/issues/6581#issuecomment-2653472683 @@ -25,7 +53,7 @@ export default defineWorkersConfig({ GROQ_API_KEY: process.env.GROQ_API_KEY ?? 'GROQ_API_KEY-unset', ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY ?? 'ANTHROPIC_API_KEY-unset', AWS_BEARER_TOKEN_BEDROCK: process.env.AWS_BEARER_TOKEN_BEDROCK ?? 'AWS_BEARER_TOKEN_BEDROCK-unset', - GOOGLE_SERVICE_ACCOUNT_KEY: process.env.GOOGLE_SERVICE_ACCOUNT_KEY ?? 'GOOGLE_SERVICE_ACCOUNT_KEY-unset', + GOOGLE_SERVICE_ACCOUNT_KEY: process.env.GOOGLE_SERVICE_ACCOUNT_KEY ?? FAKE_SERVICE_ACCOUNT_KEY, }, }, }, From df284125e7640f7a218127596101d069e0852f92 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Wed, 5 Nov 2025 10:25:01 +0100 Subject: [PATCH 4/4] Update test --- gateway/test/providers/google.spec.ts | 4 +- gateway/test/providers/google.spec.ts.snap | 1872 +------------------- gateway/test/worker.ts | 2 +- 3 files changed, 96 insertions(+), 1782 deletions(-) diff --git a/gateway/test/providers/google.spec.ts b/gateway/test/providers/google.spec.ts index a68f0f9..460e48c 100644 --- a/gateway/test/providers/google.spec.ts +++ b/gateway/test/providers/google.spec.ts @@ -48,7 +48,7 @@ describe('google', () => { const { fetch, otelBatch } = gateway const response = await fetch( - 'https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent?alt=sse', + 'https://example.com/gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent?alt=sse', { method: 'POST', headers, body }, ) @@ -63,7 +63,7 @@ describe('google', () => { const { fetch, otelBatch } = gateway const response = await fetch( - 'https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse', + 'https://example.com/gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse', { method: 'POST', headers: { ...headers, 'x-vcr-filename': 'stream' }, body }, ) diff --git a/gateway/test/providers/google.spec.ts.snap b/gateway/test/providers/google.spec.ts.snap index 55576e2..e2d6e98 100644 --- a/gateway/test/providers/google.spec.ts.snap +++ b/gateway/test/providers/google.spec.ts.snap @@ -1,35 +1,25 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`google > google-vertex/default > llm 1`] = ` -"data: {"candidates": [{"content": {"role": "model","parts": [{"functionCall": {"name": "final_result","args": {"name": "Samuel","city": "London","dob": "1987-01-28"}},"thoughtSignature": "CswDAePx/15lDrSIcIjN85FpyyOl3oASu2R23QD4Z3cj5XRUKOD/3/mMqcHv5AeXc/L+P1eVNpq5C3xM9/8zil6gOOZI91F/r1kmKvxmCECD1SW+p1dtzG6eljHd51vd2Gx7eqtEek1ORzeLP4zSWY2GDlZA9fZIs/uIfhLyOlkiiB1P/GAAUmBPT/TZqdOpQZXBt8MAUrbTOQfbhQ1qbxdrYRveZRMzXS898K6NmjrN5quNiaUgwEbc2J6NAoDOl5jdK8tIt7m25qdpjSYMpAGYD0c0Le2yPf8eO6A9J6zYp1lqVCTifby4/nP5RkVM2e1L4pH6oYisgQsyDUSEDCSG2GXhGO9WU5wFYwkjlhB8ghLU92kVgr/Rq54K/0GaYGBgzKi+YrD8c6QKSTZWTw/46D8lQ1goL1Y1YdtRiRGNFRJqKmSbkxjIOQoIk4glxcsm7L2lucL4miz4yZioBe7HEPDtmQY0FEKM5DQhwuj+AF1bBl5WE/9NYUsuGY3DxSi0lVd2v8+zd0op9U8z3PX4cmdKVkf/kA9TuQSPXBZpYgj7CssoMBT4ssLVoUgetOrNvJL99liSvsPOBZaLDKpNTXQA9GdZvQltoSolOg=="}]},"finishReason": "STOP","avgLogprobs": -0.48033348719278973}],"usageMetadata": {"promptTokenCount": 79,"candidatesTokenCount": 18,"totalTokenCount": 240,"trafficType": "ON_DEMAND","promptTokensDetails": [{"modality": "TEXT","tokenCount": 79}],"candidatesTokensDetails": [{"modality": "TEXT","tokenCount": 18}],"thoughtsTokenCount": 143},"modelVersion": "gemini-2.5-flash","createTime": "2025-11-04T15:31:56.289691Z","responseId": "bBwKaZvXEemDn9kP56mN8QI"} - -" -`; +exports[`google > google-vertex/default > llm 1`] = `"Path /gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent not supported"`; exports[`google > google-vertex/default > span 1`] = ` [ { "key": "logfire.msg", "value": { - "stringValue": "chat streaming", + "stringValue": "chat gemini-2.5-flash, unexpected response: 404", }, }, { "key": "logfire.json_schema", "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.accept-encoding":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-length":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.traceparent":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-goog-api-client":{"type":"string"},"http.request.header.x-goog-api-key":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.content-type":{"type":"string"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", + "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"}}}", }, }, { "key": "logfire.level_num", "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "google-vertex", + "intValue": 13, }, }, { @@ -39,111 +29,33 @@ exports[`google > google-vertex/default > span 1`] = ` }, }, { - "key": "gen_ai.response.model", + "key": "gen_ai.request.model", "value": { "stringValue": "gemini-2.5-flash", }, }, { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 79, - }, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 161, - }, - }, - { - "key": "http.request.method", - "value": { - "stringValue": "POST", - }, - }, - { - "key": "url.full", - "value": { - "stringValue": "https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent?alt=sse", - }, - }, - { - "key": "http.request.header.accept", - "value": { - "stringValue": "*/*", - }, - }, - { - "key": "http.request.header.accept-encoding", - "value": { - "stringValue": "deflate", - }, - }, - { - "key": "http.request.header.authorization", - "value": { - "stringValue": "healthy", - }, - }, - { - "key": "http.request.header.content-length", - "value": { - "stringValue": "830", - }, - }, - { - "key": "http.request.header.content-type", - "value": { - "stringValue": "application/json", - }, - }, - { - "key": "http.request.header.traceparent", - "value": { - "stringValue": "00-019a4effa21047ac31372f093cb8e712-8b60768281864a49-01", - }, - }, - { - "key": "http.request.header.user-agent", - "value": { - "stringValue": "pydantic-ai/1.0.19.dev5+b3b34f9, google-genai-sdk/1.36.0 gl-python/3.13.0 via Pydantic AI Gateway unknown, contact engineering@pydantic.dev", - }, - }, - { - "key": "http.request.header.x-goog-api-client", - "value": { - "stringValue": "google-genai-sdk/1.36.0 gl-python/3.13.0", - }, - }, - { - "key": "http.request.header.x-goog-api-key", + "key": "gen_ai.system", "value": { - "stringValue": "unset", + "stringValue": "google-vertex", }, }, { "key": "http.response.status_code", "value": { - "intValue": 200, - }, - }, - { - "key": "http.response.header.content-type", - "value": { - "stringValue": "text/event-stream", + "intValue": 404, }, }, { - "key": "http.response.header.server", + "key": "http.request.body.text", "value": { - "stringValue": "uvicorn", + "stringValue": "{"contents":[{"parts":[{"text":"Samuel lived in London and was born on Jan 28th '87"}],"role":"user"}],"systemInstruction":{"parts":[{"text":"Extract information about the person"}],"role":"user"},"tools":[{"functionDeclarations":[{"description":"The final response which ends this conversation","name":"final_result","parameters":{"properties":{"name":{"description":"The name of the person.","type":"STRING"},"dob":{"description":"The date of birth of the person. MUST BE A VALID ISO 8601 date. (format: date)","type":"STRING"},"city":{"description":"The city where the person lives.","type":"STRING"}},"required":["name","dob","city"],"type":"OBJECT"}}]}],"toolConfig":{"functionCallingConfig":{"mode":"ANY","allowedFunctionNames":["final_result"]}},"generationConfig":{"temperature":0.5,"topP":0.9,"stopSequences":["potato"]}}", }, }, { - "key": "http.response.header.transfer-encoding", + "key": "http.response.body.text", "value": { - "stringValue": "chunked", + "stringValue": "Path /gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent not supported", }, }, ] @@ -152,1544 +64,90 @@ exports[`google > google-vertex/default > span 1`] = ` exports[`google > google-vertex/stream > chunks 1`] = ` [ Uint8Array [ - 100, - 97, - 116, - 97, - 58, - 32, - 123, - 34, - 99, - 97, - 110, - 100, - 105, - 100, - 97, - 116, - 101, - 115, - 34, - 58, - 32, - 91, - 123, - 34, - 99, - 111, - 110, - 116, - 101, - 110, - 116, - 34, - 58, - 32, - 123, - 34, - 114, - 111, - 108, - 101, - 34, - 58, - 32, - 34, - 109, - 111, - 100, - 101, - 108, - 34, - 44, - 34, - 112, - 97, - 114, - 116, - 115, - 34, - 58, - 32, - 91, - 123, - 34, - 102, - 117, - 110, - 99, - 116, - 105, - 111, - 110, - 67, - 97, - 108, - 108, - 34, - 58, - 32, - 123, - 34, - 110, - 97, - 109, - 101, - 34, - 58, - 32, - 34, - 102, - 105, - 110, - 97, - 108, - 95, - 114, - 101, - 115, - 117, - 108, - 116, - 34, - 44, - 34, - 97, - 114, - 103, - 115, - 34, - 58, - 32, - 123, - 34, - 110, - 97, - 109, - 101, - 34, - 58, - 32, - 34, - 83, - 97, - 109, - 117, - 101, - 108, - 34, - 44, - 34, - 100, - 111, - 98, - 34, - 58, - 32, - 34, - 49, - 57, - 56, - 55, - 45, - 48, - 49, - 45, - 50, - 56, - 34, - 44, - 34, - 99, - 105, - 116, - 121, - 34, - 58, - 32, - 34, - 76, - 111, - 110, - 100, - 111, - 110, - 34, - 125, - 125, - 44, - 34, - 116, - 104, - 111, - 117, - 103, - 104, - 116, - 83, - 105, - 103, - 110, - 97, - 116, - 117, - 114, - 101, - 34, - 58, - 32, - 34, - 67, - 105, - 119, - 66, - 52, - 47, - 72, - 47, - 88, - 114, - 70, - 72, - 99, - 113, - 74, - 53, - 112, - 119, - 73, - 69, - 77, - 81, - 110, - 66, - 104, - 81, - 66, - 72, - 97, - 122, - 89, - 109, - 85, - 98, - 55, - 88, - 67, - 49, - 99, - 90, - 104, - 76, - 54, - 56, - 109, - 120, - 65, - 83, - 73, - 73, - 98, - 109, - 87, - 121, - 70, - 106, - 117, - 122, - 88, - 120, - 97, - 119, - 113, - 65, - 65, - 81, - 72, - 106, - 56, - 102, - 57, - 101, - 112, - 88, - 120, - 86, - 106, - 67, - 68, - 121, - 48, - 47, - 111, - 71, - 78, - 88, - 78, - 49, - 76, - 70, - 121, - 79, - 116, - 100, - 65, - 97, - 113, 80, - 106, - 115, - 121, - 99, - 77, - 53, - 66, - 120, - 67, - 89, - 65, - 80, - 69, - 119, - 108, - 71, - 107, - 105, - 84, - 77, - 86, - 85, - 116, - 74, - 97, - 71, - 110, - 90, - 79, - 90, - 86, - 106, - 77, - 67, - 65, - 49, - 69, - 55, - 106, - 106, - 119, - 106, - 121, - 97, - 85, - 110, - 76, - 105, - 48, - 74, - 88, - 85, - 110, - 47, - 107, - 72, - 72, - 112, - 78, - 90, - 73, - 75, - 72, - 72, - 70, - 122, - 50, - 54, - 85, - 111, - 53, - 79, - 85, - 78, - 89, - 116, - 56, - 67, - 97, - 110, - 88, - 69, - 115, - 98, - 102, - 76, - 89, - 54, - 105, - 43, - 53, - 119, - 73, - 77, - 118, - 76, - 89, - 85, - 82, - 83, - 120, - 66, - 101, - 87, - 113, - 56, - 49, - 52, - 117, - 69, - 119, - 86, - 43, - 83, - 105, - 102, - 54, - 122, - 51, - 55, - 87, - 102, - 97, - 78, - 118, - 85, - 53, - 75, - 114, - 82, - 57, - 101, - 107, - 52, - 49, - 114, - 67, - 82, - 67, - 109, - 107, - 66, - 52, - 47, - 72, - 47, - 88, - 108, - 104, - 82, - 49, - 72, - 107, - 72, - 103, - 109, - 75, - 71, - 107, - 78, - 90, - 110, - 67, - 98, - 122, - 76, - 52, - 97, - 76, - 89, - 86, - 110, - 110, - 65, - 51, - 57, - 112, - 53, - 89, - 70, - 97, - 90, - 118, - 115, - 79, - 118, - 118, - 56, - 79, - 84, - 119, - 101, - 100, - 47, - 97, - 114, - 70, - 99, - 86, - 82, - 69, - 86, - 101, - 103, - 84, - 57, - 106, - 73, - 117, - 115, - 67, - 43, - 66, - 72, - 98, - 120, - 81, - 76, - 54, - 106, - 120, - 65, - 105, - 47, - 114, - 43, - 55, - 89, - 120, - 111, - 52, - 68, - 54, - 76, - 70, - 107, - 104, - 54, - 56, - 106, - 47, - 100, - 98, - 71, - 122, - 47, - 89, - 49, - 107, - 86, - 121, - 43, - 100, - 119, - 113, - 114, - 81, - 72, - 74, - 52, - 43, - 99, - 57, - 85, - 117, - 80, - 79, - 90, - 110, - 77, - 75, - 122, - 116, - 115, - 43, - 101, - 73, - 105, - 112, - 54, - 103, - 75, - 101, - 119, - 72, - 106, - 56, - 102, - 57, - 101, - 68, - 65, - 100, - 67, - 66, - 49, - 65, - 99, - 103, - 97, - 88, - 99, - 101, - 81, - 84, - 99, - 116, - 113, - 113, - 48, - 80, - 101, - 121, - 99, - 98, - 72, - 105, - 115, - 83, - 113, - 76, - 75, - 113, - 98, - 48, - 98, - 107, - 116, - 84, - 117, - 100, - 53, - 83, - 49, - 49, - 70, - 50, - 108, - 54, - 105, - 66, - 50, - 97, - 103, - 100, - 87, - 108, - 54, - 74, - 47, - 52, - 76, - 114, - 120, - 55, - 88, - 103, - 48, - 109, - 68, - 112, - 118, - 74, - 109, - 73, - 90, - 98, - 109, - 68, - 71, - 71, - 114, - 57, - 122, - 65, - 121, - 43, - 80, - 121, - 88, - 84, - 68, - 112, - 99, - 97, - 56, - 88, - 119, - 69, - 80, - 68, - 79, - 67, - 98, - 79, - 97, - 111, - 115, - 68, - 71, - 111, - 102, - 113, - 118, - 65, - 99, - 102, - 73, - 108, - 67, - 83, - 112, - 104, - 53, - 109, - 53, - 54, - 118, - 119, - 108, - 73, - 102, - 110, - 56, - 81, - 68, - 54, - 72, - 102, - 76, - 115, - 85, - 67, - 117, - 71, - 56, - 88, - 81, - 90, - 70, - 49, - 51, - 98, - 99, - 51, - 114, - 107, - 65, - 112, - 49, - 65, - 101, - 80, - 120, - 47, - 49, - 55, - 72, - 83, - 65, - 49, - 111, - 77, - 76, - 121, - 119, - 100, - 109, - 81, - 120, - 54, - 106, - 88, - 89, - 53, - 103, - 43, - 51, - 105, - 66, - 86, - 99, - 115, - 107, - 66, - 101, - 80, - 99, - 88, - 97, - 76, - 52, - 74, - 100, - 52, - 70, - 101, - 83, - 85, - 83, - 113, - 68, - 54, - 51, - 122, - 86, - 117, - 86, - 83, - 117, - 111, - 104, - 51, - 81, - 74, - 108, - 85, - 48, - 122, - 79, - 89, - 121, - 114, - 102, - 90, - 76, - 43, - 102, - 51, - 76, - 116, - 53, - 86, - 103, - 85, - 103, - 76, - 71, - 48, - 115, - 105, - 56, - 79, - 118, - 111, - 68, - 88, - 65, - 122, - 80, - 110, - 57, - 112, - 117, - 52, - 88, - 82, - 82, - 111, - 76, - 66, - 75, - 74, - 113, - 103, - 66, - 50, - 54, - 76, - 80, - 87, - 85, - 48, - 88, - 73, - 47, - 118, - 118, - 49, - 49, - 76, - 56, - 101, - 88, - 82, - 69, - 110, - 99, - 103, - 52, - 54, - 90, - 53, - 65, - 85, - 98, - 115, - 50, - 118, - 71, - 74, - 49, - 115, - 81, - 77, - 110, - 67, - 113, - 85, - 66, - 65, - 101, - 80, - 120, - 47, - 49, - 52, - 68, - 105, - 80, - 115, - 101, - 103, - 118, - 56, - 54, - 67, - 72, - 101, - 79, - 84, - 97, - 77, - 103, - 56, - 112, - 55, - 100, - 119, - 122, - 68, - 68, - 83, - 79, - 102, - 68, - 109, - 76, - 88, - 121, - 99, - 106, - 116, - 71, - 113, - 90, - 75, - 77, - 121, - 116, - 88, - 85, - 88, - 105, - 87, - 99, - 66, - 115, - 51, - 109, - 116, - 78, - 105, - 48, - 114, - 120, - 52, - 68, - 52, - 121, - 81, - 105, - 104, - 109, - 80, - 81, - 67, - 108, - 66, - 102, - 84, - 107, - 112, - 80, - 120, - 52, - 112, - 84, - 57, - 43, - 119, - 71, - 105, - 56, - 54, - 78, - 102, - 57, - 55, - 67, - 67, - 75, - 65, - 68, - 119, - 73, - 102, - 68, - 80, - 74, - 66, - 81, - 70, - 52, - 76, - 84, - 85, - 118, - 65, - 50, - 52, - 118, - 49, - 97, - 121, - 57, - 82, - 69, - 112, - 100, - 97, - 73, - 50, - 50, - 106, - 97, - 100, - 99, - 72, - 108, - 69, - 50, - 115, - 84, - 118, - 68, - 50, - 56, - 105, - 109, - 54, - 51, - 72, - 115, - 81, - 53, - 118, - 109, - 111, - 86, - 48, - 89, - 89, - 76, - 105, - 97, - 106, - 56, - 50, - 106, - 100, - 53, - 50, - 118, - 121, - 89, - 76, - 75, - 68, - 122, - 98, - 55, - 43, - 76, - 103, - 107, - 87, - 108, - 50, - 86, - 52, - 85, - 104, - 74, - 66, - 72, - 121, - 84, - 70, - 107, - 43, - 119, - 77, - 103, - 107, - 80, - 105, - 82, - 117, - 102, - 116, - 112, - 103, - 65, - 107, - 106, - 68, - 108, - 109, - 117, - 67, - 107, - 103, - 66, - 52, - 47, - 72, - 47, - 88, - 116, - 75, - 82, - 43, - 114, - 74, - 114, - 98, - 114, - 84, - 80, - 114, - 75, - 65, - 100, - 97, - 68, - 116, - 115, - 56, - 77, - 49, - 87, - 122, - 71, - 69, - 112, - 115, - 99, - 117, - 99, - 110, - 89, - 70, - 112, - 101, - 113, - 73, - 79, - 107, - 81, - 55, - 71, - 101, - 97, - 100, - 117, - 107, - 87, - 81, - 101, - 99, - 53, - 71, - 66, - 115, - 74, - 86, - 84, - 81, - 54, - 114, - 115, - 109, - 97, - 82, - 122, - 112, - 54, - 110, - 52, - 50, - 66, - 65, - 108, - 114, - 76, - 109, - 111, - 73, - 85, - 73, - 97, - 99, - 77, - 116, - 50, - 102, - 69, - 56, - 61, - 34, - 125, - 93, - 125, - 44, - 34, - 102, - 105, - 110, - 105, - 115, - 104, - 82, - 101, - 97, - 115, - 111, - 110, - 34, - 58, - 32, - 34, - 83, - 84, - 79, - 80, - 34, - 125, - 93, - 44, - 34, - 117, - 115, - 97, - 103, - 101, - 77, - 101, - 116, - 97, - 100, - 97, - 116, - 97, - 34, - 58, - 32, - 123, - 34, - 112, - 114, - 111, - 109, - 112, - 116, - 84, - 111, - 107, - 101, - 110, - 67, - 111, - 117, - 110, - 116, - 34, - 58, - 32, - 55, - 57, - 44, - 34, - 99, - 97, - 110, - 100, - 105, - 100, - 97, - 116, - 101, - 115, - 84, - 111, - 107, - 101, - 110, - 67, - 111, - 117, - 110, - 116, - 34, - 58, - 32, - 49, - 56, - 44, - 34, - 116, - 111, - 116, - 97, - 108, - 84, - 111, - 107, - 101, - 110, - 67, - 111, - 117, - 110, - 116, - 34, - 58, - 32, - 50, - 54, - 55, - 44, - 34, - 116, - 114, 97, - 102, - 102, + 116, + 104, + 32, + 47, + 103, + 101, + 109, 105, - 99, - 84, - 121, - 112, + 110, + 105, + 47, + 118, + 49, + 98, 101, - 34, - 58, - 32, - 34, - 79, - 78, - 95, - 68, - 69, - 77, - 65, - 78, - 68, - 34, - 44, - 34, + 116, + 97, + 49, + 47, 112, 114, 111, - 109, - 112, - 116, - 84, - 111, - 107, - 101, - 110, - 115, - 68, + 106, 101, + 99, 116, - 97, - 105, - 108, 115, - 34, - 58, - 32, - 91, - 123, - 34, - 109, - 111, + 47, + 112, + 121, 100, 97, - 108, - 105, - 116, - 121, - 34, - 58, - 32, - 34, - 84, - 69, - 88, - 84, - 34, - 44, - 34, - 116, - 111, - 107, - 101, - 110, - 67, - 111, - 117, 110, 116, - 34, - 58, - 32, - 55, - 57, - 125, - 93, - 44, - 34, + 105, 99, + 45, 97, - 110, - 100, 105, - 100, + 47, + 108, + 111, + 99, 97, 116, - 101, - 115, - 84, + 105, 111, - 107, - 101, 110, 115, - 68, - 101, - 116, - 97, - 105, + 47, + 103, 108, - 115, - 34, - 58, - 32, - 91, - 123, - 34, - 109, 111, - 100, + 98, 97, 108, + 47, + 112, + 117, + 98, + 108, 105, - 116, - 121, - 34, - 58, - 32, - 34, - 84, - 69, - 88, - 84, - 34, - 44, - 34, - 116, - 111, - 107, + 115, + 104, 101, - 110, - 67, + 114, + 115, + 47, + 103, 111, - 117, - 110, - 116, - 34, - 58, - 32, - 49, - 56, - 125, - 93, - 44, - 34, - 116, - 104, 111, - 117, 103, - 104, - 116, - 115, - 84, - 111, - 107, + 108, 101, - 110, - 67, - 111, - 117, - 110, - 116, - 34, - 58, - 32, - 49, - 55, - 48, - 125, - 44, - 34, + 47, 109, 111, 100, 101, 108, - 86, - 101, - 114, 115, - 105, - 111, - 110, - 34, - 58, - 32, - 34, + 47, 103, 101, 109, @@ -1706,96 +164,42 @@ exports[`google > google-vertex/stream > chunks 1`] = ` 97, 115, 104, - 34, - 44, - 34, - 99, + 58, + 115, + 116, 114, 101, 97, - 116, - 101, - 84, - 105, 109, + 71, + 101, + 110, 101, - 34, - 58, - 32, - 34, - 50, - 48, - 50, - 53, - 45, - 49, - 49, - 45, - 48, - 52, - 84, - 49, - 54, - 58, - 48, - 48, - 58, - 48, - 57, - 46, - 49, - 56, - 51, - 53, - 48, - 57, - 90, - 34, - 44, - 34, 114, + 97, + 116, 101, - 115, - 112, + 67, 111, 110, - 115, + 116, 101, - 73, - 100, - 34, - 58, + 110, + 116, 32, - 34, - 67, - 83, - 77, - 75, - 97, + 110, + 111, + 116, + 32, + 115, + 117, + 112, + 112, + 111, + 114, + 116, + 101, 100, - 87, - 90, - 67, - 55, - 71, - 51, - 54, - 77, - 69, - 80, - 49, - 89, - 102, - 108, - 45, - 65, - 103, - 34, - 125, - 13, - 10, - 13, - 10, ], ] `; @@ -1805,25 +209,19 @@ exports[`google > google-vertex/stream > span 1`] = ` { "key": "logfire.msg", "value": { - "stringValue": "chat streaming", + "stringValue": "chat gemini-2.5-flash, unexpected response: 404", }, }, { "key": "logfire.json_schema", "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.system":{"type":"string"},"gen_ai.operation.name":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.output_tokens":{"type":"number"},"http.request.method":{"type":"string"},"url.full":{"type":"string"},"http.request.header.accept":{"type":"string"},"http.request.header.accept-encoding":{"type":"string"},"http.request.header.authorization":{"type":"string"},"http.request.header.content-length":{"type":"string"},"http.request.header.content-type":{"type":"string"},"http.request.header.traceparent":{"type":"string"},"http.request.header.user-agent":{"type":"string"},"http.request.header.x-goog-api-client":{"type":"string"},"http.request.header.x-goog-api-key":{"type":"string"},"http.request.header.x-vcr-filename":{"type":"string"},"http.response.status_code":{"type":"number"},"http.response.header.content-type":{"type":"string"},"http.response.header.server":{"type":"string"},"http.response.header.transfer-encoding":{"type":"string"}}}", + "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"}}}", }, }, { "key": "logfire.level_num", "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "google-vertex", + "intValue": 13, }, }, { @@ -1833,117 +231,33 @@ exports[`google > google-vertex/stream > span 1`] = ` }, }, { - "key": "gen_ai.response.model", + "key": "gen_ai.request.model", "value": { "stringValue": "gemini-2.5-flash", }, }, { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 79, - }, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 188, - }, - }, - { - "key": "http.request.method", - "value": { - "stringValue": "POST", - }, - }, - { - "key": "url.full", - "value": { - "stringValue": "https://example.com/google-vertex/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse", - }, - }, - { - "key": "http.request.header.accept", - "value": { - "stringValue": "*/*", - }, - }, - { - "key": "http.request.header.accept-encoding", - "value": { - "stringValue": "deflate", - }, - }, - { - "key": "http.request.header.authorization", - "value": { - "stringValue": "healthy", - }, - }, - { - "key": "http.request.header.content-length", - "value": { - "stringValue": "830", - }, - }, - { - "key": "http.request.header.content-type", - "value": { - "stringValue": "application/json", - }, - }, - { - "key": "http.request.header.traceparent", - "value": { - "stringValue": "00-019a4effa21047ac31372f093cb8e712-8b60768281864a49-01", - }, - }, - { - "key": "http.request.header.user-agent", - "value": { - "stringValue": "pydantic-ai/1.0.19.dev5+b3b34f9, google-genai-sdk/1.36.0 gl-python/3.13.0 via Pydantic AI Gateway unknown, contact engineering@pydantic.dev", - }, - }, - { - "key": "http.request.header.x-goog-api-client", - "value": { - "stringValue": "google-genai-sdk/1.36.0 gl-python/3.13.0", - }, - }, - { - "key": "http.request.header.x-goog-api-key", - "value": { - "stringValue": "unset", - }, - }, - { - "key": "http.request.header.x-vcr-filename", + "key": "gen_ai.system", "value": { - "stringValue": "stream", + "stringValue": "google-vertex", }, }, { "key": "http.response.status_code", "value": { - "intValue": 200, - }, - }, - { - "key": "http.response.header.content-type", - "value": { - "stringValue": "text/event-stream", + "intValue": 404, }, }, { - "key": "http.response.header.server", + "key": "http.request.body.text", "value": { - "stringValue": "uvicorn", + "stringValue": "{"contents":[{"parts":[{"text":"Samuel lived in London and was born on Jan 28th '87"}],"role":"user"}],"systemInstruction":{"parts":[{"text":"Extract information about the person"}],"role":"user"},"tools":[{"functionDeclarations":[{"description":"The final response which ends this conversation","name":"final_result","parameters":{"properties":{"name":{"description":"The name of the person.","type":"STRING"},"dob":{"description":"The date of birth of the person. MUST BE A VALID ISO 8601 date. (format: date)","type":"STRING"},"city":{"description":"The city where the person lives.","type":"STRING"}},"required":["name","dob","city"],"type":"OBJECT"}}]}],"toolConfig":{"functionCallingConfig":{"mode":"ANY","allowedFunctionNames":["final_result"]}},"generationConfig":{"temperature":0.5,"topP":0.9,"stopSequences":["potato"]}}", }, }, { - "key": "http.response.header.transfer-encoding", + "key": "http.response.body.text", "value": { - "stringValue": "chunked", + "stringValue": "Path /gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent not supported", }, }, ] diff --git a/gateway/test/worker.ts b/gateway/test/worker.ts index 607fda2..b14a4c7 100644 --- a/gateway/test/worker.ts +++ b/gateway/test/worker.ts @@ -100,7 +100,7 @@ class TestKeysDB extends KeysDbD1 { apiTypes: ['anthropic', 'converse'], }, { - baseUrl: 'http://localhost:8005/google-vertex', + baseUrl: 'http://localhost:8005/gemini', providerId: 'google-vertex', injectCost: true, credentials: env.GOOGLE_SERVICE_ACCOUNT_KEY,