Skip to main content

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_paths in GraphBuildingContext or 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_paths includes directories containing target workflow files with @workflow.signal handlers.
  • Unresolved signal [/?/] node: Target workflow not found in search paths or no matching @workflow.signal handler 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

temporalio_graphs-0.4.0.tar.gz (19.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

temporalio_graphs-0.4.0-py3-none-any.whl (78.2 kB view details)

Uploaded Python 3

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

Hashes for temporalio_graphs-0.4.0.tar.gz
Algorithm Hash digest
SHA256 ec0a25e2328e483a05ea740d2a8092b6d1ddbcc58366f12c6232bb7ca334fd8c
MD5 d406f3f3bfdabc6fd620b7dafc7eb3db
BLAKE2b-256 6df36224a42dbf50055423d577863e41106c7109e91eddc2cede53410fdce87c

See more details on using hashes here.

File details

Details for the file temporalio_graphs-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for temporalio_graphs-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 af2ab72ebb9dee5434c16fde4c46876f773ce53f58c710f9f4d72c34b3623d79
MD5 228ac99fc9e946abae8cb65b0f38ce35
BLAKE2b-256 c7cf1e7a3edd13746eaa2f40f8e7f5545aad4144c08163770ee651ee5fd05ce4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page