From efc6cc7ff427be90149f4332d35558dd424e98fc Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Wed, 5 Nov 2025 10:58:25 +0100 Subject: [PATCH] tests(snapshot): deserialize request on snapshot --- gateway/test/otel.ts | 92 + gateway/test/providers/anthropic.spec.ts | 7 +- gateway/test/providers/anthropic.spec.ts.snap | 1176 ++-------- gateway/test/providers/bedrock.spec.ts | 3 +- gateway/test/providers/bedrock.spec.ts.snap | 171 +- gateway/test/providers/google.spec.ts | 5 +- gateway/test/providers/google.spec.ts.snap | 148 +- gateway/test/providers/groq.spec.ts | 3 +- gateway/test/providers/groq.spec.ts.snap | 360 +-- gateway/test/providers/openai.spec.ts | 9 +- gateway/test/providers/openai.spec.ts.snap | 2060 ++++------------- 11 files changed, 945 insertions(+), 3089 deletions(-) create mode 100644 gateway/test/otel.ts diff --git a/gateway/test/otel.ts b/gateway/test/otel.ts new file mode 100644 index 0000000..9e7550d --- /dev/null +++ b/gateway/test/otel.ts @@ -0,0 +1,92 @@ +interface OtlpAttribute { + key: string + value: OtlpValue +} + +interface OtlpValue { + stringValue?: string + intValue?: number + doubleValue?: number + boolValue?: boolean + arrayValue?: { values?: OtlpValue[] } + kvlistValue?: { values?: { key: string; value: OtlpValue }[] } +} + +/** + * Deserializes OTLP trace data from a JSON string and transforms it into a cleaner JSON structure. + * This extracts the key information from the OTLP format (resource spans, scope spans, etc.) + * and returns a more readable representation suitable for test snapshots. + */ +export function deserializeRequest(data: string) { + const otlpData = JSON.parse(data) + + // Transform OTLP format into cleaner JSON + const spans = [] + + for (const resourceSpan of otlpData.resourceSpans || []) { + const resource = resourceSpan.resource?.attributes || [] + + for (const scopeSpan of resourceSpan.scopeSpans || []) { + const scope = scopeSpan.scope?.name + + for (const span of scopeSpan.spans || []) { + // Convert attributes array to object + const attributes: Record = {} + for (const attr of span.attributes || []) { + attributes[attr.key] = extractOtlpValue(attr.value) + } + + spans.push({ + name: span.name, + parentSpanId: span.parentSpanId, + kind: span.kind, + attributes, + status: span.status, + events: span.events, + links: span.links, + resource: Object.fromEntries( + resource.map((attr: OtlpAttribute) => [ + attr.key, + attr.value.stringValue ?? + attr.value.intValue ?? + attr.value.doubleValue ?? + attr.value.boolValue ?? + attr.value, + ]), + ), + scope, + }) + } + } + } + + return spans +} + +/** + * Recursively extracts the actual value from an OTLP AnyValue structure. + * Handles all OTLP value types including nested structures. + */ +function extractOtlpValue(value: OtlpValue): unknown { + if (value.stringValue !== undefined) return value.stringValue + if (value.intValue !== undefined) return value.intValue + if (value.doubleValue !== undefined) return value.doubleValue + if (value.boolValue !== undefined) return value.boolValue + + if (value.arrayValue) { + // For arrays, just extract the values directly without wrapping + return value.arrayValue.values?.map(extractOtlpValue) || [] + } + + if (value.kvlistValue) { + // For key-value lists, extract as an object + const obj: Record = {} + for (const kv of value.kvlistValue.values || []) { + obj[kv.key] = extractOtlpValue(kv.value) + } + return obj + } + + // For empty objects or unknown types, return the raw value + return value +} diff --git a/gateway/test/providers/anthropic.spec.ts b/gateway/test/providers/anthropic.spec.ts index 0e1ed9b..75bdb14 100644 --- a/gateway/test/providers/anthropic.spec.ts +++ b/gateway/test/providers/anthropic.spec.ts @@ -1,5 +1,6 @@ import Anthropic from '@anthropic-ai/sdk' import { describe, expect } from 'vitest' +import { deserializeRequest } from '../otel' import { test } from '../setup' describe('anthropic', () => { @@ -21,7 +22,7 @@ describe('anthropic', () => { }) expect(completion).toMatchSnapshot('llm') expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) - expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) test('should call anthropic via gateway with builtin tools', async ({ gateway }) => { @@ -40,7 +41,7 @@ describe('anthropic', () => { }) expect(response).toMatchSnapshot('llm') expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) - expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) test('should call anthropic via gateway with stream', async ({ gateway }) => { @@ -61,6 +62,6 @@ describe('anthropic', () => { 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') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) }) diff --git a/gateway/test/providers/anthropic.spec.ts.snap b/gateway/test/providers/anthropic.spec.ts.snap index d94df4b..643ca82 100644 --- a/gateway/test/providers/anthropic.spec.ts.snap +++ b/gateway/test/providers/anthropic.spec.ts.snap @@ -34,288 +34,80 @@ exports[`anthropic > should call anthropic via gateway > llm 1`] = ` exports[`anthropic > should call anthropic via gateway > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat claude-sonnet-4-20250514", - }, - }, - { - "key": "logfire.json_schema", - "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{"type":"number"},"gen_ai.request.top_p":{"type":"number"},"gen_ai.request.temperature":{"type":"number"},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"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"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", - }, - }, - { - "key": "logfire.level_num", - "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "claude-sonnet-4-20250514", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "anthropic", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": { - "intValue": 1024, - }, - }, - { - "key": "gen_ai.request.top_k", - "value": { - "intValue": 1, - }, - }, - { - "key": "gen_ai.request.top_p", - "value": { - "doubleValue": 0.95, - }, - }, - { - "key": "gen_ai.request.temperature", - "value": { - "doubleValue": 0.5, - }, - }, - { - "key": "gen_ai.request.stop_sequences", - "value": { - "arrayValue": { - "values": [ - { - "stringValue": "potato", - }, - ], - }, - }, - }, - { - "key": "gen_ai.request.seed", - "value": {}, - }, - { - "key": "gen_ai.response.finish_reasons", - "value": { - "arrayValue": { - "values": [ - { - "stringValue": "end_turn", - }, - ], - }, - }, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "msg_019Eoy62TSq2WGFyrVX5mRf5", - }, - }, - { - "key": "gen_ai.input.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "user", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "What is the capital of France?", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.output.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "The capital of France is Paris.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "finish_reason", - "value": { - "stringValue": "end_turn", - }, - }, - ], + "attributes": { + "gen_ai.input.messages": [ + { + "parts": [ + { + "content": "What is the capital of France?", + "type": "text", }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.system_instructions", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "You are a helpful assistant.", - }, - }, - ], + ], + "role": "user", + }, + ], + "gen_ai.operation.name": "chat", + "gen_ai.output.messages": [ + { + "finish_reason": "end_turn", + "parts": [ + { + "content": "The capital of France is Paris.", + "type": "text", }, - }, - ], - }, - }, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.request.body.text", - "value": { - "stringValue": "{"model":"claude-sonnet-4-20250514","max_tokens":1024,"top_p":0.95,"top_k":1,"temperature":0.5,"stop_sequences":["potato"],"system":"You are a helpful assistant.","messages":[{"role":"user","content":"What is the capital of France?"}]}", - }, - }, - { - "key": "http.response.body.text", - "value": { - "stringValue": "{"model":"claude-sonnet-4-20250514","id":"msg_019Eoy62TSq2WGFyrVX5mRf5","type":"message","role":"assistant","content":[{"type":"text","text":"The capital of France is Paris."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":20,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","pydantic_ai_gateway":{"cost_estimate":0.00020999999999999998}}}", - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "claude-sonnet-4-20250514", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 20, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 10, + ], + "role": "assistant", + }, + ], + "gen_ai.request.max_tokens": 1024, + "gen_ai.request.model": "claude-sonnet-4-20250514", + "gen_ai.request.seed": {}, + "gen_ai.request.stop_sequences": [ + "potato", + ], + "gen_ai.request.temperature": 0.5, + "gen_ai.request.top_k": 1, + "gen_ai.request.top_p": 0.95, + "gen_ai.response.finish_reasons": [ + "end_turn", + ], + "gen_ai.response.id": "msg_019Eoy62TSq2WGFyrVX5mRf5", + "gen_ai.response.model": "claude-sonnet-4-20250514", + "gen_ai.system": "anthropic", + "gen_ai.system_instructions": [ + { + "content": "You are a helpful assistant.", + "type": "text", + }, + ], + "gen_ai.usage.cache_audio_read_tokens": {}, + "gen_ai.usage.cache_read_tokens": 0, + "gen_ai.usage.cache_write_tokens": 0, + "gen_ai.usage.input_audio_tokens": {}, + "gen_ai.usage.input_tokens": 20, + "gen_ai.usage.output_audio_tokens": {}, + "gen_ai.usage.output_tokens": 10, + "http.request.body.text": "{"model":"claude-sonnet-4-20250514","max_tokens":1024,"top_p":0.95,"top_k":1,"temperature":0.5,"stop_sequences":["potato"],"system":"You are a helpful assistant.","messages":[{"role":"user","content":"What is the capital of France?"}]}", + "http.response.body.text": "{"model":"claude-sonnet-4-20250514","id":"msg_019Eoy62TSq2WGFyrVX5mRf5","type":"message","role":"assistant","content":[{"type":"text","text":"The capital of France is Paris."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":20,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","pydantic_ai_gateway":{"cost_estimate":0.00020999999999999998}}}", + "http.response.status_code": 200, + "logfire.json_schema": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{"type":"number"},"gen_ai.request.top_p":{"type":"number"},"gen_ai.request.temperature":{"type":"number"},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"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"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", + "logfire.level_num": 9, + "logfire.msg": "chat claude-sonnet-4-20250514", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat claude-sonnet-4-20250514", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_audio_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": {}, - }, ] `; @@ -479,212 +271,31 @@ The population standard deviation is used when your data represents the entire p exports[`anthropic > should call anthropic via gateway with builtin tools > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat claude-opus-4-1-20250805", - }, - }, - { - "key": "logfire.json_schema", - "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"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"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", - }, - }, - { - "key": "logfire.level_num", - "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "claude-opus-4-1-20250805", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "anthropic", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": { - "intValue": 4096, - }, - }, - { - "key": "gen_ai.request.top_k", - "value": {}, - }, - { - "key": "gen_ai.request.top_p", - "value": {}, - }, - { - "key": "gen_ai.request.temperature", - "value": {}, - }, - { - "key": "gen_ai.request.stop_sequences", - "value": {}, - }, - { - "key": "gen_ai.request.seed", - "value": {}, - }, - { - "key": "gen_ai.response.finish_reasons", - "value": { - "arrayValue": { - "values": [ - { - "stringValue": "end_turn", - }, - ], - }, - }, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "msg_01JHBJsdymq98CeYeSVkoMeQ", - }, - }, - { - "key": "gen_ai.input.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "user", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], + "attributes": { + "gen_ai.input.messages": [ + { + "parts": [ + { + "content": "Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", + "type": "text", }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.output.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "I'll calculate the mean and standard deviation of the given dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].", - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "tool_call", - }, - }, - { - "key": "id", - "value": { - "stringValue": "srvtoolu_014V88EfQbuZg6ZeyWb4G56e", - }, - }, - { - "key": "name", - "value": { - "stringValue": "text_editor_code_execution", - }, - }, - { - "key": "arguments", - "value": { - "kvlistValue": { - "values": [ - { - "key": "command", - "value": { - "stringValue": "create", - }, - }, - { - "key": "path", - "value": { - "stringValue": "/tmp/calculate_stats.py", - }, - }, - { - "key": "file_text", - "value": { - "stringValue": "import math + ], + "role": "user", + }, + ], + "gen_ai.operation.name": "chat", + "gen_ai.output.messages": [ + { + "finish_reason": "end_turn", + "parts": [ + { + "content": "I'll calculate the mean and standard deviation of the given dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].", + "type": "text", + }, + { + "arguments": { + "command": "create", + "file_text": "import math # Dataset data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -722,153 +333,41 @@ for x, dev, dev_squared in deviations: print(f"\\nSum of squared deviations: {sum(d[2] for d in deviations):.2f}") print(f"Population variance = {variance:.2f}") print(f"Sample variance = {variance_sample:.2f}")", - }, - }, - ], - }, - }, - }, - { - "key": "builtin", - "value": { - "boolValue": true, - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "tool_call_response", - }, - }, - { - "key": "id", - "value": { - "stringValue": "srvtoolu_014V88EfQbuZg6ZeyWb4G56e", - }, - }, - { - "key": "name", - "value": { - "stringValue": "text_editor_code_execution", - }, - }, - { - "key": "result", - "value": { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text_editor_code_execution_create_result", - }, - }, - { - "key": "is_file_update", - "value": { - "boolValue": false, - }, - }, - ], - }, - }, - }, - { - "key": "builtin", - "value": { - "boolValue": true, - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "tool_call", - }, - }, - { - "key": "id", - "value": { - "stringValue": "srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3", - }, - }, - { - "key": "name", - "value": { - "stringValue": "bash_code_execution", - }, - }, - { - "key": "arguments", - "value": { - "kvlistValue": { - "values": [ - { - "key": "command", - "value": { - "stringValue": "python /tmp/calculate_stats.py", - }, - }, - ], - }, - }, - }, - { - "key": "builtin", - "value": { - "boolValue": true, - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "tool_call_response", - }, - }, - { - "key": "id", - "value": { - "stringValue": "srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3", - }, - }, - { - "key": "name", - "value": { - "stringValue": "bash_code_execution", - }, - }, - { - "key": "result", - "value": { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "bash_code_execution_result", - }, - }, - { - "key": "stdout", - "value": { - "stringValue": "Dataset: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + "path": "/tmp/calculate_stats.py", + }, + "builtin": true, + "id": "srvtoolu_014V88EfQbuZg6ZeyWb4G56e", + "name": "text_editor_code_execution", + "type": "tool_call", + }, + { + "builtin": true, + "id": "srvtoolu_014V88EfQbuZg6ZeyWb4G56e", + "name": "text_editor_code_execution", + "result": { + "is_file_update": false, + "type": "text_editor_code_execution_create_result", + }, + "type": "tool_call_response", + }, + { + "arguments": { + "command": "python /tmp/calculate_stats.py", + }, + "builtin": true, + "id": "srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3", + "name": "bash_code_execution", + "type": "tool_call", + }, + { + "builtin": true, + "id": "srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3", + "name": "bash_code_execution", + "result": { + "content": [], + "return_code": 0, + "stderr": "", + "stdout": "Dataset: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Number of values: 10 Mean: 5.5 @@ -896,54 +395,12 @@ Sum of squared deviations: 82.50 Population variance = 8.25 Sample variance = 9.17 ", - }, - }, - { - "key": "stderr", - "value": { - "stringValue": "", - }, - }, - { - "key": "return_code", - "value": { - "intValue": 0, - }, - }, - { - "key": "content", - "value": { - "arrayValue": { - "values": [], - }, - }, - }, - ], - }, - }, - }, - { - "key": "builtin", - "value": { - "boolValue": true, - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "## Results: + "type": "bash_code_execution_result", + }, + "type": "tool_call_response", + }, + { + "content": "## Results: For the dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: @@ -958,92 +415,54 @@ For the dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: - The **sample standard deviation** (s) uses N-1 in the denominator (Bessel's correction) and equals approximately 3.03. The population standard deviation is used when your data represents the entire population, while the sample standard deviation is used when your data is a sample from a larger population. In most practical applications, the sample standard deviation is more commonly used.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "finish_reason", - "value": { - "stringValue": "end_turn", - }, - }, - ], + "type": "text", }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.system_instructions", - "value": {}, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.request.body.text", - "value": { - "stringValue": "{"model":"claude-opus-4-1-20250805","max_tokens":4096,"messages":[{"role":"user","content":"Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"}],"tools":[{"type":"code_execution_20250825","name":"code_execution"}]}", - }, - }, - { - "key": "http.response.body.text", - "value": { - "stringValue": "{"model":"claude-opus-4-1-20250805","id":"msg_01JHBJsdymq98CeYeSVkoMeQ","type":"message","role":"assistant","content":[{"type":"text","text":"I'll calculate the mean and standard deviation of the given dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]."},{"type":"server_tool_use","id":"srvtoolu_014V88EfQbuZg6ZeyWb4G56e","name":"text_editor_code_execution","input":{"command":"create","path":"/tmp/calculate_stats.py","file_text":"import math\\n\\n# Dataset\\ndata = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\\n\\n# Calculate mean\\nn = len(data)\\nmean = sum(data) / n\\n\\n# Calculate standard deviation\\n# Using population standard deviation formula\\nvariance = sum((x - mean) ** 2 for x in data) / n\\nstd_dev_population = math.sqrt(variance)\\n\\n# Also calculate sample standard deviation (dividing by n-1)\\nvariance_sample = sum((x - mean) ** 2 for x in data) / (n - 1)\\nstd_dev_sample = math.sqrt(variance_sample)\\n\\n# Display results\\nprint(f\\"Dataset: {data}\\")\\nprint(f\\"Number of values: {n}\\")\\nprint(f\\"\\\\nMean: {mean}\\")\\nprint(f\\"\\\\nPopulation Standard Deviation: {std_dev_population:.6f}\\")\\nprint(f\\"Sample Standard Deviation: {std_dev_sample:.6f}\\")\\n\\n# Show step-by-step calculation for clarity\\nprint(\\"\\\\n--- Step-by-step calculation ---\\")\\nprint(f\\"Sum of values: {sum(data)}\\")\\nprint(f\\"Mean = {sum(data)}/{n} = {mean}\\")\\n\\nprint(\\"\\\\nDeviations from mean:\\")\\ndeviations = [(x, x - mean, (x - mean) ** 2) for x in data]\\nfor x, dev, dev_squared in deviations:\\n print(f\\" {x}: deviation = {dev:.1f}, squared = {dev_squared:.2f}\\")\\n\\nprint(f\\"\\\\nSum of squared deviations: {sum(d[2] for d in deviations):.2f}\\")\\nprint(f\\"Population variance = {variance:.2f}\\")\\nprint(f\\"Sample variance = {variance_sample:.2f}\\")"}},{"type":"text_editor_code_execution_tool_result","tool_use_id":"srvtoolu_014V88EfQbuZg6ZeyWb4G56e","content":{"type":"text_editor_code_execution_create_result","is_file_update":false}},{"type":"server_tool_use","id":"srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3","name":"bash_code_execution","input":{"command":"python /tmp/calculate_stats.py"}},{"type":"bash_code_execution_tool_result","tool_use_id":"srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3","content":{"type":"bash_code_execution_result","stdout":"Dataset: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\\nNumber of values: 10\\n\\nMean: 5.5\\n\\nPopulation Standard Deviation: 2.872281\\nSample Standard Deviation: 3.027650\\n\\n--- Step-by-step calculation ---\\nSum of values: 55\\nMean = 55/10 = 5.5\\n\\nDeviations from mean:\\n 1: deviation = -4.5, squared = 20.25\\n 2: deviation = -3.5, squared = 12.25\\n 3: deviation = -2.5, squared = 6.25\\n 4: deviation = -1.5, squared = 2.25\\n 5: deviation = -0.5, squared = 0.25\\n 6: deviation = 0.5, squared = 0.25\\n 7: deviation = 1.5, squared = 2.25\\n 8: deviation = 2.5, squared = 6.25\\n 9: deviation = 3.5, squared = 12.25\\n 10: deviation = 4.5, squared = 20.25\\n\\nSum of squared deviations: 82.50\\nPopulation variance = 8.25\\nSample variance = 9.17\\n","stderr":"","return_code":0,"content":[]}},{"type":"text","text":"## Results:\\n\\nFor the dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\\n\\n- **Mean: 5.5**\\n- **Standard Deviation:**\\n - Population Standard Deviation: **2.872281** (when treating the data as the entire population)\\n - Sample Standard Deviation: **3.027650** (when treating the data as a sample from a larger population)\\n\\n### Explanation:\\n- The **mean** is calculated as the sum of all values (55) divided by the number of values (10), giving us 5.5.\\n- The **population standard deviation** (σ) uses N in the denominator and equals approximately 2.87.\\n- The **sample standard deviation** (s) uses N-1 in the denominator (Bessel's correction) and equals approximately 3.03.\\n\\nThe population standard deviation is used when your data represents the entire population, while the sample standard deviation is used when your data is a sample from a larger population. In most practical applications, the sample standard deviation is more commonly used."}],"container":{"id":"container_011CUdQHmGk52d4pDTuWk1z6","expires_at":"2025-10-30T13:43:33.209982Z"},"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":7862,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":897,"service_tier":"standard","server_tool_use":{"web_search_requests":0},"pydantic_ai_gateway":{"cost_estimate":0.185205}}}", - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "claude-opus-4-1-20250805", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 7862, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 897, + ], + "role": "assistant", + }, + ], + "gen_ai.request.max_tokens": 4096, + "gen_ai.request.model": "claude-opus-4-1-20250805", + "gen_ai.request.seed": {}, + "gen_ai.request.stop_sequences": {}, + "gen_ai.request.temperature": {}, + "gen_ai.request.top_k": {}, + "gen_ai.request.top_p": {}, + "gen_ai.response.finish_reasons": [ + "end_turn", + ], + "gen_ai.response.id": "msg_01JHBJsdymq98CeYeSVkoMeQ", + "gen_ai.response.model": "claude-opus-4-1-20250805", + "gen_ai.system": "anthropic", + "gen_ai.system_instructions": {}, + "gen_ai.usage.cache_audio_read_tokens": {}, + "gen_ai.usage.cache_read_tokens": 0, + "gen_ai.usage.cache_write_tokens": 0, + "gen_ai.usage.input_audio_tokens": {}, + "gen_ai.usage.input_tokens": 7862, + "gen_ai.usage.output_audio_tokens": {}, + "gen_ai.usage.output_tokens": 897, + "http.request.body.text": "{"model":"claude-opus-4-1-20250805","max_tokens":4096,"messages":[{"role":"user","content":"Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"}],"tools":[{"type":"code_execution_20250825","name":"code_execution"}]}", + "http.response.body.text": "{"model":"claude-opus-4-1-20250805","id":"msg_01JHBJsdymq98CeYeSVkoMeQ","type":"message","role":"assistant","content":[{"type":"text","text":"I'll calculate the mean and standard deviation of the given dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]."},{"type":"server_tool_use","id":"srvtoolu_014V88EfQbuZg6ZeyWb4G56e","name":"text_editor_code_execution","input":{"command":"create","path":"/tmp/calculate_stats.py","file_text":"import math\\n\\n# Dataset\\ndata = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\\n\\n# Calculate mean\\nn = len(data)\\nmean = sum(data) / n\\n\\n# Calculate standard deviation\\n# Using population standard deviation formula\\nvariance = sum((x - mean) ** 2 for x in data) / n\\nstd_dev_population = math.sqrt(variance)\\n\\n# Also calculate sample standard deviation (dividing by n-1)\\nvariance_sample = sum((x - mean) ** 2 for x in data) / (n - 1)\\nstd_dev_sample = math.sqrt(variance_sample)\\n\\n# Display results\\nprint(f\\"Dataset: {data}\\")\\nprint(f\\"Number of values: {n}\\")\\nprint(f\\"\\\\nMean: {mean}\\")\\nprint(f\\"\\\\nPopulation Standard Deviation: {std_dev_population:.6f}\\")\\nprint(f\\"Sample Standard Deviation: {std_dev_sample:.6f}\\")\\n\\n# Show step-by-step calculation for clarity\\nprint(\\"\\\\n--- Step-by-step calculation ---\\")\\nprint(f\\"Sum of values: {sum(data)}\\")\\nprint(f\\"Mean = {sum(data)}/{n} = {mean}\\")\\n\\nprint(\\"\\\\nDeviations from mean:\\")\\ndeviations = [(x, x - mean, (x - mean) ** 2) for x in data]\\nfor x, dev, dev_squared in deviations:\\n print(f\\" {x}: deviation = {dev:.1f}, squared = {dev_squared:.2f}\\")\\n\\nprint(f\\"\\\\nSum of squared deviations: {sum(d[2] for d in deviations):.2f}\\")\\nprint(f\\"Population variance = {variance:.2f}\\")\\nprint(f\\"Sample variance = {variance_sample:.2f}\\")"}},{"type":"text_editor_code_execution_tool_result","tool_use_id":"srvtoolu_014V88EfQbuZg6ZeyWb4G56e","content":{"type":"text_editor_code_execution_create_result","is_file_update":false}},{"type":"server_tool_use","id":"srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3","name":"bash_code_execution","input":{"command":"python /tmp/calculate_stats.py"}},{"type":"bash_code_execution_tool_result","tool_use_id":"srvtoolu_01W3mgwqbL8MsYAcRK3gaUb3","content":{"type":"bash_code_execution_result","stdout":"Dataset: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\\nNumber of values: 10\\n\\nMean: 5.5\\n\\nPopulation Standard Deviation: 2.872281\\nSample Standard Deviation: 3.027650\\n\\n--- Step-by-step calculation ---\\nSum of values: 55\\nMean = 55/10 = 5.5\\n\\nDeviations from mean:\\n 1: deviation = -4.5, squared = 20.25\\n 2: deviation = -3.5, squared = 12.25\\n 3: deviation = -2.5, squared = 6.25\\n 4: deviation = -1.5, squared = 2.25\\n 5: deviation = -0.5, squared = 0.25\\n 6: deviation = 0.5, squared = 0.25\\n 7: deviation = 1.5, squared = 2.25\\n 8: deviation = 2.5, squared = 6.25\\n 9: deviation = 3.5, squared = 12.25\\n 10: deviation = 4.5, squared = 20.25\\n\\nSum of squared deviations: 82.50\\nPopulation variance = 8.25\\nSample variance = 9.17\\n","stderr":"","return_code":0,"content":[]}},{"type":"text","text":"## Results:\\n\\nFor the dataset [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\\n\\n- **Mean: 5.5**\\n- **Standard Deviation:**\\n - Population Standard Deviation: **2.872281** (when treating the data as the entire population)\\n - Sample Standard Deviation: **3.027650** (when treating the data as a sample from a larger population)\\n\\n### Explanation:\\n- The **mean** is calculated as the sum of all values (55) divided by the number of values (10), giving us 5.5.\\n- The **population standard deviation** (σ) uses N in the denominator and equals approximately 2.87.\\n- The **sample standard deviation** (s) uses N-1 in the denominator (Bessel's correction) and equals approximately 3.03.\\n\\nThe population standard deviation is used when your data represents the entire population, while the sample standard deviation is used when your data is a sample from a larger population. In most practical applications, the sample standard deviation is more commonly used."}],"container":{"id":"container_011CUdQHmGk52d4pDTuWk1z6","expires_at":"2025-10-30T13:43:33.209982Z"},"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":7862,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":897,"service_tier":"standard","server_tool_use":{"web_search_requests":0},"pydantic_ai_gateway":{"cost_estimate":0.185205}}}", + "http.response.status_code": 200, + "logfire.json_schema": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"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"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", + "logfire.level_num": 9, + "logfire.msg": "chat claude-opus-4-1-20250805", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat claude-opus-4-1-20250805", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_audio_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": {}, - }, ] `; @@ -1122,195 +541,52 @@ exports[`anthropic > should call anthropic via gateway with stream > chunks 1`] exports[`anthropic > should call anthropic via gateway with stream > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat claude-opus-4-1-20250805", - }, - }, - { - "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.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": "anthropic", - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "claude-opus-4-1-20250805", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": { - "intValue": 1024, - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "claude-opus-4-1-20250805", - }, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "msg_01Tce1onxgEPRDM3CRhb6A4i", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 14, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 10, - }, - }, - { - "key": "http.request.method", - "value": { - "stringValue": "POST", - }, - }, - { - "key": "url.full", - "value": { - "stringValue": "https://example.com/anthropic/v1/messages?beta=true", - }, - }, - { - "key": "http.request.header.accept", - "value": { - "stringValue": "application/json", - }, - }, - { - "key": "http.request.header.anthropic-version", - "value": { - "stringValue": "2023-06-01", - }, - }, - { - "key": "http.request.header.authorization", - "value": { - "stringValue": "Bearer healthy", - }, - }, - { - "key": "http.request.header.content-type", - "value": { - "stringValue": "application/json", - }, - }, - { - "key": "http.request.header.user-agent", - "value": { - "stringValue": "Anthropic/JS 0.62.0", - }, - }, - { - "key": "http.request.header.x-stainless-arch", - "value": { - "stringValue": "unknown", - }, - }, - { - "key": "http.request.header.x-stainless-lang", - "value": { - "stringValue": "js", - }, - }, - { - "key": "http.request.header.x-stainless-os", - "value": { - "stringValue": "Unknown", - }, - }, - { - "key": "http.request.header.x-stainless-package-version", - "value": { - "stringValue": "0.62.0", - }, - }, - { - "key": "http.request.header.x-stainless-retry-count", - "value": { - "stringValue": "0", - }, - }, - { - "key": "http.request.header.x-stainless-runtime", - "value": { - "stringValue": "unknown", - }, - }, - { - "key": "http.request.header.x-stainless-runtime-version", - "value": { - "stringValue": "unknown", - }, - }, - { - "key": "http.request.header.x-stainless-timeout", - "value": { - "stringValue": "600", - }, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.response.header.content-type", - "value": { - "stringValue": "text/event-stream; charset=utf-8", - }, - }, - { - "key": "http.response.header.server", - "value": { - "stringValue": "uvicorn", - }, - }, - { - "key": "http.response.header.transfer-encoding", - "value": { - "stringValue": "chunked", + "attributes": { + "gen_ai.operation.name": "chat", + "gen_ai.request.max_tokens": 1024, + "gen_ai.request.model": "claude-opus-4-1-20250805", + "gen_ai.response.id": "msg_01Tce1onxgEPRDM3CRhb6A4i", + "gen_ai.response.model": "claude-opus-4-1-20250805", + "gen_ai.system": "anthropic", + "gen_ai.usage.cache_read_tokens": 0, + "gen_ai.usage.cache_write_tokens": 0, + "gen_ai.usage.input_tokens": 14, + "gen_ai.usage.output_tokens": 10, + "http.request.header.accept": "application/json", + "http.request.header.anthropic-version": "2023-06-01", + "http.request.header.authorization": "Bearer healthy", + "http.request.header.content-type": "application/json", + "http.request.header.user-agent": "Anthropic/JS 0.62.0", + "http.request.header.x-stainless-arch": "unknown", + "http.request.header.x-stainless-lang": "js", + "http.request.header.x-stainless-os": "Unknown", + "http.request.header.x-stainless-package-version": "0.62.0", + "http.request.header.x-stainless-retry-count": "0", + "http.request.header.x-stainless-runtime": "unknown", + "http.request.header.x-stainless-runtime-version": "unknown", + "http.request.header.x-stainless-timeout": "600", + "http.request.method": "POST", + "http.response.header.content-type": "text/event-stream; charset=utf-8", + "http.response.header.server": "uvicorn", + "http.response.header.transfer-encoding": "chunked", + "http.response.status_code": 200, + "logfire.json_schema": "{"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"}}}", + "logfire.level_num": 9, + "logfire.msg": "chat claude-opus-4-1-20250805", + "url.full": "https://example.com/anthropic/v1/messages?beta=true", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat claude-opus-4-1-20250805", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, ] diff --git a/gateway/test/providers/bedrock.spec.ts b/gateway/test/providers/bedrock.spec.ts index 297f5a8..10503a5 100644 --- a/gateway/test/providers/bedrock.spec.ts +++ b/gateway/test/providers/bedrock.spec.ts @@ -1,4 +1,5 @@ import { describe, expect } from 'vitest' +import { deserializeRequest } from '../otel' import { test } from '../setup' describe('bedrock', () => { @@ -21,6 +22,6 @@ describe('bedrock', () => { expect(result.status).toBe(200) expect(json).toMatchSnapshot('bedrock') expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) - expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) }) diff --git a/gateway/test/providers/bedrock.spec.ts.snap b/gateway/test/providers/bedrock.spec.ts.snap index aa0ba62..d7bdd61 100644 --- a/gateway/test/providers/bedrock.spec.ts.snap +++ b/gateway/test/providers/bedrock.spec.ts.snap @@ -31,140 +31,49 @@ exports[`bedrock > should call bedrock via gateway > bedrock 1`] = ` exports[`bedrock > should call bedrock via gateway > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat amazon.nova-micro-v1:0", + "attributes": { + "gen_ai.input.messages": {}, + "gen_ai.operation.name": "chat", + "gen_ai.output.messages": {}, + "gen_ai.request.max_tokens": {}, + "gen_ai.request.model": "m", + "gen_ai.request.seed": {}, + "gen_ai.request.stop_sequences": {}, + "gen_ai.request.temperature": {}, + "gen_ai.request.top_k": {}, + "gen_ai.request.top_p": {}, + "gen_ai.response.finish_reasons": {}, + "gen_ai.response.id": {}, + "gen_ai.response.model": "amazon.nova-micro-v1:0", + "gen_ai.system": "bedrock", + "gen_ai.system_instructions": {}, + "gen_ai.usage.cache_audio_read_tokens": {}, + "gen_ai.usage.cache_read_tokens": {}, + "gen_ai.usage.cache_write_tokens": {}, + "gen_ai.usage.input_audio_tokens": {}, + "gen_ai.usage.input_tokens": 13, + "gen_ai.usage.output_audio_tokens": {}, + "gen_ai.usage.output_tokens": 40, + "http.request.body.text": "{"modelId":"amazon.nova-premier-v1:0","system":[{"text":"You are a helpful assistant."}],"messages":[{"role":"user","content":[{"text":"What is the capital of France?"}]}]}", + "http.response.body.text": "{"metrics":{"latencyMs":503},"output":{"message":{"content":[{"text":"The capital of France is Paris. Paris is not only the capital city but also the most populous city in France, and it is a major center for culture, commerce, fashion, and gastronomy worldwide."}],"role":"assistant"}},"stopReason":"end_turn","usage":{"inputTokens":13,"outputTokens":40,"serverToolUsage":{},"totalTokens":53,"pydantic_ai_gateway":{"cost_estimate":0.0000060550000000000005}}}", + "http.response.status_code": 200, + "logfire.json_schema": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", + "logfire.level_num": 9, + "logfire.msg": "chat amazon.nova-micro-v1:0", }, - }, - { - "key": "logfire.json_schema", - "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", - }, - }, - { - "key": "logfire.level_num", - "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", + "events": [], + "kind": 1, + "links": [], + "name": "chat amazon.nova-micro-v1:0", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "m", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "bedrock", + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, - { - "key": "gen_ai.request.max_tokens", - "value": {}, - }, - { - "key": "gen_ai.request.top_k", - "value": {}, - }, - { - "key": "gen_ai.request.top_p", - "value": {}, - }, - { - "key": "gen_ai.request.temperature", - "value": {}, - }, - { - "key": "gen_ai.request.stop_sequences", - "value": {}, - }, - { - "key": "gen_ai.request.seed", - "value": {}, - }, - { - "key": "gen_ai.response.finish_reasons", - "value": {}, - }, - { - "key": "gen_ai.response.id", - "value": {}, - }, - { - "key": "gen_ai.input.messages", - "value": {}, - }, - { - "key": "gen_ai.output.messages", - "value": {}, - }, - { - "key": "gen_ai.system_instructions", - "value": {}, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.request.body.text", - "value": { - "stringValue": "{"modelId":"amazon.nova-premier-v1:0","system":[{"text":"You are a helpful assistant."}],"messages":[{"role":"user","content":[{"text":"What is the capital of France?"}]}]}", - }, - }, - { - "key": "http.response.body.text", - "value": { - "stringValue": "{"metrics":{"latencyMs":503},"output":{"message":{"content":[{"text":"The capital of France is Paris. Paris is not only the capital city but also the most populous city in France, and it is a major center for culture, commerce, fashion, and gastronomy worldwide."}],"role":"assistant"}},"stopReason":"end_turn","usage":{"inputTokens":13,"outputTokens":40,"serverToolUsage":{},"totalTokens":53,"pydantic_ai_gateway":{"cost_estimate":0.0000060550000000000005}}}", - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "amazon.nova-micro-v1:0", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 13, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 40, - }, - }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_audio_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": {}, - }, ] `; diff --git a/gateway/test/providers/google.spec.ts b/gateway/test/providers/google.spec.ts index 460e48c..f810b07 100644 --- a/gateway/test/providers/google.spec.ts +++ b/gateway/test/providers/google.spec.ts @@ -1,4 +1,5 @@ import { describe, expect } from 'vitest' +import { deserializeRequest } from '../otel' import { test } from '../setup' const body = JSON.stringify({ @@ -56,7 +57,7 @@ describe('google', () => { 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') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) test('google-vertex/stream', async ({ gateway }) => { @@ -74,6 +75,6 @@ describe('google', () => { 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') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) }) diff --git a/gateway/test/providers/google.spec.ts.snap b/gateway/test/providers/google.spec.ts.snap index e2d6e98..db96d7d 100644 --- a/gateway/test/providers/google.spec.ts.snap +++ b/gateway/test/providers/google.spec.ts.snap @@ -5,57 +5,29 @@ exports[`google > google-vertex/default > llm 1`] = `"Path /gemini/v1beta1/proje exports[`google > google-vertex/default > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat gemini-2.5-flash, unexpected response: 404", - }, - }, - { - "key": "logfire.json_schema", - "value": { - "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": 13, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "gemini-2.5-flash", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "google-vertex", - }, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 404, - }, - }, - { - "key": "http.request.body.text", - "value": { - "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.body.text", - "value": { - "stringValue": "Path /gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent not supported", + "attributes": { + "gen_ai.operation.name": "chat", + "gen_ai.request.model": "gemini-2.5-flash", + "gen_ai.system": "google-vertex", + "http.request.body.text": "{"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"]}}", + "http.response.body.text": "Path /gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:generateContent not supported", + "http.response.status_code": 404, + "logfire.json_schema": "{"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"}}}", + "logfire.level_num": 13, + "logfire.msg": "chat gemini-2.5-flash, unexpected response: 404", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat gemini-2.5-flash, unexpected response: {http.response.status_code}", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, ] @@ -207,57 +179,29 @@ exports[`google > google-vertex/stream > chunks 1`] = ` exports[`google > google-vertex/stream > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat gemini-2.5-flash, unexpected response: 404", - }, - }, - { - "key": "logfire.json_schema", - "value": { - "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": 13, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "gemini-2.5-flash", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "google-vertex", - }, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 404, - }, - }, - { - "key": "http.request.body.text", - "value": { - "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.body.text", - "value": { - "stringValue": "Path /gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent not supported", + "attributes": { + "gen_ai.operation.name": "chat", + "gen_ai.request.model": "gemini-2.5-flash", + "gen_ai.system": "google-vertex", + "http.request.body.text": "{"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"]}}", + "http.response.body.text": "Path /gemini/v1beta1/projects/pydantic-ai/locations/global/publishers/google/models/gemini-2.5-flash:streamGenerateContent not supported", + "http.response.status_code": 404, + "logfire.json_schema": "{"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"}}}", + "logfire.level_num": 13, + "logfire.msg": "chat gemini-2.5-flash, unexpected response: 404", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat gemini-2.5-flash, unexpected response: {http.response.status_code}", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, ] diff --git a/gateway/test/providers/groq.spec.ts b/gateway/test/providers/groq.spec.ts index 2a3beda..b376ef8 100644 --- a/gateway/test/providers/groq.spec.ts +++ b/gateway/test/providers/groq.spec.ts @@ -1,5 +1,6 @@ import Groq from 'groq-sdk' import { describe, expect } from 'vitest' +import { deserializeRequest } from '../otel' import { test } from '../setup' describe('groq', () => { @@ -20,6 +21,6 @@ describe('groq', () => { }) expect(completion).toMatchSnapshot('llm') expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) - expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) }) diff --git a/gateway/test/providers/groq.spec.ts.snap b/gateway/test/providers/groq.spec.ts.snap index cd61184..6778902 100644 --- a/gateway/test/providers/groq.spec.ts.snap +++ b/gateway/test/providers/groq.spec.ts.snap @@ -41,252 +41,64 @@ exports[`groq > should call groq via gateway > llm 1`] = ` exports[`groq > should call groq via gateway > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat llama-3.3-70b-versatile", - }, - }, - { - "key": "logfire.json_schema", - "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{"type":"number"},"gen_ai.request.temperature":{"type":"number"},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", - }, - }, - { - "key": "logfire.level_num", - "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "llama-3.3-70b-versatile", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "groq", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": { - "intValue": 1024, - }, - }, - { - "key": "gen_ai.request.top_k", - "value": {}, - }, - { - "key": "gen_ai.request.top_p", - "value": { - "doubleValue": 0.95, - }, - }, - { - "key": "gen_ai.request.temperature", - "value": { - "doubleValue": 0.5, - }, - }, - { - "key": "gen_ai.request.stop_sequences", - "value": { - "arrayValue": { - "values": [ - { - "stringValue": "potato", - }, - ], - }, - }, - }, - { - "key": "gen_ai.request.seed", - "value": {}, - }, - { - "key": "gen_ai.response.finish_reasons", - "value": { - "arrayValue": { - "values": [ - { - "stringValue": "stop", - }, - ], - }, - }, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "chatcmpl-d912623a-1d43-449e-92fe-d8d850b2555d", - }, - }, - { - "key": "gen_ai.input.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "system", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "You are a helpful assistant.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], + "attributes": { + "gen_ai.input.messages": [ + { + "parts": [ + { + "content": "You are a helpful assistant.", + "type": "text", }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "user", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "What is the capital of France?", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], + ], + "role": "system", + }, + { + "parts": [ + { + "content": "What is the capital of France?", + "type": "text", }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.output.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "The capital of France is Paris.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "finish_reason", - "value": { - "stringValue": "stop", - }, - }, - ], + ], + "role": "user", + }, + ], + "gen_ai.operation.name": "chat", + "gen_ai.output.messages": [ + { + "finish_reason": "stop", + "parts": [ + { + "content": "The capital of France is Paris.", + "type": "text", }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.system_instructions", - "value": {}, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.request.body.text", - "value": { - "stringValue": "{ + ], + "role": "assistant", + }, + ], + "gen_ai.request.max_tokens": 1024, + "gen_ai.request.model": "llama-3.3-70b-versatile", + "gen_ai.request.seed": {}, + "gen_ai.request.stop_sequences": [ + "potato", + ], + "gen_ai.request.temperature": 0.5, + "gen_ai.request.top_k": {}, + "gen_ai.request.top_p": 0.95, + "gen_ai.response.finish_reasons": [ + "stop", + ], + "gen_ai.response.id": "chatcmpl-d912623a-1d43-449e-92fe-d8d850b2555d", + "gen_ai.response.model": "llama-3.3-70b-versatile", + "gen_ai.system": "groq", + "gen_ai.system_instructions": {}, + "gen_ai.usage.cache_audio_read_tokens": {}, + "gen_ai.usage.cache_read_tokens": {}, + "gen_ai.usage.cache_write_tokens": {}, + "gen_ai.usage.input_audio_tokens": {}, + "gen_ai.usage.input_tokens": 42, + "gen_ai.usage.output_audio_tokens": {}, + "gen_ai.usage.output_tokens": 8, + "http.request.body.text": "{ "model": "llama-3.3-70b-versatile", "messages": [ { @@ -305,51 +117,25 @@ exports[`groq > should call groq via gateway > span 1`] = ` ], "max_completion_tokens": 1024 }", + "http.response.body.text": "{"id":"chatcmpl-d912623a-1d43-449e-92fe-d8d850b2555d","object":"chat.completion","created":1761823220,"model":"llama-3.3-70b-versatile","choices":[{"index":0,"message":{"role":"assistant","content":"The capital of France is Paris."},"logprobs":null,"finish_reason":"stop"}],"usage":{"queue_time":0.150119133,"prompt_tokens":42,"prompt_time":0.002274325,"completion_tokens":8,"completion_time":0.0127032,"total_tokens":50,"total_time":0.014977525,"pydantic_ai_gateway":{"cost_estimate":0.0000311}},"usage_breakdown":null,"system_fingerprint":"fp_21854da540","x_groq":{"id":"req_01k8tdhr6ke5hah7e5e3dfq95a"},"service_tier":"on_demand"}", + "http.response.status_code": 200, + "logfire.json_schema": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{"type":"number"},"gen_ai.request.temperature":{"type":"number"},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", + "logfire.level_num": 9, + "logfire.msg": "chat llama-3.3-70b-versatile", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat llama-3.3-70b-versatile", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, - { - "key": "http.response.body.text", - "value": { - "stringValue": "{"id":"chatcmpl-d912623a-1d43-449e-92fe-d8d850b2555d","object":"chat.completion","created":1761823220,"model":"llama-3.3-70b-versatile","choices":[{"index":0,"message":{"role":"assistant","content":"The capital of France is Paris."},"logprobs":null,"finish_reason":"stop"}],"usage":{"queue_time":0.150119133,"prompt_tokens":42,"prompt_time":0.002274325,"completion_tokens":8,"completion_time":0.0127032,"total_tokens":50,"total_time":0.014977525,"pydantic_ai_gateway":{"cost_estimate":0.0000311}},"usage_breakdown":null,"system_fingerprint":"fp_21854da540","x_groq":{"id":"req_01k8tdhr6ke5hah7e5e3dfq95a"},"service_tier":"on_demand"}", - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "llama-3.3-70b-versatile", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 42, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 8, - }, - }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_audio_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": {}, - }, ] `; diff --git a/gateway/test/providers/openai.spec.ts b/gateway/test/providers/openai.spec.ts index a454368..9ace1b6 100644 --- a/gateway/test/providers/openai.spec.ts +++ b/gateway/test/providers/openai.spec.ts @@ -2,6 +2,7 @@ import { env } from 'cloudflare:test' import { LimitDbD1 } from '@pydantic/ai-gateway' import OpenAI from 'openai' import { describe, expect } from 'vitest' +import { deserializeRequest } from '../otel' import { test } from '../setup' import { IDS } from '../worker' @@ -112,7 +113,7 @@ describe('openai', () => { }) expect(completion).toMatchSnapshot('llm') expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) - expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) test('openai responses with builtin tools', async ({ gateway }) => { @@ -144,7 +145,7 @@ describe('openai', () => { } `) expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) - expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) test('openai chat stream', async ({ gateway }) => { @@ -168,7 +169,7 @@ describe('openai', () => { } 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') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) test('openai chat legacy name', async ({ gateway }) => { @@ -206,6 +207,6 @@ describe('openai', () => { expect(completion).toMatchSnapshot('llm') expect(otelBatch, 'otelBatch length not 1').toHaveLength(1) - expect(JSON.parse(otelBatch[0]!).resourceSpans?.[0].scopeSpans?.[0].spans?.[0]?.attributes).toMatchSnapshot('span') + expect(deserializeRequest(otelBatch[0]!)).toMatchSnapshot('span') }) }) diff --git a/gateway/test/providers/openai.spec.ts.snap b/gateway/test/providers/openai.spec.ts.snap index 2574d82..5f54f78 100644 --- a/gateway/test/providers/openai.spec.ts.snap +++ b/gateway/test/providers/openai.spec.ts.snap @@ -801,201 +801,53 @@ exports[`openai > openai chat stream > chunks 1`] = ` exports[`openai > openai chat stream > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat gpt-5", - }, - }, - { - "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.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": "openai", - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "gpt-5", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": { - "intValue": 1024, - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "gpt-5-2025-08-07", - }, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 23, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 11, - }, - }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "http.request.method", - "value": { - "stringValue": "POST", - }, - }, - { - "key": "url.full", - "value": { - "stringValue": "https://example.com/chat/chat/completions", - }, - }, - { - "key": "http.request.header.accept", - "value": { - "stringValue": "application/json", - }, - }, - { - "key": "http.request.header.authorization", - "value": { - "stringValue": "Bearer healthy", - }, - }, - { - "key": "http.request.header.content-length", - "value": { - "stringValue": "319", - }, - }, - { - "key": "http.request.header.content-type", - "value": { - "stringValue": "application/json", - }, - }, - { - "key": "http.request.header.user-agent", - "value": { - "stringValue": "OpenAI/JS 4.104.0", - }, - }, - { - "key": "http.request.header.x-stainless-arch", - "value": { - "stringValue": "unknown", - }, - }, - { - "key": "http.request.header.x-stainless-lang", - "value": { - "stringValue": "js", - }, - }, - { - "key": "http.request.header.x-stainless-os", - "value": { - "stringValue": "Unknown", - }, - }, - { - "key": "http.request.header.x-stainless-package-version", - "value": { - "stringValue": "4.104.0", - }, - }, - { - "key": "http.request.header.x-stainless-retry-count", - "value": { - "stringValue": "0", - }, - }, - { - "key": "http.request.header.x-stainless-runtime", - "value": { - "stringValue": "unknown", - }, - }, - { - "key": "http.request.header.x-stainless-runtime-version", - "value": { - "stringValue": "unknown", - }, - }, - { - "key": "http.request.header.x-stainless-timeout", - "value": { - "stringValue": "600", - }, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.response.header.content-type", - "value": { - "stringValue": "text/event-stream; charset=utf-8", - }, - }, - { - "key": "http.response.header.server", - "value": { - "stringValue": "uvicorn", - }, - }, - { - "key": "http.response.header.transfer-encoding", - "value": { - "stringValue": "chunked", + "attributes": { + "gen_ai.operation.name": "chat", + "gen_ai.request.max_tokens": 1024, + "gen_ai.request.model": "gpt-5", + "gen_ai.response.id": "chatcmpl-CYDfFsepopZlcPzyFj5cdegEMUIO3", + "gen_ai.response.model": "gpt-5-2025-08-07", + "gen_ai.system": "openai", + "gen_ai.usage.cache_read_tokens": 0, + "gen_ai.usage.input_audio_tokens": 0, + "gen_ai.usage.input_tokens": 23, + "gen_ai.usage.output_audio_tokens": 0, + "gen_ai.usage.output_tokens": 11, + "http.request.header.accept": "application/json", + "http.request.header.authorization": "Bearer healthy", + "http.request.header.content-length": "319", + "http.request.header.content-type": "application/json", + "http.request.header.user-agent": "OpenAI/JS 4.104.0", + "http.request.header.x-stainless-arch": "unknown", + "http.request.header.x-stainless-lang": "js", + "http.request.header.x-stainless-os": "Unknown", + "http.request.header.x-stainless-package-version": "4.104.0", + "http.request.header.x-stainless-retry-count": "0", + "http.request.header.x-stainless-runtime": "unknown", + "http.request.header.x-stainless-runtime-version": "unknown", + "http.request.header.x-stainless-timeout": "600", + "http.request.method": "POST", + "http.response.header.content-type": "text/event-stream; charset=utf-8", + "http.response.header.server": "uvicorn", + "http.response.header.transfer-encoding": "chunked", + "http.response.status_code": 200, + "logfire.json_schema": "{"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"}}}", + "logfire.level_num": 9, + "logfire.msg": "chat gpt-5", + "url.full": "https://example.com/chat/chat/completions", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat gpt-5", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, ] @@ -1084,543 +936,186 @@ exports[`openai > openai responses > llm 1`] = ` exports[`openai > openai responses > span 1`] = ` [ { - "key": "logfire.msg", - "value": { - "stringValue": "chat gpt-5-2025-08-07", + "attributes": { + "gen_ai.input.messages": [ + { + "parts": [ + { + "content": "what color is the sky?", + "type": "text", + }, + ], + "role": "user", + }, + ], + "gen_ai.operation.name": "chat", + "gen_ai.output.messages": [ + { + "parts": [], + "role": "assistant", + }, + { + "parts": [ + { + "content": "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": "text", + }, + ], + "role": "assistant", + }, + ], + "gen_ai.request.max_tokens": {}, + "gen_ai.request.model": "gpt-5", + "gen_ai.request.seed": {}, + "gen_ai.request.stop_sequences": {}, + "gen_ai.request.temperature": {}, + "gen_ai.request.top_k": {}, + "gen_ai.request.top_p": {}, + "gen_ai.response.finish_reasons": {}, + "gen_ai.response.id": "resp_0460eb44ce4bf29800690a219dd0108193abee8e830ce725cb", + "gen_ai.response.model": "gpt-5-2025-08-07", + "gen_ai.system": "openai", + "gen_ai.system_instructions": {}, + "gen_ai.usage.cache_audio_read_tokens": {}, + "gen_ai.usage.cache_read_tokens": 0, + "gen_ai.usage.cache_write_tokens": {}, + "gen_ai.usage.input_audio_tokens": {}, + "gen_ai.usage.input_tokens": 20, + "gen_ai.usage.output_audio_tokens": {}, + "gen_ai.usage.output_tokens": 168, + "http.request.body.text": "{ + "model": "gpt-5", + "instructions": "reply concisely", + "input": "what color is the sky?" +}", + "http.response.body.text": "{"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":{}}", + "http.response.status_code": 200, + "logfire.json_schema": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", + "logfire.level_num": 9, + "logfire.msg": "chat gpt-5-2025-08-07", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat gpt-5-2025-08-07", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, - { - "key": "logfire.json_schema", - "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", +] +`; + +exports[`openai > openai responses legacy name > llm 1`] = ` +{ + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "annotations": [], + "content": "Paris.", + "refusal": null, + "role": "assistant", + }, }, - }, - { - "key": "logfire.level_num", - "value": { - "intValue": 9, + ], + "created": 1762271642, + "id": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", + "model": "gpt-5-2025-08-07", + "object": "chat.completion", + "service_tier": "default", + "system_fingerprint": null, + "usage": { + "completion_tokens": 11, + "completion_tokens_details": { + "accepted_prediction_tokens": 0, + "audio_tokens": 0, + "reasoning_tokens": 0, + "rejected_prediction_tokens": 0, }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", + "prompt_tokens": 23, + "prompt_tokens_details": { + "audio_tokens": 0, + "cached_tokens": 0, }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "gpt-5", + "pydantic_ai_gateway": { + "cost_estimate": 0.00013875, }, + "total_tokens": 34, }, +} +`; + +exports[`openai > openai responses legacy name > span 1`] = ` +[ { - "key": "gen_ai.system", - "value": { - "stringValue": "openai", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": {}, - }, - { - "key": "gen_ai.request.top_k", - "value": {}, - }, - { - "key": "gen_ai.request.top_p", - "value": {}, - }, - { - "key": "gen_ai.request.temperature", - "value": {}, - }, - { - "key": "gen_ai.request.stop_sequences", - "value": {}, - }, - { - "key": "gen_ai.request.seed", - "value": {}, - }, - { - "key": "gen_ai.response.finish_reasons", - "value": {}, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "resp_0460eb44ce4bf29800690a219dd0108193abee8e830ce725cb", - }, - }, - { - "key": "gen_ai.input.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "user", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "what color is the sky?", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.output.messages", - "value": { - "arrayValue": { - "values": [ - { - "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": "text", - }, - }, - { - "key": "content", - "value": { - "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.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.system_instructions", - "value": {}, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.request.body.text", - "value": { - "stringValue": "{ - "model": "gpt-5", - "instructions": "reply concisely", - "input": "what color is the sky?" -}", - }, - }, - { - "key": "http.response.body.text", - "value": { - "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":{}}", - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "gpt-5-2025-08-07", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 20, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 168, - }, - }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_audio_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": {}, - }, -] -`; - -exports[`openai > openai responses legacy name > llm 1`] = ` -{ - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "message": { - "annotations": [], - "content": "Paris.", - "refusal": null, - "role": "assistant", - }, - }, - ], - "created": 1762271642, - "id": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", - "model": "gpt-5-2025-08-07", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": null, - "usage": { - "completion_tokens": 11, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0, - }, - "prompt_tokens": 23, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0, - }, - "pydantic_ai_gateway": { - "cost_estimate": 0.00013875, - }, - "total_tokens": 34, - }, -} -`; - -exports[`openai > openai responses legacy name > span 1`] = ` -[ - { - "key": "logfire.msg", - "value": { - "stringValue": "chat gpt-5-2025-08-07", - }, - }, - { - "key": "logfire.json_schema", - "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{"type":"number"},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{"type":"number"}}}", - }, - }, - { - "key": "logfire.level_num", - "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "gpt-5", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "openai", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": { - "intValue": 1024, - }, - }, - { - "key": "gen_ai.request.top_k", - "value": {}, - }, - { - "key": "gen_ai.request.top_p", - "value": {}, - }, - { - "key": "gen_ai.request.temperature", - "value": {}, - }, - { - "key": "gen_ai.request.stop_sequences", - "value": {}, - }, - { - "key": "gen_ai.request.seed", - "value": {}, - }, - { - "key": "gen_ai.response.finish_reasons", - "value": { - "arrayValue": { - "values": [ - { - "stringValue": "stop", - }, - ], - }, - }, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", - }, - }, - { - "key": "gen_ai.input.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "system", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "You are a helpful assistant.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "user", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "What is the capital of France?", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.output.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "Paris.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "finish_reason", - "value": { - "stringValue": "stop", - }, - }, - ], - }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.system_instructions", - "value": {}, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.request.body.text", - "value": { - "stringValue": "{ - "model": "gpt-5", - "messages": [ - { - "role": "developer", - "content": "You are a helpful assistant." + "attributes": { + "gen_ai.input.messages": [ + { + "parts": [ + { + "content": "You are a helpful assistant.", + "type": "text", + }, + ], + "role": "system", + }, + { + "parts": [ + { + "content": "What is the capital of France?", + "type": "text", + }, + ], + "role": "user", + }, + ], + "gen_ai.operation.name": "chat", + "gen_ai.output.messages": [ + { + "finish_reason": "stop", + "parts": [ + { + "content": "Paris.", + "type": "text", + }, + ], + "role": "assistant", + }, + ], + "gen_ai.request.max_tokens": 1024, + "gen_ai.request.model": "gpt-5", + "gen_ai.request.seed": {}, + "gen_ai.request.stop_sequences": {}, + "gen_ai.request.temperature": {}, + "gen_ai.request.top_k": {}, + "gen_ai.request.top_p": {}, + "gen_ai.response.finish_reasons": [ + "stop", + ], + "gen_ai.response.id": "chatcmpl-CYDe6BCWOKGGGTlQLofyQ2DP3QTRV", + "gen_ai.response.model": "gpt-5-2025-08-07", + "gen_ai.system": "openai", + "gen_ai.system_instructions": {}, + "gen_ai.usage.cache_audio_read_tokens": {}, + "gen_ai.usage.cache_read_tokens": 0, + "gen_ai.usage.cache_write_tokens": {}, + "gen_ai.usage.input_audio_tokens": 0, + "gen_ai.usage.input_tokens": 23, + "gen_ai.usage.output_audio_tokens": 0, + "gen_ai.usage.output_tokens": 11, + "http.request.body.text": "{ + "model": "gpt-5", + "messages": [ + { + "role": "developer", + "content": "You are a helpful assistant." }, { "role": "user", @@ -1629,56 +1124,24 @@ exports[`openai > openai responses legacy name > span 1`] = ` ], "max_completion_tokens": 1024 }", - }, - }, - { - "key": "http.response.body.text", - "value": { - "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}", - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "gpt-5-2025-08-07", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 23, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 11, - }, - }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.cache_audio_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": { - "intValue": 0, + "http.response.body.text": "{"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}", + "http.response.status_code": 200, + "logfire.json_schema": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{"type":"number"},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{"type":"number"},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{"type":"number"}}}", + "logfire.level_num": 9, + "logfire.msg": "chat gpt-5-2025-08-07", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat gpt-5-2025-08-07", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, ] @@ -1819,817 +1282,226 @@ for p in [6, 9, 12, 15]: }, "safety_identifier": null, "service_tier": "default", - "status": "completed", - "store": true, - "temperature": 1, - "text": { - "format": { - "type": "text", - }, - "verbosity": "medium", - }, - "tool_choice": "auto", - "tools": [ - { - "container": { - "type": "auto", - }, - "type": "code_interpreter", - }, - ], - "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, - }, - "pydantic_ai_gateway": { - "cost_estimate": 0.01287, - }, - "total_tokens": 2869, - }, - "user": null, -} -`; - -exports[`openai > openai responses with builtin tools > span 1`] = ` -[ - { - "key": "logfire.msg", - "value": { - "stringValue": "chat gpt-5-2025-08-07", - }, - }, - { - "key": "logfire.json_schema", - "value": { - "stringValue": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", - }, - }, - { - "key": "logfire.level_num", - "value": { - "intValue": 9, - }, - }, - { - "key": "gen_ai.operation.name", - "value": { - "stringValue": "chat", - }, - }, - { - "key": "gen_ai.request.model", - "value": { - "stringValue": "gpt-5", - }, - }, - { - "key": "gen_ai.system", - "value": { - "stringValue": "openai", - }, - }, - { - "key": "gen_ai.request.max_tokens", - "value": {}, - }, - { - "key": "gen_ai.request.top_k", - "value": {}, - }, - { - "key": "gen_ai.request.top_p", - "value": {}, - }, - { - "key": "gen_ai.request.temperature", - "value": {}, - }, - { - "key": "gen_ai.request.stop_sequences", - "value": {}, - }, - { - "key": "gen_ai.request.seed", - "value": {}, - }, - { - "key": "gen_ai.response.finish_reasons", - "value": {}, - }, - { - "key": "gen_ai.response.id", - "value": { - "stringValue": "resp_0dc4a3c582e21a7f00690a21f2f5a881a38c2bc17284ab0bdb", - }, - }, - { - "key": "gen_ai.input.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "user", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "what's the root square of 123902139123?", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - ], + "status": "completed", + "store": true, + "temperature": 1, + "text": { + "format": { + "type": "text", + }, + "verbosity": "medium", + }, + "tool_choice": "auto", + "tools": [ + { + "container": { + "type": "auto", }, + "type": "code_interpreter", + }, + ], + "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, + }, + "pydantic_ai_gateway": { + "cost_estimate": 0.01287, + }, + "total_tokens": 2869, }, + "user": null, +} +`; + +exports[`openai > openai responses with builtin tools > span 1`] = ` +[ { - "key": "gen_ai.output.messages", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [], - }, - }, - }, - ], + "attributes": { + "gen_ai.input.messages": [ + { + "parts": [ + { + "content": "what's the root square of 123902139123?", + "type": "text", }, - }, - { - "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_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": {}, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], + ], + "role": "user", + }, + ], + "gen_ai.operation.name": "chat", + "gen_ai.output.messages": [ + { + "parts": [], + "role": "assistant", + }, + { + "parts": [ + { + "part": { + "code": "import decimal, math, fractions, sys, statistics, random", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a21f79eb081a385bc9ea91607bb8e", + "outputs": {}, + "status": "completed", + "type": "code_interpreter_call", + }, + "type": "unknown", }, - }, - { - "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 + ], + "role": "assistant", + }, + { + "parts": [ + { + "part": { + "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", - }, - }, - { - "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", - }, - }, - { - "key": "type", - "value": { - "stringValue": "code_interpreter_call", - }, - }, - { - "key": "status", - "value": { - "stringValue": "completed", - }, - }, - { - "key": "code", - "value": { - "stringValue": "int_part, int_part**2, (int_part+1)**2", - }, - }, - { - "key": "container_id", - "value": { - "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", - }, - }, - { - "key": "outputs", - "value": {}, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [], - }, - }, - }, - ], + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a21fb50d081a3839fffe407f88965", + "outputs": {}, + "status": "completed", + "type": "code_interpreter_call", + }, + "type": "unknown", }, - }, - { - "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_0dc4a3c582e21a7f00690a2200de5881a3b0fb27e1e2317788", - }, - }, - { - "key": "type", - "value": { - "stringValue": "code_interpreter_call", - }, - }, - { - "key": "status", - "value": { - "stringValue": "completed", - }, - }, - { - "key": "code", - "value": { - "stringValue": "from decimal import Decimal, getcontext -getcontext().prec = 50 -sqrt_val = Decimal(n).sqrt() -sqrt_val", - }, - }, - { - "key": "container_id", - "value": { - "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", - }, - }, - { - "key": "outputs", - "value": {}, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], + ], + "role": "assistant", + }, + { + "parts": [], + "role": "assistant", + }, + { + "parts": [ + { + "part": { + "code": "int_part, int_part**2, (int_part+1)**2", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a21fe485c81a3bc4ba9ccc7418d6e", + "outputs": {}, + "status": "completed", + "type": "code_interpreter_call", + }, + "type": "unknown", }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [], - }, - }, - }, - ], + ], + "role": "assistant", + }, + { + "parts": [], + "role": "assistant", + }, + { + "parts": [ + { + "part": { + "code": "from decimal import Decimal, getcontext +getcontext().prec = 50 +sqrt_val = Decimal(n).sqrt() +sqrt_val", + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a2200de5881a3b0fb27e1e2317788", + "outputs": {}, + "status": "completed", + "type": "code_interpreter_call", + }, + "type": "unknown", }, - }, - { - "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) + ], + "role": "assistant", + }, + { + "parts": [], + "role": "assistant", + }, + { + "parts": [ + { + "part": { + "code": "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": [], - }, - }, - }, - ], + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a2209f9cc81a39c0e1ddfa029c7a6", + "outputs": {}, + "status": "completed", + "type": "code_interpreter_call", + }, + "type": "unknown", }, - }, - { - "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 + ], + "role": "assistant", + }, + { + "parts": [], + "role": "assistant", + }, + { + "parts": [ + { + "part": { + "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))", - }, - }, - { - "key": "container_id", - "value": { - "stringValue": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", - }, - }, - { - "key": "outputs", - "value": {}, - }, - ], - }, - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], - }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [], - }, - }, - }, - ], + "container_id": "cntr_690a21f4fa088191a854e0303fd8e69406563a23f7fe0f18", + "id": "ci_0dc4a3c582e21a7f00690a220c125481a3ad139dde1b01ef42", + "outputs": {}, + "status": "completed", + "type": "code_interpreter_call", + }, + "type": "unknown", }, - }, - { - "kvlistValue": { - "values": [ - { - "key": "role", - "value": { - "stringValue": "assistant", - }, - }, - { - "key": "parts", - "value": { - "arrayValue": { - "values": [ - { - "kvlistValue": { - "values": [ - { - "key": "type", - "value": { - "stringValue": "text", - }, - }, - { - "key": "content", - "value": { - "stringValue": "The square root of 123,902,139,123 is approximately 351,997.356698882044. It’s not a perfect square.", - }, - }, - ], - }, - }, - ], - }, - }, - }, - ], + ], + "role": "assistant", + }, + { + "parts": [], + "role": "assistant", + }, + { + "parts": [ + { + "content": "The square root of 123,902,139,123 is approximately 351,997.356698882044. It’s not a perfect square.", + "type": "text", }, - }, - ], - }, - }, - }, - { - "key": "gen_ai.system_instructions", - "value": {}, - }, - { - "key": "http.response.status_code", - "value": { - "intValue": 200, - }, - }, - { - "key": "http.request.body.text", - "value": { - "stringValue": "{ + ], + "role": "assistant", + }, + ], + "gen_ai.request.max_tokens": {}, + "gen_ai.request.model": "gpt-5", + "gen_ai.request.seed": {}, + "gen_ai.request.stop_sequences": {}, + "gen_ai.request.temperature": {}, + "gen_ai.request.top_k": {}, + "gen_ai.request.top_p": {}, + "gen_ai.response.finish_reasons": {}, + "gen_ai.response.id": "resp_0dc4a3c582e21a7f00690a21f2f5a881a38c2bc17284ab0bdb", + "gen_ai.response.model": "gpt-5-2025-08-07", + "gen_ai.system": "openai", + "gen_ai.system_instructions": {}, + "gen_ai.usage.cache_audio_read_tokens": {}, + "gen_ai.usage.cache_read_tokens": 0, + "gen_ai.usage.cache_write_tokens": {}, + "gen_ai.usage.input_audio_tokens": {}, + "gen_ai.usage.input_tokens": 1808, + "gen_ai.usage.output_audio_tokens": {}, + "gen_ai.usage.output_tokens": 1061, + "http.request.body.text": "{ "model": "gpt-5", "instructions": "be precise", "input": "what's the root square of 123902139123?", @@ -2642,53 +1514,25 @@ for p in [6, 9, 12, 15]: } ] }", + "http.response.body.text": "{"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":{}}", + "http.response.status_code": 200, + "logfire.json_schema": "{"type":"object","properties":{"gen_ai.operation.name":{"type":"string"},"gen_ai.request.model":{"type":"string"},"gen_ai.system":{"type":"string"},"gen_ai.request.max_tokens":{},"gen_ai.request.top_k":{},"gen_ai.request.top_p":{},"gen_ai.request.temperature":{},"gen_ai.request.stop_sequences":{},"gen_ai.request.seed":{},"gen_ai.response.finish_reasons":{},"gen_ai.response.id":{"type":"string"},"gen_ai.input.messages":{},"gen_ai.output.messages":{},"gen_ai.system_instructions":{},"http.response.status_code":{"type":"number"},"http.request.body.text":{"type":"string"},"http.response.body.text":{"type":"string"},"gen_ai.response.model":{"type":"string"},"gen_ai.usage.input_tokens":{"type":"number"},"gen_ai.usage.cache_read_tokens":{"type":"number"},"gen_ai.usage.cache_write_tokens":{},"gen_ai.usage.output_tokens":{"type":"number"},"gen_ai.usage.input_audio_tokens":{},"gen_ai.usage.cache_audio_read_tokens":{},"gen_ai.usage.output_audio_tokens":{}}}", + "logfire.level_num": 9, + "logfire.msg": "chat gpt-5-2025-08-07", + }, + "events": [], + "kind": 1, + "links": [], + "name": "chat gpt-5-2025-08-07", + "parentSpanId": undefined, + "resource": { + "service.name": "PAIG", + "service.version": "test", + }, + "scope": "pydantic-ai-gateway", + "status": { + "code": 1, }, }, - { - "key": "http.response.body.text", - "value": { - "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":{}}", - }, - }, - { - "key": "gen_ai.response.model", - "value": { - "stringValue": "gpt-5-2025-08-07", - }, - }, - { - "key": "gen_ai.usage.input_tokens", - "value": { - "intValue": 1808, - }, - }, - { - "key": "gen_ai.usage.cache_read_tokens", - "value": { - "intValue": 0, - }, - }, - { - "key": "gen_ai.usage.cache_write_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_tokens", - "value": { - "intValue": 1061, - }, - }, - { - "key": "gen_ai.usage.input_audio_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.cache_audio_read_tokens", - "value": {}, - }, - { - "key": "gen_ai.usage.output_audio_tokens", - "value": {}, - }, ] `;