Skip to content

Add TypeScript tabs to the graph workflow pages - #2167

Merged
joefernandez merged 9 commits into
google:mainfrom
kalenkevich:docs/typescript-graph-workflows
Aug 28, 2026
Merged

Add TypeScript tabs to the graph workflow pages#2167
joefernandez merged 9 commits into
google:mainfrom
kalenkevich:docs/typescript-graph-workflows

Conversation

@kalenkevich

@kalenkevich kalenkevich commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

What this does

Adds TypeScript coverage to the five graph-workflow pages under /graphs/.
Every section that had a Python tab now has a TypeScript tab, placed
between Python and Go to match the ordering used elsewhere in the repo — 30
tabs in total.

Page TypeScript tabs
graphs/index.md 2
graphs/routes.md 7
graphs/data-handling.md 8
graphs/human-input.md 4
graphs/dynamic.md 9

The code lives in 26 new snippet files under
examples/typescript/snippets/graphs/, pulled in with --8<-- region
includes, following the convention already used by the Go snippets and by the
existing TypeScript snippets in callbacks/, runtime/ and agents/.

Why the Python tab wasn't enough

The Python examples don't translate line-for-line, so a TypeScript reader had
to guess at several things that differ:

  • there is no @node decorator — node(fn, options) is the factory form, and
    new FunctionNode(name, fn, config) the explicit one
  • schemas are Zod objects (or a genai Schema), not pydantic models
  • state is written through ctx.state, not returned on an event
  • a user-facing message is the event's content; output is what reaches the
    next node
  • ctx.runNode() resolves to a node result, so you read .output

The tabs also call out three behaviours that are easy to get wrong and have no
Python counterpart:

  • ctx.runNode() does not throw when a child interrupts — it resolves with
    interruptIds populated and output still undefined, so an orchestrator
    that doesn't check will decide on an answer the human never gave
  • a second event carrying output silently overwrites the first
  • LlmAgent.inputSchema is not the node's input contract inside a graph; the
    validating schema belongs on the node via node(agent, {inputSchema})

Provenance of the snippets

These are ports of the runnable samples in
google/adk-js under samples/workflows/,
which already map 1:1 to the section anchors on these pages and are executed in
that repo's CI (every sample is constructed, and the offline ones run
end-to-end with the model stubbed). So the code here is derived from examples
that are known to run, not written fresh against the docs.

Verification

  • mkdocs build --strict passes.
  • All 30 TypeScript tabs render, in the correct Python → TypeScript → Go order,
    with no tab set left without a TypeScript entry.
  • All 53 --8<-- references on these pages (Go's 23 + the new 30) resolve to a
    real file and region; no unresolved directives in the output.
  • All 26 snippets type-check clean under tsc --noEmit with strict,
    noUnusedLocals and noUnusedParameters, against the adk-js workflow API.
  • License headers sit outside the include regions, so they don't leak into the
    rendered code blocks (verified against the built HTML).
  • The markdown diff is purely additive apart from the five
    language-support-tag lines.

Open questions for reviewers

  1. Version label. Resolved. @google/adk@2.0.0 is now published, so
    the ^2.0.0 pin installs and the TypeScript v2.0.0 tags are correct. All 26
    snippets type-check clean under strict against the published package.

  2. No CI for TypeScript snippets. Go and Kotlin have
    go-snippets-pr-check.yaml / kotlin-snippets-pr-check.yaml, but nothing
    compiles the TypeScript ones, so they will rot silently. I'd suggest a
    follow-up workflow running tsc across examples/typescript/snippets/*
    that would cover the whole tree, not just these pages. Can do it in this PR
    or separately, whichever you prefer. (It would be red until point 1 is
    resolved, since the package can't install today.)

Notes

  • Draft while the version question above is open.
  • No existing content was rewritten; Python and Go tabs are untouched.

The five /graphs/ pages documented graph workflows for Python and Go only,
so a TypeScript reader had to infer the API from the Python tab — which
does not translate: TypeScript has no `@node` decorator, schemas are Zod
objects rather than pydantic models, state is written through `ctx.state`
instead of returned on an event, and a user-facing message is the event's
`content` rather than a `message` field.

Every section that has a Python tab now has a TypeScript tab before the Go
one, backed by 26 snippet files under examples/typescript/snippets/graphs/.
The snippets are ported from the runnable samples in adk-js
(samples/workflows/), which already map 1:1 to these section anchors, and
they all type-check against the adk-js workflow API.

The tabs also call out the behaviours that are easy to get wrong and have
no Python equivalent: `ctx.runNode()` resolves to a node result rather than
the output, and does not throw when a child interrupts; a second event
carrying `output` silently overwrites the first; `LlmAgent.inputSchema` is
not the node's input contract inside a graph.
@netlify

netlify Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy Preview for adk-docs-preview ready!

Name Link
🔨 Latest commit 83af7c9
🔍 Latest deploy log https://app.netlify.com/projects/adk-docs-preview/deploys/6a88ba1e304b64000865cc1a
😎 Deploy Preview https://deploy-preview-2167--adk-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

The `//` annotations inside the snippet regions duplicated the prose that
already introduces each tab, and they were the first thing a reader saw in a
rendered sample rather than the API itself.

Removes the 83 `//` comments inside the `--8<--` regions across all 26 files.
JSDoc blocks stay, since they document what a function or schema is rather
than annotating a line; the Apache headers and the per-file orientation
comments above each region are untouched, and neither renders on the docs
site anyway.

Verified comment-only: compiling every file before and after with
`tsc --removeComments` produces byte-identical `.js` and `.d.ts` output
across all 52 emitted files.
@kalenkevich
kalenkevich marked this pull request as ready for review August 24, 2026 22:32
The 26 files landed double-quoted, which reads as a deliberate choice next to
the existing TypeScript snippets under examples/typescript/snippets/ — those
are predominantly single-quoted (49 of 68 imports). The repo has no prettier
config, so nothing enforces either style; this just stops the new directory
looking different from its neighbours.

Formatting only: `prettier --no-config --single-quote`, and every changed line
differs from the original by a quote character alone. Compiling before and
after with `tsc --removeComments` produces output whose only differences are
the same quote swaps, since tsc preserves the source quote style.
Comment thread examples/typescript/snippets/graphs/routes/nested_workflow.ts Outdated
Porting samples/workflows/routes/nested_workflow inlined the `titleCase`
helper and dropped the check that a character's uppercase form is a single
code point, along with the comment explaining why it is there. That changed
behaviour for word-initial characters whose uppercase expands: "first draft"
became "FIrst Draft" and "ßeta test" became "SSeta Test", where upstream
leaves both alone.

Restores the helper, the guard and the rationale. Because the helper sits
inside the --8<-- region, the explanation now renders on the page as well, so
the next person to touch it can see what the guard is for.

Also restores an unused `_ctx` parameter in the user_message snippet, the only
other place the port had drifted from upstream.

Verified by compiling each of the 26 snippets and its upstream counterpart at
adk-v2.0.0 with `tsc --removeComments` and comparing the emitted JavaScript:
all 26 are now semantically identical to samples/workflows/.

@joefernandez joefernandez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. Please address comments.

Comment thread docs/graphs/data-handling.md Outdated
Comment thread docs/graphs/data-handling.md Outdated
Comment thread docs/graphs/dynamic.md Outdated
Comment thread docs/graphs/dynamic.md Outdated
Comment thread docs/graphs/dynamic.md Outdated
Comment thread docs/graphs/routes.md Outdated
Comment thread docs/graphs/routes.md Outdated
Comment thread docs/graphs/routes.md Outdated
Comment thread docs/graphs/routes.md Outdated
Comment thread examples/typescript/snippets/graphs/routes/nested_workflow.ts Outdated
…tabs

Wording, across all TypeScript tabs:

- No sentence starts with code syntax. "`route` is independent of..." becomes
  "The `route` value is independent of...", and the same for the other cases.
- Removed informal and editorial phrasing: "earn their keep", "dropped
  straight into", "reach for", "hands you", "two things to know going in",
  "kick the children off", "fails loudly".
- Spelled out "/" as "and" in the `inputSchema` and `outputSchema` sentence.
- Described the `ctx.runNode()` interrupt behaviour in full rather than only
  as "does not throw": it returns normally with `interruptIds` populated and
  `output` undefined, and an orchestrator that skips the check continues with
  a value the user never supplied.
- Explained what a JoinNode waits for instead of referring to "the barrier".
- Tied the `rerunOnResume` option back to the code sample it follows, and
  introduced the two orchestrator details by saying when they matter.

Structure:

- The "Response schema input limitations" note appeared in both the Python
  and TypeScript tabs. Replaced both with one language-neutral note after the
  code examples.
- The "Stuck JoinNode" caution appeared in all three tabs. Replaced them with
  one caution after the code examples, stating the rule that every node
  feeding a join must produce an output.
- Moved the unbounded-cycle caution out of the TypeScript tab to the end of
  the section, since it is not language specific.

Snippet header comments got the same wording pass. Verified afterwards: the
26 snippets still type-check, all 53 snippet includes resolve, and every
snippet is still semantically identical to samples/workflows/ at adk-v2.0.0.
@kalenkevich

kalenkevich commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, changes applied.

Wording. Applied every suggestion. Beyond those, I did a pass over all 30 TypeScript tabs for the same problems rather than only the flagged lines:

  • No sentence starts with code syntax.
  • Removed the informal phrasing: "earn their keep", "dropped straight into", "reach for", "hands you", "two things to know going in", "kick the children off", "fails loudly".
  • Spelled out / as "and".
  • Described the ctx.runNode() interrupt behaviour concretely instead of "does not throw": it returns normally, with interruptIds populated and output still undefined, and an orchestrator that skips the check continues with a value the user never supplied.
  • Replaced "the barrier" with what the JoinNode actually waits for.
  • Tied rerunOnResume back to the sample it follows, and introduced the two orchestrator details by saying when they matter.

Same pass over the snippet header comments, including the nested_workflow.ts one you flagged.

Notes moved out of the tabs. All three are now single, language-neutral notes after the code examples:

  • Response schema limitations — was duplicated in the Python tab as well, so both are replaced by one note.
  • Stuck JoinNode — was in all three tabs, now one caution.
  • Unbounded graph cycles — moved out of the TypeScript tab.

Two things worth your call

1. The three JoinNode cautions did not say the same thing. Consolidating them surfaced a behavioural disagreement rather than a wording one.

Python: "the JoinNode is stuck and workflow execution stops."
Go: "the JoinNode is stuck and workflow execution stops."
TypeScript (mine): the join still releases, with undefined for that predecessor.

The TypeScript one is right for adk-js. In workflow.ts:578 the barrier releases when every predecessor reaches NodeStatus.COMPLETED — not when it produces output — and the successor's record is built with loop.nodeOutputs.get(p), which is undefined for a predecessor that completed without one. So it does not stall; it hands the next node a missing value, and the failure appears further downstream.

I could not verify the Python or Go runtimes, so I did not want to assert either behaviour for all three languages. The consolidated caution states the actionable rule that holds regardless — every node feeding a join must produce an output — and describes the consequence without claiming a specific mechanism. If the Python and Go statements are accurate for those runtimes, this is a genuine cross-language difference and probably deserves to be documented explicitly; happy to add that if you can confirm the Python side.

2. Definition-list bullets left as they are. Your "fix the other sentences that start with code syntax" note — I read that as prose sentences, not the - **`message`:** Text shown to… bullets, since the Python and Go tabs on the same pages use that form. Say the word if you want those changed too, but it would mean changing the other languages' tabs to match.

Comment thread docs/graphs/routes.md Outdated

@joefernandez joefernandez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for updates! Approved for publish

@joefernandez
joefernandez merged commit 1203686 into google:main Aug 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants