Skip to content

docs(skills): fix broken refs in adk-workflow skill#6044

Open
freddypatota wants to merge 2 commits into
google:v2from
freddypatota:fix/adk-workflow-skill-doc-bugs
Open

docs(skills): fix broken refs in adk-workflow skill#6044
freddypatota wants to merge 2 commits into
google:v2from
freddypatota:fix/adk-workflow-skill-doc-bugs

Conversation

@freddypatota

@freddypatota freddypatota commented Jun 10, 2026

Copy link
Copy Markdown

Summary

Fixes 5 documentation bugs in the adk-workflow skill on v2. Each is empirically verified against google-adk==2.2.0 and v2 source. Full verification transcript inline below.

Fixes in this PR

  1. advanced-patterns.md — Remove the hard-coded local-filesystem URL file:///Users/deanchen/Desktop/... left in a doc link; replace with the relative path dynamic-nodes.md.

  2. testing.md — All examples imported from tests.unittests.*, which is not in the published google-adk wheel. Rewrite to use the public from google.adk.runners import InMemoryRunner plus a small inline run() helper. Three rewritten snippets (basic workflow, state, parallel worker) were executed end-to-end against google-adk==2.2.0 and pass. The MockModel section is replaced with a FakeLlm(BaseLlm) pattern that uses only public symbols.

  3. llm-agent-nodes.md — The doc claims "LlmAgentWrapper outputs types.Content, NOT str." The source (_llm_agent_wrapper.process_llm_agent_output) sets event.output = text (a str) when output_schema is unset, and the validated dict when set. Rewrite the section, the table, and drop the "use Any and extract text" workaround that depended on the wrong claim.

  4. parallel-and-fanout.md + import-paths.md — Drop from google.adk.workflow._parallel_worker import ParallelWorker. The class doesn't exist under that name; only the private _ParallelWorker does. The same files already document the public API (parallel_worker=True flag on @node or LlmAgent) — rewrite samples to use it consistently.

  5. state-and-events.md (+ one cross-reference in advanced-patterns.md) — Drop triggered_by, in_nodes, execution_id, retry_count from the Context property tables and code samples. None of them exist on Context in v2 source (verified by grep in src/google/adk/agents/context.py). Rename retry_countattempt_count (the live name). Also drop get_next_child_execution_id from the methods table for the same reason.

Scope

All five fixes target the same file tree (.agents/skills/adk-workflow/references/) with the same concern: "skill docs reference symbols/imports/paths that don't exist." Per CONTRIBUTING.md "small, focused PRs", they're bundled because each is a surgical edit and they share verification setup. Happy to split if reviewers prefer.

Testing plan

Doc-only changes. No source code or behavior modified.

  1. Pip-install reproduction of every bug claimed in a clean venv with google-adk==2.2.0. See "Verification details" below for the verbatim ImportError, hasattr == False, and source quotes that prove each claim.

  2. Rewritten testing.md snippets executed end-to-end against google-adk==2.2.0:

    • test_simple_workflow — PASSED
    • test_state_management — PASSED
    • test_parallel_worker — PASSED
  3. Pre-commit hooks ran clean on the changed files. mdformat is excluded for .agents/ by the repo's .pre-commit-config.yaml, and the other hooks (isort, pyink, addlicense) target Python/shell files only.

Notes for reviewers

  • All claims are pinned to 2.2.0 + v2 HEAD as of the date of this PR.
  • The testing.md rewrite is the largest delta (~500 lines), but almost every line either drops a tests.unittests.* import or replaces a testing_utils.X call with public-API equivalents.
  • A previously-considered "bug" (parallel-worker naming as {name}__{index}) was dropped from this PR after confirming it had already been fixed on v2 to use {name}@{run_id} with run_id starting at "1".

Verification details

Setup:

uv venv --python 3.13 .venv && source .venv/bin/activate
uv pip install google-adk
python -c "import google.adk; print(google.adk.__version__)"
# -> 2.2.0

Bug 1 — Hard-coded file:/// URL

$ grep -n 'file:///' .agents/skills/adk-workflow/references/advanced-patterns.md
38:See the dedicated [Dynamic Node Scheduling Reference](file:///Users/deanchen/Desktop/adk-workflow/.agents/skills/adk-workflow/references/dynamic-nodes.md) for detailed rules, examples, and best practices.

A developer's local filesystem path leaked into the published skill.

Bug 2 — tests.unittests... imports unreachable from pip install

>>> from tests.unittests.workflow import testing_utils
ModuleNotFoundError: No module named 'tests'

>>> from tests.unittests.testing_utils import InMemoryRunner, MockModel
ModuleNotFoundError: No module named 'tests'

The tests/ directory ships only in the source repo, not in the installed google-adk wheel. Any user copying these snippets gets ModuleNotFoundError. The PR rewrites samples to use the public from google.adk.runners import InMemoryRunner and demonstrates a publicly-importable mock pattern (subclass BaseLlm).

The three rewritten snippets (basic, state, parallel) were run end-to-end against google-adk==2.2.0 and all three passed.

Bug 3 — LlmAgentWrapper output type doc is wrong

The skill claims: "LlmAgentWrapper outputs types.Content, NOT str."

The source in both 2.2.0 and v2 says otherwise. From src/google/adk/workflow/_llm_agent_wrapper.py:

def process_llm_agent_output(agent: Any, ctx: Context, event: Event) -> None:
    ...
    text = (
        ''.join(p.text for p in event.content.parts if p.text and not p.thought)
        if event.content.parts else ''
    )
    if agent.output_schema:
        if text.strip():
            output = validate_schema(agent.output_schema, text)
        else:
            output = None
    else:
        output = text          # <-- str, not types.Content
    ...
    event.output = output

When output_schema is unset, event.output is the concatenated string of the model's text parts. When output_schema=MyModel is set, it's the validated model_dump() dict. The PR rewrites the section, table, and the "use Any and extract text" workaround that depended on the wrong claim.

Bug 4 — _parallel_worker.ParallelWorker is not importable

The class doesn't exist under that name — only the underscore-prefixed _ParallelWorker does, and that path is private:

>>> from google.adk.workflow._parallel_worker import ParallelWorker
ImportError: cannot import name 'ParallelWorker' from 'google.adk.workflow._parallel_worker'

>>> from google.adk.workflow._parallel_worker import _ParallelWorker
>>> _ParallelWorker
<class 'google.adk.workflow._parallel_worker._ParallelWorker'>

The recommended public API is the parallel_worker=True flag — already documented as preferred in the same files. Verified end-to-end:

from google.adk.workflow import node, Workflow

@node(parallel_worker=True)
def double(node_input: int) -> int:
    return node_input * 2

# Workflow constructs OK; `double` is an internal _ParallelWorker
# under the hood — no user-visible private-API surface needed.

PR drops the private import from parallel-and-fanout.md and the import-paths.md table, and rewrites samples to use the flag.

Bug 5 — Removed Context properties documented as live

>>> from google.adk.agents.context import Context
>>> for name in ['triggered_by', 'in_nodes', 'execution_id', 'retry_count', 'attempt_count']:
...     print(name, hasattr(Context, name))
triggered_by False
in_nodes False
execution_id False
retry_count False
attempt_count True

The same is true on v2 source — grep for those names in src/google/adk/agents/context.py returns nothing, while attempt_count has 4 hits.

Code samples using ctx.retry_count / ctx.triggered_by raise AttributeError. PR removes the four absent properties from the docs and renames retry_countattempt_count everywhere it's mentioned. get_next_child_execution_id is also gone from Context on v2; the PR removes it from the methods table for the same reason.

Five doc bugs in the adk-workflow skill produce import errors,
attribute errors, or misleading guidance against the published
google-adk wheel and v2 source. Each verified empirically against
google-adk==2.2.0 and the v2 dev branch:

- advanced-patterns.md: remove hard-coded file:///Users/deanchen/...
  link to dynamic-nodes.md; use a relative path.
- testing.md: rewrite examples to use the public InMemoryRunner from
  google.adk.runners instead of `tests.unittests.workflow.testing_utils`
  (which raises ModuleNotFoundError under pip install). Replace
  MockModel with a FakeLlm(BaseLlm) pattern. Three rewritten snippets
  executed end-to-end against google-adk==2.2.0 and pass.
- llm-agent-nodes.md: correct the claim that LlmAgentWrapper outputs
  types.Content. process_llm_agent_output (in _llm_agent_wrapper.py)
  sets event.output = text (a str) when output_schema is unset, and
  the validated model_dump dict when set. Drop the
  workaround section and update the summary table.
- parallel-and-fanout.md, import-paths.md: stop importing the
  non-existent `ParallelWorker` from the private `_parallel_worker`
  module (only `_ParallelWorker` exists and the path is private).
  Rewrite samples to use the documented `parallel_worker=True` flag.
- state-and-events.md, advanced-patterns.md: drop `triggered_by`,
  `in_nodes`, `execution_id`, `retry_count`, and
  `get_next_child_execution_id` from the Context property/method
  tables and code samples — none of them exist on Context in v2.
  Rename `retry_count` → `attempt_count` (the live name).
@adk-bot adk-bot added the documentation [Component] This issue is related to documentation, it will be transferred to adk-docs label Jun 10, 2026
@freddypatota

Copy link
Copy Markdown
Author

CI failures here are pre-existing on v2 and unrelated to this PR. tests/unittests/test_release_dependencies.py:31 does an unconditional import tomllib, which fails on Python 3.10 with ModuleNotFoundError. The 3.11–3.14 jobs are then cancelled by the matrix's fail-fast policy mid-run, not failing on their own merits.

Same failure is reproducible on the most recent unrelated v2 PR (#5938), which never touched any Python code.

This PR only modifies markdown files under .agents/skills/adk-workflow/references/, i.e. no source code, no test code.

@freddypatota freddypatota marked this pull request as ready for review June 10, 2026 00:34
process_llm_agent_output writes event.output internally, but the
framework clears it before the event surfaces to user code in
runner.run_async(...). The validated value still flows forward to the
next node's node_input and to session.state[output_key] — the user-
facing surface that matters.

Spell out the caveat and direct readers to alternative assertion
targets so tests do not false-fail when checking event.output on an
LLM agent's own event.
@freddypatota

Copy link
Copy Markdown
Author

Added one follow-up commit (4a61d38): an observability caveat on the LLM Agent Output Types section.

While doing a fuller verification pass of the skill, I traced process_llm_agent_output end-to-end against google-adk==2.2.0: the function does set event.output (matching the description in this PR), but the framework clears that field before the event reaches user code in runner.run_async(...). The validated value still flows to the next node's node_input and to session.state[output_key] — the user-facing surfaces — so the summary table in this PR remains correct. The new commit just spells out that event.output itself isn't the right thing to assert on for an LLM agent's event, and points to the alternatives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation [Component] This issue is related to documentation, it will be transferred to adk-docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants