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/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..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,
@@ -71,6 +76,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 +125,199 @@ def test_agno_simple_agent_execution(memory_logger):
)
+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
+ 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}
+
+ 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
+ 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
+@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 d605682e..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
@@ -313,9 +314,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:
@@ -625,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})
- 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})
- 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)
# ===========================================================================
@@ -701,10 +818,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 +839,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 +862,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 +875,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 +907,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 +920,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 +950,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 +971,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 +994,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 +1007,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 +1039,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 +1052,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 +1085,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 +1111,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 +1130,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,
@@ -993,15 +1156,17 @@ 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
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 +1181,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 +1192,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"