Commit 0478b02
fix(a2a): Support to_a2a(Workflow) and reject non-agent root nodes
### Link to Issue
- Related: #5487
**Problem:**
`to_a2a(workflow)` fails because `AgentCardBuilder` requires field `sub_agents` which `Workflow` does not have.
**Solution:**
- Make `to_a2a()` and `AgentCardBuilder` accept `Workflow` (the v2 graph orchestrator) as a root, not just `BaseAgent`. Previously crashed with`RuntimeError: 'Workflow' object has no attribute 'sub_agents'`.
- Tighten the public type contract to `BaseAgent | Workflow` and reject other `BaseNode` subtypes (e.g. `FunctionNode`, `JoinNode`) at call time with `TypeError`. They previously produced a degenerate "custom agent" card silently.
### Testing Plan
**tests/unittests/a2a/utils/test_agent_card_builder.py** — 9 new tests:
- test_get_agent_type_workflow — returns 'graph_workflow' for the new v2 Workflow.
- test_get_agent_skill_name_workflow — returns 'workflow' for Workflow.
- test_init_rejects_function_node — AgentCardBuilder(agent=FunctionNode(...)) raises TypeError (regression coverage for the runtime guard).
- test_init_rejects_arbitrary_object — AgentCardBuilder(agent="...") raises TypeError.
- test_build_succeeds_for_llm_agent — regression coverage that the original BaseAgent path still works after the type narrowing.
- test_build_succeeds_for_workflow_with_llm_agent_node — exact OP repro shape, end-to-end through build().
- test_build_succeeds_for_workflow_with_output_schema_node — covers the output_schema shape mentioned in the issue.
- test_build_succeeds_for_empty_workflow — degenerate but valid case (no edges).
- test_get_workflow_description_workflow_with_nodes — verifies graph nodes appear in the description string.
- test_get_workflow_description_empty_workflow — returns None when no nodes.
**tests/unittests/a2a/utils/test_agent_to_a2a.py** — 2 new tests, 1 rewritten:
- test_to_a2a_succeeds_for_workflow (new) — end-to-end through the actual Starlette lifespan (the exact code path that crashed in the OP's repro).
- test_to_a2a_rejects_function_node (new) — to_a2a(FunctionNode(...)) raises TypeError at call time.
- test_to_a2a_rejects_non_agent_non_workflow (rewrote existing test_to_a2a_with_invalid_agent_type) — now asserts TypeError raised eagerly at to_a2a() call time instead of AttributeError raised lazily during request handling. This is a deliberate behavior change, not a regression: the old test encoded buggy lazy-failure UX where misuse only surfaced when a client hit the endpoint.
**Unit Tests:**
- [x] I have added or updated unit tests for my change.
- [x] All unit tests pass locally.
_Please include a summary of passed `pytest` results._
ran `uv run pytest tests/unittests`
`6275 passed, 14 skipped, 25 xfailed, 10 xpassed, 2532 warnings `
### Checklist
- [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [x] I have performed a self-review of my own code.
- [x] I have commented my code, particularly in hard-to-understand areas.
- [x] I have added tests that prove my fix is effective or that my feature works.
- [x] New and existing unit tests pass locally with my changes.
- [x] I have manually tested my changes end-to-end.
- [x] Any dependent changes have been merged and published in downstream modules.
Merge #5710
Change-Id: I2330b2f3036cfd99c89b5d5c492f1c46c275974f1 parent bb16958 commit 0478b02
4 files changed
Lines changed: 290 additions & 51 deletions
File tree
- src/google/adk/a2a/utils
- tests/unittests/a2a/utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
35 | 38 | | |
36 | 39 | | |
37 | 40 | | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
41 | 44 | | |
42 | | - | |
| 45 | + | |
43 | 46 | | |
44 | | - | |
45 | | - | |
46 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
47 | 50 | | |
48 | 51 | | |
49 | 52 | | |
50 | 53 | | |
51 | 54 | | |
52 | | - | |
| 55 | + | |
53 | 56 | | |
54 | 57 | | |
55 | 58 | | |
| |||
59 | 62 | | |
60 | 63 | | |
61 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
62 | 70 | | |
63 | 71 | | |
64 | 72 | | |
| |||
96 | 104 | | |
97 | 105 | | |
98 | 106 | | |
99 | | - | |
100 | | - | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
101 | 118 | | |
102 | 119 | | |
103 | 120 | | |
| |||
140 | 157 | | |
141 | 158 | | |
142 | 159 | | |
143 | | - | |
144 | | - | |
| 160 | + | |
| 161 | + | |
145 | 162 | | |
146 | | - | |
| 163 | + | |
147 | 164 | | |
148 | 165 | | |
149 | 166 | | |
| |||
225 | 242 | | |
226 | 243 | | |
227 | 244 | | |
228 | | - | |
229 | | - | |
| 245 | + | |
| 246 | + | |
230 | 247 | | |
231 | 248 | | |
232 | 249 | | |
| |||
249 | 266 | | |
250 | 267 | | |
251 | 268 | | |
252 | | - | |
253 | | - | |
| 269 | + | |
| 270 | + | |
254 | 271 | | |
255 | 272 | | |
256 | 273 | | |
| |||
259 | 276 | | |
260 | 277 | | |
261 | 278 | | |
262 | | - | |
| 279 | + | |
263 | 280 | | |
264 | | - | |
| 281 | + | |
265 | 282 | | |
266 | | - | |
| 283 | + | |
267 | 284 | | |
268 | 285 | | |
269 | 286 | | |
| |||
281 | 298 | | |
282 | 299 | | |
283 | 300 | | |
284 | | - | |
| 301 | + | |
285 | 302 | | |
286 | 303 | | |
287 | 304 | | |
| |||
291 | 308 | | |
292 | 309 | | |
293 | 310 | | |
| 311 | + | |
| 312 | + | |
294 | 313 | | |
295 | 314 | | |
296 | 315 | | |
297 | 316 | | |
298 | | - | |
| 317 | + | |
299 | 318 | | |
300 | 319 | | |
301 | 320 | | |
302 | | - | |
| 321 | + | |
303 | 322 | | |
304 | 323 | | |
305 | 324 | | |
306 | 325 | | |
307 | 326 | | |
308 | | - | |
| 327 | + | |
309 | 328 | | |
310 | 329 | | |
311 | 330 | | |
| |||
382 | 401 | | |
383 | 402 | | |
384 | 403 | | |
385 | | - | |
386 | | - | |
387 | | - | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
388 | 407 | | |
389 | 408 | | |
390 | 409 | | |
| |||
393 | 412 | | |
394 | 413 | | |
395 | 414 | | |
| 415 | + | |
| 416 | + | |
396 | 417 | | |
397 | 418 | | |
398 | 419 | | |
| |||
448 | 469 | | |
449 | 470 | | |
450 | 471 | | |
451 | | - | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
452 | 491 | | |
453 | 492 | | |
454 | 493 | | |
455 | 494 | | |
456 | 495 | | |
457 | 496 | | |
| 497 | + | |
458 | 498 | | |
459 | 499 | | |
460 | 500 | | |
| |||
492 | 532 | | |
493 | 533 | | |
494 | 534 | | |
495 | | - | |
| 535 | + | |
496 | 536 | | |
497 | 537 | | |
498 | 538 | | |
| |||
558 | 598 | | |
559 | 599 | | |
560 | 600 | | |
561 | | - | |
| 601 | + | |
562 | 602 | | |
563 | 603 | | |
564 | 604 | | |
| |||
568 | 608 | | |
569 | 609 | | |
570 | 610 | | |
571 | | - | |
| 611 | + | |
572 | 612 | | |
573 | 613 | | |
574 | 614 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
| |||
77 | 78 | | |
78 | 79 | | |
79 | 80 | | |
80 | | - | |
| 81 | + | |
81 | 82 | | |
82 | 83 | | |
83 | 84 | | |
| |||
89 | 90 | | |
90 | 91 | | |
91 | 92 | | |
92 | | - | |
| 93 | + | |
93 | 94 | | |
94 | 95 | | |
95 | | - | |
| 96 | + | |
| 97 | + | |
96 | 98 | | |
97 | 99 | | |
98 | 100 | | |
| |||
152 | 154 | | |
153 | 155 | | |
154 | 156 | | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
159 | 160 | | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
165 | 171 | | |
166 | 172 | | |
167 | 173 | | |
| |||
0 commit comments