Skip to content

Commit 362512e

Browse files
committed
fix(adk): avoid logging stream cleanup as errors
Handle GeneratorExit and asyncio.CancelledError inside ADK stream tracing so sub-agent delegation closes spans normally while preserving global SpanImpl behavior and re-raising cleanup signals. Add VCR-backed delegation coverage for both supported ADK versions and focused core coverage for BaseException logging. Fixes #620
1 parent 079699a commit 362512e

5 files changed

Lines changed: 417 additions & 5 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
interactions:
2+
- request:
3+
body: '{"contents": [{"parts": [{"text": "What is the capital of France? Delegate
4+
this to the specialist."}], "role": "user"}], "systemInstruction": {"parts":
5+
[{"text": "You cannot answer questions yourself. For every request, immediately
6+
call transfer_to_agent with agent_name=''capital_specialist''.\n\nYou are an
7+
agent. Your internal name is \"coordinator\".\n\n The description about you
8+
is \"Routes geography questions to the capital specialist without answering
9+
them.\"\n\n\nYou have a list of other agents to transfer to:\n\n\nAgent name:
10+
capital_specialist\nAgent description: The only agent allowed to answer geography
11+
questions.\n\n\nIf you are the best to answer the question according to your
12+
description, you\ncan answer it.\n\nIf another agent is better for answering
13+
the question according to its\ndescription, call `transfer_to_agent` function
14+
to transfer the\nquestion to that agent. When transferring, do not generate
15+
any text other than\nthe function call.\n\n**NOTE**: the only available agents
16+
for `transfer_to_agent` function are `capital_specialist`.\n"}], "role": "user"},
17+
"tools": [{"functionDeclarations": [{"description": "Transfer the question to
18+
another agent.\n\nThis tool hands off control to another agent when it''s more
19+
suitable to\nanswer the user''s question according to the agent''s description.\n\nArgs:\n agent_name:
20+
the agent name to transfer to.\n", "name": "transfer_to_agent", "parameters":
21+
{"properties": {"agent_name": {"type": "STRING"}}, "required": ["agent_name"],
22+
"type": "OBJECT"}}]}], "generationConfig": {}}'
23+
headers:
24+
Content-Type:
25+
- application/json
26+
user-agent:
27+
- google-genai-sdk/1.75.0 gl-python/3.14.6 google-adk/1.14.1 gl-python/3.14.6
28+
x-goog-api-client:
29+
- google-genai-sdk/1.75.0 gl-python/3.14.6 google-adk/1.14.1 gl-python/3.14.6
30+
method: POST
31+
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
32+
response:
33+
body:
34+
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
35+
[\n {\n \"functionCall\": {\n \"name\": \"transfer_to_agent\",\n
36+
\ \"args\": {\n \"agent_name\": \"capital_specialist\"\n
37+
\ }\n },\n \"thoughtSignature\": \"CsgDARFNMg9EuJxGSb6Rw2/4urHga+Zn9WrSEdnTxJhJfb0a/9BQdsCh4EzVx5DHs4AYOpfVkx8GKuSmv3g3DQuQhQtaPqVURt+5PHAQ53BQO04DSUnyVOcyp4PYJVJc+eP6zdMPCyNfJiE1hSDPLdma0+0L1H5dirwmjNLVmehQe8OuE39nVqtm3BYQaZ+eyTXRIno9P1YCi4//rtrciIYqzq5GDhpwBoRB/A+wwC1pu5BSJK3vzV/OvS4vFapAwptwkd3iKpAvOh4jlwTElrN+wAsASVGuZ70Fg1nmFrrdCBAq0yW4wOJv7mY4OOrPFYAJFkEJ24on8eo8qR4bLVn5MoYTnBMWQqR4QtbK4dv9bdrKEfmV926a+lB9kOyxCahYATKEklEiq24OKGcH+zaB4hY5fXArGKOpfxYvj7fS1CIXxvFl7I5Ur3E4V2PVIhstsDyGIaue9mBHP2oLfg+CGOmLNz666gPYkrnwZna6gfkxYLDxvMVFpLnIauLWdITWcQbwOQcaTkRkfH/wMir8XKK3loisYHarOVMOqjerNk9hA+MMWki/ZIhEpX3uIqDz0ymJR4WoDHh0BP7PLWvVpHmg/zLA+rvf\"\n
38+
\ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\":
39+
\"STOP\",\n \"index\": 0,\n \"finishMessage\": \"Model generated
40+
function call(s).\"\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
41+
296,\n \"candidatesTokenCount\": 22,\n \"totalTokenCount\": 409,\n \"promptTokensDetails\":
42+
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 296\n
43+
\ }\n ],\n \"thoughtsTokenCount\": 91,\n \"serviceTier\": \"standard\"\n
44+
\ },\n \"modelVersion\": \"gemini-2.5-flash\",\n \"responseId\": \"lwFqaoiON-WrjrEPrbb88Ak\"\n}\n"
45+
headers:
46+
Alt-Svc:
47+
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
48+
Content-Type:
49+
- application/json; charset=UTF-8
50+
Date:
51+
- Wed, 29 Jul 2026 13:35:20 GMT
52+
Server:
53+
- scaffolding on HTTPServer2
54+
Server-Timing:
55+
- gfet4t7; dur=765
56+
Transfer-Encoding:
57+
- chunked
58+
Vary:
59+
- Origin
60+
- X-Origin
61+
- Referer
62+
X-Content-Type-Options:
63+
- nosniff
64+
X-Frame-Options:
65+
- SAMEORIGIN
66+
X-Gemini-Service-Tier:
67+
- standard
68+
X-XSS-Protection:
69+
- '0'
70+
content-length:
71+
- '1439'
72+
status:
73+
code: 200
74+
message: OK
75+
- request:
76+
body: '{"contents": [{"parts": [{"text": "What is the capital of France? Delegate
77+
this to the specialist."}], "role": "user"}, {"parts": [{"text": "For context:"},
78+
{"text": "[coordinator] called tool `transfer_to_agent` with parameters: {''agent_name'':
79+
''capital_specialist''}"}], "role": "user"}, {"parts": [{"text": "For context:"},
80+
{"text": "[coordinator] `transfer_to_agent` tool returned result: {''result'':
81+
None}"}], "role": "user"}], "systemInstruction": {"parts": [{"text": "Answer
82+
geography questions accurately and in one short sentence.\n\nYou are an agent.
83+
Your internal name is \"capital_specialist\".\n\n The description about you
84+
is \"The only agent allowed to answer geography questions.\"\n\n\nYou have a
85+
list of other agents to transfer to:\n\n\nAgent name: coordinator\nAgent description:
86+
Routes geography questions to the capital specialist without answering them.\n\n\nIf
87+
you are the best to answer the question according to your description, you\ncan
88+
answer it.\n\nIf another agent is better for answering the question according
89+
to its\ndescription, call `transfer_to_agent` function to transfer the\nquestion
90+
to that agent. When transferring, do not generate any text other than\nthe function
91+
call.\n\n**NOTE**: the only available agents for `transfer_to_agent` function
92+
are `coordinator`.\n\nIf neither you nor the other agents are best for the question,
93+
transfer to your parent agent coordinator.\n"}], "role": "user"}, "tools": [{"functionDeclarations":
94+
[{"description": "Transfer the question to another agent.\n\nThis tool hands
95+
off control to another agent when it''s more suitable to\nanswer the user''s
96+
question according to the agent''s description.\n\nArgs:\n agent_name: the
97+
agent name to transfer to.\n", "name": "transfer_to_agent", "parameters": {"properties":
98+
{"agent_name": {"type": "STRING"}}, "required": ["agent_name"], "type": "OBJECT"}}]}],
99+
"generationConfig": {}}'
100+
headers:
101+
Content-Type:
102+
- application/json
103+
user-agent:
104+
- google-genai-sdk/1.75.0 gl-python/3.14.6 google-adk/1.14.1 gl-python/3.14.6
105+
x-goog-api-client:
106+
- google-genai-sdk/1.75.0 gl-python/3.14.6 google-adk/1.14.1 gl-python/3.14.6
107+
method: POST
108+
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
109+
response:
110+
body:
111+
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
112+
[\n {\n \"text\": \"The capital of France is Paris.\",\n
113+
\ \"thoughtSignature\": \"CrcDARFNMg+U1J/4jHXZRfFf7mqUm/nZzRdTIDuD9yyxroAVdvYxYiIlZc4++vCuNqT5XQJ+N5SfjvN26U1xReX9a7uMicxcpLZRhs+KZr4L1pSVFas3u6W8ylpFFl5F5FauQqY1tpMLGhF0bLp/MqzOSVtSfHp7ZXfWFNh7KIhodg6UDYlN9sGEE/jzLDJaYGjJBFYGo0MuXuv3nm8Fi2om9MVQ3XOVEUVnPtRKg4vJRrL6YuL1i9A2YO1tO11iVepTOQoGJ5v7I0rJhii9BhhGC/uV8kIvBo2XbxWxdNKTnEQfmhMPeZp1MQWm0kdwJvBAJnEBoPFqeafM1q555MnaMlo2HVj9qjgqUtLXwnrNLp1b6esIBuUqrEyAD7U8VQVJq51i0Ppo6fD0VJRkkbQkTvKWehgHkwipz3lVkpD0NAf3++Ylz+u39hBF0MS7mj8P+Jo2RDY8TnWdY+mK2TpvU0bJgcPyIsk61gNGw74F/HmmH0DGfkhMuwB0jxy/EZgUE50OIZeCJEX9AD62g/CncsPQK+/WvsW4ejxNdtfcQnqPo4WWM7KMacF4y1Ha18qcI1KM+FnGMw==\"\n
114+
\ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\":
115+
\"STOP\",\n \"index\": 0\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
116+
350,\n \"candidatesTokenCount\": 7,\n \"totalTokenCount\": 436,\n \"promptTokensDetails\":
117+
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 350\n
118+
\ }\n ],\n \"thoughtsTokenCount\": 79,\n \"serviceTier\": \"standard\"\n
119+
\ },\n \"modelVersion\": \"gemini-2.5-flash\",\n \"responseId\": \"mAFqasSWMqOLjrEP1dG6-Qg\"\n}\n"
120+
headers:
121+
Alt-Svc:
122+
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
123+
Content-Type:
124+
- application/json; charset=UTF-8
125+
Date:
126+
- Wed, 29 Jul 2026 13:35:21 GMT
127+
Server:
128+
- scaffolding on HTTPServer2
129+
Server-Timing:
130+
- gfet4t7; dur=728
131+
Transfer-Encoding:
132+
- chunked
133+
Vary:
134+
- Origin
135+
- X-Origin
136+
- Referer
137+
X-Content-Type-Options:
138+
- nosniff
139+
X-Frame-Options:
140+
- SAMEORIGIN
141+
X-Gemini-Service-Tier:
142+
- standard
143+
X-XSS-Protection:
144+
- '0'
145+
content-length:
146+
- '1234'
147+
status:
148+
code: 200
149+
message: OK
150+
version: 1
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
interactions:
2+
- request:
3+
body: '{"contents": [{"parts": [{"text": "What is the capital of France? Delegate
4+
this to the specialist."}], "role": "user"}], "systemInstruction": {"parts":
5+
[{"text": "You cannot answer questions yourself. For every request, immediately
6+
call transfer_to_agent with agent_name=''capital_specialist''.\n\nYou are an
7+
agent. Your internal name is \"coordinator\". The description about you is \"Routes
8+
geography questions to the capital specialist without answering them.\".\n\n\nYou
9+
have a list of other agents to transfer to:\n\n\nAgent name: capital_specialist\nAgent
10+
description: The only agent allowed to answer geography questions.\n\n\nIf you
11+
are the best to answer the question according to your description,\nyou can
12+
answer it.\n\nIf another agent is better for answering the question according
13+
to its\ndescription, call `transfer_to_agent` function to transfer the question
14+
to that\nagent. When transferring, do not generate any text other than the function\ncall.\n\n**NOTE**:
15+
the only available agents for `transfer_to_agent` function are\n`capital_specialist`.\n"}],
16+
"role": "user"}, "tools": [{"functionDeclarations": [{"description": "Transfer
17+
the query to another agent.\n\nUse this tool to hand off control to another
18+
agent that is more suitable to\nanswer the user''s query according to the agent''s
19+
description.\n\nArgs:\n agent_name: the agent name to transfer to.", "name":
20+
"transfer_to_agent", "parameters_json_schema": {"properties": {"agent_name":
21+
{"title": "Agent Name", "type": "string", "enum": ["capital_specialist"]}},
22+
"required": ["agent_name"], "title": "transfer_to_agentParams", "type": "object"}}]}],
23+
"generationConfig": {}}'
24+
headers:
25+
accept:
26+
- '*/*'
27+
accept-encoding:
28+
- gzip, deflate
29+
connection:
30+
- keep-alive
31+
content-length:
32+
- '1646'
33+
content-type:
34+
- application/json
35+
host:
36+
- generativelanguage.googleapis.com
37+
user-agent:
38+
- google-genai-sdk/2.14.0 gl-python/3.14.6 google-adk/2.5.0 gl-python/3.14.6
39+
x-goog-api-client:
40+
- google-genai-sdk/2.14.0 gl-python/3.14.6 google-adk/2.5.0 gl-python/3.14.6
41+
method: POST
42+
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
43+
response:
44+
body:
45+
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
46+
[\n {\n \"functionCall\": {\n \"name\": \"transfer_to_agent\",\n
47+
\ \"args\": {\n \"agent_name\": \"capital_specialist\"\n
48+
\ }\n },\n \"thoughtSignature\": \"CqwDARFNMg/j+9eaPm9CUpDwYKmqmHGZqfm7bYvgkuBJs+rzg1obPejEnFWaQf/v0ATSzFnaSan470RJspYICGrXp7jyuC/SAVL2v+vHaRvjXHkGq4Z4XxyohC0CJwuYNoxQZ0RAgNK9rkh0OXiv4FbyMOMed6ATAxYgDwV6ugRoDzdorj7x/lqQG1V9mWwK8viOhS0LQ76K48dIu7BMph5J2Jlr4EAO4JC8KhuAdWYE36PlLBQhz3KuS6AvWPwt8lmW4s0ZM+AwZZ6H1nU6hNlRTNUtiBVj5aU1qrbA/BoX/GUcqFfqHq+ymkK2mgW3HkPEgmv8Qry3UL1C+jzU1gfdL2ou2T8t8jZOzt9DO/wrINAhWx0Xx7w6NlfESCo1qQctLpXzGBgzkuzwVHAgJPP0eF38joQdN2VDH2cXZ2ao82Aq6uc/DU6CEASfoCq5aI5z4WELa4o9mzH3q+S6mc8dc81lTsFTz/4WN+G/3lp/KD9lUWE6eUTiettCwT+5x0kM5ymA3CT6iPf7Rz9NkmUqFUENjDrCngeZnN/muHqSUFIo7ZYCMdQ/Yn7i6ZQ=\"\n
49+
\ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\":
50+
\"STOP\",\n \"index\": 0,\n \"finishMessage\": \"Model generated
51+
function call(s).\"\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
52+
301,\n \"candidatesTokenCount\": 22,\n \"totalTokenCount\": 405,\n \"promptTokensDetails\":
53+
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 301\n
54+
\ }\n ],\n \"thoughtsTokenCount\": 82,\n \"serviceTier\": \"standard\"\n
55+
\ },\n \"modelVersion\": \"gemini-2.5-flash\",\n \"responseId\": \"fgFqapXpHLSrsOIPsOff0Ag\"\n}\n"
56+
headers:
57+
alt-svc:
58+
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
59+
content-length:
60+
- '1403'
61+
content-type:
62+
- application/json; charset=UTF-8
63+
date:
64+
- Wed, 29 Jul 2026 13:34:55 GMT
65+
server:
66+
- scaffolding on HTTPServer2
67+
server-timing:
68+
- gfet4t7; dur=832
69+
transfer-encoding:
70+
- chunked
71+
vary:
72+
- Origin
73+
- X-Origin
74+
- Referer
75+
x-content-type-options:
76+
- nosniff
77+
x-frame-options:
78+
- SAMEORIGIN
79+
x-gemini-service-tier:
80+
- standard
81+
x-xss-protection:
82+
- '0'
83+
status:
84+
code: 200
85+
message: OK
86+
- request:
87+
body: '{"contents": [{"parts": [{"text": "What is the capital of France? Delegate
88+
this to the specialist."}], "role": "user"}, {"parts": [{"text": "For context:"},
89+
{"text": "[coordinator] called tool `transfer_to_agent` with parameters: {''agent_name'':
90+
''capital_specialist''}"}], "role": "user"}, {"parts": [{"text": "For context:"},
91+
{"text": "[coordinator] `transfer_to_agent` tool returned result: {''result'':
92+
None}"}], "role": "user"}], "systemInstruction": {"parts": [{"text": "Answer
93+
geography questions accurately and in one short sentence.\n\nYou are an agent.
94+
Your internal name is \"capital_specialist\". The description about you is \"The
95+
only agent allowed to answer geography questions.\".\n\n\nYou have a list of
96+
other agents to transfer to:\n\n\nAgent name: coordinator\nAgent description:
97+
Routes geography questions to the capital specialist without answering them.\n\n\nIf
98+
you are the best to answer the question according to your description,\nyou
99+
can answer it.\n\nIf another agent is better for answering the question according
100+
to its\ndescription, call `transfer_to_agent` function to transfer the question
101+
to that\nagent. When transferring, do not generate any text other than the function\ncall.\n\n**NOTE**:
102+
the only available agents for `transfer_to_agent` function are\n`coordinator`.\n\nIf
103+
neither you nor the other agents are best for the question, transfer to your
104+
parent agent coordinator.\n"}], "role": "user"}, "tools": [{"functionDeclarations":
105+
[{"description": "Transfer the query to another agent.\n\nUse this tool to hand
106+
off control to another agent that is more suitable to\nanswer the user''s query
107+
according to the agent''s description.\n\nArgs:\n agent_name: the agent name
108+
to transfer to.", "name": "transfer_to_agent", "parameters_json_schema": {"properties":
109+
{"agent_name": {"title": "Agent Name", "type": "string", "enum": ["coordinator"]}},
110+
"required": ["agent_name"], "title": "transfer_to_agentParams", "type": "object"}}]}],
111+
"generationConfig": {}}'
112+
headers:
113+
accept:
114+
- '*/*'
115+
accept-encoding:
116+
- gzip, deflate
117+
connection:
118+
- keep-alive
119+
content-length:
120+
- '1985'
121+
content-type:
122+
- application/json
123+
host:
124+
- generativelanguage.googleapis.com
125+
user-agent:
126+
- google-genai-sdk/2.14.0 gl-python/3.14.6 google-adk/2.5.0 gl-python/3.14.6
127+
x-goog-api-client:
128+
- google-genai-sdk/2.14.0 gl-python/3.14.6 google-adk/2.5.0 gl-python/3.14.6
129+
method: POST
130+
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
131+
response:
132+
body:
133+
string: "{\n \"candidates\": [\n {\n \"content\": {\n \"parts\":
134+
[\n {\n \"text\": \"The capital of France is Paris.\",\n
135+
\ \"thoughtSignature\": \"Cp0DARFNMg+zy0SfhDvFiWb+X7RwVYg2gND5/R8NYbIXVz2uXsKTzOu1zjMeMYCpJBlwGHeJAuWPG1X6+GKB35F2H9d1aJgdvCgjKC8iwXYjNDpkr3wPPmYZfzqB5xX7YvErtnJC3bLcDKaxtgfcrY+VgWhs5IW/ysrUXpiq+DJvX0RiwXgvmRg25sgGm/JRD8Q1hFQCFLwWrUAs8cg4fhSzHTfdSiP2t1WsmVOJrt7a7K6EiKjt88TAbrb8z7HTyxQtQJbNesMPIslrdIZGvGFSEy+no6KGNuN524eCH2/VYMvsQJJLxYYlYNoh6B7G6IkmfP64nqYbsiJcNxG4MJ3OvVsdLhMJsjBdtIfBsSF/lq14c1IZy2dj4rpa4JBmIityz3y1OSbFWcIeaGlaAmQjzpTu2XtdgeTfqopfPY8KLNN8ugzZgkpdOoq/RzOCebpgx9dUX6jr8xhlwbYBpbkfBapRWu5PKt2GkiKVUBUqWgT/RGh+rm4yLMyJ18nYwO0bDQVOdQNXeyRxuSIunvKi6xFCI1Jdheoa8iYfCTY=\"\n
136+
\ }\n ],\n \"role\": \"model\"\n },\n \"finishReason\":
137+
\"STOP\",\n \"index\": 0\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\":
138+
352,\n \"candidatesTokenCount\": 7,\n \"totalTokenCount\": 439,\n \"promptTokensDetails\":
139+
[\n {\n \"modality\": \"TEXT\",\n \"tokenCount\": 352\n
140+
\ }\n ],\n \"thoughtsTokenCount\": 80,\n \"serviceTier\": \"standard\"\n
141+
\ },\n \"modelVersion\": \"gemini-2.5-flash\",\n \"responseId\": \"fwFqapjNF4mkjrEP0rKCUQ\"\n}\n"
142+
headers:
143+
alt-svc:
144+
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
145+
content-length:
146+
- '1197'
147+
content-type:
148+
- application/json; charset=UTF-8
149+
date:
150+
- Wed, 29 Jul 2026 13:34:56 GMT
151+
server:
152+
- scaffolding on HTTPServer2
153+
server-timing:
154+
- gfet4t7; dur=896
155+
transfer-encoding:
156+
- chunked
157+
vary:
158+
- Origin
159+
- X-Origin
160+
- Referer
161+
x-content-type-options:
162+
- nosniff
163+
x-frame-options:
164+
- SAMEORIGIN
165+
x-gemini-service-tier:
166+
- standard
167+
x-xss-protection:
168+
- '0'
169+
status:
170+
code: 200
171+
message: OK
172+
version: 1

0 commit comments

Comments
 (0)