Compare commits

...
Author SHA1 Message Date
Sydney Runkle 3a302cf919 Reapply "Remove MessageGraph (#4875)"
This reverts commit 3fa3a586b5.
2025-06-29 11:20:08 -04:00
joaquin-borggio-lcandGitHub 84e14f47bf docs: Add pre-req for egress to control plane (#5241)
added pre-req for egress
2025-06-27 14:18:39 -07:00
Lauren Hirata SinghandGitHub 83bbe42eab docs: Nav reorg (#5236)
* docs: Nav consolidation

* nav

* reorg

* fix links

* fix

* prebuilts

* fix spelling

* reorg

* reorg
2025-06-27 14:06:26 -04:00
David DuongandGitHub 70894af72c fix(sdk-js): avoid stale client when fetching history (#5240) 2025-06-27 20:04:41 +02:00
Tat Dat Duong e466327524 Bump to 0.0.87 2025-06-27 20:03:03 +02:00
Tat Dat Duong b6655fe083 Use the client hash only in useEffect 2025-06-27 19:59:21 +02:00
Tat Dat Duong 9b7cc1c82e fix(sdk-js): avoid stale client when fetching history 2025-06-27 19:51:49 +02:00
Nuno CamposandGitHub 80d6bddd1b Fix deadlock in SqliteStore (#5234) 2025-06-27 09:03:57 -07:00
Nuno Campos c406aede96 Fix deadlock in SqliteStore
- If setup wasnt called separately _cursor() and setup() would deadlock
- The call to setup() in _cursor() should be outside the lock block, as setup() also acquires the lock and re-checks the setup flag
2025-06-27 08:57:35 -07:00
Eugene YurtsevandGitHub 96bfc8bad9 docs: cross links for functional api (#5231)
add cross-links
2025-06-27 11:31:18 -04:00
0fd6306623 docs: Time travel and breakpoints (#5215)
* docs: Time travel and breakpoints

* fix links

* fix

* edits

* Fix link

* Remove circular redirects

* revert llms.txt

---------

Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
2025-06-27 14:49:53 +00:00
Yassin NouhandGitHub f01ab13cab DOC: Add missing docs_txt = format_docs() lines in adaptive RAG tutorial (#5222) 2025-06-27 10:45:12 -04:00
Eugene YurtsevandGitHub 1beaf48cf9 docs: quick nav fixes (#5229)
Quick nav fixes
2025-06-27 10:37:42 -04:00
Sam CrowderandGitHub 847b0e8243 add warning about serverless (#5218) 2025-06-27 07:31:52 -07:00
Eugene YurtsevandGitHub 02a2467610 docs: context update (#5207)
Update context
2025-06-27 14:25:54 +00:00
Sydney RunkleandGitHub fa5e7a97c2 release[prebuilt]: bump version to v0.5.1 (#5228)
version bump
2025-06-27 14:02:14 +00:00
Sydney RunkleandGitHub 233a92ccb9 fix[prebuilt]: bind tools even if it's only provider tools (#5227)
bind tools even if just provider tools
2025-06-27 09:49:26 -04:00
lc-arjunandGitHub e277a6bd9b chore: sdk bumps (#5220)
* chore: sdk bumps

* uv lock
2025-06-26 17:57:05 -07:00
lc-arjunandGitHub 16250fe038 chore: update api ref docs and fix schemas (#5219) 2025-06-26 17:41:03 -07:00
Nuno Campos 216d1be0a5 Revert "Reapply "docs: deploy from v0 branch for now (#4960)" (#5184) (#5185)"
This reverts commit cb63c1ab72.
2025-06-26 15:52:45 -07:00
50 changed files with 958 additions and 3420 deletions
+4 -6
View File
@@ -4,11 +4,9 @@ on:
push: push:
branches: branches:
- main - main
- v0
pull_request: pull_request:
branches: branches:
- main - main
- v0
workflow_dispatch: workflow_dispatch:
permissions: permissions:
@@ -84,9 +82,9 @@ jobs:
run: make llms-text run: make llms-text
- name: Build site - name: Build site
run: | run: |
# If this is v0 branch, then we want to download stats. we do this # If this is main branch, then we want to download stats. we do this
# with the env variable DOWNLOAD_STATS=true # with the env variable DOWNLOAD_STATS=true
if [ "${{ github.ref }}" == "refs/heads/v0" ]; then if [ "${{ github.ref }}" == "refs/heads/main" ]; then
DOWNLOAD_STATS=true make build-docs DOWNLOAD_STATS=true make build-docs
else else
make build-docs make build-docs
@@ -146,7 +144,7 @@ jobs:
fi fi
- name: Configure GitHub Pages - name: Configure GitHub Pages
if: github.ref == 'refs/heads/v0' if: github.ref == 'refs/heads/main'
uses: actions/configure-pages@v5 uses: actions/configure-pages@v5
- name: Upload Pages Artifact - name: Upload Pages Artifact
@@ -156,6 +154,6 @@ jobs:
path: ./docs/site/ path: ./docs/site/
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/v0' if: github.ref == 'refs/heads/main'
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v4
+3 -2
View File
@@ -100,9 +100,9 @@ REDIRECT_MAP = {
"how-tos/create-react-agent-system-prompt.ipynb": "agents/context.md#prompts", "how-tos/create-react-agent-system-prompt.ipynb": "agents/context.md#prompts",
"how-tos/create-react-agent-structured-output.ipynb": "agents/agents.md#structured-output", "how-tos/create-react-agent-structured-output.ipynb": "agents/agents.md#structured-output",
# Time-travel # Time-travel
"how-tos/human_in_the_loop/edit-graph-state.ipynb": "how-tos/human_in_the_loop/time-travel.ipynb", "how-tos/human_in_the_loop/edit-graph-state.ipynb": "how-tos/human_in_the_loop/time-travel.md",
# breakpoints # breakpoints
"how-tos/human_in_the_loop/dynamic_breakpoints.ipynb": "how-tos/human_in_the_loop/breakpoints.ipynb", "how-tos/human_in_the_loop/dynamic_breakpoints.ipynb": "how-tos/human_in_the_loop/breakpoints.md",
# misc # misc
"prebuilt.md": "agents/prebuilt.md", "prebuilt.md": "agents/prebuilt.md",
"reference/prebuilt.md": "reference/agents.md", "reference/prebuilt.md": "reference/agents.md",
@@ -111,6 +111,7 @@ REDIRECT_MAP = {
"concepts/v0-human-in-the-loop.md": "concepts/human-in-the-loop.md", "concepts/v0-human-in-the-loop.md": "concepts/human-in-the-loop.md",
"how-tos/index.md": "index.md", "how-tos/index.md": "index.md",
"tutorials/introduction.ipynb": "concepts/why-langgraph.md", "tutorials/introduction.ipynb": "concepts/why-langgraph.md",
"agents/deployment.md": "tutorials/langgraph-platform/local-server.md",
# deployment redirects # deployment redirects
"how-tos/deploy-self-hosted.md": "cloud/deployment/self_hosted_data_plane.md", "how-tos/deploy-self-hosted.md": "cloud/deployment/self_hosted_data_plane.md",
"concepts/self_hosted.md": "concepts/langgraph_self_hosted_data_plane.md", "concepts/self_hosted.md": "concepts/langgraph_self_hosted_data_plane.md",
+91 -143
View File
@@ -1,17 +1,8 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Context # Context
Agents often require more than a list of messages to function effectively. They need **context**. **Context engineering** is the practice of building dynamic systems that provide the right information and tools, in the right format, so that a language model can plausibly accomplish a task.
Context includes *any* data outside the message list that can shape agent behavior or tool execution. This can be: Context includes *any* data outside the message list that can shape behavior. This can be:
- Information passed at runtime, like a `user_id` or API credentials. - Information passed at runtime, like a `user_id` or API credentials.
- Internal state updated during a multi-step reasoning process. - Internal state updated during a multi-step reasoning process.
@@ -22,18 +13,10 @@ LangGraph provides **three** primary ways to supply context:
| Type | Description | Mutable? | Lifetime | | Type | Description | Mutable? | Lifetime |
|------------------------------------------------------------------------------|-----------------------------------------------|----------|-------------------------| |------------------------------------------------------------------------------|-----------------------------------------------|----------|-------------------------|
| [**Config**](#config-static-context) | data passed at the start of a run | ❌ | per run | | [**Config**](#config-static-context) | data passed at the start of a run | ❌ | per run |
| [**State**](#state-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation | | [**Short-term memory (State)**](#short-term-memory-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation |
| [**Long-term Memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations | | [**Long-term memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations |
You can use context to: ## Provide runtime context
- Adjust the system prompt the model sees
- Feed tools with necessary inputs
- Track facts during an ongoing conversation
## Providing Runtime Context
Use this when you need to inject data into an agent at runtime.
### Config (static context) ### Config (static context)
@@ -44,88 +27,83 @@ Specify configuration using a key called **"configurable"** which is reserved
for this purpose: for this purpose:
```python ```python
agent.invoke( graph.invoke( # (1)!
{"messages": [{"role": "user", "content": "hi!"}]}, {"messages": [{"role": "user", "content": "hi!"}]}, # (2)!
# highlight-next-line # highlight-next-line
config={"configurable": {"user_id": "user_123"}} config={"configurable": {"user_id": "user_123"}} # (3)!
) )
``` ```
### State (mutable context) 1. This is the invocation of the agent or graph. The `invoke` method runs the underlying graph with the provided input.
2. This example uses messages as an input, which is common, but your application may use different input structures.
3. This is where you pass the configuration data. The `config` parameter allows you to provide additional context that the agent can use during its execution.
State acts as short-term memory during a run. It holds dynamic data that can evolve during execution, such as values derived from tools or LLM outputs. === "Agent prompt"
```python
class CustomState(AgentState):
# highlight-next-line
user_name: str
agent = create_react_agent(
# Other agent parameters...
# highlight-next-line
state_schema=CustomState,
)
agent.invoke({
"messages": "hi!",
"user_name": "Jane"
})
```
!!! tip "Turning on memory"
Please see the [memory guide](../how-tos/memory/add-memory.md) for more details on how to enable memory. This is a powerful feature that allows you to persist the agent's state across multiple invocations.
Otherwise, the state is scoped only to a single agent run.
### Long-Term Memory (cross-conversation context)
For context that spans *across* conversations or sessions, LangGraph allows access to **long-term memory** via a `store`. This can be used to read or update persistent facts (e.g., user profiles, preferences, prior interactions). For more, see the [Memory guide](../how-tos/memory/add-memory.md).
## Customizing Prompts with Context { #prompts }
Prompts define how the agent behaves. To incorporate runtime context, you can dynamically generate prompts based on the agent's state or config.
Common use cases:
- Personalization
- Role or goal customization
- Conditional behavior (e.g., user is admin)
=== "Using config"
```python ```python
from langchain_core.messages import AnyMessage from langchain_core.messages import AnyMessage
from langchain_core.runnables import RunnableConfig from langchain_core.runnables import RunnableConfig
from langgraph.prebuilt import create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.prebuilt import create_react_agent
def prompt( # highlight-next-line
state: AgentState, def prompt(state: AgentState, config: RunnableConfig) -> list[AnyMessage]:
# highlight-next-line
config: RunnableConfig,
) -> list[AnyMessage]:
# highlight-next-line
user_name = config["configurable"].get("user_name") user_name = config["configurable"].get("user_name")
system_msg = f"You are a helpful assistant. User's name is {user_name}" system_msg = f"You are a helpful assistant. Address the user as {user_name}."
return [{"role": "system", "content": system_msg}] + state["messages"] return [{"role": "system", "content": system_msg}] + state["messages"]
agent = create_react_agent( agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest", model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather], tools=[get_weather],
# highlight-next-line
prompt=prompt prompt=prompt
) )
agent.invoke( agent.invoke(
..., {"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line # highlight-next-line
config={"configurable": {"user_name": "John Smith"}} config={"configurable": {"user_name": "John Smith"}}
) )
``` ```
=== "Using state" * See [Agents](../agents/agents.md) for details.
=== "Workflow node"
```python
from langchain_core.runnables import RunnableConfig
# highlight-next-line
def node(state: State, config: RunnableConfig):
user_name = config["configurable"].get("user_name")
...
```
* See [the Graph API](https://langchain-ai.github.io/langgraph/how-tos/graph-api/#add-runtime-configuration) for details.
=== "In a tool"
```python
from langchain_core.runnables import RunnableConfig
@tool
# highlight-next-line
def get_user_info(config: RunnableConfig) -> str:
"""Retrieve user information based on user ID."""
user_id = config["configurable"].get("user_id")
return "User is John Smith" if user_id == "user_123" else "Unknown user"
```
See the [tool calling guide](../how-tos/tool-calling.md#configuration) for details.
### Short-term memory (mutable context)
State acts as [short-term memory](../concepts/memory.md) during a run. It holds dynamic data that can evolve during execution, such as values derived from tools or LLM outputs.
=== "In an agent"
Example shows how to incorporate state into an agent **prompt**.
State can also be accessed by the agent's **tools**, which can read or update the state as needed. See [tool calling guide](../how-tos/tool-calling.md#short-term-memory) for details.
```python ```python
from langchain_core.messages import AnyMessage from langchain_core.messages import AnyMessage
@@ -133,15 +111,14 @@ Common use cases:
from langgraph.prebuilt import create_react_agent from langgraph.prebuilt import create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState from langgraph.prebuilt.chat_agent_executor import AgentState
class CustomState(AgentState): # highlight-next-line
# highlight-next-line class CustomState(AgentState): # (1)!
user_name: str user_name: str
def prompt( def prompt(
# highlight-next-line # highlight-next-line
state: CustomState state: CustomState
) -> list[AnyMessage]: ) -> list[AnyMessage]:
# highlight-next-line
user_name = state["user_name"] user_name = state["user_name"]
system_msg = f"You are a helpful assistant. User's name is {user_name}" system_msg = f"You are a helpful assistant. User's name is {user_name}"
return [{"role": "system", "content": system_msg}] + state["messages"] return [{"role": "system", "content": system_msg}] + state["messages"]
@@ -150,87 +127,58 @@ Common use cases:
model="anthropic:claude-3-7-sonnet-latest", model="anthropic:claude-3-7-sonnet-latest",
tools=[...], tools=[...],
# highlight-next-line # highlight-next-line
state_schema=CustomState, state_schema=CustomState, # (2)!
# highlight-next-line
prompt=prompt prompt=prompt
) )
agent.invoke({ agent.invoke({
"messages": "hi!", "messages": "hi!",
# highlight-next-line
"user_name": "John Smith" "user_name": "John Smith"
}) })
``` ```
## Accessing Context in Tools { #tools } 1. Define a custom state schema that extends `AgentState` or `MessagesState`.
2. Pass the custom state schema to the agent. This allows the agent to access and modify the state during execution.
Tools can access context through special parameter **annotations**.
* Use `RunnableConfig` for config access
* Use `Annotated[StateSchema, InjectedState]` for agent state
!!! tip === "In a workflow"
These annotations prevent LLMs from attempting to fill in the values. These parameters will be **hidden** from the LLM.
=== "Using config"
```python ```python
def get_user_info( from typing_extensions import TypedDict
# highlight-next-line from langchain_core.messages import AnyMessage
config: RunnableConfig, from langgraph.graph import StateGraph
) -> str:
"""Look up user info."""
# highlight-next-line
user_id = config["configurable"].get("user_id")
return "User is John Smith" if user_id == "user_123" else "Unknown user"
agent = create_react_agent( # highlight-next-line
model="anthropic:claude-3-7-sonnet-latest", class CustomState(TypedDict): # (1)!
tools=[get_user_info], messages: list[AnyMessage]
) extra_field: int
agent.invoke( # highlight-next-line
{"messages": [{"role": "user", "content": "look up user information"}]}, def node(state: CustomState): # (2)!
# highlight-next-line messages = state["messages"]
config={"configurable": {"user_id": "user_123"}} ...
) return { # (3)!
# highlight-next-line
"extra_field": state["extra_field"] + 1
}
builder = StateGraph(State)
builder.add_node(node)
builder.set_entry_point("node")
graph = builder.compile()
``` ```
1. Define a custom state
2. Access the state in any node or tool
3. The Graph API is designed to work as easily as possible with state. The return value of a node represents a requested update to the state.
=== "Using State"
```python !!! tip "Turning on memory"
from typing import Annotated
from langgraph.prebuilt import InjectedState
class CustomState(AgentState): Please see the [memory guide](../how-tos/memory/add-memory.md) for more details on how to enable memory. This is a powerful feature that allows you to persist the agent's state across multiple invocations. Otherwise, the state is scoped only to a single run.
# highlight-next-line
user_id: str
def get_user_info( ### Long-term memory (cross-conversation context)
# highlight-next-line
state: Annotated[CustomState, InjectedState]
) -> str:
"""Look up user info."""
# highlight-next-line
user_id = state["user_id"]
return "User is John Smith" if user_id == "user_123" else "Unknown user"
agent = create_react_agent( For context that spans *across* conversations or sessions, LangGraph allows access to **long-term memory** via a `store`. This can be used to read or update persistent facts (e.g., user profiles, preferences, prior interactions).
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info],
# highlight-next-line
state_schema=CustomState,
)
agent.invoke({ For more information, see the [Memory guide](../how-tos/memory/add-memory.md).
"messages": "look up user information",
# highlight-next-line
"user_id": "user_123"
})
```
### Update Context from Tools
Tools can update agent's context (state and long-term memory) during execution. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts. See [Memory](../how-tos/memory/add-memory.md#read-short-term) guide for more information.
-92
View File
@@ -1,92 +0,0 @@
---
search:
boost: 2
tags:
- agent
hide:
- tags
---
# Deployment
To deploy your LangGraph agent, create and configure a LangGraph app. This setup supports both local development and production deployments.
Features:
* 🖥️ Local server for development
* 🧩 Studio Web UI for visual debugging
* ☁️ Cloud and 🔧 self-hosted deployment options
* 📊 LangSmith integration for tracing and observability
!!! info "Requirements"
- ✅ You **must** have a [LangSmith account](https://www.langchain.com/langsmith). You can sign up for **free** and get started with the free tier.
## Create a LangGraph app
```bash
pip install -U "langgraph-cli[inmem]"
langgraph new path/to/your/app --template new-langgraph-project-python
```
This will create an empty LangGraph project. You can modify it by replacing the code in `src/agent/graph.py` with your agent code. For example:
```python
from langgraph.prebuilt import create_react_agent
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
graph = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
prompt="You are a helpful assistant"
)
```
### Install dependencies
In the root of your new LangGraph app, install the dependencies in `edit` mode so your local changes are used by the server:
```shell
pip install -e .
```
### Create an `.env` file
You will find a `.env.example` in the root of your new LangGraph app. Create
a `.env` file in the root of your new LangGraph app and copy the contents of the `.env.example` file into it, filling in the necessary API keys:
```bash
LANGSMITH_API_KEY=lsv2...
ANTHROPIC_API_KEY=sk-
```
## Launch LangGraph server locally
```shell
langgraph dev
```
This will start up the LangGraph API server locally. If this runs successfully, you should see something like:
> Ready!
>
> - API: [http://localhost:2024](http://localhost:2024/)
>
> - Docs: http://localhost:2024/docs
>
> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
See this [tutorial](https://langchain-ai.github.io/langgraph/tutorials/langgraph-platform/local-server/) to learn more about running LangGraph app locally.
## LangGraph Studio Web UI
LangGraph Studio Web is a specialized UI that you can connect to LangGraph API server to enable visualization, interaction, and debugging of your application locally. Test your graph in the LangGraph Studio Web UI by visiting the URL provided in the output of the `langgraph dev` command.
> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
## Deployment
Once your LangGraph app is running locally, you can deploy it using LangGraph Platform. Refer to the [deployment options guide](../concepts/deployment_options.md) for detailed instructions on all supported deployment models.
+2 -2
View File
@@ -8,9 +8,9 @@ hide:
- tags - tags
--- ---
# Agent development with LangGraph # Agent development using prebuilt components
**LangGraph** provides both low-level primitives and high-level prebuilt components for building agent-based applications. This section focuses on the **prebuilt**, **reusable** components designed to help you construct agentic systems quickly and reliably—without the need to implement orchestration, memory, or human feedback handling from scratch. LangGraph provides both low-level primitives and high-level prebuilt components for building agent-based applications. This section focuses on the prebuilt, ready-to-use components designed to help you construct agentic systems quickly and reliably—without the need to implement orchestration, memory, or human feedback handling from scratch.
## What is an agent? ## What is an agent?
+5 -5
View File
@@ -20,7 +20,7 @@ my-app/
|-- openai_agent.py # code for your graph |-- openai_agent.py # code for your graph
``` ```
where the graph is defined in `openai_agent.py`. where the graph is defined in `openai_agent.py`.
### No rebuild ### No rebuild
@@ -28,11 +28,11 @@ In the standard LangGraph API configuration, the server uses the compiled graph
```python ```python
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, MessageGraph from langgraph.graph import END, START, StateGraph, MessagesState
model = ChatOpenAI(temperature=0) model = ChatOpenAI(temperature=0)
graph_workflow = MessageGraph() graph_workflow = StateGraph(MessagesState)
graph_workflow.add_node("agent", model) graph_workflow.add_node("agent", model)
graph_workflow.add_edge("agent", END) graph_workflow.add_edge("agent", END)
@@ -61,7 +61,7 @@ To make your graph rebuild on each new run with custom configuration, you need t
from typing import Annotated from typing import Annotated
from typing_extensions import TypedDict from typing_extensions import TypedDict
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, MessageGraph from langgraph.graph import END, START
from langgraph.graph.state import StateGraph from langgraph.graph.state import StateGraph
from langgraph.graph.message import add_messages from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode from langgraph.prebuilt import ToolNode
@@ -144,4 +144,4 @@ Finally, you need to specify the path to your graph-making function (`make_graph
} }
``` ```
See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file) See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file)
@@ -15,11 +15,15 @@ Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](.
### Prerequisites ### Prerequisites
1. `KEDA` is installed on your cluster. 1. `KEDA` is installed on your cluster.
helm repo add kedacore https://kedacore.github.io/charts helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda --namespace keda --create-namespace helm install keda kedacore/keda --namespace keda --create-namespace
1. A valid `Ingress` controller is installed on your cluster. 1. A valid `Ingress` controller is installed on your cluster.
1. You have slack space in your cluster for multiple deployments. `Cluster-Autoscaler` is recommended to automatically provision new nodes. 1. You have slack space in your cluster for multiple deployments. `Cluster-Autoscaler` is recommended to automatically provision new nodes.
1. You will need to enable egress to two control plane URLs. The listener polls these endpoints for deployments:
https://api.host.langchain.com
https://api.smith.langchain.com
### Setup ### Setup
@@ -1,4 +1,4 @@
# Human-in-the-loop in LangGraph Server # Human-in-the-loop using Server API
To review, edit, and approve tool calls in an agent or workflow, use LangGraph's [human-in-the-loop](../../concepts/human_in_the_loop.md) features. To review, edit, and approve tool calls in an agent or workflow, use LangGraph's [human-in-the-loop](../../concepts/human_in_the_loop.md) features.
@@ -1,10 +1,16 @@
# Breakpoints # Set breakpoints using Server API
[Breakpoints](../../concepts/breakpoints.md) pause graph execution at defined points and let you step through each stage. They use LangGraph's [**persistence layer**](../../concepts/persistence.md), which saves the graph state after each step. [Breakpoints](../../concepts/breakpoints.md) pause graph execution at defined points and let you step through each stage. They use LangGraph's [**persistence layer**](../../concepts/persistence.md), which saves the graph state after each step.
With breakpoints, you can inspect the graph's state and node inputs at any point. Execution pauses **indefinitely** until you resume, as the checkpointer preserves the state. With breakpoints, you can inspect the graph's state and node inputs at any point. Execution pauses indefinitely until you resume, as the checkpointer preserves the state.
## Set breakpoints !!! tip
For conceptual information on breakpoints, see [Breakpoints](../../concepts/breakpoints.md).
## Set static breakpoints
Static breakpoints are triggered either before or after a node executes. You can set static breakpoints by specifying `interrupt_before` and `interrupt_after` at compile time or run time.
=== "Compile time" === "Compile time"
@@ -78,10 +84,9 @@ With breakpoints, you can inspect the graph's state and node inputs at any point
}" }"
``` ```
!!! tip ## Example
This example shows how to add **static** breakpoints. See [this guide](../../how-tos/human_in_the_loop/breakpoints.ipynb) for more options for how to add breakpoints.
This example shows how to add **static** breakpoints. See [Use breakpoints](../../how-tos/human_in_the_loop/breakpoints.md) for more options on adding breakpoints.
=== "Python" === "Python"
@@ -177,8 +182,4 @@ With breakpoints, you can inspect the graph's state and node inputs at any point
--data "{ --data "{
\"assistant_id\": \"agent\" \"assistant_id\": \"agent\"
}" }"
``` ```
## Learn more
- [**LangGraph breakpoints guide**](../../how-tos/human_in_the_loop/breakpoints.ipynb): learn more about adding breakpoints in LangGraph.
@@ -1,10 +1,8 @@
# Time travel # Time travel using Server API
LangGraph provides [**time travel**](../../concepts/time-travel.md) functionality to **resume execution from a prior checkpoint** — either replaying the same state or modifying it to explore alternatives. In all cases, resuming past execution produces a **new fork** in the history. LangGraph provides the [**time travel**](../../concepts/time-travel.md) functionality to resume execution from a prior checkpoint, either replaying the same state or modifying it to explore alternatives. In all cases, resuming past execution produces a new fork in the history.
## Use time travel To time travel using the LangGraph Server API (via the LangGraph SDK):
To use time-travel in LangGraph:
1. **Run the graph** with initial inputs using [LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/)'s [`client.runs.wait`][langgraph_sdk.client.RunsClient.wait] or [`client.runs.stream`][langgraph_sdk.client.RunsClient.stream] APIs. 1. **Run the graph** with initial inputs using [LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/)'s [`client.runs.wait`][langgraph_sdk.client.RunsClient.wait] or [`client.runs.stream`][langgraph_sdk.client.RunsClient.stream] APIs.
2. **Identify a checkpoint in an existing thread**: Use [`client.threads.get_history`][langgraph_sdk.client.ThreadsClient.get_history] method to retrieve the execution history for a specific `thread_id` and locate the desired `checkpoint_id`. 2. **Identify a checkpoint in an existing thread**: Use [`client.threads.get_history`][langgraph_sdk.client.ThreadsClient.get_history] method to retrieve the execution history for a specific `thread_id` and locate the desired `checkpoint_id`.
@@ -12,7 +10,7 @@ To use time-travel in LangGraph:
3. **(Optional) modify the graph state**: Use the [`client.threads.update_state`][langgraph_sdk.client.ThreadsClient.update_state] method to modify the graph’s state at the checkpoint and resume execution from alternative state. 3. **(Optional) modify the graph state**: Use the [`client.threads.update_state`][langgraph_sdk.client.ThreadsClient.update_state] method to modify the graph’s state at the checkpoint and resume execution from alternative state.
4. **Resume execution from the checkpoint**: Use the [`client.runs.wait`][langgraph_sdk.client.RunsClient.wait] or [`client.runs.stream`][langgraph_sdk.client.RunsClient.stream] APIs with an input of `None` and the appropriate `thread_id` and `checkpoint_id`. 4. **Resume execution from the checkpoint**: Use the [`client.runs.wait`][langgraph_sdk.client.RunsClient.wait] or [`client.runs.stream`][langgraph_sdk.client.RunsClient.stream] APIs with an input of `None` and the appropriate `thread_id` and `checkpoint_id`.
## Example ## Use time travel in a workflow
??? example "Example graph" ??? example "Example graph"
@@ -237,4 +235,4 @@ To use time-travel in LangGraph:
## Learn more ## Learn more
- [**LangGraph time travel guide**](../../how-tos/human_in_the_loop/time-travel.ipynb): learn more about using time travel in LangGraph. - [**LangGraph time travel guide**](../../how-tos/human_in_the_loop/time-travel.md): learn more about using time travel in LangGraph.
+20 -2
View File
@@ -2601,8 +2601,7 @@
"description": "Configuration to use for the graph. Useful when graph is configurable and you want to update the assistant's configuration." "description": "Configuration to use for the graph. Useful when graph is configurable and you want to update the assistant's configuration."
}, },
"metadata": { "metadata": {
"type": "object", "type": "object", "title": "Metadata",
"title": "Metadata",
"description": "Metadata to merge with existing assistant metadata." "description": "Metadata to merge with existing assistant metadata."
}, },
"name": { "name": {
@@ -2708,6 +2707,13 @@
"title": "Schedule", "title": "Schedule",
"description": "The cron schedule to execute this job on." "description": "The cron schedule to execute this job on."
}, },
"end_time": {
"type": "string",
"format": "date-time",
"title": "End Time",
"description": "The end date to stop running the cron."
},
"assistant_id": { "assistant_id": {
"anyOf": [ "anyOf": [
{ {
@@ -2814,6 +2820,18 @@
"description": "The number of results to skip.", "description": "The number of results to skip.",
"default": 0, "default": 0,
"minimum": 0 "minimum": 0
},
"sort_by": {
"type": "string",
"enum": ["cron_id", "assistant_id", "thread_id", "next_run_date", "end_time", "created_at", "updated_at"],
"title": "Sort By",
"description": "The field to sort by."
},
"sort_order": {
"type": "string",
"enum": ["asc", "desc"],
"title": "Sort Order",
"description": "The order to sort by."
} }
}, },
"type": "object", "type": "object",
+5 -1
View File
@@ -5,10 +5,14 @@ search:
# Breakpoints # Breakpoints
Breakpoints pause graph execution at defined points and let you step through each stage. They use LangGraph's [**persistence layer**](./persistence.md), which saves the graph state after each step. [Breakpoints](../how-tos/human_in_the_loop/breakpoints.md) pause graph execution at defined points and let you step through each stage. They use LangGraph's [**persistence layer**](./persistence.md), which saves the graph state after each step.
With breakpoints, you can inspect the graph's state and node inputs at any point. Execution pauses **indefinitely** until you resume, as the checkpointer preserves the state. With breakpoints, you can inspect the graph's state and node inputs at any point. Execution pauses **indefinitely** until you resume, as the checkpointer preserves the state.
<figure markdown="1"> <figure markdown="1">
![image](img/breakpoints.png){: style="max-height:400px"} ![image](img/breakpoints.png){: style="max-height:400px"}
<figcaption>An example graph consisting of 3 sequential steps with a breakpoint before step_3. </figcaption> </figure> <figcaption>An example graph consisting of 3 sequential steps with a breakpoint before step_3. </figcaption> </figure>
!!! tip
For information on how to use breakpoints, see [Set breakpoints](../how-tos/human_in_the_loop/breakpoints.md) and [Set breakpoints using Server API](../cloud/how-tos/human_in_the_loop_breakpoint.md).
+14 -12
View File
@@ -18,10 +18,21 @@ The Functional API uses two key building blocks:
This provides a minimal abstraction for building workflows with state management and streaming. This provides a minimal abstraction for building workflows with state management and streaming.
!!! tip !!! tip
For information on how to use the functional API, see [Use Functional API](../how-tos/use-functional-api.md).
## Functional API vs. Graph API
For users who prefer a more declarative approach, LangGraph's [Graph API](./low_level.md) allows you to define workflows using a Graph paradigm. Both APIs share the same underlying runtime, so you can use them together in the same application.
Here are some key differences:
- **Control flow**: The Functional API does not require thinking about graph structure. You can use standard Python constructs to define workflows. This will usually trim the amount of code you need to write.
- **Short-term memory**: The **GraphAPI** requires declaring a [**State**](./low_level.md#state) and may require defining [**reducers**](./low_level.md#reducers) to manage updates to the graph state. `@entrypoint` and `@tasks` do not require explicit state management as their state is scoped to the function and is not shared across functions.
- **Checkpointing**: Both APIs generate and use checkpoints. In the **Graph API** a new checkpoint is generated after every [superstep](./low_level.md). In the **Functional API**, when tasks are executed, their results are saved to an existing checkpoint associated with the given entrypoint instead of creating a new checkpoint.
- **Visualization**: The Graph API makes it easy to visualize the workflow as a graph which can be useful for debugging, understanding the workflow, and sharing with others. The Functional API does not support visualization as the graph is dynamically generated during runtime.
For users who prefer a more declarative approach, LangGraph's [Graph API](./low_level.md) allows you to define workflows using a Graph paradigm. Both APIs share the same underlying runtime, so you can use them together in the same application.
Please see the [Functional API vs. Graph API](#functional-api-vs-graph-api) section for a comparison of the two paradigms.
## Example ## Example
@@ -532,15 +543,6 @@ While different runs of a workflow can produce different results, resuming a **s
Idempotency ensures that running the same operation multiple times produces the same result. This helps prevent duplicate API calls and redundant processing if a step is rerun due to a failure. Always place API calls inside **tasks** functions for checkpointing, and design them to be idempotent in case of re-execution. Re-execution can occur if a **task** starts, but does not complete successfully. Then, if the workflow is resumed, the **task** will run again. Use idempotency keys or verify existing results to avoid duplication. Idempotency ensures that running the same operation multiple times produces the same result. This helps prevent duplicate API calls and redundant processing if a step is rerun due to a failure. Always place API calls inside **tasks** functions for checkpointing, and design them to be idempotent in case of re-execution. Re-execution can occur if a **task** starts, but does not complete successfully. Then, if the workflow is resumed, the **task** will run again. Use idempotency keys or verify existing results to avoid duplication.
## Functional API vs. Graph API
The **Functional API** and the [Graph APIs (StateGraph)](./low_level.md#stategraph) provide two different paradigms to create applications with LangGraph. Here are some key differences:
- **Control flow**: The Functional API does not require thinking about graph structure. You can use standard Python constructs to define workflows. This will usually trim the amount of code you need to write.
- **Short-term memory**: The **GraphAPI** requires declaring a [**State**](./low_level.md#state) and may require defining [**reducers**](./low_level.md#reducers) to manage updates to the graph state. `@entrypoint` and `@tasks` do not require explicit state management as their state is scoped to the function and is not shared across functions.
- **Checkpointing**: Both APIs generate and use checkpoints. In the **Graph API** a new checkpoint is generated after every [superstep](./low_level.md). In the **Functional API**, when tasks are executed, their results are saved to an existing checkpoint associated with the given entrypoint instead of creating a new checkpoint.
- **Visualization**: The Graph API makes it easy to visualize the workflow as a graph which can be useful for debugging, understanding the workflow, and sharing with others. The Functional API does not support visualization as the graph is dynamically generated during runtime.
## Common Pitfalls ## Common Pitfalls
### Handling side effects ### Handling side effects
+4
View File
@@ -17,6 +17,10 @@ To review, edit, and approve tool calls in an agent or workflow, [use LangGraph'
![image](../concepts/img/human_in_the_loop/tool-call-review.png){: style="max-height:400px"} ![image](../concepts/img/human_in_the_loop/tool-call-review.png){: style="max-height:400px"}
</figure> </figure>
!!! tip
For information on how to use human-in-the-loop, see [Enable human intervention](../how-tos/human_in_the_loop/add-human-in-the-loop.md) and [Human-in-the-loop using Server API](../cloud/how-tos/add-human-in-the-loop.md).
## Key capabilities ## Key capabilities
* **Persistent execution state**: LangGraph allows you to pause execution **indefinitely** — for minutes, hours, or even days—until human input is received. This is possible because LangGraph checkpoints the graph state after each step, which allows the system to persist execution context and later resume the workflow, continuing from where it left off. This supports asynchronous human review or input without time constraints. * **Persistent execution state**: LangGraph allows you to pause execution **indefinitely** — for minutes, hours, or even days—until human input is received. This is possible because LangGraph checkpoints the graph state after each step, which allows the system to persist execution context and later resume the workflow, continuing from where it left off. This supports asynchronous human review or input without time constraints.
@@ -17,6 +17,10 @@ The Standalone Container deployment option is the least restrictive model for de
| **Where is it hosted?** | n/a | Your cloud | | **Where is it hosted?** | n/a | Your cloud |
| **Who provisions and manages it?** | n/a | You | | **Who provisions and manages it?** | n/a | You |
!!! warning
LangGraph Platform should not be deployed in serverless environments.
## Architecture ## Architecture
![Standalone Container](./img/langgraph_platform_deployment_architecture.png) ![Standalone Container](./img/langgraph_platform_deployment_architecture.png)
+4 -4
View File
@@ -33,7 +33,7 @@ The state of a thread at a particular point in time is called a checkpoint. Chec
- `metadata`: Metadata associated with this checkpoint. - `metadata`: Metadata associated with this checkpoint.
- `values`: Values of the state channels at this point in time. - `values`: Values of the state channels at this point in time.
- `next` A tuple of the node names to execute next in the graph. - `next` A tuple of the node names to execute next in the graph.
- `tasks`: A tuple of `PregelTask` objects that contain information about next tasks to be executed. If the step was previously attempted, it will include error information. If a graph was interrupted [dynamically](../how-tos/human_in_the_loop/breakpoints.ipynb#dynamic-breakpoints) from within a node, tasks will contain additional data associated with interrupts. - `tasks`: A tuple of `PregelTask` objects that contain information about next tasks to be executed. If the step was previously attempted, it will include error information. If a graph was interrupted [dynamically](../how-tos/human_in_the_loop/breakpoints.md#dynamic-breakpoints) from within a node, tasks will contain additional data associated with interrupts.
Checkpoints are persisted and can be used to restore the state of a thread at a later time. Checkpoints are persisted and can be used to restore the state of a thread at a later time.
@@ -174,7 +174,7 @@ config = {"configurable": {"thread_id": "1", "checkpoint_id": "0c62ca34-ac19-445
graph.invoke(None, config=config) graph.invoke(None, config=config)
``` ```
Importantly, LangGraph knows whether a particular step has been executed previously. If it has, LangGraph simply *re-plays* that particular step in the graph and does not re-execute the step, but only for the steps _before_ the provided `checkpoint_id`. All of the steps _after_ `checkpoint_id` will be executed (i.e., a new fork), even if they have been executed previously. See this [how to guide on time-travel to learn more about replaying](../how-tos/human_in_the_loop/time-travel.ipynb). Importantly, LangGraph knows whether a particular step has been executed previously. If it has, LangGraph simply *re-plays* that particular step in the graph and does not re-execute the step, but only for the steps _before_ the provided `checkpoint_id`. All of the steps _after_ `checkpoint_id` will be executed (i.e., a new fork), even if they have been executed previously. See this [how to guide on time-travel to learn more about replaying](../how-tos/human_in_the_loop/time-travel.md).
![Replay](img/persistence/re_play.png) ![Replay](img/persistence/re_play.png)
@@ -224,7 +224,7 @@ The `foo` key (channel) is completely changed (because there is no reducer speci
#### `as_node` #### `as_node`
The final thing you can optionally specify when calling `update_state` is `as_node`. If you provided it, the update will be applied as if it came from node `as_node`. If `as_node` is not provided, it will be set to the last node that updated the state, if not ambiguous. The reason this matters is that the next steps to execute depend on the last node to have given an update, so this can be used to control which node executes next. See this [how to guide on time-travel to learn more about forking state](../how-tos/human_in_the_loop/time-travel.ipynb). The final thing you can optionally specify when calling `update_state` is `as_node`. If you provided it, the update will be applied as if it came from node `as_node`. If `as_node` is not provided, it will be set to the last node that updated the state, if not ambiguous. The reason this matters is that the next steps to execute depend on the last node to have given an update, so this can be used to control which node executes next. See this [how to guide on time-travel to learn more about forking state](../how-tos/human_in_the_loop/time-travel.md).
![Update](img/persistence/checkpoints_full_story.jpg) ![Update](img/persistence/checkpoints_full_story.jpg)
@@ -525,7 +525,7 @@ When running on LangGraph Platform, encryption is automatically enabled whenever
### Human-in-the-loop ### Human-in-the-loop
First, checkpointers facilitate [human-in-the-loop workflows](agentic_concepts.md#human-in-the-loop) workflows by allowing humans to inspect, interrupt, and approve graph steps. Checkpointers are needed for these workflows as the human has to be able to view the state of a graph at any point in time, and the graph has to be to resume execution after the human has made any updates to the state. See [these how-to guides](../how-tos/human_in_the_loop/breakpoints.ipynb) for concrete examples. First, checkpointers facilitate [human-in-the-loop workflows](agentic_concepts.md#human-in-the-loop) workflows by allowing humans to inspect, interrupt, and approve graph steps. Checkpointers are needed for these workflows as the human has to be able to view the state of a graph at any point in time, and the graph has to be to resume execution after the human has made any updates to the state. See [these how-to guides](../how-tos/human_in_the_loop/breakpoints.md) for concrete examples.
### Memory ### Memory
+7 -4
View File
@@ -7,9 +7,12 @@ search:
When working with non-deterministic systems that make model-based decisions (e.g., agents powered by LLMs), it can be useful to examine their decision-making process in detail: When working with non-deterministic systems that make model-based decisions (e.g., agents powered by LLMs), it can be useful to examine their decision-making process in detail:
1. 🤔 **Understand Reasoning**: Analyze the steps that led to a successful result. 1. 🤔 **Understand reasoning**: Analyze the steps that led to a successful result.
2. 🐞 **Debug Mistakes**: Identify where and why errors occurred. 2. 🐞 **Debug mistakes**: Identify where and why errors occurred.
3. 🔍 **Explore Alternatives**: Test different paths to uncover better solutions. 3. 🔍 **Explore alternatives**: Test different paths to uncover better solutions.
LangGraph provides [time travel functionality](../how-tos/human_in_the_loop/time-travel.md) to support these use cases. Specifically, you can resume execution from a prior checkpoint — either replaying the same state or modifying it to explore alternatives. In all cases, resuming past execution produces a new fork in the history.
LangGraph provides **time travel** functionality to support these use cases. Specifically, you can **resume execution from a prior checkpoint** — either replaying the same state or modifying it to explore alternatives. In all cases, resuming past execution produces a **new fork** in the history. !!! tip
For information on how to use time travel, see [Use time travel](../how-tos/human_in_the_loop/time-travel.md) and [Time travel using Server API](../cloud/how-tos/human_in_the_loop_time_travel.md).
@@ -883,7 +883,7 @@ def node_in_parent_graph(state: State):
### Using multiple interrupts ### Using multiple interrupts
Using multiple interrupts within a **single** node can be helpful for patterns like [validating human input](../how-tos/human_in_the_loop/add-human-in-the-loop.md#validate-human-input). However, using multiple interrupts in the same node can lead to unexpected behavior if not handled carefully. Using multiple interrupts within a **single** node can be helpful for patterns like [validating human input](#validate-human-input). However, using multiple interrupts in the same node can lead to unexpected behavior if not handled carefully.
When a node contains multiple interrupt calls, LangGraph keeps a list of resume values specific to the task executing the node. Whenever execution resumes, it starts at the beginning of the node. For each interrupt encountered, LangGraph checks if a matching value exists in the task's resume list. Matching is **strictly index-based**, so the order of interrupt calls within the node is critical. When a node contains multiple interrupt calls, LangGraph keeps a list of resume values specific to the task executing the node. Whenever execution resumes, it starts at the beginning of the node. For each interrupt encountered, LangGraph checks if a matching value exists in the task's resume list. Matching is **strictly index-based**, so the order of interrupt calls within the node is critical.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,342 @@
# Set breakpoints
There are two places where you can set breakpoints:
1. **Before** or **after** a node executes by setting breakpoints at **compile time** or **run time**. We call these [**static breakpoints**](#static-breakpoints).
2. **Inside** a node using the `NodeInterrupt` exception. We call these [**dynamic breakpoints**](#dynamic-breakpoints).
To use breakpoints, you will need to:
1. [**Specify a checkpointer**](../../concepts/persistence.md#checkpoints) to save the graph state after each step.
2. **Set breakpoints** to specify where execution should pause.
3. **Run the graph** with a [**thread ID**](../../concepts/persistence.md#threads) to pause execution at the breakpoint.
4. **Resume execution** using `invoke`/`ainvoke`/`stream`/`astream` passing a `None` as the argument for the inputs.
!!! tip
For a conceptual overview of breakpoints, see [Breakpoints](../../concepts/breakpoints.md).
## Static breakpoints
Static breakpoints are triggered either before or after a node executes. You can set static breakpoints by specifying `interrupt_before` and `interrupt_after` at compile time or run time.
Static breakpoints can be especially useful for debugging if you want to step through the graph execution one
node at a time or if you want to pause the graph execution at specific nodes.
=== "Compile time"
```python
# highlight-next-line
graph = graph_builder.compile( # (1)!
# highlight-next-line
interrupt_before=["node_a"], # (2)!
# highlight-next-line
interrupt_after=["node_b", "node_c"], # (3)!
checkpointer=checkpointer, # (4)!
)
config = {
"configurable": {
"thread_id": "some_thread"
}
}
# Run the graph until the breakpoint
graph.invoke(inputs, config=thread_config) # (5)!
# Resume the graph
graph.invoke(None, config=thread_config) # (6)!
```
1. The breakpoints are set during `compile` time.
2. `interrupt_before` specifies the nodes where execution should pause before the node is executed.
3. `interrupt_after` specifies the nodes where execution should pause after the node is executed.
4. A checkpointer is required to enable breakpoints.
5. The graph is run until the first breakpoint is hit.
6. The graph is resumed by passing in `None` for the input. This will run the graph until the next breakpoint is hit.
=== "Run time"
```python
# highlight-next-line
graph.invoke( # (1)!
inputs,
# highlight-next-line
interrupt_before=["node_a"], # (2)!
# highlight-next-line
interrupt_after=["node_b", "node_c"] # (3)!
config={
"configurable": {"thread_id": "some_thread"}
},
)
config = {
"configurable": {
"thread_id": "some_thread"
}
}
# Run the graph until the breakpoint
graph.invoke(inputs, config=config) # (4)!
# Resume the graph
graph.invoke(None, config=config) # (5)!
```
1. `graph.invoke` is called with the `interrupt_before` and `interrupt_after` parameters. This is a run-time configuration and can be changed for every invocation.
2. `interrupt_before` specifies the nodes where execution should pause before the node is executed.
3. `interrupt_after` specifies the nodes where execution should pause after the node is executed.
4. The graph is run until the first breakpoint is hit.
5. The graph is resumed by passing in `None` for the input. This will run the graph until the next breakpoint is hit.
!!! note
You cannot set static breakpoints at runtime for **sub-graphs**.
If you have a sub-graph, you must set the breakpoints at compilation time.
??? example "Setting static breakpoints"
```python
from IPython.display import Image, display
from typing_extensions import TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
input: str
def step_1(state):
print("---Step 1---")
pass
def step_2(state):
print("---Step 2---")
pass
def step_3(state):
print("---Step 3---")
pass
builder = StateGraph(State)
builder.add_node("step_1", step_1)
builder.add_node("step_2", step_2)
builder.add_node("step_3", step_3)
builder.add_edge(START, "step_1")
builder.add_edge("step_1", "step_2")
builder.add_edge("step_2", "step_3")
builder.add_edge("step_3", END)
# Set up a checkpointer
checkpointer = InMemorySaver() # (1)!
graph = builder.compile(
checkpointer=checkpointer, # (2)!
interrupt_before=["step_3"] # (3)!
)
# View
display(Image(graph.get_graph().draw_mermaid_png()))
# Input
initial_input = {"input": "hello world"}
# Thread
thread = {"configurable": {"thread_id": "1"}}
# Run the graph until the first interruption
for event in graph.stream(initial_input, thread, stream_mode="values"):
print(event)
# This will run until the breakpoint
# You can get the state of the graph at this point
print(graph.get_state(config))
# You can continue the graph execution by passing in `None` for the input
for event in graph.stream(None, thread, stream_mode="values"):
print(event)
```
## Dynamic breakpoints
Use dynamic breakpoints if you need to interrupt the graph from inside a given node based on a condition.
```python
from langgraph.errors import NodeInterrupt
def step_2(state: State) -> State:
# highlight-next-line
if len(state["input"]) > 5:
# highlight-next-line
raise NodeInterrupt( # (1)!
f"Received input that is longer than 5 characters: {state['foo']}"
)
return state
```
1. raise NodeInterrupt exception based on a some condition. In this example, we create a dynamic breakpoint if the length of the attribute `input` is longer than 5 characters.
<details class="example"><summary>Using dynamic breakpoints</summary>
```python
from typing_extensions import TypedDict
from IPython.display import Image, display
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langgraph.errors import NodeInterrupt
class State(TypedDict):
input: str
def step_1(state: State) -> State:
print("---Step 1---")
return state
def step_2(state: State) -> State:
# Let's optionally raise a NodeInterrupt
# if the length of the input is longer than 5 characters
if len(state["input"]) > 5:
raise NodeInterrupt(
f"Received input that is longer than 5 characters: {state['input']}"
)
print("---Step 2---")
return state
def step_3(state: State) -> State:
print("---Step 3---")
return state
builder = StateGraph(State)
builder.add_node("step_1", step_1)
builder.add_node("step_2", step_2)
builder.add_node("step_3", step_3)
builder.add_edge(START, "step_1")
builder.add_edge("step_1", "step_2")
builder.add_edge("step_2", "step_3")
builder.add_edge("step_3", END)
# Set up memory
memory = MemorySaver()
# Compile the graph with memory
graph = builder.compile(checkpointer=memory)
# View
display(Image(graph.get_graph().draw_mermaid_png()))
```
First, let's run the graph with an input that <= 5 characters long. This should safely ignore the interrupt condition we defined and return the original input at the end of the graph execution.
```python
initial_input = {"input": "hello"}
thread_config = {"configurable": {"thread_id": "1"}}
for event in graph.stream(initial_input, thread_config, stream_mode="values"):
print(event)
```
If we inspect the graph at this point, we can see that there are no more tasks left to run and that the graph indeed finished execution.
```python
state = graph.get_state(thread_config)
print(state.next)
print(state.tasks)
```
Now, let's run the graph with an input that's longer than 5 characters. This should trigger the dynamic interrupt we defined via raising a `NodeInterrupt` error inside the `step_2` node.
```python
initial_input = {"input": "hello world"}
thread_config = {"configurable": {"thread_id": "2"}}
# Run the graph until the first interruption
for event in graph.stream(initial_input, thread_config, stream_mode="values"):
print(event)
```
We can see that the graph now stopped while executing `step_2`. If we inspect the graph state at this point, we can see the information on what node is set to execute next (`step_2`), as well as what node raised the interrupt (also `step_2`), and additional information about the interrupt.
```python
state = graph.get_state(thread_config)
print(state.next)
print(state.tasks)
```
If we try to resume the graph from the breakpoint, we will simply interrupt again as our inputs & graph state haven't changed.
```python
# NOTE: to resume the graph from a dynamic interrupt we use the same syntax as with regular interrupts -- we pass None as the input
for event in graph.stream(None, thread_config, stream_mode="values"):
print(event)
```
```python
state = graph.get_state(thread_config)
print(state.next)
print(state.tasks)
```
</details>
## Use with subgraphs
To add breakpoints to subgraph either:
* Define [static breakpoints](#static-breakpoints) by specifying them when **compiling** the subgraph.
* Define [dynamic breakpoints](#dynamic-breakpoints).
<details class="example"><summary>Add breakpoints to subgraphs</summary>
```python
from typing_extensions import TypedDict
from langgraph.graph import START, StateGraph
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt
class State(TypedDict):
foo: str
def subgraph_node_1(state: State):
return {"foo": state["foo"]}
subgraph_builder = StateGraph(State)
subgraph_builder.add_node(subgraph_node_1)
subgraph_builder.add_edge(START, "subgraph_node_1")
subgraph = subgraph_builder.compile(interrupt_before=["subgraph_node_1"])
builder = StateGraph(State)
builder.add_node("node_1", subgraph) # directly include subgraph as a node
builder.add_edge(START, "node_1")
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "1"}}
graph.invoke({"foo": ""}, config)
# Fetch state including subgraph state.
print(graph.get_state(config, subgraphs=True).tasks[0].state)
# resume the subgraph
graph.invoke(None, config)
```
</details>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,190 @@
# Use time-travel
To use [time-travel](../../concepts/time-travel.md) in LangGraph:
1. [Run the graph](#1-run-the-graph) with initial inputs using [`invoke`][langgraph.graph.state.CompiledStateGraph.invoke] or [`stream`][langgraph.graph.state.CompiledStateGraph.stream] methods.
2. [Identify a checkpoint in an existing thread](#2-identify-a-checkpoint): Use the [`get_state_history()`][langgraph.graph.state.CompiledStateGraph.get_state_history] method to retrieve the execution history for a specific `thread_id` and locate the desired `checkpoint_id`.
Alternatively, set a [breakpoint](../../concepts/breakpoints.md) before the node(s) where you want execution to pause. You can then find the most recent checkpoint recorded up to that breakpoint.
3. [Update the graph state (optional)](#3-update-the-state-optional): Use the [`update_state`][langgraph.graph.state.CompiledStateGraph.update_state] method to modify the graph's state at the checkpoint and resume execution from alternative state.
4. [Resume execution from the checkpoint](#4-resume-execution-from-the-checkpoint): Use the `invoke` or `stream` methods with an input of `None` and a configuration containing the appropriate `thread_id` and `checkpoint_id`.
!!! tip
For a conceptual overview of time-travel, see [Time travel](../../concepts/time-travel.md).
## In a workflow
This example builds a simple LangGraph workflow that generates a joke topic and writes a joke using an LLM. It demonstrates how to run the graph, retrieve past execution checkpoints, optionally modify the state, and resume execution from a chosen checkpoint to explore alternate outcomes.
### Setup
First we need to install the packages required
```python
%%capture --no-stderr
%pip install --quiet -U langgraph langchain_anthropic
```
Next, we need to set API keys for Anthropic (the LLM we will use)
```python
import getpass
import os
def _set_env(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"{var}: ")
_set_env("ANTHROPIC_API_KEY")
```
<div class="admonition tip">
<p class="admonition-title">Set up <a href="https://smith.langchain.com">LangSmith</a> for LangGraph development</p>
<p style="padding-top: 5px;">
Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href="https://docs.smith.langchain.com">here</a>.
</p>
</div>
```python
import uuid
from typing_extensions import TypedDict, NotRequired
from langgraph.graph import StateGraph, START, END
from langchain.chat_models import init_chat_model
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
topic: NotRequired[str]
joke: NotRequired[str]
llm = init_chat_model(
"anthropic:claude-3-7-sonnet-latest",
temperature=0,
)
def generate_topic(state: State):
"""LLM call to generate a topic for the joke"""
msg = llm.invoke("Give me a funny topic for a joke")
return {"topic": msg.content}
def write_joke(state: State):
"""LLM call to write a joke based on the topic"""
msg = llm.invoke(f"Write a short joke about {state['topic']}")
return {"joke": msg.content}
# Build workflow
workflow = StateGraph(State)
# Add nodes
workflow.add_node("generate_topic", generate_topic)
workflow.add_node("write_joke", write_joke)
# Add edges to connect nodes
workflow.add_edge(START, "generate_topic")
workflow.add_edge("generate_topic", "write_joke")
workflow.add_edge("write_joke", END)
# Compile
checkpointer = InMemorySaver()
graph = workflow.compile(checkpointer=checkpointer)
graph
```
### 1. Run the graph
```python
config = {
"configurable": {
"thread_id": uuid.uuid4(),
}
}
state = graph.invoke({}, config)
print(state["topic"])
print()
print(state["joke"])
```
**Output:**
```
How about "The Secret Life of Socks in the Dryer"? You know, exploring the mysterious phenomenon of how socks go into the laundry as pairs but come out as singles. Where do they go? Are they starting new lives elsewhere? Is there a sock paradise we don't know about? There's a lot of comedic potential in the everyday mystery that unites us all!
# The Secret Life of Socks in the Dryer
I finally discovered where all my missing socks go after the dryer. Turns out they're not missing at all—they've just eloped with someone else's socks from the laundromat to start new lives together.
My blue argyle is now living in Bermuda with a red polka dot, posting vacation photos on Sockstagram and sending me lint as alimony.
```
### 2. Identify a checkpoint
```python
# The states are returned in reverse chronological order.
states = list(graph.get_state_history(config))
for state in states:
print(state.next)
print(state.config["configurable"]["checkpoint_id"])
print()
```
**Output:**
```
()
1f02ac4a-ec9f-6524-8002-8f7b0bbeed0e
('write_joke',)
1f02ac4a-ce2a-6494-8001-cb2e2d651227
('generate_topic',)
1f02ac4a-a4e0-630d-8000-b73c254ba748
('__start__',)
1f02ac4a-a4dd-665e-bfff-e6c8c44315d9
```
```python
# This is the state before last (states are listed in chronological order)
selected_state = states[1]
print(selected_state.next)
print(selected_state.values)
```
**Output:**
```
('write_joke',)
{'topic': 'How about "The Secret Life of Socks in the Dryer"? You know, exploring the mysterious phenomenon of how socks go into the laundry as pairs but come out as singles. Where do they go? Are they starting new lives elsewhere? Is there a sock paradise we don\\'t know about? There\\'s a lot of comedic potential in the everyday mystery that unites us all!'}
```
### 3. Update the state (optional)
`update_state` will create a new checkpoint. The new checkpoint will be associated with the same thread, but a new checkpoint ID.
```python
new_config = graph.update_state(selected_state.config, values={"topic": "chickens"})
print(new_config)
```
**Output:**
```
{'configurable': {'thread_id': 'c62e2e03-c27b-4cb6-8cea-ea9bfedae006', 'checkpoint_ns': '', 'checkpoint_id': '1f02ac4a-ecee-600b-8002-a1d21df32e4c'}}
```
### 4. Resume execution from the checkpoint
```python
graph.invoke(None, new_config)
```
**Output:**
```python
{'topic': 'chickens',
'joke': 'Why did the chicken join a band?\n\nBecause it had excellent drumsticks!'}
```
+1 -1
View File
@@ -507,7 +507,7 @@ def update_user_name(
new_name: str, new_name: str,
tool_call_id: Annotated[str, InjectedToolCallId] tool_call_id: Annotated[str, InjectedToolCallId]
) -> Command: ) -> Command:
"""Update user name in short-term memory.""" """Update user-name in short-term memory."""
# highlight-next-line # highlight-next-line
return Command(update={ return Command(update={
# highlight-next-line # highlight-next-line
+8 -1
View File
@@ -1,5 +1,12 @@
# Use the functional API # Use the functional API
The [**Functional API**](../concepts/functional_api.md) allows you to add LangGraph's key features — [persistence](../concepts/persistence.md), [memory](../how-tos/memory/add-memory.md), [human-in-the-loop](../concepts/human_in_the_loop.md), and [streaming](../concepts/streaming.md) — to your applications with minimal changes to your existing code.
!!! tip
For conceptual information on the functional API, see [Functional API](../concepts/functional_api.md).
## Creating a simple workflow ## Creating a simple workflow
When defining an `entrypoint`, input is restricted to the first argument of the function. To pass multiple inputs, you can use a dictionary. When defining an `entrypoint`, input is restricted to the first argument of the function. To pass multiple inputs, you can use a dictionary.
@@ -832,4 +839,4 @@ for chunk in workflow.stream([input_message], config, stream_mode="values"):
## Integrate with other libraries ## Integrate with other libraries
* [Add LangGraph's features to other frameworks using the functional API](./autogen-integration-functional.ipynb): Add LangGraph features like persistence, memory and streaming to other agent frameworks that do not provide them out of the box. * [Add LangGraph's features to other frameworks using the functional API](./autogen-integration-functional.ipynb): Add LangGraph features like persistence, memory and streaming to other agent frameworks that do not provide them out of the box.
+3 -3
View File
@@ -89,7 +89,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 2, "execution_count": null,
"id": "baf669a0-04ee-492d-80d8-8fcb658ed128", "id": "baf669a0-04ee-492d-80d8-8fcb658ed128",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@@ -313,8 +313,8 @@
"\n", "\n",
" builder.add_edge(\"finalizer\", END)\n", " builder.add_edge(\"finalizer\", END)\n",
"\n", "\n",
" # These functions let the step be used in a MessageGraph\n", " # These functions let the step be used in a\n",
" # or a StateGraph with 'messages' as the key.\n", " # StateGraph with 'messages' as the key.\n",
" def encode(x: Union[Sequence[AnyMessage], PromptValue]) -> dict:\n", " def encode(x: Union[Sequence[AnyMessage], PromptValue]) -> dict:\n",
" \"\"\"Ensure the input is the correct format.\"\"\"\n", " \"\"\"Ensure the input is the correct format.\"\"\"\n",
" if isinstance(x, PromptValue):\n", " if isinstance(x, PromptValue):\n",
@@ -1,4 +1,4 @@
# LangGraph Platform quickstart # Run a local server
This guide shows you how to run a LangGraph application locally. This guide shows you how to run a LangGraph application locally.
@@ -336,7 +336,8 @@
"rag_chain = prompt | llm | StrOutputParser()\n", "rag_chain = prompt | llm | StrOutputParser()\n",
"\n", "\n",
"# Run\n", "# Run\n",
"generation = rag_chain.invoke({\"context\": docs, \"question\": question})\n", "docs_txt = format_docs(docs)\n",
"generation = rag_chain.invoke({\"context\": docs_txt, \"question\": question})\n",
"print(generation)" "print(generation)"
] ]
}, },
@@ -625,7 +626,8 @@
" documents = state[\"documents\"]\n", " documents = state[\"documents\"]\n",
"\n", "\n",
" # RAG generation\n", " # RAG generation\n",
" generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n", " docs_txt = format_docs(documents)\n",
" generation = rag_chain.invoke({\"context\": docs_txt, \"question\": question})\n",
" return {\"documents\": documents, \"question\": question, \"generation\": generation}\n", " return {\"documents\": documents, \"question\": question, \"generation\": generation}\n",
"\n", "\n",
"\n", "\n",
+123 -121
View File
@@ -92,30 +92,75 @@ nav:
- Get started: - Get started:
- index.md - index.md
- Quickstarts: - Quickstarts:
- Agent: agents/agents.md - Start with a prebuilt agent: agents/agents.md
- Local server: tutorials/langgraph-platform/local-server.md - Build a custom workflow:
- Deployment: cloud/quick_start.md - concepts/why-langgraph.md
- General concepts: - 1. Build a basic chatbot: tutorials/get-started/1-build-basic-chatbot.md
- Common patterns: - 2. Add tools: tutorials/get-started/2-add-tools.md
- Agent architectures: concepts/agentic_concepts.md - 3. Add memory: tutorials/get-started/3-add-memory.md
- Workflows & agents: tutorials/workflows.md - 4. Add human-in-the-loop: tutorials/get-started/4-human-in-the-loop.md
- Agent development: agents/overview.md - 5. Customize state: tutorials/get-started/5-customize-state.md
- Workflow orchestration: - 6. Time travel: tutorials/get-started/6-time-travel.md
- Graphs: concepts/low_level.md - Run a local server: tutorials/langgraph-platform/local-server.md
- Subgraphs: concepts/subgraphs.md - Agent development:
- Runtime: concepts/pregel.md - Workflows & agents: tutorials/workflows.md
- Functional API: concepts/functional_api.md - Prebuilt components: agents/overview.md
- Run an agent: agents/run_agents.md
- Agent architectures: concepts/agentic_concepts.md
- Guides:
- LangGraph APIs:
- Graph API:
- Overview: concepts/low_level.md
- Use the Graph API: how-tos/graph-api.ipynb
- Functional API:
- Overview: concepts/functional_api.md
- Use the Functional API: how-tos/use-functional-api.md
- Runtime: concepts/pregel.md
- Core capabilities: - Core capabilities:
- Streaming: concepts/streaming.md - Streaming:
- Persistence: concepts/persistence.md - Overview: concepts/streaming.md
- Durable execution: concepts/durable_execution.md - Stream outputs: how-tos/streaming.md
- Memory: concepts/memory.md - Use Server API: cloud/how-tos/streaming.md
- Tools: concepts/tools.md - Persistence:
- Human-in-the-loop: concepts/human_in_the_loop.md - Overview: concepts/persistence.md
- Breakpoints: concepts/breakpoints.md - Durable execution:
- Time travel: concepts/time-travel.md - Overview: concepts/durable_execution.md
- Multi-agent: concepts/multi_agent.md - Memory:
- Platform capabilities: - Overview: concepts/memory.md
- Add memory: how-tos/memory/add-memory.md
- Context:
- Add context: agents/context.md
- Models:
- Configure model: agents/models.md
- Tools:
- Overview: concepts/tools.md
- Call tools: how-tos/tool-calling.md
- Human-in-the-loop:
- Overview: concepts/human_in_the_loop.md
- Add human intervention: how-tos/human_in_the_loop/add-human-in-the-loop.md
- Use Server API: cloud/how-tos/add-human-in-the-loop.md
- Breakpoints:
- Overview: concepts/breakpoints.md
- Set breakpoints: how-tos/human_in_the_loop/breakpoints.md
- Use Server API: cloud/how-tos/human_in_the_loop_breakpoint.md
- Time travel:
- Overview: concepts/time-travel.md
- Use time travel: how-tos/human_in_the_loop/time-travel.md
- Use Server API: cloud/how-tos/human_in_the_loop_time_travel.md
- Subgraphs:
- Overview: concepts/subgraphs.md
- Use subgraphs: how-tos/subgraph.ipynb
- Multi-agent:
- Overview: concepts/multi_agent.md
- Prebuilt implementation: agents/multi-agent.md
- Custom implementation: how-tos/multi_agent.ipynb
- MCP:
- Use MCP: agents/mcp.md
- Server API: concepts/server-mcp.md
- Evaluation:
- Basic implementation: agents/evals.md
- Platform-only capabilities:
- LangGraph Platform: - LangGraph Platform:
- Overview: concepts/langgraph_platform.md - Overview: concepts/langgraph_platform.md
- Components: - Components:
@@ -125,105 +170,72 @@ nav:
- Data plane: concepts/langgraph_data_plane.md - Data plane: concepts/langgraph_data_plane.md
- Control plane: concepts/langgraph_control_plane.md - Control plane: concepts/langgraph_control_plane.md
- LangGraph CLI: concepts/langgraph_cli.md - LangGraph CLI: concepts/langgraph_cli.md
- LangGraph Studio: concepts/langgraph_studio.md - LangGraph Studio:
- Overview: concepts/langgraph_studio.md
- Quickstart: cloud/how-tos/studio/quick_start.md
- cloud/how-tos/invoke_studio.md
- cloud/how-tos/studio/manage_assistants.md
- cloud/how-tos/threads_studio.md
- cloud/how-tos/iterate_graph_studio.md
- cloud/how-tos/studio/run_evals.md
- cloud/how-tos/clone_traces_studio.md
- cloud/how-tos/datasets_studio.md
- LangGraph SDK: concepts/sdk.md - LangGraph SDK: concepts/sdk.md
- Plans & pricing: concepts/plans.md - Plans & pricing: concepts/plans.md
- Application structure: concepts/application_structure.md - Application structure: concepts/application_structure.md
- Scalability & resilience: concepts/scalability_and_resilience.md - Scalability & resilience: concepts/scalability_and_resilience.md
- Authentication & access control: concepts/auth.md
- Assistants: concepts/assistants.md
- Double-texting: concepts/double_texting.md
- Webhooks: cloud/concepts/webhooks.md
- Cron jobs: cloud/concepts/cron_jobs.md
- Deployment:
- Overview: concepts/deployment_options.md
- Deployment options:
- Cloud SaaS: concepts/langgraph_cloud.md
- Self-Hosted Data Plane: concepts/langgraph_self_hosted_data_plane.md
- Self-Hosted Control Plane: concepts/langgraph_self_hosted_control_plane.md
- Standalone Container: concepts/langgraph_standalone_container.md
- Guides:
- LangGraph APIs:
- Use the Graph API: how-tos/graph-api.ipynb
- Use the Functional API: how-tos/use-functional-api.md
- Models:
- Configure model: agents/models.md
- Streaming:
- Stream outputs: how-tos/streaming.md
- Use Server API: cloud/how-tos/streaming.md
- Context:
- Use in agent: agents/context.md
- Memory:
- Add memory: how-tos/memory/add-memory.md
- Human-in-the-loop:
- how-tos/human_in_the_loop/add-human-in-the-loop.md
- Use Server API: cloud/how-tos/add-human-in-the-loop.md
- Time travel:
- Use Server API: cloud/how-tos/human_in_the_loop_time_travel.md
- Breakpoints:
- Set breakpoints: how-tos/human_in_the_loop/breakpoints.ipynb
- Use Server API: cloud/how-tos/human_in_the_loop_breakpoint.md
- Tools:
- Call tools: how-tos/tool-calling.md
- Subgraphs:
- Use subgraphs: how-tos/subgraph.ipynb
- Multi-agent:
- Prebuilt implementation: agents/multi-agent.md
- Custom implementation: how-tos/multi_agent.ipynb
- MCP:
- Use MCP: agents/mcp.md
- Server API: concepts/server-mcp.md
- Deployment:
- Basic deployment: agents/deployment.md
- Set up your application:
- Use requirements.txt: cloud/deployment/setup.md
- Use pyproject.toml: cloud/deployment/setup_pyproject.md
- Use JavaScript: cloud/deployment/setup_javascript.md
- Use custom Docker: cloud/deployment/custom_docker.md
- Deploy to production:
- Cloud SaaS: cloud/deployment/cloud.md
- Self-Hosted Data Plane: cloud/deployment/self_hosted_data_plane.md
- Self-Hosted Control Plane: cloud/deployment/self_hosted_control_plane.md
- Standalone Container: cloud/deployment/standalone_container.md
- Evaluation:
- Basic implementation: agents/evals.md
- Platform capabilities:
- LangGraph Studio:
- Quickstart: cloud/how-tos/studio/quick_start.md
- cloud/how-tos/invoke_studio.md
- cloud/how-tos/studio/manage_assistants.md
- cloud/how-tos/threads_studio.md
- cloud/how-tos/iterate_graph_studio.md
- cloud/how-tos/studio/run_evals.md
- cloud/how-tos/clone_traces_studio.md
- cloud/how-tos/datasets_studio.md
- Authentication & access control: - Authentication & access control:
- how-tos/auth/custom_auth.md - Overview: concepts/auth.md
- how-tos/auth/openapi_security.md - how-tos/auth/custom_auth.md
- how-tos/auth/openapi_security.md
- Assistants: - Assistants:
- cloud/how-tos/configuration_cloud.md - Overview: concepts/assistants.md
- Threads: cloud/how-tos/use_threads.md - cloud/how-tos/configuration_cloud.md
- Runs: - Threads: cloud/how-tos/use_threads.md
- cloud/how-tos/background_run.md - Runs:
- cloud/how-tos/same-thread.md - cloud/how-tos/background_run.md
- cloud/how-tos/same-thread.md
- cloud/how-tos/cron_jobs.md
- cloud/how-tos/stateless_runs.md
- cloud/how-tos/configurable_headers.md
- Double-texting:
- Overview: concepts/double_texting.md
- cloud/how-tos/interrupt_concurrent.md
- cloud/how-tos/rollback_concurrent.md
- cloud/how-tos/reject_concurrent.md
- cloud/how-tos/enqueue_concurrent.md
- Webhooks:
- Overview: cloud/how-tos/webhooks.md
- cloud/how-tos/webhooks.md
- Cron jobs:
- Overview: cloud/how-tos/cron_jobs.md
- cloud/how-tos/cron_jobs.md - cloud/how-tos/cron_jobs.md
- cloud/how-tos/stateless_runs.md
- cloud/how-tos/configurable_headers.md
- Double-texting:
- cloud/how-tos/interrupt_concurrent.md
- cloud/how-tos/rollback_concurrent.md
- cloud/how-tos/reject_concurrent.md
- cloud/how-tos/enqueue_concurrent.md
- Webhooks: cloud/how-tos/webhooks.md
- Cron jobs: cloud/how-tos/cron_jobs.md
- Server customization: - Server customization:
- how-tos/http/custom_lifespan.md - how-tos/http/custom_lifespan.md
- how-tos/http/custom_middleware.md - how-tos/http/custom_middleware.md
- how-tos/http/custom_routes.md - how-tos/http/custom_routes.md
- Data management: - Data management:
- Add semantic search: cloud/deployment/semantic_search.md - Add semantic search: cloud/deployment/semantic_search.md
- Add TTLs: how-tos/ttl/configure_ttl.md - Add TTLs: how-tos/ttl/configure_ttl.md
- Deployment:
- Overview: concepts/deployment_options.md
- Quickstart: cloud/quick_start.md
- Set up your application:
- Use requirements.txt: cloud/deployment/setup.md
- Use pyproject.toml: cloud/deployment/setup_pyproject.md
- Use JavaScript: cloud/deployment/setup_javascript.md
- Use custom Docker: cloud/deployment/custom_docker.md
- Rebuild graph at runtime: cloud/deployment/graph_rebuild.md
- Deployment options:
- Cloud SaaS: concepts/langgraph_cloud.md
- Self-Hosted Data Plane: concepts/langgraph_self_hosted_data_plane.md
- Self-Hosted Control Plane: concepts/langgraph_self_hosted_control_plane.md
- Standalone Container: concepts/langgraph_standalone_container.md
- Deploy to production:
- Cloud SaaS: cloud/deployment/cloud.md
- Self-Hosted Data Plane: cloud/deployment/self_hosted_data_plane.md
- Self-Hosted Control Plane: cloud/deployment/self_hosted_control_plane.md
- Standalone Container: cloud/deployment/standalone_container.md
- Reference: - Reference:
- reference/index.md - reference/index.md
@@ -253,15 +265,6 @@ nav:
- Environment variables: cloud/reference/env_var.md - Environment variables: cloud/reference/env_var.md
- Examples: - Examples:
- agents/run_agents.md
- LangGraph basics:
- concepts/why-langgraph.md
- Build a basic chatbot: tutorials/get-started/1-build-basic-chatbot.md
- tutorials/get-started/2-add-tools.md
- tutorials/get-started/3-add-memory.md
- Add human-in-the-loop: tutorials/get-started/4-human-in-the-loop.md
- tutorials/get-started/5-customize-state.md
- tutorials/get-started/6-time-travel.md
- Template applications: concepts/template_applications.md # TODO: make tutorial - Template applications: concepts/template_applications.md # TODO: make tutorial
- Agentic RAG: tutorials/rag/langgraph_agentic_rag.ipynb - Agentic RAG: tutorials/rag/langgraph_agentic_rag.ipynb
- Agent Supervisor: tutorials/multi_agent/agent_supervisor.ipynb - Agent Supervisor: tutorials/multi_agent/agent_supervisor.ipynb
@@ -273,7 +276,6 @@ nav:
- tutorials/auth/getting_started.md - tutorials/auth/getting_started.md
- tutorials/auth/resource_auth.md - tutorials/auth/resource_auth.md
- tutorials/auth/add_auth_server.md - tutorials/auth/add_auth_server.md
- Rebuild graph at runtime: cloud/deployment/graph_rebuild.md
- Use RemoteGraph: how-tos/use-remote-graph.md - Use RemoteGraph: how-tos/use-remote-graph.md
- Deploy CrewAI, AutoGen, and other frameworks: how-tos/autogen-langgraph-platform.ipynb - Deploy CrewAI, AutoGen, and other frameworks: how-tos/autogen-langgraph-platform.ipynb
# combine with how-tos/autogen-integration.ipynb # combine with how-tos/autogen-integration.ipynb
Generated
+3 -3
View File
@@ -2590,7 +2590,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph" name = "langgraph"
version = "0.5.0rc1" version = "0.5.0"
source = { editable = "../libs/langgraph" } source = { editable = "../libs/langgraph" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -2894,7 +2894,7 @@ test = [
[[package]] [[package]]
name = "langgraph-prebuilt" name = "langgraph-prebuilt"
version = "0.5.0rc0" version = "0.5.1"
source = { editable = "../libs/prebuilt" } source = { editable = "../libs/prebuilt" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -2925,7 +2925,7 @@ dev = [
[[package]] [[package]]
name = "langgraph-sdk" name = "langgraph-sdk"
version = "0.1.70" version = "0.1.72"
source = { editable = "../libs/sdk-py" } source = { editable = "../libs/sdk-py" }
dependencies = [ dependencies = [
{ name = "httpx" }, { name = "httpx" },
+1 -33
View File
@@ -1,33 +1 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "ac22b8de",
"metadata": {},
"source": [
"This file has been moved to https://github.com/langchain-ai/langgraph/blob/main/docs/docs/how-tos/human_in_the_loop/breakpoints.ipynb"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -223,10 +223,9 @@ class AsyncSqliteStore(AsyncBatchedBaseStore, BaseSqliteStore):
Yields: Yields:
An SQLite cursor object. An SQLite cursor object.
""" """
if not self.is_setup:
await self.setup()
async with self.lock: async with self.lock:
if not self.is_setup:
await self.setup()
if transaction: if transaction:
await self.conn.execute("BEGIN") await self.conn.execute("BEGIN")
@@ -981,10 +981,9 @@ class SqliteStore(BaseSqliteStore, BaseStore):
Args: Args:
transaction (bool): whether to use transaction for the DB operations transaction (bool): whether to use transaction for the DB operations
""" """
if not self.is_setup:
self.setup()
with self.lock: with self.lock:
if not self.is_setup:
self.setup()
if transaction: if transaction:
self.conn.execute("BEGIN") self.conn.execute("BEGIN")
@@ -1002,10 +1001,10 @@ class SqliteStore(BaseSqliteStore, BaseStore):
This method creates the necessary tables in the SQLite database if they don't This method creates the necessary tables in the SQLite database if they don't
already exist and runs database migrations. It should be called before first use. already exist and runs database migrations. It should be called before first use.
""" """
if self.is_setup:
return
with self.lock: with self.lock:
if self.is_setup:
return
# Create migrations table if it doesn't exist # Create migrations table if it doesn't exist
self.conn.executescript( self.conn.executescript(
""" """
+1 -2
View File
@@ -1,12 +1,11 @@
from langgraph.constants import END, START from langgraph.constants import END, START
from langgraph.graph.message import MessageGraph, MessagesState, add_messages from langgraph.graph.message import MessagesState, add_messages
from langgraph.graph.state import StateGraph from langgraph.graph.state import StateGraph
__all__ = [ __all__ = [
"END", "END",
"START", "START",
"StateGraph", "StateGraph",
"MessageGraph",
"add_messages", "add_messages",
"MessagesState", "MessagesState",
] ]
-52
View File
@@ -25,7 +25,6 @@ from langchain_core.messages import (
from typing_extensions import TypedDict from typing_extensions import TypedDict
from langgraph.constants import CONF, CONFIG_KEY_SEND from langgraph.constants import CONF, CONFIG_KEY_SEND
from langgraph.graph.state import StateGraph
Messages = Union[list[MessageLikeRepresentation], MessageLikeRepresentation] Messages = Union[list[MessageLikeRepresentation], MessageLikeRepresentation]
@@ -227,57 +226,6 @@ def add_messages(
return merged return merged
class MessageGraph(StateGraph):
"""A StateGraph where every node receives a list of messages as input and returns one or more messages as output.
MessageGraph is a subclass of StateGraph whose entire state is a single, append-only* list of messages.
Each node in a MessageGraph takes a list of messages as input and returns zero or more
messages as output. The `add_messages` function is used to merge the output messages from each node
into the existing list of messages in the graph's state.
Examples:
```pycon
>>> from langgraph.graph.message import MessageGraph
...
>>> builder = MessageGraph()
>>> builder.add_node("chatbot", lambda state: [("assistant", "Hello!")])
>>> builder.set_entry_point("chatbot")
>>> builder.set_finish_point("chatbot")
>>> builder.compile().invoke([("user", "Hi there.")])
[HumanMessage(content="Hi there.", id='...'), AIMessage(content="Hello!", id='...')]
```
```pycon
>>> from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
>>> from langgraph.graph.message import MessageGraph
...
>>> builder = MessageGraph()
>>> builder.add_node(
... "chatbot",
... lambda state: [
... AIMessage(
... content="Hello!",
... tool_calls=[{"name": "search", "id": "123", "args": {"query": "X"}}],
... )
... ],
... )
>>> builder.add_node(
... "search", lambda state: [ToolMessage(content="Searching...", tool_call_id="123")]
... )
>>> builder.set_entry_point("chatbot")
>>> builder.add_edge("chatbot", "search")
>>> builder.set_finish_point("search")
>>> builder.compile().invoke([HumanMessage(content="Hi there. Can you search for X?")])
{'messages': [HumanMessage(content="Hi there. Can you search for X?", id='b8b7d8f4-7f4d-4f4d-9c1d-f8b8d8f4d9c1'),
AIMessage(content="Hello!", id='f4d9c1d8-8d8f-4d9c-b8b7-d8f4f4d9c1d8'),
ToolMessage(content="Searching...", id='d8f4f4d9-c1d8-4f4d-b8b7-d8f4f4d9c1d8', tool_call_id="123")]}
```
"""
def __init__(self) -> None:
super().__init__(Annotated[list[AnyMessage], add_messages]) # type: ignore[arg-type]
class MessagesState(TypedDict): class MessagesState(TypedDict):
messages: Annotated[list[AnyMessage], add_messages] messages: Annotated[list[AnyMessage], add_messages]
File diff suppressed because it is too large Load Diff
+1 -410
View File
@@ -20,10 +20,9 @@ from langgraph.channels.last_value import LastValue
from langgraph.channels.untracked_value import UntrackedValue from langgraph.channels.untracked_value import UntrackedValue
from langgraph.checkpoint.base import BaseCheckpointSaver from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.constants import END, PULL, PUSH, START from langgraph.constants import END, PULL, PUSH, START
from langgraph.graph.message import MessageGraph, add_messages from langgraph.graph.message import add_messages
from langgraph.graph.state import StateGraph from langgraph.graph.state import StateGraph
from langgraph.prebuilt.chat_agent_executor import create_react_agent from langgraph.prebuilt.chat_agent_executor import create_react_agent
from langgraph.prebuilt.tool_node import ToolNode
from langgraph.pregel import NodeBuilder, Pregel from langgraph.pregel import NodeBuilder, Pregel
from langgraph.types import PregelTask, Send, StateSnapshot, StreamWriter from langgraph.types import PregelTask, Send, StateSnapshot, StreamWriter
from tests.any_int import AnyInt from tests.any_int import AnyInt
@@ -2059,415 +2058,7 @@ async def test_state_graph_packets(async_checkpointer: BaseCheckpointSaver) -> N
) )
async def test_message_graph(async_checkpointer: BaseCheckpointSaver) -> None:
from langchain_core.language_models.fake_chat_models import (
FakeMessagesListChatModel,
)
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.tools import tool
class FakeFuntionChatModel(FakeMessagesListChatModel):
def bind_functions(self, functions: list):
return self
@tool()
def search_api(query: str) -> str:
"""Searches the API for the query."""
return f"result for {query}"
tools = [search_api]
model = FakeFuntionChatModel(
responses=[
AIMessage(
content="",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "query"},
}
],
id="ai1",
),
AIMessage(
content="",
tool_calls=[
{
"id": "tool_call456",
"name": "search_api",
"args": {"query": "another"},
}
],
id="ai2",
),
AIMessage(content="answer", id="ai3"),
]
)
# Define the function that determines whether to continue or not
def should_continue(messages):
last_message = messages[-1]
# If there is no function call, then we finish
if not last_message.tool_calls:
return "end"
# Otherwise if there is, we continue
else:
return "continue"
# Define a new graph
workflow = MessageGraph()
# Define the two nodes we will cycle between
workflow.add_node("agent", model)
workflow.add_node("tools", ToolNode(tools))
# Set the entrypoint as `agent`
# This means that this node is the first one called
workflow.set_entry_point("agent")
# We now add a conditional edge
workflow.add_conditional_edges(
# First, we define the start node. We use `agent`.
# This means these are the edges taken after the `agent` node is called.
"agent",
# Next, we pass in the function that will determine which node is called next.
should_continue,
# Finally we pass in a mapping.
# The keys are strings, and the values are other nodes.
# END is a special node marking that the graph should finish.
# What will happen is we will call `should_continue`, and then the output of that
# will be matched against the keys in this mapping.
# Based on which one it matches, that node will then be called.
{
# If `tools`, then we call the tool node.
"continue": "tools",
# Otherwise we finish.
"end": END,
},
)
# We now add a normal edge from `tools` to `agent`.
# This means that after `tools` is called, `agent` node is called next.
workflow.add_edge("tools", "agent")
# Finally, we compile it!
# This compiles it into a LangChain Runnable,
# meaning you can use it as you would any other runnable
app = workflow.compile()
assert await app.ainvoke(HumanMessage(content="what is weather in sf")) == [
_AnyIdHumanMessage(
content="what is weather in sf",
),
AIMessage(
content="",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "query"},
}
],
id="ai1", # respects ids passed in
),
_AnyIdToolMessage(
content="result for query",
name="search_api",
tool_call_id="tool_call123",
),
AIMessage(
content="",
tool_calls=[
{
"id": "tool_call456",
"name": "search_api",
"args": {"query": "another"},
}
],
id="ai2",
),
_AnyIdToolMessage(
content="result for another",
name="search_api",
tool_call_id="tool_call456",
),
AIMessage(content="answer", id="ai3"),
]
assert [
c async for c in app.astream([HumanMessage(content="what is weather in sf")])
] == [
{
"agent": AIMessage(
content="",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "query"},
}
],
id="ai1",
)
},
{
"tools": [
_AnyIdToolMessage(
content="result for query",
name="search_api",
tool_call_id="tool_call123",
)
]
},
{
"agent": AIMessage(
content="",
tool_calls=[
{
"id": "tool_call456",
"name": "search_api",
"args": {"query": "another"},
}
],
id="ai2",
)
},
{
"tools": [
_AnyIdToolMessage(
content="result for another",
name="search_api",
tool_call_id="tool_call456",
)
]
},
{"agent": AIMessage(content="answer", id="ai3")},
]
app_w_interrupt = workflow.compile(
checkpointer=async_checkpointer,
interrupt_after=["agent"],
)
config = {"configurable": {"thread_id": "1"}}
assert [
c
async for c in app_w_interrupt.astream(
HumanMessage(content="what is weather in sf"),
config,
checkpoint_during=False,
)
] == [
{
"agent": AIMessage(
content="",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "query"},
}
],
id="ai1",
)
},
{"__interrupt__": ()},
]
tup = await app_w_interrupt.checkpointer.aget_tuple(config)
assert await app_w_interrupt.aget_state(config) == StateSnapshot(
values=[
_AnyIdHumanMessage(content="what is weather in sf"),
AIMessage(
content="",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "query"},
}
],
id="ai1",
),
],
tasks=(PregelTask(AnyStr(), "tools", (PULL, "tools")),),
next=("tools",),
config=tup.config,
created_at=tup.checkpoint["ts"],
metadata={
"parents": {},
"source": "loop",
"step": 1,
},
parent_config=None,
interrupts=(),
)
# modify ai message
last_message = (await app_w_interrupt.aget_state(config)).values[-1]
last_message.tool_calls[0]["args"] = {"query": "a different query"}
await app_w_interrupt.aupdate_state(config, last_message)
# message was replaced instead of appended
tup = await app_w_interrupt.checkpointer.aget_tuple(config)
assert await app_w_interrupt.aget_state(config) == StateSnapshot(
values=[
_AnyIdHumanMessage(content="what is weather in sf"),
AIMessage(
content="",
id="ai1",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "a different query"},
}
],
),
],
tasks=(PregelTask(AnyStr(), "tools", (PULL, "tools")),),
next=("tools",),
config=tup.config,
created_at=tup.checkpoint["ts"],
metadata={
"parents": {},
"source": "update",
"step": 2,
},
parent_config=(
[c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)][
-1
].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
{
"tools": [
_AnyIdToolMessage(
content="result for a different query",
name="search_api",
tool_call_id="tool_call123",
)
]
},
{
"agent": AIMessage(
content="",
tool_calls=[
{
"id": "tool_call456",
"name": "search_api",
"args": {"query": "another"},
}
],
id="ai2",
)
},
{"__interrupt__": ()},
]
tup = await app_w_interrupt.checkpointer.aget_tuple(config)
assert await app_w_interrupt.aget_state(config) == StateSnapshot(
values=[
_AnyIdHumanMessage(content="what is weather in sf"),
AIMessage(
content="",
id="ai1",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "a different query"},
}
],
),
_AnyIdToolMessage(
content="result for a different query",
name="search_api",
tool_call_id="tool_call123",
),
AIMessage(
content="",
tool_calls=[
{
"id": "tool_call456",
"name": "search_api",
"args": {"query": "another"},
}
],
id="ai2",
),
],
tasks=(PregelTask(AnyStr(), "tools", (PULL, "tools")),),
next=("tools",),
config=tup.config,
created_at=tup.checkpoint["ts"],
metadata={
"parents": {},
"source": "loop",
"step": 4,
},
parent_config=(
[c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)][
-1
].config
),
interrupts=(),
)
await app_w_interrupt.aupdate_state(
config,
AIMessage(content="answer", id="ai2"),
)
# replaces message even if object identity is different, as long as id is the same
tup = await app_w_interrupt.checkpointer.aget_tuple(config)
assert await app_w_interrupt.aget_state(config) == StateSnapshot(
values=[
_AnyIdHumanMessage(content="what is weather in sf"),
AIMessage(
content="",
id="ai1",
tool_calls=[
{
"id": "tool_call123",
"name": "search_api",
"args": {"query": "a different query"},
}
],
),
_AnyIdToolMessage(
content="result for a different query",
name="search_api",
tool_call_id="tool_call123",
),
AIMessage(content="answer", id="ai2"),
],
tasks=(),
next=(),
config=tup.config,
created_at=tup.checkpoint["ts"],
metadata={
"parents": {},
"source": "update",
"step": 5,
},
parent_config=(
[c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)][
-1
].config
),
interrupts=(),
)
async def test_in_one_fan_out_out_one_graph_state() -> None: async def test_in_one_fan_out_out_one_graph_state() -> None:
def sorted_add(x: list[str], y: list[str]) -> list[str]:
return sorted(operator.add(x, y))
class State(TypedDict, total=False): class State(TypedDict, total=False):
query: str query: str
answer: str answer: str
+15 -5
View File
@@ -46,7 +46,7 @@ from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START
from langgraph.errors import InvalidUpdateError, ParentCommand from langgraph.errors import InvalidUpdateError, ParentCommand
from langgraph.func import entrypoint, task from langgraph.func import entrypoint, task
from langgraph.graph import END, StateGraph from langgraph.graph import END, StateGraph
from langgraph.graph.message import MessageGraph, MessagesState, add_messages from langgraph.graph.message import MessagesState, add_messages
from langgraph.prebuilt.tool_node import ToolNode from langgraph.prebuilt.tool_node import ToolNode
from langgraph.pregel import ( from langgraph.pregel import (
GraphRecursionError, GraphRecursionError,
@@ -3984,9 +3984,14 @@ def test_checkpoint_metadata(sync_checkpointer: BaseCheckpointSaver) -> None:
def test_remove_message_via_state_update( def test_remove_message_via_state_update(
sync_checkpointer: BaseCheckpointSaver, sync_checkpointer: BaseCheckpointSaver,
) -> None: ) -> None:
from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage from langchain_core.messages import (
AIMessage,
AnyMessage,
HumanMessage,
RemoveMessage,
)
workflow = MessageGraph() workflow = StateGraph(Annotated[list[AnyMessage], add_messages])
workflow.add_node( workflow.add_node(
"chatbot", "chatbot",
lambda state: [ lambda state: [
@@ -4017,9 +4022,14 @@ def test_remove_message_via_state_update(
def test_remove_message_from_node(): def test_remove_message_from_node():
from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage from langchain_core.messages import (
AIMessage,
AnyMessage,
HumanMessage,
RemoveMessage,
)
workflow = MessageGraph() workflow = StateGraph(Annotated[list[AnyMessage], add_messages])
workflow.add_node( workflow.add_node(
"chatbot", "chatbot",
lambda state: [ lambda state: [
+2 -2
View File
@@ -1423,7 +1423,7 @@ inmem = [
[[package]] [[package]]
name = "langgraph-prebuilt" name = "langgraph-prebuilt"
version = "0.5.0" version = "0.5.1"
source = { editable = "../prebuilt" } source = { editable = "../prebuilt" }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -1471,7 +1471,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph-sdk" name = "langgraph-sdk"
version = "0.1.71" version = "0.1.72"
source = { editable = "../sdk-py" } source = { editable = "../sdk-py" }
dependencies = [ dependencies = [
{ name = "httpx" }, { name = "httpx" },
@@ -448,7 +448,7 @@ def create_react_agent(
if ( if (
_should_bind_tools(model, tool_classes, num_builtin=len(llm_builtin_tools)) _should_bind_tools(model, tool_classes, num_builtin=len(llm_builtin_tools))
and len(tool_classes) > 0 and len(tool_classes + llm_builtin_tools) > 0
): ):
model = cast(BaseChatModel, model).bind_tools(tool_classes + llm_builtin_tools) # type: ignore[operator] model = cast(BaseChatModel, model).bind_tools(tool_classes + llm_builtin_tools) # type: ignore[operator]
@@ -629,7 +629,7 @@ def tools_condition(
Args: Args:
state: The state to check for state: The state to check for
tool calls. Must have a list of messages (MessageGraph) or have the tool calls. Must have a list of messages or have the
"messages" key (StateGraph). "messages" key (StateGraph).
Returns: Returns:
@@ -2,7 +2,7 @@
in a langchain graph. It applies a pydantic schema to tool_calls in the models' outputs, in a langchain graph. It applies a pydantic schema to tool_calls in the models' outputs,
and returns a ToolMessage with the validated content. If the schema is not valid, it and returns a ToolMessage with the validated content. If the schema is not valid, it
returns a ToolMessage with the error message. The ValidationNode can be used in a returns a ToolMessage with the error message. The ValidationNode can be used in a
StateGraph with a "messages" key or in a MessageGraph. If multiple tool calls are StateGraph with a "messages" key. If multiple tool calls are
requested, they will be run in parallel. requested, they will be run in parallel.
""" """
@@ -49,7 +49,7 @@ def _default_format_error(
class ValidationNode(RunnableCallable): class ValidationNode(RunnableCallable):
"""A node that validates all tools requests from the last AIMessage. """A node that validates all tools requests from the last AIMessage.
It can be used either in StateGraph with a "messages" key or in MessageGraph. It can be used in StateGraph with a "messages" key.
!!! note !!! note
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "langgraph-prebuilt" name = "langgraph-prebuilt"
version = "0.5.0" version = "0.5.1"
description = "Library with high-level APIs for creating and executing LangGraph agents and tools." description = "Library with high-level APIs for creating and executing LangGraph agents and tools."
authors = [] authors = []
requires-python = ">=3.9" requires-python = ">=3.9"
+2 -2
View File
@@ -464,7 +464,7 @@ dev = [
[[package]] [[package]]
name = "langgraph-prebuilt" name = "langgraph-prebuilt"
version = "0.5.0" version = "0.5.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "langchain-core" }, { name = "langchain-core" },
@@ -511,7 +511,7 @@ dev = [
[[package]] [[package]]
name = "langgraph-sdk" name = "langgraph-sdk"
version = "0.1.71" version = "0.1.72"
source = { editable = "../sdk-py" } source = { editable = "../sdk-py" }
dependencies = [ dependencies = [
{ name = "httpx" }, { name = "httpx" },
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@langchain/langgraph-sdk", "name": "@langchain/langgraph-sdk",
"version": "0.0.85", "version": "0.0.87",
"description": "Client library for interacting with the LangGraph API", "description": "Client library for interacting with the LangGraph API",
"type": "module", "type": "module",
"packageManager": "yarn@1.22.19", "packageManager": "yarn@1.22.19",
+30
View File
@@ -1658,7 +1658,30 @@ export class Client<
*/ */
public "~ui": UiClient; public "~ui": UiClient;
/**
* @internal Used to obtain a stable key representing the client.
*/
private "~configHash": string | undefined;
constructor(config?: ClientConfig) { constructor(config?: ClientConfig) {
this["~configHash"] = (() =>
JSON.stringify({
apiUrl: config?.apiUrl,
apiKey: config?.apiKey,
timeoutMs: config?.timeoutMs,
defaultHeaders: config?.defaultHeaders,
maxConcurrency: config?.callerOptions?.maxConcurrency,
maxRetries: config?.callerOptions?.maxRetries,
callbacks: {
onFailedResponseHook:
config?.callerOptions?.onFailedResponseHook != null,
onRequest: config?.onRequest != null,
fetch: config?.callerOptions?.fetch != null,
},
}))();
this.assistants = new AssistantsClient(config); this.assistants = new AssistantsClient(config);
this.threads = new ThreadsClient(config); this.threads = new ThreadsClient(config);
this.runs = new RunsClient(config); this.runs = new RunsClient(config);
@@ -1667,3 +1690,10 @@ export class Client<
this["~ui"] = new UiClient(config); this["~ui"] = new UiClient(config);
} }
} }
/**
* @internal Used to obtain a stable key representing the client.
*/
export function getClientConfigHash(client: Client): string | undefined {
return client["~configHash"];
}
+7 -2
View File
@@ -1,7 +1,7 @@
/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */ /* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */
"use client"; "use client";
import { Client, type ClientConfig } from "../client.js"; import { Client, getClientConfigHash, type ClientConfig } from "../client.js";
import type { import type {
Command, Command,
DisconnectMode, DisconnectMode,
@@ -321,11 +321,16 @@ function useThreadHistory<StateType extends Record<string, unknown>>(
) { ) {
const [history, setHistory] = useState<ThreadState<StateType>[]>([]); const [history, setHistory] = useState<ThreadState<StateType>[]>([]);
const clientHash = getClientConfigHash(client);
const clientRef = useRef(client);
clientRef.current = client;
const fetcher = useCallback( const fetcher = useCallback(
( (
threadId: string | undefined | null, threadId: string | undefined | null,
): Promise<ThreadState<StateType>[]> => { ): Promise<ThreadState<StateType>[]> => {
if (threadId != null) { if (threadId != null) {
const client = clientRef.current;
return fetchHistory<StateType>(client, threadId).then((history) => { return fetchHistory<StateType>(client, threadId).then((history) => {
setHistory(history); setHistory(history);
return history; return history;
@@ -342,7 +347,7 @@ function useThreadHistory<StateType extends Record<string, unknown>>(
useEffect(() => { useEffect(() => {
if (submittingRef.current) return; if (submittingRef.current) return;
fetcher(threadId); fetcher(threadId);
}, [fetcher, submittingRef, threadId]); }, [fetcher, clientHash, submittingRef, threadId]);
return { return {
data: history, data: history,
+12
View File
@@ -178,6 +178,9 @@ export interface Cron {
/** The ID of the cron */ /** The ID of the cron */
cron_id: string; cron_id: string;
/** The ID of the assistant */
assistant_id: string;
/** The ID of the thread */ /** The ID of the thread */
thread_id: Optional<string>; thread_id: Optional<string>;
@@ -195,6 +198,15 @@ export interface Cron {
/** The run payload to use for creating new run. */ /** The run payload to use for creating new run. */
payload: Record<string, unknown>; payload: Record<string, unknown>;
/** The user ID of the cron */
user_id: Optional<string>;
/** The next run date of the cron */
next_run_date: Optional<string>;
/** The metadata of the cron */
metadata: Record<string, unknown>;
} }
export type DefaultValues = Record<string, unknown>[] | Record<string, unknown>; export type DefaultValues = Record<string, unknown>[] | Record<string, unknown>;
+8
View File
@@ -319,6 +319,8 @@ class Cron(TypedDict):
cron_id: str cron_id: str
"""The ID of the cron.""" """The ID of the cron."""
assistant_id: str
"""The ID of the assistant."""
thread_id: str | None thread_id: str | None
"""The ID of the thread.""" """The ID of the thread."""
end_time: datetime | None end_time: datetime | None
@@ -331,6 +333,12 @@ class Cron(TypedDict):
"""The last time the cron was updated.""" """The last time the cron was updated."""
payload: dict payload: dict
"""The run payload to use for creating new run.""" """The run payload to use for creating new run."""
user_id: str | None
"""The user ID of the cron."""
next_run_date: datetime | None
"""The next run date of the cron."""
metadata: dict
"""The metadata of the cron."""
class RunCreate(TypedDict): class RunCreate(TypedDict):
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "langgraph-sdk" name = "langgraph-sdk"
version = "0.1.71" version = "0.1.72"
description = "SDK for interacting with LangGraph API" description = "SDK for interacting with LangGraph API"
authors = [] authors = []
requires-python = ">=3.9" requires-python = ">=3.9"
+1 -1
View File
@@ -119,7 +119,7 @@ wheels = [
[[package]] [[package]]
name = "langgraph-sdk" name = "langgraph-sdk"
version = "0.1.71" version = "0.1.72"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "httpx" }, { name = "httpx" },