Generate complete workflow visualization diagrams from Temporal workflows using static code analysis
Project description
temporalio-graphs
Static analysis for Temporal Python workflows that outputs Mermaid diagrams covering every possible execution path.
What it does
- Parses workflow source with Python's AST; no Temporal server or activity execution required.
- Detects activities, decision points (
to_decision), signal/wait points (wait_condition), and child workflow calls. - Generates every path permutation (2^n across decisions + signals) with configurable limits to prevent explosion.
- Emits validation warnings for unreachable activities and branch-limit overruns.
- Optional multi-workflow view for
execute_child_workflow()using reference, inline, or subgraph rendering modes.
Install
# Recommended for development
uv pip install temporalio-graphs
# Standard
pip install temporalio-graphs
Requires Python 3.10+ and temporalio>=1.7.1.
Quick start
from datetime import timedelta
from temporalio import workflow
from temporalio_graphs import analyze_workflow, to_decision, wait_condition
@workflow.defn
class PaymentWorkflow:
def __init__(self) -> None:
self.approved = False
@workflow.run
async def run(self, amount: int) -> str:
await workflow.execute_activity(validate_payment, amount)
if await to_decision(amount > 1_000, "HighValue"):
await workflow.execute_activity(require_approval, amount)
if await wait_condition(lambda: self.approved, timedelta(hours=24), "WaitForApproval"):
await workflow.execute_activity(process_payment, amount)
return "approved"
await workflow.execute_activity(handle_timeout, amount)
return "timeout"
print(analyze_workflow("payment_workflow.py")) # Mermaid diagram string
String literal names are required for to_decision(..., "Name") and wait_condition(..., "Name") so they can be detected statically.
Configuration (GraphBuildingContext)
Create a context and pass it to analyze_workflow() or analyze_workflow_graph().
from temporalio_graphs import GraphBuildingContext
context = GraphBuildingContext(
max_decision_points=10, # decisions + signals allowed before warning
max_paths=1024, # hard cap on generated paths
split_names_by_words=True,
include_path_list=True, # included when output_format="full"
output_format="full", # "mermaid", "paths", or "full"
child_workflow_expansion="reference", # also "inline" or "subgraph"
graph_output_file=None, # write to file when set
)
Key defaults: start/end labels "Start"/"End", decision edge labels "yes"/"no", signal edges "Signaled"/"Timeout".
Signal Visualization Options (Epic 8)
For analyze_signal_graph(), additional context options control signal discovery:
context = GraphBuildingContext(
# Maximum recursion depth for signal-based workflow discovery (default: 10)
signal_max_discovery_depth=10,
# Emit warnings for signals that cannot be resolved to handlers (default: True)
warn_unresolved_signals=True,
# Strategy: "by_name" (default), "explicit", or "hybrid"
signal_resolution_strategy="by_name",
# Visualization mode: "subgraph" (default) or "unified"
signal_visualization_mode="subgraph",
)
| Option | Type | Default | Description |
|---|---|---|---|
resolve_signal_targets |
bool |
False |
Enable signal resolution for cross-workflow visualization |
signal_target_search_paths |
tuple[Path, ...] |
() |
Additional directories to search for target workflows |
signal_resolution_strategy |
Literal |
"by_name" |
Strategy: "by_name", "explicit", "hybrid" |
signal_visualization_mode |
Literal |
"subgraph" |
Mode: "subgraph" or "unified" |
signal_max_discovery_depth |
int |
10 |
Maximum recursion depth for signal chain discovery |
warn_unresolved_signals |
bool |
True |
Emit warnings for unresolved signals |
Multi-workflow graphs
Use analyze_workflow_graph() when a parent workflow spawns children via execute_child_workflow().
from temporalio_graphs import analyze_workflow_graph, GraphBuildingContext
context = GraphBuildingContext(child_workflow_expansion="inline")
diagram = analyze_workflow_graph(
"parent_workflow.py",
workflow_search_paths=["./workflows"],
context=context,
)
child_workflow_expansion modes:
reference(default): render children as[[ChildWorkflow]]nodes without multiplying paths.inline: full end-to-end paths across parent and children.subgraph: separate Mermaid subgraphs for each workflow.
Cross-Workflow Signal Visualization
Use analyze_signal_graph() when workflows communicate via peer-to-peer signals (external workflow handles).
from temporalio_graphs import analyze_signal_graph
# Analyze workflows connected by signals (Order -> Shipping -> Notification)
diagram = analyze_signal_graph(
"workflows/order_workflow.py",
search_paths=["workflows/"],
)
print(diagram) # Mermaid diagram with connected subgraphs
Example output showing three workflows connected by signals:
flowchart TB
subgraph OrderWorkflow
s_Order((Start)) --> ProcessOrder[Process Order]
ProcessOrder --> ext_sig_ship[/Signal 'ship_order'\]
ext_sig_ship --> e_Order((End))
end
subgraph ShippingWorkflow
sig_handler_ship{{ship_order}}
s_Ship((Start)) --> ShipPackage[Ship Package]
ShipPackage --> ext_sig_notify[/Signal 'notify_shipped'\]
end
subgraph NotificationWorkflow
sig_handler_notify{{notify_shipped}}
end
%% Cross-workflow signal connections
ext_sig_ship -.ship_order.-> sig_handler_ship
ext_sig_notify -.notify_shipped.-> sig_handler_notify
%% Signal handler styling
style sig_handler_ship fill:#e6f3ff,stroke:#0066cc
style sig_handler_notify fill:#e6f3ff,stroke:#0066cc
Signal Visualization Syntax
| Element | Mermaid Syntax | Purpose |
|---|---|---|
| Subgraph wrapper | subgraph Name ... end |
Groups workflow nodes |
| Signal handler | {{signal_name}} |
Hexagon for signal reception |
| External signal | [/Signal 'name'\] |
Trapezoid for signal send |
| Cross-workflow edge | -.signal_name.-> |
Dashed edge between workflows |
| Handler styling | fill:#e6f3ff,stroke:#0066cc |
Blue color for hexagons |
| Unresolved signal | [/?/] |
Warning node when handler not found |
Signal Types Comparison
| Signal Type | Function | Shape |
|---|---|---|
| Internal (Epic 4) | wait_condition() |
Hexagon {{name}} |
| Parent-Child (Epic 6) | execute_child_workflow() |
Subroutine [[name]] |
| Peer-to-Peer (Epic 7-8) | get_external_workflow_handle().signal() |
Trapezoid + Hexagon |
Examples
Run the curated examples (linear, branching, signals, multi-decision, cross-workflow signals):
make run-examples
# or individually
python examples/simple_linear/run.py # Linear workflow (Epic 2)
python examples/money_transfer/run.py # Decision branches (Epic 3)
python examples/signal_workflow/run.py # Wait conditions (Epic 4)
python examples/multi_decision/run.py # Multiple decisions
python examples/signal_flow/run.py # Cross-workflow signals (Epic 8)
The examples/signal_flow/ directory demonstrates peer-to-peer workflow signaling with three connected workflows (Order -> Shipping -> Notification). See expected_output.md for the expected Mermaid diagram.
Troubleshooting
- Too many branches: Raise
max_decision_points/max_pathsinGraphBuildingContextor simplify the workflow; errors include the calculated path count. - Names missing: Decision/signal names must be string literals; variables and f-strings are ignored by static analysis.
- Unsupported flow: Loops or dynamic activity names are not graphed; refactor to explicit steps or decisions.
- Signals not resolved: Ensure
search_pathsincludes directories containing target workflow files with@workflow.signalhandlers. - Unresolved signal
[/?/]node: Target workflow not found in search paths or no matching@workflow.signalhandler exists. Check that signal name matches handler method name. - Multiple handlers warning: Multiple workflows handle the same signal name. This may be intentional (e.g., broadcast signals) or indicate naming conflicts. Review handler implementations.
Development
uv venv && source .venv/bin/activate
uv sync
pytest -v --cov=src/temporalio_graphs
mypy src/
ruff check src/ tests/ examples/
ruff format src/ tests/ examples/
Reference
- API:
docs/api-reference.md - Architecture rationale:
docs/architecture.md,spike/EXECUTIVE_SUMMARY.md - Examples:
examples/ - License: MIT (
LICENSE)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file temporalio_graphs-0.4.0.tar.gz.
File metadata
- Download URL: temporalio_graphs-0.4.0.tar.gz
- Upload date:
- Size: 19.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec0a25e2328e483a05ea740d2a8092b6d1ddbcc58366f12c6232bb7ca334fd8c
|
|
| MD5 |
d406f3f3bfdabc6fd620b7dafc7eb3db
|
|
| BLAKE2b-256 |
6df36224a42dbf50055423d577863e41106c7109e91eddc2cede53410fdce87c
|
File details
Details for the file temporalio_graphs-0.4.0-py3-none-any.whl.
File metadata
- Download URL: temporalio_graphs-0.4.0-py3-none-any.whl
- Upload date:
- Size: 78.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af2ab72ebb9dee5434c16fde4c46876f773ce53f58c710f9f4d72c34b3623d79
|
|
| MD5 |
228ac99fc9e946abae8cb65b0f38ce35
|
|
| BLAKE2b-256 |
c7cf1e7a3edd13746eaa2f40f8e7f5545aad4144c08163770ee651ee5fd05ce4
|