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

Real application walkthrough

Walk a real graph, not a toy

QuoteGraph collects driver, vehicle, insurance history, and coverage details, rates them deterministically, then presents and accepts a quote. It is the same application used in the demo repository and it follows the current EZGraph response contract end to end.

Source of truth

Read and run the implementation in ezgraph-demo. The site explains the design; the repository is the executable reference.

  1. Run the deterministic suite first

    $git clone https://github.com/picoflowio/ezgraph-demo.git
    $cd ezgraph-demo && yarn
    $yarn test:quote-graph

    The normal test target uses scripted models and exercises validation, state replacement, transitions, rating, and acceptance without calling a provider. Run the live scenario only when you opt in:

    $USE_ENV=1 yarn test2:quote-graph
  2. Start with state ownership

    Each durable fact belongs to the node that validates it. The registry is converted into the annotated LangGraph state once, in the graph-state module.

    quote-graph.state.ts
    export type QuoteGraphNodes = {
      DriverNode?: NodeStateValue<{ driver?: DriverProfile }>;
      VehicleNode?: NodeStateValue<{ resolvedVehicleId?: string; vehicle?: VehicleUse }>;
      HistoryNode?: NodeStateValue<{ history?: InsuranceHistory }>;
      CoverageNode?: NodeStateValue<{ coverage?: CoverageSelection }>;
      QuoteNode?: NodeStateValue<{ tiers?: QuoteTier[]; acceptedTier?: QuoteTierName }>;
    };
    
    export const QuoteGraphState = createGraphStateAnnotation(
      DriverNode.name,
      () => ({} as QuoteGraphNodes),
    );
  3. A collection node validates, saves, and moves on

    The driver node validates model input in code. A rejection remains in the same node; an accepted driver is immediately persisted and transitions to vehicle collection.

    nodes/driver.node.ts
    export class DriverNode extends ConversationNode<QuoteGraphStateType> {
      @Tool("capture_driver")
      async captureDriver(input: DriverInput): Promise<ToolResponse> {
        const driver = validateDriver(input); // deterministic domain validation
        if (!driver.ok) return stay(JSON.stringify(driver.error));
    
        this.saveState({ driver: driver.value });
        return go(VehicleNode);
      }
    }

    This is intentionally direct: there is no separately maintained turn context, no synthetic handoff payload, and no automatic router to configure.

  4. Keep policy and presentation code-owned

    When coverage becomes valid, the graph can save the rate into a target channel and steer the model into quote presentation. Once a quote needs exact amounts and explicit approval, the response is built in code.

    nodes/coverage.node.ts
    this.saveState({ coverage });
    const tiers = rateQuote(driver, vehicle, history, coverage);
    return go(QuoteNode).withState({ tiers });
    nodes/quote.node.ts
    this.saveState({ acceptedTier: tier, referenceNumber });
    return direct(renderQuoteConfirmation(tier, referenceNumber));
  5. Topology is declared in one place

    quote-graph.ts
    graph.registerTurnNodes(
      DriverNode, VehicleNode, HistoryNode, CoverageNode, QuoteNode,
      TerminateSessionNode,
    );
    graph.addEdge(TerminateSessionNode, END);

    Every conversational node, including the built-in termination node, is registered. Node-returned go() responses decide ordinary transitions.

Read deeper

The response contract scales to corrections and side effects

Hotel, support, and invoice examples use the same primitives for corrections, approval gates, attachments, cleanup, and terminal actions.