Write Python. Get a production LLM graph. Declarative LLM graph compiler that compiles @node-decorated functions into LangGraph.
Project description
NeoGraph
Write Python. Get a production graph.
Docs & guides: neograph.pro — full documentation site with tutorials, API reference, and side-by-side LangGraph comparisons.
# uv (recommended)
uv add neograph
# pip
pip install neograph
Define your LLM pipeline as Python functions. The framework infers the topology, validates types at assembly time, and compiles to LangGraph with checkpointing, observability, and tool orchestration. No DSL. No YAML. No add_node / add_edge.
A function is a node. A parameter name is an edge. An if is a branch.
Functions are nodes
from neograph import node, construct_from_module, compile, run
@node(outputs=Claims, prompt='rw/decompose', model='reason')
def decompose(topic: RawText) -> Claims: ...
@node(outputs=Classified, prompt='rw/classify', model='fast')
def classify(decompose: Claims) -> Classified: ...
@node(outputs=Report)
def report(classify: Classified) -> Report:
return Report(summary=f"{len(classify.items)} claims processed")
pipeline = construct_from_module(sys.modules[__name__])
graph = compile(pipeline)
result = run(graph, input={'node_id': 'doc-001'})
classify(decompose: Claims) — the parameter name IS the dependency. Rename a function, downstream breaks at import time. Fan-in is just more parameters: def report(claims, scores, verified).
Mode is inferred. prompt= + model= means LLM call (think mode). Neither means the function body runs (scripted mode).
if is a branch
from neograph import ForwardConstruct, Node, compile
class Analysis(ForwardConstruct):
check = Node(outputs=CheckResult, prompt='check', model='fast')
deep = Node(outputs=Result, prompt='deep-analysis', model='reason')
shallow = Node(outputs=Result, prompt='quick-scan', model='fast')
def forward(self, topic):
checked = self.check(topic)
if checked.confidence > 0.8:
return self.shallow(checked)
else:
return self.deep(checked)
graph = compile(Analysis())
The if compiles to a conditional edge. for compiles to fan-out. Python is the graph language. Your type checker sees everything. Your debugger works.
Everything else is a keyword
# Fan-out over a collection
@node(outputs=MatchResult, map_over='clusters.groups', map_key='label')
def verify(cluster: ClusterGroup) -> MatchResult: ...
# N-way ensemble with merge
@node(outputs=Claims, prompt='decompose', model='reason',
ensemble_n=3, merge_fn='merge_claims')
def decompose() -> Claims: ...
# Fan-out + ensemble on the same node (Each x Oracle fusion)
@node(outputs=ClaimGroupingResult, prompt='decompose', model='reason',
ensemble_n=3, merge_fn='group_claims',
map_over='chunk_document', map_key='chunk_idx')
def decompose(chunk: ReadContext) -> ClaimGroupingResult: ...
# Human-in-the-loop interrupt
@node(outputs=ValidationResult,
interrupt_when=lambda s: {'issues': s.check_quality.issues} if not s.check_quality.passed else None)
def check_quality(claims: Claims) -> ValidationResult: ...
# Agent with tools — typed tool results preserved
@node(outputs={"result": ExplorationResult, "tool_log": list[ToolInteraction]},
mode='agent', model='research', prompt='explore',
tools=[Tool("search", budget=5)],
context=["catalog"]) # verbatim state injection
def explore(claim: VerifyClaim) -> ExplorationResult: ...
# Non-node parameters: runtime input, config, constants
from typing import Annotated
from neograph import FromInput, FromConfig
@node(outputs=Report)
def summarize(
claims: Claims, # upstream node
topic: Annotated[str, FromInput], # from run(input={...})
rate_limiter: Annotated[RateLimiter, FromConfig], # from config
max_items: int = 10, # constant
) -> Report: ...
Catches mistakes before you run
ConstructError: Node 'verify' declares inputs=ClusterGroup but no upstream
produces a compatible value.
upstream producers:
- node 'cluster': Clusters
hint: did you forget to fan out? try .map(lambda s: s.cluster.groups, key='...')
at my_pipeline.py:42
Types are validated at assembly time — when you define the pipeline, not when you execute it. 48 compile-time checks backed by a rustc-style fixture suite. CLI validation: neograph check my_pipeline.py.
Visualize the compiled graph
from neograph import compile, describe_graph
graph = compile(pipeline)
print(describe_graph(graph)) # Mermaid diagram — paste into GitHub, docs, mermaid.live
Set NEOGRAPH_DEV=1 for auto-printed DAG summaries after every compile().
Scales to real systems
Organize by module. Each pipeline is a Python module. Import nodes across modules. construct_from_module finds them all.
Sub-constructs from @node functions. construct_from_functions("verify", [explore, score], input=Claim, output=Result) builds a sub-construct with port param resolution. Mix @node functions and sub-constructs in one construct_from_functions call.
Observe everything. Structured logs on every node. Pass trace providers and shared resources via Annotated[T, FromConfig].
Retry on failure. compile(pipeline, retry_policy=RetryPolicy(max_attempts=3)) retries LLM nodes on malformed JSON, validation errors, and transient API failures.
Test at every level. node.run_isolated() for unit tests. compile() + run() for integration. forward() direct-call for debugging.
LLMs can build the graph too
For runtime construction — an LLM emitting a pipeline via tool calls, a config system defining workflows — use the programmatic API with the | pipe syntax:
from neograph import Node, Construct, Oracle, Each, compile, run
decompose = Node("decompose", mode="think", outputs=Claims,
prompt="rw/decompose", model="reason") | Oracle(n=3, merge_fn="merge")
verify = Node("verify", mode="agent", outputs=MatchResult,
prompt="verify", model="fast") | Each(over="decompose.items", key="label")
pipeline = Construct("dynamic", nodes=[decompose, verify])
graph = compile(pipeline)
Three surfaces — @node, ForwardConstruct, Node | Modifier — one compiler.
Documentation
Full documentation is at neograph.pro:
- Quick Start — install, configure, build a pipeline, run it
- The @node API — functions as nodes, modifier kwargs, FromInput/FromConfig, organizing pipelines
- ForwardConstruct — class-based pipelines with Python
if/for/try - Runtime Construction — LLM-driven pipeline assembly, programmatic API
- vs LangGraph — side-by-side for five common patterns
- API Reference
Examples
16 runnable examples in examples/, 3 multi-file mini-projects (lead-research, code-review, spec-builder), and 5 side-by-side LangGraph comparisons in examples/vs_langgraph/. Walkthroughs at neograph.pro.
License
Code: MIT
Documentation content © 2025-2026 Constantine Mirin, mirin.pro. Licensed under CC BY-ND 4.0.
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
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 neograph-0.3.0.tar.gz.
File metadata
- Download URL: neograph-0.3.0.tar.gz
- Upload date:
- Size: 96.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
691a34567ce7f3f6364d5470de92f71af924e0ee0abf439eccbab31692fb1d81
|
|
| MD5 |
472fe4bed713322050e9e65ae3ac242d
|
|
| BLAKE2b-256 |
3ca9de07b28fb9d51ab078e4396cb6f53d9d2466aa6b2032949b065d04508196
|
Provenance
The following attestation bundles were made for neograph-0.3.0.tar.gz:
Publisher:
publish.yml on KonstantinMirin/neograph
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
neograph-0.3.0.tar.gz -
Subject digest:
691a34567ce7f3f6364d5470de92f71af924e0ee0abf439eccbab31692fb1d81 - Sigstore transparency entry: 1262558494
- Sigstore integration time:
-
Permalink:
KonstantinMirin/neograph@dc8883667f4e88858e0c6a61803382683ae72fcd -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/KonstantinMirin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dc8883667f4e88858e0c6a61803382683ae72fcd -
Trigger Event:
push
-
Statement type:
File details
Details for the file neograph-0.3.0-py3-none-any.whl.
File metadata
- Download URL: neograph-0.3.0-py3-none-any.whl
- Upload date:
- Size: 103.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d117d11f4e5a7e66d03b2509d24747aee0301f9a160ba8de9c762b0985eb84a9
|
|
| MD5 |
744b892b521ce221b3030eb50ef3d596
|
|
| BLAKE2b-256 |
fffade1aea6aadbe8ffc94b461a83edf1ef80434122f75cd7fff3e3c717b76f3
|
Provenance
The following attestation bundles were made for neograph-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on KonstantinMirin/neograph
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
neograph-0.3.0-py3-none-any.whl -
Subject digest:
d117d11f4e5a7e66d03b2509d24747aee0301f9a160ba8de9c762b0985eb84a9 - Sigstore transparency entry: 1262558513
- Sigstore integration time:
-
Permalink:
KonstantinMirin/neograph@dc8883667f4e88858e0c6a61803382683ae72fcd -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/KonstantinMirin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dc8883667f4e88858e0c6a61803382683ae72fcd -
Trigger Event:
push
-
Statement type: