From 6b9838d0d4aa91aeb2849fbca60b8e24958adf1e Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 22 Sep 2026 11:24:29 -0400 Subject: [PATCH 1/2] fix(agno): preserve session ids across resumed runs When an Agno run pauses for a client-side tool, resuming it bypasses the instrumented run methods. Subsequent model calls become unrelated trace roots. Instrument `continue_run` and `acontinue_run` on Agent and Team, and preserve Agno's `session_id` in task span metadata, including generated IDs and per-call overrides. Separate requests can produce separate traces with a shared session ID. Before: initial trace: task -> llm -> pause resume trace: llm After: initial trace: task [session_id=S] -> llm -> pause resume trace: task [session_id=S] -> llm Resumes retain the caller's parent span when one exists. Without an ambient parent, the shared `metadata.session_id` correlates the separate traces. Refs #764 --- ...sume_session_id[response-async-agent].yaml | 264 +++++++++++++++++ ...esume_session_id[response-sync-agent].yaml | 264 +++++++++++++++++ ...resume_session_id[stream-async-agent].yaml | 272 ++++++++++++++++++ ..._resume_session_id[stream-sync-agent].yaml | 272 ++++++++++++++++++ ...sume_session_id[response-async-agent].yaml | 254 ++++++++++++++++ ...esume_session_id[response-sync-agent].yaml | 262 +++++++++++++++++ ...resume_session_id[stream-async-agent].yaml | 262 +++++++++++++++++ ..._resume_session_id[stream-sync-agent].yaml | 270 +++++++++++++++++ ...sume_session_id[response-async-agent].yaml | 214 ++++++++++++++ ...esume_session_id[response-sync-agent].yaml | 214 ++++++++++++++ ...resume_session_id[response-sync-team].yaml | 214 ++++++++++++++ ...resume_session_id[stream-async-agent].yaml | 222 ++++++++++++++ ..._resume_session_id[stream-async-team].yaml | 222 ++++++++++++++ ..._resume_session_id[stream-sync-agent].yaml | 222 ++++++++++++++ .../braintrust/integrations/agno/patchers.py | 36 +++ .../braintrust/integrations/agno/test_agno.py | 89 ++++++ .../braintrust/integrations/agno/tracing.py | 136 +++++++-- .../auto_test_scripts/test_auto_agno.py | 5 + 18 files changed, 3665 insertions(+), 29 deletions(-) create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-async-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-sync-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-async-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-sync-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-async-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-sync-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-async-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-sync-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-async-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-team].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-agent].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-team].yaml create mode 100644 py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-sync-agent].yaml diff --git a/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-async-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-async-agent].yaml new file mode 100644 index 00000000..9f3e5dcb --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-async-agent].yaml @@ -0,0 +1,264 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '485' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOUXqHa7o3aHZDlHL54DDFynpkB\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089702,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_2ih1AqnWUl55CU7INsTarQrx\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 70,\n \"completion_tokens\": + 14,\n \"total_tokens\": 84,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_23703e8e1f\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f2397f2effc440-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:23 GMT + openai-processing-ms: + - '805' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999965' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_d058bb69246141a0876f8ed2167f2fad + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_2ih1AqnWUl55CU7INsTarQrx","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_2ih1AqnWUl55CU7INsTarQrx"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '774' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOW4eKnV5HS2wvAslMmH1pQ1kDr\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089704,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"The weather in Paris is 72\xB0F and + sunny.\",\n \"refusal\": null,\n \"annotations\": []\n },\n + \ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n + \ \"usage\": {\n \"prompt_tokens\": 103,\n \"completion_tokens\": 12,\n + \ \"total_tokens\": 115,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_23703e8e1f\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f23989787b4207-EWR + connection: + - keep-alive + content-length: + - '849' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:25 GMT + openai-processing-ms: + - '1204' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999955' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_b8f33bf761784434a2b8a5fa1443b048 + status: + code: 200 + message: OK +- request: + body: '{"session_id":"f112aaca-a7f0-4054-9577-aa2a696aa94e","run_id":"97bef072-61f1-4fe1-9a7e-6c10d02b585e","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.1.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '458' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.1.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 97bef072-61f1-4fe1-9a7e-6c10d02b585e","status":"success"}' + headers: + connection: + - keep-alive + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:25 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-sync-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-sync-agent].yaml new file mode 100644 index 00000000..8f53b1e4 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[response-sync-agent].yaml @@ -0,0 +1,264 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '485' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOSr2aOfDpgbf0z9n6fJCD00BXO\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089700,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_5SdYvmuWwYKKIwloPCWi3Drm\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 70,\n \"completion_tokens\": + 14,\n \"total_tokens\": 84,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_23703e8e1f\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f23971fbabf812-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:20 GMT + openai-processing-ms: + - '425' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999967' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_32a988fb311a496498d6b220a1d4b610 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_5SdYvmuWwYKKIwloPCWi3Drm","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_5SdYvmuWwYKKIwloPCWi3Drm"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '774' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOTEk49lBF2K3am68az6yJRIpA7\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089701,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"The weather in Paris is 72\xB0F and + sunny.\",\n \"refusal\": null,\n \"annotations\": []\n },\n + \ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n + \ \"usage\": {\n \"prompt_tokens\": 103,\n \"completion_tokens\": 12,\n + \ \"total_tokens\": 115,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_23703e8e1f\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f23977291ffd86-EWR + connection: + - keep-alive + content-length: + - '849' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:21 GMT + openai-processing-ms: + - '607' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999952' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_9d26db40e4d84162a7c2eb2054845369 + status: + code: 200 + message: OK +- request: + body: '{"session_id":"8d9de3eb-b1d1-4b00-8122-67b65e046612","run_id":"30882b22-fe86-47b8-8c01-eb97633c8bff","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.1.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '458' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.1.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 30882b22-fe86-47b8-8c01-eb97633c8bff","status":"success"}' + headers: + connection: + - keep-alive + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:22 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-async-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-async-agent].yaml new file mode 100644 index 00000000..873ceb63 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-async-agent].yaml @@ -0,0 +1,272 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '485' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOapMfDedqRBap2ez12zJav31DT\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089708,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_UYs27m5J1ZC4pMUx8GpVOyEs\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 70,\n \"completion_tokens\": + 14,\n \"total_tokens\": 84,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_23703e8e1f\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239a0987e99cb-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:28 GMT + openai-processing-ms: + - '945' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999965' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_7de42a550a28472aa7e7c5a2ab3c026b + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_UYs27m5J1ZC4pMUx8GpVOyEs","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_UYs27m5J1ZC4pMUx8GpVOyEs"}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '828' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "data: {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"0imErKlJd\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"EeVMlkDk\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"HeY\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"5GOrUhvv\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + Paris\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"EK4u9\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"0IP5RfyD\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"xISgf8ero7\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"xQb8JkzgS\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"c1ZWHitqH\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"GtZFV30\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"oqNGP\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"j8fQ5wOTij\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"OCag6\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOb7H8jxNeeBFPvYkhssMZpNbhy\",\"object\":\"chat.completion.chunk\",\"created\":1790089709,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[],\"usage\":{\"prompt_tokens\":103,\"completion_tokens\":12,\"total_tokens\":115,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"1UGgKDD6\"}\n\ndata: + [DONE]\n\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239a9793ee80f-EWR + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 22 Sep 2026 15:08:29 GMT + openai-processing-ms: + - '301' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999955' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_b6868eea6fb044d381b7b7d404d02dc3 + status: + code: 200 + message: OK +- request: + body: '{"session_id":"5f7ea7f3-5b3a-4157-9952-1accc8a20600","run_id":"8f5d48cd-c109-4a34-8e91-6288a7d83a03","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.1.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '458' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.1.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 8f5d48cd-c109-4a34-8e91-6288a7d83a03","status":"success"}' + headers: + connection: + - keep-alive + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:29 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-sync-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-sync-agent].yaml new file mode 100644 index 00000000..121c5d60 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.1.0/test_agno_resume_session_id[stream-sync-agent].yaml @@ -0,0 +1,272 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '485' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOYa5RepNWr8Qfp75D1WjznFYV6\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089706,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_HfqpLLEvHNKgNwsclpCfqbHx\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 70,\n \"completion_tokens\": + 14,\n \"total_tokens\": 84,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_23703e8e1f\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239956fd18c9b-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:26 GMT + openai-processing-ms: + - '499' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999965' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_91e18aacaea046dea6b7097d6c4a68cb + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"\nUse get_weather + to find the weather. Report the result briefly.\n"},{"role":"user","content":"What''s + the weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_HfqpLLEvHNKgNwsclpCfqbHx","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_HfqpLLEvHNKgNwsclpCfqbHx"}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '828' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "data: {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"NTxXDdUXI\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Bgi1IJ5d\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"U1f\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"3Lc10v4G\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + Paris\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"CekwW\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"JT5H7QFS\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"ssXh0TGFRk\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"CdmPMa36M\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"mZdmwkYln\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"390njvn\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"7kJ6u\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"CXqXZxcWzD\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"fmk06\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOYnS7eWQ5X53AXfR08xijaqs3d\",\"object\":\"chat.completion.chunk\",\"created\":1790089706,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_23703e8e1f\",\"choices\":[],\"usage\":{\"prompt_tokens\":103,\"completion_tokens\":12,\"total_tokens\":115,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"lvGrhUPb\"}\n\ndata: + [DONE]\n\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f2399a3a1e4352-EWR + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 22 Sep 2026 15:08:27 GMT + openai-processing-ms: + - '300' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999955' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_dbf42dc455784643bf9afd3718cd4ddf + status: + code: 200 + message: OK +- request: + body: '{"session_id":"3e671d34-7800-4c38-be7d-e592217d238d","run_id":"dcd9afe1-5a74-41c2-8c86-7756ec2416f6","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.1.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '458' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.1.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: dcd9afe1-5a74-41c2-8c86-7756ec2416f6","status":"success"}' + headers: + connection: + - keep-alive + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:27 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-async-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-async-agent].yaml new file mode 100644 index 00000000..981a3ba1 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-async-agent].yaml @@ -0,0 +1,254 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOf5X8WNaz9dYw9L6KfbFiiE7Hb\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089713,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_x9qksL0f8muDGIgjjT6jzFr2\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239c4bdd59aa4-EWR + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:34 GMT + openai-processing-ms: + - '656' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999975' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_79cd5499f14b42d1a4577d8e3563660d + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_x9qksL0f8muDGIgjjT6jzFr2","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_x9qksL0f8muDGIgjjT6jzFr2"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '741' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOg3sWLwvDsHjka9XAbidwfYnzI\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089714,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"The weather in Paris is 72\xB0F and + sunny.\",\n \"refusal\": null,\n \"annotations\": []\n },\n + \ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n + \ \"usage\": {\n \"prompt_tokens\": 97,\n \"completion_tokens\": 12,\n + \ \"total_tokens\": 109,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239ca0fe09aa4-EWR + content-length: + - '848' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:34 GMT + openai-processing-ms: + - '568' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_4d942e49615e4951a02e512dfd8d7204 + status: + code: 200 + message: OK +- request: + body: '{"session_id":"ca072b75-28fb-44e0-a830-8fdca3cbc9ae","run_id":"502803b4-d6e4-4f40-9553-27470e6d6c8c","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_learnings":false,"has_culture":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.4.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '500' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.4.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 502803b4-d6e4-4f40-9553-27470e6d6c8c","status":"success"}' + headers: + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:35 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-sync-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-sync-agent].yaml new file mode 100644 index 00000000..119a7463 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[response-sync-agent].yaml @@ -0,0 +1,262 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOdjcGiBz1JHnSrphxocj6KCuDp\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089711,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_BeDryYZ3sMDuoNdsGHtYtiCq\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239b698cd41fb-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:32 GMT + openai-processing-ms: + - '801' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999972' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_3059d1ae071c4b6c80ba707024bdf38d + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_BeDryYZ3sMDuoNdsGHtYtiCq","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_BeDryYZ3sMDuoNdsGHtYtiCq"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '741' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOe1j9pgjGyLqiwGmV8b8fhLbct\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089712,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"The weather in Paris is 72\xB0F and + sunny.\",\n \"refusal\": null,\n \"annotations\": []\n },\n + \ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n + \ \"usage\": {\n \"prompt_tokens\": 97,\n \"completion_tokens\": 12,\n + \ \"total_tokens\": 109,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239bcff9c41fb-EWR + connection: + - keep-alive + content-length: + - '848' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:32 GMT + openai-processing-ms: + - '763' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_8047bae5984346bc915248059d62db73 + status: + code: 200 + message: OK +- request: + body: '{"session_id":"3de7b0e6-19fa-4311-860b-b830dad68c07","run_id":"67a9c617-33d0-4aae-a5bb-15168f402d80","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_learnings":false,"has_culture":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.4.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '500' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.4.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 67a9c617-33d0-4aae-a5bb-15168f402d80","status":"success"}' + headers: + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:33 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-async-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-async-agent].yaml new file mode 100644 index 00000000..82d1f8d2 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-async-agent].yaml @@ -0,0 +1,262 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '1' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOjKtkcaVkPJPutMk4oIuXjO4Ry\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089717,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_Rd0vefAgssKJOTQI7wZzkCGt\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239dd1cd321eb-EWR + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:38 GMT + openai-processing-ms: + - '564' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999975' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_a95c2e9157654180a7795497c5117076 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_Rd0vefAgssKJOTQI7wZzkCGt","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_Rd0vefAgssKJOTQI7wZzkCGt"}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '795' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "data: {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"o4ghBkp4r\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"5m17dRVY\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"sjh\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"IaI04eDJ\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + Paris\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"yO7qi\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"vdXDnA5w\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"YxsVHNOdSA\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"OesHzHCbQ\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Db5Zus8jx\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"rN9vxPV\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"ZlySu\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"KIIf59Uys9\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"T3z8S\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOk0raNNkV9I4jQotIDbnYoF5CX\",\"object\":\"chat.completion.chunk\",\"created\":1790089718,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[],\"usage\":{\"prompt_tokens\":97,\"completion_tokens\":12,\"total_tokens\":109,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"AvccccPTr\"}\n\ndata: + [DONE]\n\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239e2095721eb-EWR + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 22 Sep 2026 15:08:38 GMT + openai-processing-ms: + - '291' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_611b07b502b0442482e38e7e1729c158 + status: + code: 200 + message: OK +- request: + body: '{"session_id":"495bda98-ef2c-4a39-9503-3eb99d346dd9","run_id":"eee202ba-f40b-4abf-9aca-797f9e3c92ae","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_learnings":false,"has_culture":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.4.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '500' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.4.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: eee202ba-f40b-4abf-9aca-797f9e3c92ae","status":"success"}' + headers: + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:38 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-sync-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-sync-agent].yaml new file mode 100644 index 00000000..6a2c27ab --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/2.4.0/test_agno_resume_session_id[stream-sync-agent].yaml @@ -0,0 +1,270 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwOhNzaTU9AW5B5xPlOl8irSqZ5l\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089715,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_MrcFmnVwIyC6lT88H9LwK8P0\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239cfccd941fb-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:08:35 GMT + openai-processing-ms: + - '421' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999972' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_e19e3f01f149479aa8b898aa21b08713 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_MrcFmnVwIyC6lT88H9LwK8P0","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_MrcFmnVwIyC6lT88H9LwK8P0"}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '795' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "data: {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"ownj98nSw\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"A34TbYZJ\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"NOR\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"ucJBn7hV\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + Paris\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"UT9t6\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"ZMqUqQC7\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"wzXh6jn2Sl\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Osz2uybGF\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Qd8OeyqLL\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"AgcvEjY\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"3Fppw\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"xV6uQULWuM\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"59iOp\"}\n\ndata: + {\"id\":\"chatcmpl-EQwOhFgGM5eGUYLjoXnFtmp3jGDFE\",\"object\":\"chat.completion.chunk\",\"created\":1790089715,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[],\"usage\":{\"prompt_tokens\":97,\"completion_tokens\":12,\"total_tokens\":109,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"T5xb4SLCe\"}\n\ndata: + [DONE]\n\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f239d3084541fb-EWR + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 22 Sep 2026 15:08:36 GMT + openai-processing-ms: + - '326' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_7236f1b4a2cd4c6c8f803f05dfa84efa + status: + code: 200 + message: OK +- request: + body: '{"session_id":"a30dcbdc-42e8-45aa-814e-2ec2a214fe94","run_id":"5d089418-5f5d-4de1-93cb-0fa0129754b2","data":{"agent_id":"weather-assistant","db_type":null,"model_provider":"OpenAI","model_name":"OpenAIChat","model_id":"gpt-4o-mini","parser_model":null,"output_model":null,"has_tools":true,"has_memory":false,"has_learnings":false,"has_culture":false,"has_reasoning":false,"has_knowledge":false,"has_input_schema":false,"has_output_schema":false,"has_team":false},"sdk_version":"2.4.0","type":"agent"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '500' + content-type: + - application/json + host: + - os-api.agno.com + user-agent: + - agno/2.4.0 + method: POST + uri: https://os-api.agno.com/telemetry/runs + response: + body: + string: '{"message":"Run creation acknowledged: 5d089418-5f5d-4de1-93cb-0fa0129754b2","status":"success"}' + headers: + content-length: + - '96' + content-type: + - application/json + cross-origin-opener-policy: + - same-origin + cross-origin-resource-policy: + - same-origin + date: + - Tue, 22 Sep 2026 15:08:36 GMT + permissions-policy: + - camera=(), microphone=(), geolocation=() + referrer-policy: + - strict-origin-when-cross-origin + server: + - uvicorn + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-frame-options: + - DENY + status: + code: 201 + message: Created +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-async-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-async-agent].yaml new file mode 100644 index 00000000..b62b1cd1 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-async-agent].yaml @@ -0,0 +1,214 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNplWOiQR3B08sDlLGrNR0O8daC\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089661,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_u5De7ujiXAqOgd9h72TVsPbK\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f2387fdb77429b-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:42 GMT + openai-processing-ms: + - '476' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999972' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_bbfb7cb301b44bd4a7871f74dc1bc645 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_u5De7ujiXAqOgd9h72TVsPbK","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_u5De7ujiXAqOgd9h72TVsPbK"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '741' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNqENbcL4XMovu7GdDHwe2UIBS5\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089662,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"The weather in Paris is 72\xB0F and + sunny.\",\n \"refusal\": null,\n \"annotations\": []\n },\n + \ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n + \ \"usage\": {\n \"prompt_tokens\": 97,\n \"completion_tokens\": 12,\n + \ \"total_tokens\": 109,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f23884780d429b-EWR + connection: + - keep-alive + content-length: + - '848' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:42 GMT + openai-processing-ms: + - '397' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_8d93c31bcc1c43d59bdb182bb5b9699a + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-agent].yaml new file mode 100644 index 00000000..26f65607 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-agent].yaml @@ -0,0 +1,214 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNmHOKyOzKxMLCGzHZuZlQ7UXrO\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089658,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_KfNPib24eICPEgw6NIs7Y3gV\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f2386abf75b9c6-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:38 GMT + openai-processing-ms: + - '585' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999975' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_0f608e5b5a7042aa9568bb206e2e4a8a + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_KfNPib24eICPEgw6NIs7Y3gV","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_KfNPib24eICPEgw6NIs7Y3gV"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '741' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNn0q1ab4yYZ9frwOpcB7oHgthc\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089659,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"The weather in Paris is 72\xB0F and + sunny.\",\n \"refusal\": null,\n \"annotations\": []\n },\n + \ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n + \ \"usage\": {\n \"prompt_tokens\": 97,\n \"completion_tokens\": 12,\n + \ \"total_tokens\": 109,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f2386f8df7b9c6-EWR + connection: + - keep-alive + content-length: + - '848' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:39 GMT + openai-processing-ms: + - '460' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_1679cca545814866a39298eb14ebfb42 + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-team].yaml b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-team].yaml new file mode 100644 index 00000000..5248c2a5 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[response-sync-team].yaml @@ -0,0 +1,214 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNnJcY3ryjDVI1un46uBnjG0UmD\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089659,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_J3XOyctsI3Q657X3nNZYVAfZ\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f238743bfffef2-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:40 GMT + openai-processing-ms: + - '406' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999975' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_dd98c25c51d8411a9bfea13c9dcaaa9d + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_J3XOyctsI3Q657X3nNZYVAfZ","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_J3XOyctsI3Q657X3nNZYVAfZ"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '741' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNo0yP5CLAZXJJiFY2YxWqMX669\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089660,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": \"The weather in Paris is 72\xB0F and + sunny.\",\n \"refusal\": null,\n \"annotations\": []\n },\n + \ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n + \ \"usage\": {\n \"prompt_tokens\": 97,\n \"completion_tokens\": 12,\n + \ \"total_tokens\": 109,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f23878988afef2-EWR + connection: + - keep-alive + content-length: + - '848' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:41 GMT + openai-processing-ms: + - '711' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_90141f6fd0094f34a898c90be6898b50 + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-agent].yaml new file mode 100644 index 00000000..5812d130 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-agent].yaml @@ -0,0 +1,222 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNx0lbDBGVBMNgICJWemCgQhbkc\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089669,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_NPUEvcU7dNKTSOYlpC214O2M\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f238ad596d33d5-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:49 GMT + openai-processing-ms: + - '676' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999975' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_dedff75b057044da80366d8b6d141c7f + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_NPUEvcU7dNKTSOYlpC214O2M","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_NPUEvcU7dNKTSOYlpC214O2M"}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '795' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "data: {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"OnKpzHoWN\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"mrTZgSGM\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"fBd\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"UjtQWizR\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + Paris\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"GVsLk\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"hWBMriW8\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"10066EbwBy\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"jrggMOjR3\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"4Euwta0l3\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"t2WlAx8\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"EoRVy\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"oYV8r11hp9\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"OTzJ9\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNxRgO30dctHLmqpaA4myOSZLj3\",\"object\":\"chat.completion.chunk\",\"created\":1790089669,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[],\"usage\":{\"prompt_tokens\":97,\"completion_tokens\":12,\"total_tokens\":109,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"4vJ4J3Xj2\"}\n\ndata: + [DONE]\n\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f238b25c5433d5-EWR + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 22 Sep 2026 15:07:50 GMT + openai-processing-ms: + - '476' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_fa6e647a922b48cb8ed36a847452deb9 + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-team].yaml b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-team].yaml new file mode 100644 index 00000000..acd33a37 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-async-team].yaml @@ -0,0 +1,222 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNycVojyV7quFl5dsiaFyMZocSf\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089670,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_cZ74oYpJTM2KSoWWVrlsxUuk\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f238b78e7bb12a-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:50 GMT + openai-processing-ms: + - '427' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999975' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_d9da24ac0e7d48158d9b91ecd7199672 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_cZ74oYpJTM2KSoWWVrlsxUuk","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_cZ74oYpJTM2KSoWWVrlsxUuk"}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '795' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - AsyncOpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "data: {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"swYRVZH5V\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"1JQjgjtJ\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"HIU\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"wkJQ23HM\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + Paris\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"9gMla\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"qngEhQa2\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"cy7QdisExe\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"6SZ1o5sMB\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"4x8hyf54b\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"3o3sfI8\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"6NSrc\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"atKgN9uRCH\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"zemjl\"}\n\ndata: + {\"id\":\"chatcmpl-EQwO0tPZSAgUMCJMbexscN1Rg3Fqp\",\"object\":\"chat.completion.chunk\",\"created\":1790089672,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[],\"usage\":{\"prompt_tokens\":97,\"completion_tokens\":12,\"total_tokens\":109,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"JJ3xgzXh3\"}\n\ndata: + [DONE]\n\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f238bb1b56b12a-EWR + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 22 Sep 2026 15:07:52 GMT + openai-processing-ms: + - '1362' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999962' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_c6b4ec4c9fd246ae8ce24ed855a2547c + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-sync-agent].yaml b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-sync-agent].yaml new file mode 100644 index 00000000..a55cf8a3 --- /dev/null +++ b/py/src/braintrust/integrations/agno/cassettes/latest/test_agno_resume_session_id[stream-sync-agent].yaml @@ -0,0 +1,222 @@ +interactions: +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"}],"model":"gpt-4o-mini","tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '452' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "{\n \"id\": \"chatcmpl-EQwNtvUT5y5kUG1COWEbRr5kmSczp\",\n \"object\": + \"chat.completion\",\n \"created\": 1790089665,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n + \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": + \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n + \ \"id\": \"call_KtqqPIfkXRjaZaVXmjuz1Inw\",\n \"type\": + \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n + \ \"arguments\": \"{\\\"city\\\":\\\"Paris\\\"}\"\n }\n + \ }\n ],\n \"refusal\": null,\n \"annotations\": + []\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 64,\n \"completion_tokens\": + 14,\n \"total_tokens\": 78,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": + {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": + 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": + \"default\",\n \"system_fingerprint\": \"fp_b494cd1199\"\n}\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f23896bd757b0b-EWR + connection: + - keep-alive + content-length: + - '1084' + content-type: + - application/json + date: + - Tue, 22 Sep 2026 15:07:45 GMT + openai-processing-ms: + - '682' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999972' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_cd26f40e9cfc4ff292c91deebf52b6f6 + status: + code: 200 + message: OK +- request: + body: '{"messages":[{"role":"developer","content":"Use get_weather to find the + weather. Report the result briefly."},{"role":"user","content":"What''s the + weather in Paris?"},{"role":"assistant","tool_calls":[{"id":"call_KtqqPIfkXRjaZaVXmjuz1Inw","function":{"arguments":"{\"city\":\"Paris\"}","name":"get_weather"},"type":"function"}],"content":""},{"role":"tool","content":"The + weather in Paris is 72F and sunny.","tool_call_id":"call_KtqqPIfkXRjaZaVXmjuz1Inw"}],"model":"gpt-4o-mini","stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","description":"Get + the current weather for a city.","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false},"external_execution":true}}]}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '795' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.31.0 + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 2.31.0 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.14.7 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: "data: {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"M5dGx6Qpy\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"p1PT5EZz\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"UUX\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"wL1ijO3k\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + Paris\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"ZLQvB\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"XUTwQVTF\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"uupArGPYM1\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Wr5jQtXHT\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"VboYs59u4\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"xKXIDwb\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"mGmkm\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"VuAvmHOlpY\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"M413K\"}\n\ndata: + {\"id\":\"chatcmpl-EQwNuMBPU0pYFuTPmhJZ8BduoNquW\",\"object\":\"chat.completion.chunk\",\"created\":1790089666,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_b494cd1199\",\"choices\":[],\"usage\":{\"prompt_tokens\":97,\"completion_tokens\":12,\"total_tokens\":109,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"zmFAwkLxm\"}\n\ndata: + [DONE]\n\n" + headers: + access-control-expose-headers: + - X-Request-ID + - CF-Ray + - CF-Ray + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + cf-ray: + - a3f2389cbbcd7b0b-EWR + connection: + - keep-alive + content-type: + - text/event-stream; charset=utf-8 + date: + - Tue, 22 Sep 2026 15:07:46 GMT + openai-processing-ms: + - '418' + openai-version: + - '2020-10-01' + server: + - cloudflare + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-content-type-options: + - nosniff + x-openai-proxy-wasm: + - v0.1 + x-ratelimit-limit-requests: + - '30000' + x-ratelimit-limit-tokens: + - '150000000' + x-ratelimit-remaining-requests: + - '29999' + x-ratelimit-remaining-tokens: + - '149999960' + x-ratelimit-reset-requests: + - 2ms + x-ratelimit-reset-tokens: + - 0s + x-request-id: + - req_d035521e3ea346e497ddf59e89fb1994 + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/agno/patchers.py b/py/src/braintrust/integrations/agno/patchers.py index a405e5c2..cd3cc289 100644 --- a/py/src/braintrust/integrations/agno/patchers.py +++ b/py/src/braintrust/integrations/agno/patchers.py @@ -21,9 +21,11 @@ _reliability_run_wrapper, ) from .tracing import ( + _agent_acontinue_run_wrapper, _agent_arun_private_wrapper, _agent_arun_public_wrapper, _agent_arun_stream_wrapper, + _agent_continue_run_wrapper, _agent_run_private_wrapper, _agent_run_public_wrapper, _agent_run_stream_wrapper, @@ -37,9 +39,11 @@ _model_invoke_wrapper, _model_response_stream_wrapper, _model_response_wrapper, + _team_acontinue_run_wrapper, _team_arun_private_wrapper, _team_arun_public_wrapper, _team_arun_stream_wrapper, + _team_continue_run_wrapper, _team_run_private_wrapper, _team_run_public_wrapper, _team_run_stream_wrapper, @@ -133,11 +137,27 @@ class _AgentArunPublicPatcher(_AgnoFunctionWrapperPatcher): superseded_by = (_AgentArunPrivatePatcher, _AgentArunStreamPatcher) +class _AgentContinueRunPatcher(_AgnoFunctionWrapperPatcher): + name = "agno.agent.continue_run" + target_module = "agno.agent" + target_path = "Agent.continue_run" + wrapper = _agent_continue_run_wrapper + + +class _AgentAcontinueRunPatcher(_AgnoFunctionWrapperPatcher): + name = "agno.agent.acontinue_run" + target_module = "agno.agent" + target_path = "Agent.acontinue_run" + wrapper = _agent_acontinue_run_wrapper + + class AgentPatcher(CompositeFunctionWrapperPatcher): """Patch ``agno.agent.Agent`` for tracing.""" name = "agno.agent" sub_patchers = ( + _AgentContinueRunPatcher, + _AgentAcontinueRunPatcher, _AgentRunPrivatePatcher, _AgentRunPublicPatcher, _AgentArunPrivatePatcher, @@ -205,11 +225,27 @@ class _TeamArunPublicPatcher(_AgnoFunctionWrapperPatcher): superseded_by = (_TeamArunPrivatePatcher, _TeamArunStreamPatcher) +class _TeamContinueRunPatcher(_AgnoFunctionWrapperPatcher): + name = "agno.team.continue_run" + target_module = "agno.team" + target_path = "Team.continue_run" + wrapper = _team_continue_run_wrapper + + +class _TeamAcontinueRunPatcher(_AgnoFunctionWrapperPatcher): + name = "agno.team.acontinue_run" + target_module = "agno.team" + target_path = "Team.acontinue_run" + wrapper = _team_acontinue_run_wrapper + + class TeamPatcher(CompositeFunctionWrapperPatcher): """Patch ``agno.team.Team`` for tracing.""" name = "agno.team" sub_patchers = ( + _TeamContinueRunPatcher, + _TeamAcontinueRunPatcher, _TeamRunPrivatePatcher, _TeamRunPublicPatcher, _TeamArunPrivatePatcher, diff --git a/py/src/braintrust/integrations/agno/test_agno.py b/py/src/braintrust/integrations/agno/test_agno.py index f69a8cce..647d4478 100644 --- a/py/src/braintrust/integrations/agno/test_agno.py +++ b/py/src/braintrust/integrations/agno/test_agno.py @@ -71,6 +71,8 @@ def test_agno_simple_agent_execution(memory_logger): assert len(spans) == 2, f"Expected 2 spans, got {len(spans)}" root_span = spans[0] + assert response.session_id + assert root_span["metadata"]["session_id"] == response.session_id assert root_span["context"]["span_origin"]["instrumentation"]["name"] == "agno-auto" assert root_span["span_attributes"]["name"] == "Author Agent.run" assert root_span["span_attributes"]["type"].value == "task" @@ -118,6 +120,93 @@ def test_agno_simple_agent_execution(memory_logger): ) +@pytest.mark.vcr +@pytest.mark.asyncio +@pytest.mark.parametrize( + "component,async_mode,stream", + [ + pytest.param("agent", False, False, id="response-sync-agent"), + pytest.param("agent", True, False, id="response-async-agent"), + pytest.param("agent", False, True, id="stream-sync-agent"), + pytest.param("agent", True, True, id="stream-async-agent"), + # Team uses the same dispatch wrappers; cover both patch targets without + # repeating every return contract already exercised by Agent. + pytest.param("team", False, False, id="response-sync-team"), + pytest.param("team", True, True, id="stream-async-team"), + ], +) +async def test_agno_resume_session_id(memory_logger, component, async_mode, stream): + from agno.agent import Agent + from agno.models.openai import OpenAIChat + from agno.team import Team + from agno.tools import tool + + cls = Agent if component == "agent" else Team + if not hasattr(cls, "continue_run"): + pytest.skip("This Agno version does not support team continuation") + + @tool(external_execution=True) + def get_weather(city: str) -> str: + """Get the current weather for a city.""" + raise AssertionError("The client must execute this tool") + + instance = cls( + name="Weather Assistant", + model=OpenAIChat(id="gpt-4o-mini"), + tools=[get_weather], + instructions="Use get_weather to find the weather. Report the result briefly.", + session_id="configured-session", + **({"members": []} if component == "team" else {}), + ) + if async_mode: + paused = await instance.arun("What's the weather in Paris?", session_id="run-session") + else: + paused = instance.run("What's the weather in Paris?") + assert paused.is_paused + original = memory_logger.pop()[0] + assert paused.session_id == ("run-session" if async_mode else "configured-session") + assert original["metadata"]["session_id"] == paused.session_id + + # Older Agno uses updated_tools; newer versions use requirements. + if getattr(paused, "requirements", None): + for requirement in paused.requirements: + requirement.set_external_execution_result("The weather in Paris is 72F and sunny.") + continuation = {"requirements": paused.requirements} + else: + for execution in paused.tools: + if execution.external_execution_required: + execution.result = "The weather in Paris is 72F and sunny." + continuation = {"updated_tools": paused.tools} + + # Resume without an ambient parent, as in a separate AG-UI request. + assert logger.current_span() == logger.NOOP_SPAN + method = instance.acontinue_run if async_mode else instance.continue_run + result = method(paused, stream=stream, **continuation) + if isawaitable(result): + result = await result + if stream: + if async_mode: + chunks = [chunk async for chunk in result] + else: + chunks = list(result) + assert chunks + else: + assert result.content + assert not result.is_paused + + resumed = memory_logger.pop() + root = resumed[0] + assert root["span_attributes"]["type"].value == "task" + assert root["metadata"]["session_id"] == paused.session_id + assert root["root_span_id"] != original["root_span_id"] + assert root["span_attributes"]["name"] == f"Weather Assistant.{'a' if async_mode else ''}continue_run" + assert root["context"]["span_origin"]["instrumentation"]["name"] == "agno-auto" + llm_spans = [s for s in resumed if s["span_attributes"]["type"].value == "llm"] + assert llm_spans + assert all(s["span_parents"] == [root["span_id"]] for s in llm_spans) + assert logger.current_span() == logger.NOOP_SPAN + + @pytest.mark.vcr def test_agno_agent_tools_metadata_placement(memory_logger): """Tool definitions must live in `metadata.tools`, not in the span input. diff --git a/py/src/braintrust/integrations/agno/tracing.py b/py/src/braintrust/integrations/agno/tracing.py index d605682e..172a3202 100644 --- a/py/src/braintrust/integrations/agno/tracing.py +++ b/py/src/braintrust/integrations/agno/tracing.py @@ -313,9 +313,17 @@ def _materialize_agno_output_media(aggregated: dict[str, Any]) -> dict[str, Any] } +def _session_metadata(source: Any) -> dict[str, str]: + """Read the session ID without serializing a run response or event.""" + session_id = source.get("session_id") if isinstance(source, dict) else getattr(source, "session_id", None) + return {"session_id": session_id} if isinstance(session_id, str) and session_id else {} + + def extract_metadata(instance: Any, component: str) -> dict[str, Any]: """Extract metadata from any component (model, agent, team).""" metadata = {"component": component} + if component in ("agent", "team"): + metadata.update(_session_metadata(instance)) if component == "model": if hasattr(instance, "id") and instance.id: @@ -633,7 +641,7 @@ def _inner(): all_chunks = [] for chunk in result: if first: - span.log(metrics={"time_to_first_token": time.time() - start}) + span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) first = False all_chunks.append(chunk) yield chunk @@ -661,7 +669,7 @@ async def _inner(): all_chunks = [] async for chunk in result: if first: - span.log(metrics={"time_to_first_token": time.time() - start}) + span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) first = False all_chunks.append(chunk) yield chunk @@ -701,10 +709,14 @@ def _agent_run_private_wrapper(wrapped: Any, instance: Any, args: Any, kwargs: A name=f"{agent_name}.run", type=SpanTypeAttribute.TASK, input=input_data, - metadata={**omit(kwargs, list(input_data.keys())), **extract_metadata(instance, "agent")}, + metadata={ + **omit(kwargs, list(input_data.keys())), + **extract_metadata(instance, "agent"), + **_session_metadata(run_response), + }, ) as span: result = wrapped(*args, **kwargs) - span.log(output=result, metrics=extract_metrics(result)) + span.log(output=result, metrics=extract_metrics(result), metadata=_session_metadata(result)) return result @@ -718,10 +730,14 @@ async def _agent_arun_private_wrapper(wrapped: Any, instance: Any, args: Any, kw name=f"{agent_name}.arun", type=SpanTypeAttribute.TASK, input=input_data, - metadata={**omit(kwargs, list(input_data.keys())), **extract_metadata(instance, "agent")}, + metadata={ + **omit(kwargs, list(input_data.keys())), + **extract_metadata(instance, "agent"), + **_session_metadata(run_response), + }, ) as span: result = await wrapped(*args, **kwargs) - span.log(output=result, metrics=extract_metrics(result)) + span.log(output=result, metrics=extract_metrics(result), metadata=_session_metadata(result)) return result @@ -737,7 +753,11 @@ def _trace_stream(): name=f"{agent_name}.run_stream", type=SpanTypeAttribute.TASK, input={"run_response": run_response, "run_messages": run_messages}, - metadata={**omit(kwargs, ["run_response", "run_messages"]), **extract_metadata(instance, "agent")}, + metadata={ + **omit(kwargs, ["run_response", "run_messages"]), + **extract_metadata(instance, "agent"), + **_session_metadata(run_response), + }, ) span.set_current() should_unset = True @@ -746,7 +766,7 @@ def _trace_stream(): all_chunks = [] for chunk in wrapped(*args, **kwargs): if first: - span.log(metrics={"time_to_first_token": time.time() - start}) + span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) first = False all_chunks.append(chunk) yield chunk @@ -778,7 +798,11 @@ async def _trace_stream(): name=f"{agent_name}.arun_stream", type=SpanTypeAttribute.TASK, input={"run_response": run_response, "input": input}, - metadata={**omit(kwargs, ["run_response", "input"]), **extract_metadata(instance, "agent")}, + metadata={ + **omit(kwargs, ["run_response", "input"]), + **extract_metadata(instance, "agent"), + **_session_metadata(run_response), + }, ) span.set_current() should_unset = True @@ -787,7 +811,7 @@ async def _trace_stream(): all_chunks = [] async for chunk in wrapped(*args, **kwargs): if first: - span.log(metrics={"time_to_first_token": time.time() - start}) + span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) first = False all_chunks.append(chunk) yield chunk @@ -817,10 +841,14 @@ def _team_run_private_wrapper(wrapped: Any, instance: Any, args: Any, kwargs: An name=f"{team_name}.run", type=SpanTypeAttribute.TASK, input=input_data, - metadata={**omit(kwargs, list(input_data.keys())), **extract_metadata(instance, "team")}, + metadata={ + **omit(kwargs, list(input_data.keys())), + **extract_metadata(instance, "team"), + **_session_metadata(run_response), + }, ) as span: result = wrapped(*args, **kwargs) - span.log(output=result, metrics=extract_metrics(result)) + span.log(output=result, metrics=extract_metrics(result), metadata=_session_metadata(result)) return result @@ -834,10 +862,14 @@ async def _team_arun_private_wrapper(wrapped: Any, instance: Any, args: Any, kwa name=f"{team_name}.arun", type=SpanTypeAttribute.TASK, input=input_data, - metadata={**omit(kwargs, list(input_data.keys())), **extract_metadata(instance, "team")}, + metadata={ + **omit(kwargs, list(input_data.keys())), + **extract_metadata(instance, "team"), + **_session_metadata(run_response), + }, ) as span: result = await wrapped(*args, **kwargs) - span.log(output=result, metrics=extract_metrics(result)) + span.log(output=result, metrics=extract_metrics(result), metadata=_session_metadata(result)) return result @@ -853,7 +885,11 @@ def _trace_stream(): name=f"{team_name}.run_stream", type=SpanTypeAttribute.TASK, input={"run_response": run_response, "run_messages": run_messages}, - metadata={**omit(kwargs, ["run_response", "run_messages"]), **extract_metadata(instance, "team")}, + metadata={ + **omit(kwargs, ["run_response", "run_messages"]), + **extract_metadata(instance, "team"), + **_session_metadata(run_response), + }, ) span.set_current() should_unset = True @@ -862,7 +898,7 @@ def _trace_stream(): all_chunks = [] for chunk in wrapped(*args, **kwargs): if first: - span.log(metrics={"time_to_first_token": time.time() - start}) + span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) first = False all_chunks.append(chunk) yield chunk @@ -894,7 +930,11 @@ async def _trace_stream(): name=f"{team_name}.arun_stream", type=SpanTypeAttribute.TASK, input={"run_response": run_response, "input": input}, - metadata={**omit(kwargs, ["run_response", "input"]), **extract_metadata(instance, "team")}, + metadata={ + **omit(kwargs, ["run_response", "input"]), + **extract_metadata(instance, "team"), + **_session_metadata(run_response), + }, ) span.set_current() should_unset = True @@ -903,7 +943,7 @@ async def _trace_stream(): all_chunks = [] async for chunk in wrapped(*args, **kwargs): if first: - span.log(metrics={"time_to_first_token": time.time() - start}) + span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) first = False all_chunks.append(chunk) yield chunk @@ -936,15 +976,22 @@ def _run_public_dispatch_wrapper( *, default_name: str, metadata_component: str, + operation: str = "run", ) -> Any: """Trace a public synchronous `run(...)` dispatch method.""" component_name = getattr(instance, "name", None) or default_name - input_arg = args[0] if len(args) > 0 else kwargs.get("input") - input_data = {"input": input_arg} - metadata = {**omit(kwargs, ["input"]), **extract_metadata(instance, metadata_component)} + input_key = "run_response" if operation == "continue_run" else "input" + input_arg = args[0] if len(args) > 0 else kwargs.get(input_key) + input_data = {input_key: input_arg} + metadata = { + **omit(kwargs, [input_key]), + **extract_metadata(instance, metadata_component), + **(_session_metadata(input_arg) if input_key == "run_response" else {}), + **_session_metadata(kwargs), + } span = start_span( - name=f"{component_name}.run", + name=f"{component_name}.{operation}", type=SpanTypeAttribute.TASK, input=input_data, metadata=metadata, @@ -955,7 +1002,7 @@ def _run_public_dispatch_wrapper( result = wrapped(*args, **kwargs) if is_sync_iterator(result): return _trace_sync_stream(result, span, start) - span.log(output=result, metrics=extract_metrics(result)) + span.log(output=result, metrics=extract_metrics(result), metadata=_session_metadata(result)) span.unset_current() span.end() return result @@ -974,15 +1021,22 @@ def _arun_public_dispatch_wrapper( *, default_name: str, metadata_component: str, + operation: str = "arun", ) -> Any: """Trace a public `arun(...)` dispatch method across async return contracts.""" component_name = getattr(instance, "name", None) or default_name - input_arg = args[0] if len(args) > 0 else kwargs.get("input") - input_data = {"input": input_arg} - metadata = {**omit(kwargs, ["input"]), **extract_metadata(instance, metadata_component)} + input_key = "run_response" if operation == "acontinue_run" else "input" + input_arg = args[0] if len(args) > 0 else kwargs.get(input_key) + input_data = {input_key: input_arg} + metadata = { + **omit(kwargs, [input_key]), + **extract_metadata(instance, metadata_component), + **(_session_metadata(input_arg) if input_key == "run_response" else {}), + **_session_metadata(kwargs), + } span = start_span( - name=f"{component_name}.arun", + name=f"{component_name}.{operation}", type=SpanTypeAttribute.TASK, input=input_data, metadata=metadata, @@ -1001,7 +1055,7 @@ async def _trace_awaitable(): if is_async_iterator(awaited): should_end_span = False return _trace_async_stream(awaited, span, start) - span.log(output=awaited, metrics=extract_metrics(awaited)) + span.log(output=awaited, metrics=extract_metrics(awaited), metadata=_session_metadata(awaited)) return awaited except Exception as e: span.log(error=e) @@ -1016,7 +1070,7 @@ async def _trace_awaitable(): if is_async_iterator(result): return _trace_async_stream(result, span, start) - span.log(output=result, metrics=extract_metrics(result)) + span.log(output=result, metrics=extract_metrics(result), metadata=_session_metadata(result)) span.unset_current() span.end() return result @@ -1027,6 +1081,30 @@ async def _trace_awaitable(): raise +def _agent_continue_run_wrapper(wrapped: Any, instance: Any, args: Any, kwargs: Any): + return _run_public_dispatch_wrapper( + wrapped, instance, args, kwargs, default_name="Agent", metadata_component="agent", operation="continue_run" + ) + + +def _agent_acontinue_run_wrapper(wrapped: Any, instance: Any, args: Any, kwargs: Any): + return _arun_public_dispatch_wrapper( + wrapped, instance, args, kwargs, default_name="Agent", metadata_component="agent", operation="acontinue_run" + ) + + +def _team_continue_run_wrapper(wrapped: Any, instance: Any, args: Any, kwargs: Any): + return _run_public_dispatch_wrapper( + wrapped, instance, args, kwargs, default_name="Team", metadata_component="team", operation="continue_run" + ) + + +def _team_acontinue_run_wrapper(wrapped: Any, instance: Any, args: Any, kwargs: Any): + return _arun_public_dispatch_wrapper( + wrapped, instance, args, kwargs, default_name="Team", metadata_component="team", operation="acontinue_run" + ) + + def _agent_run_public_wrapper(wrapped: Any, instance: Any, args: Any, kwargs: Any): return _run_public_dispatch_wrapper( wrapped, instance, args, kwargs, default_name="Agent", metadata_component="agent" diff --git a/py/src/braintrust/integrations/auto_test_scripts/test_auto_agno.py b/py/src/braintrust/integrations/auto_test_scripts/test_auto_agno.py index 7cdfa01a..c085a731 100644 --- a/py/src/braintrust/integrations/auto_test_scripts/test_auto_agno.py +++ b/py/src/braintrust/integrations/auto_test_scripts/test_auto_agno.py @@ -39,12 +39,17 @@ def check_wrapped(klass, private_method, public_method, required=True): check_wrapped(Agent, "_arun", "arun", required=True) check_wrapped(Agent, "_run_stream", None, required=False) # Optional - only in 2.4.0 check_wrapped(Agent, "_arun_stream", None, required=False) # Optional - only in 2.4.0 +check_wrapped(Agent, None, "continue_run") +check_wrapped(Agent, None, "acontinue_run") # Team methods check_wrapped(Team, "_run", "run", required=True) check_wrapped(Team, "_arun", "arun", required=True) check_wrapped(Team, "_run_stream", None, required=False) check_wrapped(Team, "_arun_stream", None, required=False) +for method in ("continue_run", "acontinue_run"): + if hasattr(Team, method): + check_wrapped(Team, None, method) # Model methods (all public, all required) assert hasattr(Model, "invoke") and hasattr(Model.invoke, "__wrapped__"), "Model.invoke should be wrapped" From 7230e4b039d4d59e611d465ea5c0a78c09cecfa7 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 22 Sep 2026 12:51:09 -0400 Subject: [PATCH 2/2] fix(agno): finalize streams without leaking span context Agno continuation streams left their task span current between iterations. Closing or abandoning a stream could leak context, and dropping an unstarted iterator never finalized its span. Before: caller -> create stream -> continuation stays current -> close/drop -> incomplete cleanup After: caller -> create stream -> caller context restored -> next/await -> isolated stream context -> caller context restored -> close/drop -> log partial output and end span once Keep suspended provider spans in the stream's context across sync and async iteration. Finalize on exhaustion, errors, cancellation, explicit close, or GC, including streams that were never consumed. Async GC finalizes trace state without scheduling provider cleanup on the event loop. Refs #764 --- .pre-commit-config.yaml | 2 +- .../braintrust/integrations/agno/test_agno.py | 143 +++++++++++-- .../braintrust/integrations/agno/tracing.py | 195 ++++++++++++++---- 3 files changed, 281 insertions(+), 59 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7260ebf9..628256bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,4 +47,4 @@ repos: )$ args: - "-L" - - "rouge,coo,couldn,unsecure,ontext,afterall,als" + - "rouge,coo,couldn,unsecure,ontext,afterall,als,asend" diff --git a/py/src/braintrust/integrations/agno/test_agno.py b/py/src/braintrust/integrations/agno/test_agno.py index 647d4478..34160031 100644 --- a/py/src/braintrust/integrations/agno/test_agno.py +++ b/py/src/braintrust/integrations/agno/test_agno.py @@ -4,6 +4,10 @@ # pyright: reportUnknownParameterType=false # pyright: reportUnknownVariableType=false # pyright: reportUnknownArgumentType=false +import asyncio +import gc +import time + import pytest from braintrust import logger from braintrust.integrations.agno import setup_agno @@ -15,6 +19,7 @@ from ._test_agno_helpers import ( PROJECT_NAME, + FakeEvent, StrictSpan, isawaitable, make_fake_async_dispatch_component, @@ -120,22 +125,7 @@ def test_agno_simple_agent_execution(memory_logger): ) -@pytest.mark.vcr -@pytest.mark.asyncio -@pytest.mark.parametrize( - "component,async_mode,stream", - [ - pytest.param("agent", False, False, id="response-sync-agent"), - pytest.param("agent", True, False, id="response-async-agent"), - pytest.param("agent", False, True, id="stream-sync-agent"), - pytest.param("agent", True, True, id="stream-async-agent"), - # Team uses the same dispatch wrappers; cover both patch targets without - # repeating every return contract already exercised by Agent. - pytest.param("team", False, False, id="response-sync-team"), - pytest.param("team", True, True, id="stream-async-team"), - ], -) -async def test_agno_resume_session_id(memory_logger, component, async_mode, stream): +async def _pause_agno_run(memory_logger, component, async_mode): from agno.agent import Agent from agno.models.openai import OpenAIChat from agno.team import Team @@ -178,6 +168,27 @@ def get_weather(city: str) -> str: execution.result = "The weather in Paris is 72F and sunny." continuation = {"updated_tools": paused.tools} + return instance, paused, continuation, original + + +@pytest.mark.vcr +@pytest.mark.asyncio +@pytest.mark.parametrize( + "component,async_mode,stream", + [ + pytest.param("agent", False, False, id="response-sync-agent"), + pytest.param("agent", True, False, id="response-async-agent"), + pytest.param("agent", False, True, id="stream-sync-agent"), + pytest.param("agent", True, True, id="stream-async-agent"), + # Team uses the same dispatch wrappers; cover both patch targets without + # repeating every return contract already exercised by Agent. + pytest.param("team", False, False, id="response-sync-team"), + pytest.param("team", True, True, id="stream-async-team"), + ], +) +async def test_agno_resume_session_id(memory_logger, component, async_mode, stream): + instance, paused, continuation, original = await _pause_agno_run(memory_logger, component, async_mode) + # Resume without an ambient parent, as in a separate AG-UI request. assert logger.current_span() == logger.NOOP_SPAN method = instance.acontinue_run if async_mode else instance.continue_run @@ -207,6 +218,106 @@ def get_weather(city: str) -> str: assert logger.current_span() == logger.NOOP_SPAN +@pytest.mark.vcr +@pytest.mark.asyncio +@pytest.mark.parametrize( + "async_mode,vcr_cassette_name", + [ + (False, "test_agno_resume_session_id[stream-sync-agent]"), + (True, "test_agno_resume_session_id[stream-async-agent]"), + ], + ids=["sync", "async"], +) +@pytest.mark.parametrize("consume", [False, True], ids=["unstarted", "partial"]) +@pytest.mark.parametrize("cleanup", ["close", "gc"]) +async def test_agno_resume_stream_cleanup(memory_logger, async_mode, vcr_cassette_name, consume, cleanup): + instance, paused, continuation, _ = await _pause_agno_run(memory_logger, "agent", async_mode) + with start_span(name="caller") as parent: + method = instance.acontinue_run if async_mode else instance.continue_run + stream = method(paused, stream=True, **continuation) + if isawaitable(stream): + stream = await stream + assert logger.current_span() is parent + if consume: + # Stop after actual content, with the nested model stream suspended. + if async_mode: + async for chunk in stream: + if getattr(chunk, "content", None): + break + else: + for chunk in stream: + if getattr(chunk, "content", None): + break + assert logger.current_span() is parent + if cleanup == "close": + if async_mode: + await stream.aclose() + await stream.aclose() + else: + stream.close() + stream.close() + del stream + gc.collect() + assert logger.current_span() is parent + with start_span(name="after"): + pass + + spans = memory_logger.pop() + root = next(s for s in spans if s["span_attributes"]["name"].endswith("continue_run")) + assert root["span_parents"] == [parent.span_id] + assert root["metadata"]["session_id"] == paused.session_id + assert "end" in root["metrics"] + assert bool(root["output"].get("content")) == consume + after = next(s for s in spans if s["span_attributes"]["name"] == "after") + assert after["span_parents"] == [parent.span_id] + gc.collect() + assert not memory_logger.pop() + + +@pytest.mark.asyncio +async def test_agno_async_stream_cancellation_restores_context(memory_logger): + # Local coverage for cancellation at an actual await: cassette playback + # cannot reliably suspend the provider at a specific point. + waiting = asyncio.Event() + cleaned_up = asyncio.Event() + + async def source(): + try: + with start_span(name="nested"): + yield FakeEvent("RunContent", content="partial") + waiting.set() + await asyncio.Event().wait() + finally: + cleaned_up.set() + + with start_span(name="caller") as parent: + span = start_span(name="continuation") + span.set_current() + stream = agno_tracing_module._trace_async_stream(source(), span, time.time()) + assert (await stream.__anext__()).content == "partial" + assert logger.current_span() is parent + task = asyncio.create_task(stream.__anext__()) + await waiting.wait() + task.cancel() + with pytest.raises(asyncio.CancelledError): + await task + assert cleaned_up.is_set() + assert logger.current_span() is parent + await stream.aclose() + + spans = memory_logger.pop() + root = next(s for s in spans if s["span_attributes"]["name"] == "continuation") + nested = next(s for s in spans if s["span_attributes"]["name"] == "nested") + assert root["span_parents"] == [parent.span_id] + assert nested["span_parents"] == [root["span_id"]] + assert root["output"]["content"] == "partial" + assert "end" in root["metrics"] + assert "end" in nested["metrics"] + del stream + gc.collect() + assert not memory_logger.pop() + + @pytest.mark.vcr def test_agno_agent_tools_metadata_placement(memory_logger): """Tool definitions must live in `metadata.tools`, not in the span input. diff --git a/py/src/braintrust/integrations/agno/tracing.py b/py/src/braintrust/integrations/agno/tracing.py index 172a3202..ca9e8354 100644 --- a/py/src/braintrust/integrations/agno/tracing.py +++ b/py/src/braintrust/integrations/agno/tracing.py @@ -1,5 +1,6 @@ import contextvars import time +import weakref from contextlib import contextmanager from inspect import isawaitable from typing import Any @@ -633,60 +634,168 @@ def _aggregate_workflow_chunks(chunks: list[Any], workflow_run_response: Any | N # --------------------------------------------------------------------------- -def _trace_sync_stream(result: Any, span: Any, start: float): - def _inner(): - should_unset = True +class _ContextIterator: + """Drive an await iterator in one context, including send/throw after suspension. + + Unlike creating a task per chunk, this preserves context tokens across async + generator yields and also works on Python 3.10. + """ + + def __init__(self, iterator: Any, context: contextvars.Context): + self.iterator = iterator + self.context = context + + def __iter__(self): + return self + + def __next__(self): + return self.context.run(next, self.iterator) + + def send(self, value): + return self.context.run(self.iterator.send, value) + + def throw(self, *args): + return self.context.run(self.iterator.throw, *args) + + def close(self): + return self.context.run(self.iterator.close) + + +class _ContextAwaitable: + def __init__(self, awaitable: Any, context: contextvars.Context): + self.awaitable = awaitable + self.context = context + + def __await__(self): + return _ContextIterator(self.awaitable.__await__(), self.context) + + +class _StreamState: + """Trace state only: a GC callback must not retain the stream or its context.""" + + def __init__(self, span: Any, start: float): + self.span = span + self.start = start + self.chunks: list[Any] = [] + self.finished = False + + def record(self, chunk: Any): + if not self.chunks: + self.span.log(metrics={"time_to_first_token": time.time() - self.start}, metadata=_session_metadata(chunk)) + self.chunks.append(chunk) + return chunk + + def finish(self, error: BaseException | None = None): + if self.finished: + return + self.finished = True try: - first = True - all_chunks = [] - for chunk in result: - if first: - span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) - first = False - all_chunks.append(chunk) - yield chunk - aggregated = _aggregate_agent_chunks(all_chunks) - span.log(output=aggregated, metrics=extract_streaming_metrics(aggregated, start)) - except GeneratorExit: - should_unset = False + if error is not None: + self.span.log(error=error) + aggregated = _aggregate_agent_chunks(self.chunks) + self.span.log(output=aggregated, metrics=extract_streaming_metrics(aggregated, self.start)) + finally: + self.chunks.clear() + self.span.end() + + +class _TracedSyncStream: + def __init__(self, result: Any, span: Any, start: float): + self.result = result + self.context = contextvars.copy_context() + self.state = _StreamState(span, start) + # The captured context owns the suspended provider/child spans. Never + # leave them active in application code between iterator operations. + span.unset_current() + + def __iter__(self): + return self + + def _advance(self, method, *args): + try: + chunk = self.context.run(method, *args) + except StopIteration: + self.state.finish() raise - except Exception as e: - span.log(error=e) + except BaseException as error: + self.state.finish(error) raise + return self.state.record(chunk) + + def __next__(self): + return self._advance(next, self.result) + + def send(self, value): + return self._advance(self.result.send, value) + + def throw(self, *args): + return self._advance(self.result.throw, *args) + + def close(self): + try: + close = getattr(self.result, "close", None) + if close is not None: + return self.context.run(close) finally: - if should_unset: - span.unset_current() - span.end() + self.state.finish() - return _inner() + def __del__(self): + # Synchronous generator finalizers must also run in the stream's + # context, or nested provider spans may reset the caller's context. + try: + self.close() + except BaseException: + pass + + +class _TracedAsyncStream: + def __init__(self, result: Any, span: Any, start: float): + self.result = result + self.context = contextvars.copy_context() + self.state = _StreamState(span, start) + # Only finalize trace state on GC. Leave async transport cleanup to the + # provider/runtime; do not schedule event-loop work from a finalizer. + weakref.finalize(self, self.state.finish) + span.unset_current() + def __aiter__(self): + return self -def _trace_async_stream(result: Any, span: Any, start: float): - async def _inner(): - should_unset = True + async def _advance(self, method, *args): try: - first = True - all_chunks = [] - async for chunk in result: - if first: - span.log(metrics={"time_to_first_token": time.time() - start}, metadata=_session_metadata(chunk)) - first = False - all_chunks.append(chunk) - yield chunk - aggregated = _aggregate_agent_chunks(all_chunks) - span.log(output=aggregated, metrics=extract_streaming_metrics(aggregated, start)) - except GeneratorExit: - should_unset = False + chunk = await _ContextAwaitable(self.context.run(method, *args), self.context) + except StopAsyncIteration: + self.state.finish() raise - except Exception as e: - span.log(error=e) + except BaseException as error: + self.state.finish(error) raise + return self.state.record(chunk) + + async def __anext__(self): + return await self._advance(self.result.__anext__) + + async def asend(self, value): + return await self._advance(self.result.asend, value) + + async def athrow(self, *args): + return await self._advance(self.result.athrow, *args) + + async def aclose(self): + try: + close = getattr(self.result, "aclose", None) + if close is not None: + return await _ContextAwaitable(self.context.run(close), self.context) finally: - if should_unset: - span.unset_current() - span.end() + self.state.finish() + - return _inner() +def _trace_sync_stream(result: Any, span: Any, start: float): + return _TracedSyncStream(result, span, start) + + +def _trace_async_stream(result: Any, span: Any, start: float): + return _TracedAsyncStream(result, span, start) # =========================================================================== @@ -1047,8 +1156,10 @@ def _arun_public_dispatch_wrapper( result = wrapped(*args, **kwargs) if isawaitable(result): + span.unset_current() async def _trace_awaitable(): + span.set_current() should_end_span = True try: awaited = await result