LangGraphLangGraph 1.2 · Python 3.10+
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
37 small wins to finish your pathNext lesson

Answers with a fixed shape

A TypedDict describes your state but never checks it. If you would rather be told when something is wrong, use a Pydantic model instead.

Everything you have written so far used TypedDict, which is a description and nothing more. Put a number where a string belongs and it will sail through and break somewhere later. A Pydantic model is the same description with the checking turned on.

python
from pydantic import BaseModel
from langgraph.graph import StateGraph, START

class State(BaseModel):
    ticket: str
    urgent: bool = False

def triage(state: State):
    return {"urgent": "charge" in state.ticket}
Example
builder = StateGraph(State)
builder.add_node("triage", triage)
builder.add_edge(START, "triage")

print(builder.compile().invoke({"ticket": "charged twice"}))

Two differences from a TypedDict. The node reads state.ticket with a dot instead of square brackets, and urgent has a default, so you did not have to pass it in.

What you get for it

python
from pydantic import BaseModel, ValidationError
from langgraph.graph import StateGraph, START

class State(BaseModel):
    ticket: str

def triage(state: State):
    return {"ticket": state.ticket.upper()}
Example
builder = StateGraph(State)
builder.add_node("triage", triage)
builder.add_edge(START, "triage")

try:
    builder.compile().invoke({"ticket": 12345})
except ValidationError as e:
    print("rejected at the door:", e.errors()[0]["msg"])

The run stopped before any node ran. With a TypedDict that same call would have reached triage and failed on .upper(), several steps away from the thing that was actually wrong.

Which to use

UseWhen
TypedDictMost of the time. It is lighter, and the state is coming from your own code.
BaseModelWhen the state comes from outside, from a web request or a form, and you want it checked at the edge.
There is a cost
A Pydantic state is checked when a run starts and again each time a node is handed the state, which is where a bad value gets caught. It is not checked on the way out of a node, so if the last node returns something wrong there is nobody left to check it and it comes back to you as it is. Checking is also not free, and on a state carrying a long conversation it is real work, which is why most graphs stay on TypedDict.

The other kind of fixed shape

There is a related idea worth knowing the name of. You can also ask the model to answer in a fixed shape rather than in prose, by handing with_structured_output a schema.

python
from pydantic import BaseModel

class Triage(BaseModel):
    category: str
    urgent: bool

sorted_ticket = model.with_structured_output(Triage).invoke(messages)
print(sorted_ticket.category, sorted_ticket.urgent)

That one has no output on this page, because our stand-in model cannot do it. It works by binding the schema as a tool, so it needs a model that really understands tools. Come back to it in lesson 30, once you have a real model.

Try it yourself
  • Add a third field with a default and invoke without passing it.
  • Return the wrong type from triage and see where the error appears.
  • Change the state to a TypedDict and confirm the bad ticket is no longer rejected.

This is what real progress feels like.