Large LangGraph applications become easier to reason about when a group of nodes has its own state contract. A compiled subgraph can occupy one node in a parent StateGraph, so the parent delegates that section while keeping the surrounding route explicit.
Direct composition works when the parent and child schemas share the keys that cross their boundary. The child can also define private keys for its internal nodes; those keys stay out of the parent's returned state when the parent schema does not include them.
The model-free program uses request and result as the shared interface, while normalized exists only inside the child. Its printed node order and final dictionary show that execution returns to the parent after the child updates the shared result.
Related: Build a state graph in LangGraph
Related: Install LangGraph with pip
Steps to add a LangGraph subgraph to a parent graph:
- Create subgraph_demo.py with the parent and subgraph state schemas.
- subgraph_demo.py
from typing_extensions import TypedDict from langgraph.graph import END, START, StateGraph class ParentState(TypedDict): request: str result: str class SubgraphState(TypedDict): request: str result: str normalized: str
- Add the child node functions below the state schemas.
def normalize_request(state: SubgraphState) -> dict: print("subgraph: normalize") return {"normalized": state["request"].upper()} def approve_request(state: SubgraphState) -> dict: print("subgraph: approve") return {"result": f'{state["normalized"]}: approved'}
- Build the compiled review subgraph below its node functions.
subgraph_builder = StateGraph(SubgraphState) subgraph_builder.add_node("normalize", normalize_request) subgraph_builder.add_node("approve", approve_request) subgraph_builder.add_edge(START, "normalize") subgraph_builder.add_edge("normalize", "approve") subgraph_builder.add_edge("approve", END) review_subgraph = subgraph_builder.compile()
- Add the parent node functions below the compiled subgraph.
def prepare_request(state: ParentState) -> dict: print("parent: prepare") return {"request": state["request"].strip()} def finish_request(state: ParentState) -> dict: print(f'parent: finish ({state["result"]})') return {}
- Build the parent graph with the compiled subgraph registered as its review node.
parent_builder = StateGraph(ParentState) parent_builder.add_node("prepare", prepare_request) parent_builder.add_node("review", review_subgraph) parent_builder.add_node("finish", finish_request) parent_builder.add_edge(START, "prepare") parent_builder.add_edge("prepare", "review") parent_builder.add_edge("review", "finish") parent_builder.add_edge("finish", END) parent_graph = parent_builder.compile()
The compiled child can be passed directly to add_node() because request and result exist in both state schemas. A wrapper node is required when the schemas need input or output transformation.
- Append the parent invocation below the compiled parent graph.
final_state = parent_graph.invoke({"request": " order-42 ", "result": ""}) print(final_state)
- Run the completed program to confirm the subgraph-to-parent state handoff.
$ python subgraph_demo.py parent: prepare subgraph: normalize subgraph: approve parent: finish (ORDER-42: approved) {'request': 'order-42', 'result': 'ORDER-42: approved'}
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.