Skip to content

fix(a2a): Support to_a2a(Workflow) and reject non-agent root nodes#5710

Closed
sokoliva wants to merge 1 commit into
google:mainfrom
sokoliva:fix/5487-to-a2a-workflow
Closed

fix(a2a): Support to_a2a(Workflow) and reject non-agent root nodes#5710
sokoliva wants to merge 1 commit into
google:mainfrom
sokoliva:fix/5487-to-a2a-workflow

Conversation

@sokoliva

Copy link
Copy Markdown
Member

Link to Issue

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 withRuntimeError: '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:

  • I have added or updated unit tests for my change.
  • 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

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

@sokoliva sokoliva changed the base branch from main to release/v2.0.0-beta.1 May 15, 2026 08:40
@sokoliva sokoliva changed the title Fix/5487 to a2a workflow fix(a2a): Support to_a2a(Workflow) and reject non-agent root nodes May 15, 2026
@rohityan rohityan self-assigned this May 18, 2026
@rohityan rohityan added the a2a [Component] This issue is related a2a support inside ADK. label May 18, 2026
@rohityan rohityan closed this May 18, 2026
@rohityan rohityan reopened this May 20, 2026
@haranrk haranrk self-assigned this May 21, 2026
@haranrk haranrk added the ready to pull [Status] This PR is ready to be imported back to Google label May 29, 2026
@sokoliva sokoliva changed the base branch from release/v2.0.0-beta.1 to main June 1, 2026 07:33
@sokoliva sokoliva changed the base branch from main to release/v2.0.0-beta.1 June 1, 2026 08:20
@sokoliva sokoliva force-pushed the fix/5487-to-a2a-workflow branch from b3eac05 to f3f5019 Compare June 1, 2026 08:26
@sokoliva sokoliva changed the base branch from release/v2.0.0-beta.1 to main June 1, 2026 08:26
@adk-bot adk-bot added the core [Component] This issue is related to the core interface and implementation label Jun 1, 2026
@haranrk haranrk added ready to pull [Status] This PR is ready to be imported back to Google and removed ready to pull [Status] This PR is ready to be imported back to Google labels Jun 1, 2026
copybara-service Bot pushed a commit that referenced this pull request Jun 2, 2026
### 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: I2330b2f3036cfd99c89b5d5c492f1c46c275974f
@adk-bot

adk-bot commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Thank you @sokoliva for your contribution! 🎉

Your changes have been successfully imported and merged via Copybara in commit 0478b02.

Closing this PR as the changes are now in the main branch.

@adk-bot adk-bot added the merged [Status] This PR is merged label Jun 2, 2026
@adk-bot adk-bot closed this Jun 2, 2026
copybara-service Bot pushed a commit that referenced this pull request Jun 8, 2026
Port of GitHub PR: #5710

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.

Ported tests to test_agent_card_builder.py and test_agent_to_a2a.py.

Co-authored-by: Yifan Wang <wanyif@google.com>
PiperOrigin-RevId: 927500389
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a2a [Component] This issue is related a2a support inside ADK. core [Component] This issue is related to the core interface and implementation merged [Status] This PR is merged ready to pull [Status] This PR is ready to be imported back to Google

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants