Bordered avatar

Street Learner

Author
7 min read

Last Updated: a year ago

Part 1 – Introduction to LangGraph & Understanding State, Nodes, Edges and Conditional Routing (with Typesafe Python)

Part 1 – Introduction to LangGraph & Understanding State, Nodes, Edges and Conditional Routing (with Typesafe Python)

Langgraph is a Python library for building complex, stateful AI workflows. It allows developers to construct graph-based pipelines where each node represents a computational step, such as a language model call, a decision, or a transformation. With stateful graphs, conditional routing, and type safety, Langgraph helps build robust and maintainable AI applications.

In this blog (Part 1), we will cover:

  1. Introduction to Langgraph
  2. Required packages
  3. Understanding State, Nodes, Edges
  4. Building START → END graphs
  5. Implementing conditional nodes

We will use TypeSafe Python throughout to ensure correctness and readability.

Section 1: Imports and Environment Setup

Before working with Langgraph, we need to import required libraries and load environment variables.

import os
from dotenv import load_dotenv

# --- Langgraph Imports ---
from langgraph.graph import START, END, StateGraph

# Typing for state safety
from typing_extensions import TypedDict
from typing import Sequence, Literal

# OpenAI chat model
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.messages import HumanMessage, BaseMessage
from langchain_core.runnables import Runnable

Explanation:

  • START and END: Special nodes in Langgraph that mark entry and exit points in the graph.
  • StateGraph: Core class to define and manage a graph of nodes.
  • TypedDict and Sequence: Provide type safety for Python dictionaries and lists.
  • ChatOpenAI: Interface to call OpenAI chat models.
  • HumanMessage and BaseMessage: Represent messages passed through the state.

Next, load environment variables:

load_dotenv()

This ensures any API keys (e.g., OpenAI) are loaded from .env.

Section 2: Define the State and a Basic Node

In Langgraph, state is the central object passed between nodes. It contains all information needed at any step.

class State(TypedDict):
    messages: Sequence[BaseMessage]

# Initialize state with a human message
state = State(messages=[HumanMessage("Could you tell me a grook by Piet Hein?")])

Explanation:

  • State defines the structure of our state dictionary. Here it only contains messages.
  • TypedDict ensures each state key has a defined type (messages is a sequence of BaseMessage).
  • HumanMessage is from LangChain and represents user input.

Next, initialize a chat model:

chat = ChatOpenAI(
    model="gpt-4o",
    seed=365,
    temperature=0,
    max_completion_tokens=100
)

Explanation:

  • ChatOpenAI is a callable LLM interface from LangChain.
  • invoke(messages) sends a list of messages to the model and returns a response object.
  • temperature=0 ensures deterministic responses.

We can test it directly:

response = chat.invoke(state["messages"])
response.pretty_print()  # Display output nicely

Defining a Node Function

A node in Langgraph is a Python function that takes a State and returns a State.

def chatbot(state: State) -> State:
    print("\n-------> ENTERING chatbot node:")
    
    response = chat.invoke(state["messages"])  # Call OpenAI
    response.pretty_print()
    
    return State(messages=[response])
  • Node input: Current state
  • Node output: Updated state
  • Function logic: Calls the LLM and stores response in state

Test it:

chatbot(state)

Section 3: Building a Simple Graph (START → END)

Langgraph allows graph-based chaining of nodes. First, we create a simple graph:

graph = StateGraph(State)

# Add nodes
graph.add_node("chatbot", chatbot)

# Connect nodes with edges
graph.add_edge(START, "chatbot")
graph.add_edge("chatbot", END)

# Compile the graph
graph_compiled = graph.compile()

Explanation:

  • add_node(name, function): Registers a node in the graph.
  • add_edge(source, destination): Defines the flow between nodes.
  • compile(): Converts the graph into a Runnable object, executable like a function.

Check if it’s a runnable:

print(isinstance(graph, Runnable))
graph_compiled.invoke(state)

Section 4: Conditional Nodes

Conditional nodes allow branching based on the state. For example, after answering a question, we can ask: “Do you want to ask another question?”

def ask_question(state: State) -> State:
    user_input = "What is the capital of France?"  # Simulated input
    return State(messages=[HumanMessage(user_input)])

def ask_another_question(state: State) -> State:
    user_input = "yes"  # Simulated input
    return State(messages=[HumanMessage(user_input)])

Routing function determines next node:

def routing_function_final(state: State) -> Literal["ask_question", "__end__"]:
    if state["messages"][0].content == "yes":
        return "ask_question"
    else:
        return "__end__"

Section 5: Building the Conditional Graph

We can now build a graph with conditional edges:

graph_conditional = StateGraph(State)

graph_conditional.add_node("ask_question", ask_question)
graph_conditional.add_node("chatbot", chatbot)
graph_conditional.add_node("ask_another_question", ask_another_question)

graph_conditional.add_edge(START, "ask_question")
graph_conditional.add_edge("ask_question", "chatbot")
graph_conditional.add_edge("chatbot", "ask_another_question")

graph_conditional.add_conditional_edges("ask_another_question", routing_function_final)

graph_conditional_compiled = graph_conditional.compile()

Test it safely:

# Stop infinite loop by simulating "no"
def ask_another_question_once(state: State) -> State:
    return State(messages=[HumanMessage("no")])

graph_safe = StateGraph(State)
graph_safe.add_node("ask_question", ask_question)
graph_safe.add_node("chatbot", chatbot)
graph_safe.add_node("ask_another_question", ask_another_question_once)
graph_safe.add_edge(START, "ask_question")
graph_safe.add_edge("ask_question", "chatbot")
graph_safe.add_edge("chatbot", "ask_another_question")
graph_safe.add_conditional_edges("ask_another_question", routing_function_final)

graph_safe_compiled = graph_safe.compile()
graph_safe_compiled.invoke(State(messages=[]))

Summary of Part 1

In this first part of the Langgraph series, we learned:

  1. Langgraph introduction – building stateful AI pipelines
  2. Packages – Langgraph, LangChain, TypedDict, OpenAI models
  3. State – defines the structure of data passed between nodes
  4. Nodes – Python functions that operate on state
  5. Edges – connect nodes, including START and END
  6. Conditional nodes – dynamically route workflow based on state content

Related Stories