Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions cookbooks/agent-path-correction/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ from langgraph.graph.message import add_messages

Intent = Literal["refund", "replacement", "escalate"]


class SupportState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
item: str | None
Expand Down Expand Up @@ -115,6 +116,7 @@ _CATALOG = {
"monitor": ("AeroPro 4K Monitor", "ORD-33170"),
}


def _lookup_order(text: str) -> tuple[str | None, str | None]:
lowered = text.lower()
for keyword, (item, order_id) in _CATALOG.items():
Expand Down Expand Up @@ -162,6 +164,7 @@ def _classify_intent(text: str) -> Intent:
return "refund"
return "escalate"


def classify(state: SupportState) -> SupportState:
intent = _classify_intent(_latest_user_text(state["messages"]))
return {"intent": intent}
Expand All @@ -186,6 +189,7 @@ def refund(state: SupportState) -> SupportState:
"messages": [AIMessage(content=f"I can handle that as a refund for order {order_id}.")],
}


def replacement(state: SupportState) -> SupportState:
order_id = state["order_id"]
return {
Expand All @@ -195,6 +199,7 @@ def replacement(state: SupportState) -> SupportState:
],
}


def escalate(state: SupportState) -> SupportState:
order_id = state["order_id"]
return {
Expand All @@ -218,6 +223,7 @@ rewind to: order id known, decision not yet made.
def route(state: SupportState) -> Intent:
return state["intent"] or "escalate"


builder = StateGraph(SupportState)
builder.add_node("identify_order", identify_order)
builder.add_node("classify", classify)
Expand All @@ -228,7 +234,8 @@ builder.add_node("escalate", escalate)
builder.add_edge(START, "identify_order")
builder.add_edge("identify_order", "classify")
builder.add_conditional_edges(
"classify", route,
"classify",
route,
{"refund": "refund", "replacement": "replacement", "escalate": "escalate"},
)
builder.add_edge("refund", END)
Expand Down Expand Up @@ -322,8 +329,14 @@ until `identify_order` runs, then carried by every later checkpoint.
history = list(saver.list(prod_config))
for i, tpl in enumerate(reversed(history), start=1):
values = tpl.checkpoint.get("channel_values", {})
print(i, tpl.config["configurable"]["checkpoint_id"], _stage(tpl.metadata.get("step")),
values.get("order_id"), values.get("intent"), values.get("resolution"))
print(
i,
tpl.config["configurable"]["checkpoint_id"],
_stage(tpl.metadata.get("step")),
values.get("order_id"),
values.get("intent"),
values.get("resolution"),
)
```

**In the code:** `demo.py`, `# === Step 9 ===` (Phase 3, after the customer's
Expand All @@ -350,11 +363,14 @@ rehydrates it.

```python
fork_point = next(
(tpl for tpl in reversed(history)
if _values(tpl).get("order_id") and not _values(tpl).get("resolution")),
(
tpl
for tpl in reversed(history)
if _values(tpl).get("order_id") and not _values(tpl).get("resolution")
),
history[-1],
)
rehydrated = saver.get_tuple(fork_point.config) # one checkpoint read from Aerospike
rehydrated = saver.get_tuple(fork_point.config) # one checkpoint read from Aerospike
# rehydrated state: order_id == "ORD-10482", intent == None, resolution == None
```

Expand All @@ -376,8 +392,8 @@ same `ORD-10482`. LangGraph writes new checkpoints from here; the refund run fro
Phase 1 stays in Aerospike under its own checkpoint ids.

```python
fork_config = fork_point.config # thread_id + checkpoint_ns + checkpoint_id
forked = _resolve(graph, fork_config, CORRECTED_REQUEST) # "...send a replacement instead."
fork_config = fork_point.config # thread_id + checkpoint_ns + checkpoint_id
forked = _resolve(graph, fork_config, CORRECTED_REQUEST) # "...send a replacement instead."
# forked.order_id == "ORD-10482" (reused from the checkpoint)
# forked.resolution == "Replacement selected for order ORD-10482"
```
Expand Down
11 changes: 8 additions & 3 deletions cookbooks/expiring-chat-sessions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ from typing import Annotated, TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages


class ChatState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
```
Expand All @@ -101,7 +102,7 @@ and appends the reply. Split this into two pieces in `agent.py`:
**Part A — create the model** (`agent.py`, `# === Step 2 ===`):

```python
def _make_llm() -> BaseChatModel: # generic interface; FakeListChatModel is one impl
def _make_llm() -> BaseChatModel: # generic interface; FakeListChatModel is one impl
return FakeListChatModel(
responses=[
"Hi! I'm your support assistant. How can I help?",
Expand All @@ -116,12 +117,13 @@ def _make_llm() -> BaseChatModel: # generic interface; FakeListChatModel

```python
def build_chat_graph(checkpointer: BaseCheckpointSaver) -> CompiledStateGraph:
llm = _make_llm() # <-- Part A is called here
llm = _make_llm() # <-- Part A is called here

def chatbot(state: ChatState) -> ChatState:
system = SystemMessage(content="You are a concise, helpful support assistant.")
response: AIMessage = llm.invoke([system, *state["messages"]])
return {"messages": [response]}

# ... Step 3 continues in the next section
```

Expand Down Expand Up @@ -177,6 +179,7 @@ AEROSPIKE_HOST: str = "127.0.0.1"
AEROSPIKE_PORT: int = 3000
AEROSPIKE_NAMESPACE: str = "test"


@contextmanager
def _connect() -> Iterator[aerospike.Client]:
client = aerospike.client({"hosts": [(AEROSPIKE_HOST, AEROSPIKE_PORT)]}).connect()
Expand All @@ -203,6 +206,7 @@ automatically when the time elapses.
CHAT_TTL_MINUTES: int = 1
CHAT_REFRESH_ON_READ: bool = False


def _build_checkpointer(client: aerospike.Client) -> AerospikeSaver:
return AerospikeSaver(
client=client,
Expand Down Expand Up @@ -241,6 +245,7 @@ THREAD_ID: str = "session-demo-001"

config: RunnableConfig = {"configurable": {"thread_id": THREAD_ID}}


def _say(graph: CompiledStateGraph, config: RunnableConfig, text: str) -> int:
result = graph.invoke({"messages": [HumanMessage(text)]}, config)
return len(result["messages"])
Expand Down Expand Up @@ -321,7 +326,7 @@ time.sleep(wait_seconds)

tpl = saver.get_tuple(config)
if tpl is not None:
return 1 # checkpoint still present — TTL has not elapsed yet
return 1 # checkpoint still present — TTL has not elapsed yet

count = _say(graph, config, "Hello again?") # back to 2 messages
```
Expand Down
3 changes: 3 additions & 0 deletions packages/langgraph-checkpoint-aerospike/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,17 @@ docker run -d --name aerospike -p 3000-3002:3000-3002 container.aerospike.com/ae
client = aerospike.client({"hosts": [("127.0.0.1", 3000)]}).connect()
checkpointer = AerospikeSaver(client=client, namespace="test")


# 2. Define a minimal LangGraph graph.
class State(TypedDict):
messages: Annotated[list, add_messages]


def chatbot(state: State) -> State:
last = state["messages"][-1].content
return {"messages": [("assistant", f"You said: {last}")]}


builder = StateGraph(State)
builder.add_node("chatbot", chatbot)
builder.add_edge(START, "chatbot")
Expand Down
3 changes: 3 additions & 0 deletions packages/langgraph-store-aerospike/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ from langgraph.store.aerospike import AerospikeStore
client = aerospike.client({"hosts": [("127.0.0.1", 3000)]}).connect()
store = AerospikeStore(client=client, namespace="test", set="langgraph_store")


# 2. Define a graph whose node reads and writes long-term memory.
class State(TypedDict):
user_id: str
food: str


def remember_preference(state: State) -> State:
store = get_store()
namespace = ("users", state["user_id"])
Expand All @@ -56,6 +58,7 @@ def remember_preference(state: State) -> State:
print(profile.value) # {"favorite_food": "pizza"}
return state


builder = StateGraph(State)
builder.add_node("remember_preference", remember_preference)
builder.add_edge(START, "remember_preference")
Expand Down
Loading
Loading