No EZGraph key required. Free production use. No runtime fee. Bring your own model provider. Run in your own infrastructure. Optional support is available.

The conversational application layer above LangGraph

EZGraph runs on LangGraph. The comparison here is not “LangGraph versus a different runtime”; it is the cost of writing a guided conversational application directly on the graph runtime versus using a shared application contract above it.

The measured application is Sequoia Auto Insurance quoting. Both implementations collect a driver, vehicle, history, and coverage; validate tool calls; rate in deterministic code; allow quote adjustments; accept a tier; maintain three history spaces; expire idle sessions; and offer termination. The domain backend, prompts, catalog, and clock helpers are shared by behavior and independently normalize to the same size.

Results, recounted from the current source

The site count is generated against the current ezgraph-demo checkout by scripts/ezgraph-compare-metrics.mjs. It removes blank lines, comment-only lines, and imports from the application files. Framework source is excluded on both sides.

Scope EZGraph quote-graph Direct LangGraph quote-langgraph Reduction
Framework-facing graph code 617 1,283 51.9%
Shared domain backend 287 287 0% — identical
NestJS controller 47 74 36.5%
Graph code + controller 664 1,357 51.1%
Raw graph code + controller 924 1,588 41.8%

The headline delta is 666 normalized lines. It is one application, not a universal performance or productivity claim.

Source-artifact breakdown

These are additive source files, so the arithmetic is independently checkable:

Artifact Direct LangGraph EZGraph Delta
Main graph and all stage implementations 980 551 −429
State definition and reducers 83 66 −17
Session store: memory, SQLite, MongoDB, serialization 177 0 −177
Standalone domain type module 43 0 — types live with node state −43
Total 1,283 617 −666
EZGraph file Normalized lines Direct LangGraph file Normalized lines
quote-graph.ts 49 quote-langgraph.ts 980
quote-graph.state.ts 66 quote-langgraph.state.ts 83
nodes/driver.node.ts 81 quote-session-store.ts 177
nodes/vehicle.node.ts 110 quote-types.ts 43
nodes/history.node.ts 79
nodes/coverage.node.ts 71
nodes/quote.node.ts 161

The current EZGraph contract

State starts with a typed registry that names the durable channel owned by each node. The graph converts that registry into its annotated LangGraph state.

export type SupportGraphNodes = {
  VerifyNode?: NodeStateValue<{ order?: Order }>;
  ReturnNode?: NodeStateValue<{ selectedLines?: ReturnLine[]; reason?: string }>;
  ApprovalNode?: NodeStateValue<{ refund?: Refund }>;
};

A node validates input and writes the fact where it becomes true:

this.saveState({ selectedLines, reason });

The reducer replaces that node channel. A handler then makes the next outcome explicit:

if (!reason) return stay("Ask the customer for a return reason.");
if (needsApproval(refund)) return go(ApprovalNode).withState({ refund });
return direct(renderApprovedRefund(refund));

There is no turn-context object, context-creation hook, synthetic tool result, outcome builder, or automatic outcome-router configuration. A normal ConversationNode<GraphState> uses the graph-state generic only.

Return Meaning
stay(feedback) Keep the model in the current node; usually validation feedback.
go(TargetNode) Move to a registered node in the current invocation. Use .withState() for an atomic target-state update.
direct(content) Return an exact code-owned customer response: a price table, policy decision, ticket ID, or confirmation.
finish(content) Complete the conversation with an exact final response.

Use withMessages() for real messages or attachments and withCleanup() when cleanup belongs to the tool result.

Repeated patterns, counted

The direct-LangGraph counts below are exact ripgrep counts in the current source tree. The EZGraph values are the current QuoteGraph implementation.

Pattern Direct LangGraph EZGraph
Manual Schema.parse(call.args) blocks 8 0
Tool-name dispatch comparisons 14 0
terminate_session handling sites in application graph code 7 0 — built-in termination node
Explicit route: state writes 16 0
Hand-written reducer helpers / channels 2 helpers × 17 channels 0
Hand-written session store implementations 3 0
Domain tool declarations 8 8
Explicit next-stage returns state-route writes 5 go() responses
Validation retry returns ad hoc branches 8 stay() responses
Code-owned terminal/presentation responses ad hoc graph branches 1 direct(), 1 finish()

Modularity: adding a sixth stage

Addition Direct LangGraph EZGraph
Add a Discounts stage between coverage and quote 14 coordinated edits: stage union, history map, schema, tool wrapper, stage tools, model binding, agent, dispatch, nodes, routes, phase, and channels 5 edits: node file, node-state registry key, history-space entry, node registration, and the upstream go(QuoteNode) target

In EZGraph, prompt, tool schema, validation, durable state, and response behavior live together in the node; only the registry and graph topology are shared edit points.

Reproduce the measurement

From the PicoFlow website checkout, with the sibling demo checkout present:

node scripts/ezgraph-compare-metrics.mjs

For the repeated-pattern counts:

cd ../ezgraph-demo
rg -c 'Schema\.parse\(call\.args\)' src/graphs/quote-langgraph/quote-langgraph.ts
rg -c 'call\.name ===|call\.name !==' src/graphs/quote-langgraph/quote-langgraph.ts
rg -c 'route: "' src/graphs/quote-langgraph/quote-langgraph.ts
rg -c '@Tool\(' src/graphs/quote-graph/nodes/*.ts

Where direct LangGraph remains the right choice

Direct LangGraph is a strong choice when the work requires its lowest-level control: native mid-execution interrupts, custom state semantics outside this conversational contract, deeply bespoke graph scheduling, or a permissive dependency. EZGraph is deliberately narrower. It is useful when durable guided customer conversations are the recurring application shape.

Continue with the small tutorial, the QuoteGraph walkthrough, or the developer guide.