Skip to content

Commit 574d98b

Browse files
authored
optimize a2a simple (#80)
2 parents 1084c20 + 505be06 commit 574d98b

File tree

13 files changed

+402
-277
lines changed

13 files changed

+402
-277
lines changed

.issues/SUBMIT_GUIDE.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# veadk-python Bug Report 提交指南
2+
3+
## Issue 已创建
4+
5+
Issue 文档位置: `.issues/veadk-nonetype-get-function-responses.md`
6+
Patch 文件位置: `.issues/veadk-runner-fix.patch`
7+
8+
## 提交方式
9+
10+
### 方式一:GitHub Issue
11+
12+
1. 访问 veadk-python 仓库:**https://github.com/volcengine/veadk-python**
13+
14+
2. 点击 **Issues****New Issue**
15+
16+
3. 复制 `.issues/veadk-nonetype-get-function-responses.md` 的内容粘贴
17+
18+
4. 添加标签:`bug`, `runner`, `null-safety`
19+
20+
### 方式二:提交 Pull Request
21+
22+
1. Fork 仓库:https://github.com/volcengine/veadk-python
23+
24+
2. Clone 你的 fork:
25+
```bash
26+
git clone https://github.com/YOUR_USERNAME/veadk-python.git
27+
cd veadk-python
28+
```
29+
30+
3. 创建修复分支:
31+
```bash
32+
git checkout -b fix/null-check-get-function-responses
33+
```
34+
35+
4. 应用 patch 或手动修改 `veadk/runner.py`
36+
37+
在第 139 行后添加:
38+
```python
39+
# Skip None events to prevent AttributeError
40+
if event is None:
41+
logger.warning("Received None event from generator, skipping...")
42+
continue
43+
```
44+
45+
5. 提交并推送:
46+
```bash
47+
git add veadk/runner.py
48+
git commit -m "fix: add null check before accessing event methods in runner.py
49+
50+
- Prevents 'NoneType' object has no attribute 'get_function_responses' error
51+
- Occurs when using MCP tools or complex tool chains that may yield None events
52+
- Adds warning log for debugging purposes
53+
54+
Fixes #XXX"
55+
56+
git push origin fix/null-check-get-function-responses
57+
```
58+
59+
6. 在 GitHub 上创建 Pull Request
60+
61+
## 临时本地修复
62+
63+
如果需要立即修复,可以直接修改本地安装的 veadk:
64+
65+
```bash
66+
# 找到 veadk 安装位置
67+
pip show veadk-python | grep Location
68+
69+
# 编辑 runner.py(以下路径根据实际情况调整)
70+
code ~/.local/lib/python3.12/site-packages/veadk/runner.py
71+
```
72+
73+
在第 139 行 `yield event` 后添加:
74+
75+
```python
76+
if event is None:
77+
continue
78+
```
79+
80+
## 相关信息
81+
82+
- **veadk-python 版本**: 0.2.29
83+
- **GitHub 仓库**: https://github.com/volcengine/veadk-python
84+
- **受影响用例**: `02-use-cases/video_gen` (使用 MCP 工具的视频生成案例)
85+
86+
## 联系方式
87+
88+
如有问题可联系 veadk-python 维护者:
89+
90+
91+
92+
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# [Bug] NoneType object has no attribute 'get_function_responses' in runner.py
2+
3+
## Summary
4+
5+
When running agents with complex tool chains (especially MCP tools), the application crashes with `AttributeError: 'NoneType' object has no attribute 'get_function_responses'`. This occurs because the `intercept_new_message` decorator in `veadk/runner.py` doesn't perform null checks on event objects before accessing their methods.
6+
7+
## Environment
8+
9+
- **veadk-python version**: (check with `pip show veadk-python`)
10+
- **Python version**: 3.12
11+
- **OS**: macOS / Linux
12+
13+
## Error Message
14+
15+
```
16+
AttributeError: 'NoneType' object has no attribute 'get_function_responses'
17+
```
18+
19+
## Steps to Reproduce
20+
21+
1. Create an agent that uses MCP tools (e.g., video-clip-mcp)
22+
2. Run the agent with a complex multi-tool workflow
23+
3. If any tool times out or returns an unexpected response, the error occurs
24+
25+
Example agent configuration (`video_gen/agent.py`):
26+
27+
```python
28+
from google.adk.tools.mcp_tool.mcp_toolset import (
29+
McpToolset,
30+
StdioConnectionParams,
31+
StdioServerParameters,
32+
)
33+
from veadk import Runner
34+
from veadk.agent_builder import AgentBuilder
35+
36+
server_parameters = StdioServerParameters(
37+
command="npx",
38+
args=["@pickstar-2002/video-clip-mcp@latest"],
39+
)
40+
mcpTool = McpToolset(
41+
connection_params=StdioConnectionParams(
42+
server_params=server_parameters, timeout=600.0
43+
),
44+
errlog=None,
45+
)
46+
47+
agent = agent_builder.build(path="agent.yaml")
48+
agent.tools.append(mcpTool)
49+
50+
runner = Runner(agent=agent, app_name="storyvideo")
51+
# Run the agent...
52+
```
53+
54+
## Root Cause Analysis
55+
56+
The issue is located in `veadk/runner.py`, lines 141-157, within the `intercept_new_message` decorator:
57+
58+
```python
59+
# veadk/runner.py - intercept_new_message decorator
60+
61+
async for event in func(
62+
user_id=user_id,
63+
session_id=session_id,
64+
new_message=new_message,
65+
**kwargs,
66+
):
67+
yield event
68+
event_metadata = f"| agent_name: {event.author} , user_id: {user_id} , session_id: {session_id} , invocation_id: {event.invocation_id}"
69+
if event.get_function_calls(): # ← Crashes here if event is None
70+
for function_call in event.get_function_calls():
71+
logger.debug(f"Function call: {function_call} {event_metadata}")
72+
elif event.get_function_responses(): # ← Or crashes here
73+
for function_response in event.get_function_responses():
74+
logger.debug(
75+
f"Function response: {function_response} {event_metadata}"
76+
)
77+
# ...
78+
```
79+
80+
**Problem**: The code accesses `event.author`, `event.get_function_calls()`, and `event.get_function_responses()` without first checking if `event` is `None`.
81+
82+
**When does event become None?**
83+
- MCP tool connection timeouts
84+
- Upstream generator yielding None on edge cases (connection closed, errors)
85+
- Certain error conditions in the ADK event stream
86+
87+
## Expected Behavior
88+
89+
The runner should gracefully handle `None` events by skipping them instead of crashing.
90+
91+
## Proposed Fix
92+
93+
Add null check before accessing event properties:
94+
95+
```python
96+
# veadk/runner.py - intercept_new_message decorator (lines 134-159)
97+
98+
async for event in func(
99+
user_id=user_id,
100+
session_id=session_id,
101+
new_message=new_message,
102+
**kwargs,
103+
):
104+
yield event
105+
106+
# Add null check to prevent AttributeError
107+
if event is None:
108+
logger.warning("Received None event from generator, skipping...")
109+
continue
110+
111+
event_metadata = f"| agent_name: {event.author} , user_id: {user_id} , session_id: {session_id} , invocation_id: {event.invocation_id}"
112+
if event.get_function_calls():
113+
for function_call in event.get_function_calls():
114+
logger.debug(f"Function call: {function_call} {event_metadata}")
115+
elif event.get_function_responses():
116+
for function_response in event.get_function_responses():
117+
logger.debug(
118+
f"Function response: {function_response} {event_metadata}"
119+
)
120+
elif (
121+
event.content is not None
122+
and event.content.parts
123+
and event.content.parts[0].text is not None
124+
and len(event.content.parts[0].text.strip()) > 0
125+
):
126+
final_output = event.content.parts[0].text
127+
logger.debug(f"Event output: {final_output} {event_metadata}")
128+
```
129+
130+
## Additional Context
131+
132+
### Related Code Locations That May Also Need Fixes
133+
134+
1. **`google/adk/flows/llm_flows/contents.py`** (line 145):
135+
```python
136+
function_responses = events[-1].get_function_responses()
137+
```
138+
Should add check: `if events and events[-1] is not None:`
139+
140+
2. **`google/adk/flows/llm_flows/base_llm_flow.py`** (line 160):
141+
```python
142+
if event.get_function_responses():
143+
```
144+
Should add null check before this line.
145+
146+
### Affected Use Cases
147+
148+
- `02-use-cases/video_gen` - Video generation with MCP tools
149+
- Any agent using long-running tools or external service integrations
150+
- Agents with complex multi-tool workflows
151+
152+
## Workaround (Temporary)
153+
154+
Until this is fixed in veadk, users can wrap their agent execution in try-catch:
155+
156+
```python
157+
try:
158+
async for event in runner.run_async(...):
159+
if event is not None:
160+
# Process event
161+
pass
162+
except AttributeError as e:
163+
if "'NoneType' object has no attribute 'get_function_responses'" in str(e):
164+
logger.warning("Encountered None event, gracefully handling...")
165+
else:
166+
raise
167+
```
168+
169+
## Labels
170+
171+
- `bug`
172+
- `runner`
173+
- `null-safety`
174+
175+
## Priority
176+
177+
**High** - This causes complete application crashes and affects production environments using complex tool chains.

.issues/veadk-runner-fix.patch

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--- a/veadk/runner.py
2+
+++ b/veadk/runner.py
3+
@@ -137,6 +137,12 @@ def intercept_new_message(process_func):
4+
):
5+
yield event
6+
+
7+
+ # Skip None events to prevent AttributeError
8+
+ if event is None:
9+
+ logger.warning("Received None event from generator, skipping...")
10+
+ continue
11+
+
12+
event_metadata = f"| agent_name: {event.author} , user_id: {user_id} , session_id: {session_id} , invocation_id: {event.invocation_id}"
13+
if event.get_function_calls():
14+
for function_call in event.get_function_calls():

0 commit comments

Comments
 (0)