ec2b666284
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
Dynamic Fan-Out / Fan-In with Dynamic Nodes
Overview
This sample demonstrates how to perform Dynamic Fan-Out and Fan-In using ADK's dynamic node scheduling (ctx.run_node()).
Unlike static graph-based parallel execution (which requires pre-defined branches), this pattern allows you to determine the number of parallel tasks at runtime based on the input data.
Sample Inputs
-
AI, Cloud Computing, Quantum Computing -
Python, Go, Rust, TypeScript
Graph
graph TD
START --> Orchestrator
Orchestrator --> Gen_0[Generator Task 0]
Orchestrator --> Gen_1[Generator Task 1]
Orchestrator --> Gen_N[Generator Task N]
Gen_0 --> Aggregator[Orchestrator Fan-In]
Gen_1 --> Aggregator
Gen_N --> Aggregator
How To
Key techniques demonstrated in this sample:
- Dynamic Scheduling: Using a loop to create tasks via
ctx.run_node(). - Context Isolation: Using
sub_branchinrun_nodeto isolate events for each parallel task, preventing context contamination. rerun_on_resume=True: Required on the orchestrator node to support resumption if any child node interrupts.
Code Snippet
# Fan-out: Schedule a dynamic node for each topic
tasks = []
for i, topic in enumerate(topics):
tasks.append(
ctx.run_node(
generator,
node_input=topic,
sub_branch=f"branch_{i}"
)
)
# Wait for all tasks to complete
results = await asyncio.gather(*tasks)
Pro Tip: Custom run_id
ADK auto-generates numeric IDs (e.g., @1), but you can pass a custom run_id to improve log readability (e.g., generator@task_AI) or map events to business keys.
Rules:
- Unique: Must be unique per node for fresh executions (otherwise returns cached results).
- Non-Numeric: Must contain non-numeric characters to avoid collision with auto-generated IDs.