Skip to main content

Build LangGraph graphs from JSON configuration

Project description

dynamic-langgraph — DynamicLangGraph Engine

PyPI version Python versions License Coverage

Build LangGraph graphs from JSON configuration — no hardcoded topologies.

dynamic-langgraph is a Python library that converts declarative JSON config documents into fully compiled, executable LangGraph graphs. Define your nodes, edges, state schema, and execution settings in JSON. The engine validates, compiles, and hands you back an async-ready graph.


Features

  • JSON-driven graph construction — declare nodes, edges, and state in config; skip the boilerplate
  • Pre-compilation validation — catches unreachable nodes, static cycles, bad module paths, and signature mismatches before any node runs
  • Async-first — sync handlers are auto-wrapped in run_in_executor; no event loop blocking
  • Human-in-the-loopinterrupt_before/interrupt_after breakpoints with Command(resume=...) support
  • Map-reduce fan-outSend API for parallel branch execution and aggregation
  • Nested subgraphs — register compiled CompiledStateGraph instances as nodes in a parent graph
  • Streaming — all 5 LangGraph stream modes (values, updates, debug, messages, custom)
  • PersistenceInMemorySaver, SqliteSaver, or any custom BaseCheckpointSaver
  • Per-node cachingCachePolicy with configurable TTL
  • LangSmith telemetry — structured trace attributes emitted automatically
  • Graph visualization — export to Mermaid (.mmd) and images (PNG, SVG, JPG, PDF)
  • Strictly typed — fully annotated public API; passes mypy --strict

Requirements

  • Python 3.11+
  • langgraph >= 1.0.6
  • jsonschema >= 4.23

Installation

pip install dynamic-langgraph

From source (development):

git clone https://github.com/your-org/dynamic-langgraph.git
cd dynamic-langgraph
uv sync --dev

Quickstart

1. Write a JSON config

{
  "graph_id": "echo-graph",
  "version": "1.0.0",
  "state_definition": {
    "type": "typed_dict",
    "module_path": "myapp.state",
    "class_name": "EchoState"
  },
  "nodes": [
    {
      "name": "echo",
      "handler": {
        "module_path": "myapp.nodes",
        "function_name": "echo_node"
      }
    }
  ],
  "edges": {
    "entry_point": "echo",
    "static_edges": [
      {"from": "echo", "to": "__end__"}
    ]
  }
}

2. Compile and run

import asyncio
from dlg import DLGEngine

graph = DLGEngine.from_file("graph_config.json")
result = asyncio.run(graph.ainvoke({"message": "hello"}))
print(result)

Or validate and compile separately:

from dlg import DLGValidator, DLGCompiler

config = {"graph_id": "echo-graph", "version": "1.0.0", ...}

DLGValidator(config).validate()
graph = DLGCompiler(config).compile()

Human-in-the-Loop

Add "interrupt_before": true to a node and provide a checkpointer:

{
  "nodes": [
    {
      "name": "review",
      "handler": {"module_path": "myapp.nodes", "function_name": "review_node"},
      "interrupt_before": true
    }
  ],
  "checkpointer": {"type": "memory"}
}
import asyncio
from langgraph.types import Command
from dlg import DLGEngine

graph = DLGEngine.from_file("graph_config.json")
thread = {"configurable": {"thread_id": "session-1"}}

# Pauses at the interrupt_before node
await graph.ainvoke({"task": "review this"}, config=thread)

# Resume after human decision
result = await graph.ainvoke(Command(resume="approved"), config=thread)

Map-Reduce Fan-Out

{
  "edges": {
    "entry_point": "fan_out",
    "conditional_edges": [
      {
        "from": "fan_out",
        "send_router": {
          "module_path": "myapp.routers",
          "function_name": "fan_out_router"
        }
      }
    ]
  }
}
from langgraph.types import Send

def fan_out_router(state):
    return [Send("process_item", {"item": x}) for x in state["items"]]

Streaming

async for event in graph.astream(inputs, stream_mode="updates"):
    print(event)

Supported modes: "values", "updates", "debug", "messages", "custom".


Graph Visualization

Python API

from dlg.mermaid import json_to_mermaid, convert_json_to_mmd, convert_mmd_to_image

# Get Mermaid diagram text from a config dict
config = {"graph_id": "my-graph", "version": "1.0.0", ...}
print(json_to_mermaid(config))
# flowchart TD
#     __start__((__start__)) --> echo
#     echo --> __end__((__end__))

# Write .mmd beside the JSON file
mmd_path = convert_json_to_mmd("graph_config.json")
# -> graph_config.mmd

# Convert .mmd to PNG (requires mmdc or npx)
img_path = convert_mmd_to_image(mmd_path, "png")
# -> graph_config.png

# Write image to a specific output directory
img_path = convert_mmd_to_image(mmd_path, "png", output_dir="graph-previews/")
# -> graph-previews/graph_config.png

CLI

# Generate .mmd only
python -m dlg graph_config.json

# Generate .mmd and a PNG
python -m dlg graph_config.json --format png

# Generate .mmd, PNG and SVG into a target folder
python -m dlg graph_config.json -f png -f svg --output-dir graph-previews/

# After pip install dynamic-langgraph:
dlg graph_config.json -f png --output-dir graph-previews/

mmdc from @mermaid-js/mermaid-cli is required for image export. Install globally with npm i -g @mermaid-js/mermaid-cli, or ensure npx is available (falls back automatically).


Error Hierarchy

All validation errors are raised before any node executes.

Exception Trigger
GraphValidationError Schema violation, missing field, bad module path
UnreachableNodeException Node with no path from entry point
StaticLoopException Cycle in static-only edges
MixedRoutingConflict Node has both static edge and Command(goto=...)
SendTargetError Send targets an undeclared node
SubgraphCompatibilityError Subgraph state schema conflicts with parent
CheckpointerMissingError Interrupt/resume attempted without checkpointer
SchemaVersionMismatch Checkpoint config version differs from current config

Testing Utilities

from dlg.testing import DLGTestHarness

harness = DLGTestHarness(config)
result = await harness.invoke_node("echo", state={"message": "hi"})
assert result["message"] == "hi"

Configuration Reference

A full JSON config supports the following top-level keys:

Key Required Description
graph_id Yes Unique graph identifier
version Yes Semantic version string (e.g. "1.0.0")
state_definition Yes State schema — type (typed_dict, pydantic, dataclass), module_path, class_name
nodes Yes List of node descriptors (each with name and handler or subgraph)
edges Yes entry_point, static_edges, conditional_edges
checkpointer No {"type": "memory"}, {"type": "sqlite", "db_path": "..."}, or custom
settings No recursion_limit, max_concurrency
global_defaults No Default retry_policy, timeout_seconds
cache No Global cache policy
security No allowed_module_prefixes allowlist

See docs/concepts/json-schema.md for the full schema reference.


Project Structure

dlg/
├── __init__.py        # Public API exports
├── __main__.py        # CLI entry point (python -m dlg / dlg command)
├── compiler.py        # DLGCompiler — StateGraph assembly
├── mermaid.py         # Mermaid/image export (json_to_mermaid, convert_*)
├── validator.py       # DLGValidator — all pre-compilation checks
├── exceptions.py      # Typed exception hierarchy
├── testing.py         # DLGTestHarness utility
├── _config.py         # Pydantic config models (GraphConfig, NodeConfig, …)
├── _schema.py         # JSON metaschema
├── _wrappers.py       # Async node wrapper + sync adapter
└── _telemetry.py      # LangSmith integration

tests/
├── unit/              # DLGValidator, DLGCompiler, mermaid, models
└── integration/       # Full graph execution, streaming, checkpointing

docs/                  # MkDocs site (concepts, API reference, examples)

Development

# Install dev dependencies
uv sync --dev

# Run tests
uv run python -m pytest

# Run tests with coverage
uv run python -m pytest --cov=dlg --cov-report=term-missing

# Type check
uv run mypy dlg --strict

# Build docs locally
uv run mkdocs serve

Contributing

  1. Fork the repo and create a feature branch
  2. Add tests for new behavior (coverage target: 90%)
  3. Ensure mypy --strict passes
  4. Open a pull request — CI runs on Python 3.11, 3.12, and 3.13

License

Apache License 2.0 — see LICENSE.


Acknowledgements

Built on top of LangGraph by LangChain, Inc.

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

dynamic_langgraph-0.1.1.tar.gz (172.9 kB view details)

Uploaded Source

Built Distribution

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

dynamic_langgraph-0.1.1-py3-none-any.whl (20.5 kB view details)

Uploaded Python 3

File details

Details for the file dynamic_langgraph-0.1.1.tar.gz.

File metadata

  • Download URL: dynamic_langgraph-0.1.1.tar.gz
  • Upload date:
  • Size: 172.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for dynamic_langgraph-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b54aeea300d5724fb6371898746b84ba9aeff31ead4ad8845d70d752ebbe7351
MD5 c4510ec28a60dfc587b5e3fa2d479187
BLAKE2b-256 7764b8fc48298151e2987f400a45b5b235b57f48bc52e090b2c73f63e48f3cad

See more details on using hashes here.

File details

Details for the file dynamic_langgraph-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: dynamic_langgraph-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 20.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for dynamic_langgraph-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 dfda58ff8cc78d87e2eef3b2c137077ea0ebe1bd6c177aed3d29ca62847f21cc
MD5 70775576ec14a66a235625320e1e7682
BLAKE2b-256 f3debaefdbb56e81e7814be1ccbcacec065bd9b0d71705f4248fa05644023d4b

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