mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-09 19:27:54 +02:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98f105fd5f | ||
|
|
28c529feb2 | ||
|
|
91ebc8d3ed | ||
|
|
6e08f4c12e | ||
|
|
f2dc0653f1 | ||
|
|
67177a5610 | ||
|
|
b1b238c7ea | ||
|
|
598796ef86 | ||
|
|
3c7981201e | ||
|
|
109c0dfb93 | ||
|
|
d7c364c5bb | ||
|
|
746142fb07 | ||
|
|
b9c9c32c31 | ||
|
|
c89fe4c45d | ||
|
|
b0e28851a6 | ||
|
|
48fb91deda |
@@ -13,7 +13,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v0'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- v0
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- v0
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -82,9 +84,9 @@ jobs:
|
|||||||
run: make llms-text
|
run: make llms-text
|
||||||
- name: Build site
|
- name: Build site
|
||||||
run: |
|
run: |
|
||||||
# If this is main branch, then we want to download stats. we do this
|
# If this is v0 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/main" ]; then
|
if [ "${{ github.ref }}" == "refs/heads/v0" ]; then
|
||||||
DOWNLOAD_STATS=true make build-docs
|
DOWNLOAD_STATS=true make build-docs
|
||||||
else
|
else
|
||||||
make build-docs
|
make build-docs
|
||||||
@@ -144,8 +146,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Configure GitHub Pages
|
- name: Configure GitHub Pages
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/v0'
|
||||||
uses: actions/configure-pages@v4
|
uses: actions/configure-pages@v5
|
||||||
|
|
||||||
- name: Upload Pages Artifact
|
- name: Upload Pages Artifact
|
||||||
# if: github.ref == 'refs/heads/main'
|
# if: github.ref == 'refs/heads/main'
|
||||||
@@ -154,6 +156,6 @@ jobs:
|
|||||||
path: ./docs/site/
|
path: ./docs/site/
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/v0'
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v0'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
|
"""mkdocs hooks for adding custom logic to documentation pipeline.
|
||||||
|
|
||||||
|
Lifecycle events: https://www.mkdocs.org/dev-guide/plugins/#events
|
||||||
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from mkdocs.config.defaults import MkDocsConfig
|
||||||
from mkdocs.structure.files import Files, File
|
from mkdocs.structure.files import Files, File
|
||||||
from mkdocs.structure.pages import Page
|
from mkdocs.structure.pages import Page
|
||||||
|
|
||||||
@@ -101,8 +108,7 @@ REDIRECT_MAP = {
|
|||||||
"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",
|
||||||
# assistant redirects
|
# assistant redirects
|
||||||
"cloud/how-tos/assistant_versioning.md": "cloud/how-tos/configuration_cloud.md"
|
"cloud/how-tos/assistant_versioning.md": "cloud/how-tos/configuration_cloud.md",
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -292,7 +298,7 @@ Redirecting...
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def write_html(site_dir, old_path, new_path):
|
def _write_html(site_dir, old_path, new_path):
|
||||||
"""Write an HTML file in the site_dir with a meta redirect to the new page"""
|
"""Write an HTML file in the site_dir with a meta redirect to the new page"""
|
||||||
# Determine all relevant paths
|
# Determine all relevant paths
|
||||||
old_path_abs = os.path.join(site_dir, old_path)
|
old_path_abs = os.path.join(site_dir, old_path)
|
||||||
@@ -308,6 +314,52 @@ def write_html(site_dir, old_path, new_path):
|
|||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
def _inject_gtm(html: str) -> str:
|
||||||
|
"""Inject Google Tag Manager code into the HTML.
|
||||||
|
|
||||||
|
Code to inject Google Tag Manager noscript tag immediately after <body>.
|
||||||
|
|
||||||
|
This is done via hooks rather than via a template because the MkDocs material
|
||||||
|
theme does not seem to allow placing the code immediately after the <body> tag
|
||||||
|
without modifying the template files directly.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
html: The HTML content to modify.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The modified HTML content with GTM code injected.
|
||||||
|
"""
|
||||||
|
# Code was copied from Google Tag Manager setup instructions.
|
||||||
|
gtm_code = """
|
||||||
|
<!-- Google Tag Manager (noscript) -->
|
||||||
|
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-T35S4S46"
|
||||||
|
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||||
|
<!-- End Google Tag Manager (noscript) -->
|
||||||
|
"""
|
||||||
|
soup = BeautifulSoup(html, "html.parser")
|
||||||
|
body = soup.body
|
||||||
|
if body:
|
||||||
|
# Insert the GTM code as raw HTML at the top of <body>
|
||||||
|
body.insert(0, BeautifulSoup(gtm_code, "html.parser"))
|
||||||
|
return str(soup)
|
||||||
|
else:
|
||||||
|
return html # fallback if no <body> found
|
||||||
|
|
||||||
|
|
||||||
|
def on_post_page(output: str, page: Page, config: MkDocsConfig) -> str:
|
||||||
|
"""Inject Google Tag Manager noscript tag immediately after <body>.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output: The HTML output of the page.
|
||||||
|
page: The page instance.
|
||||||
|
config: The MkDocs configuration object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
modified HTML output with GTM code injected.
|
||||||
|
"""
|
||||||
|
return _inject_gtm(output)
|
||||||
|
|
||||||
|
|
||||||
# Create HTML files for redirects after site dir has been built
|
# Create HTML files for redirects after site dir has been built
|
||||||
def on_post_build(config):
|
def on_post_build(config):
|
||||||
use_directory_urls = config.get("use_directory_urls")
|
use_directory_urls = config.get("use_directory_urls")
|
||||||
@@ -324,4 +376,4 @@ def on_post_build(config):
|
|||||||
+ hash
|
+ hash
|
||||||
+ suffix
|
+ suffix
|
||||||
)
|
)
|
||||||
write_html(config["site_dir"], old_html_path, new_html_path)
|
_write_html(config["site_dir"], old_html_path, new_html_path)
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](.
|
|||||||
1. Configure your `langgraph-dataplane-values.yaml` file.
|
1. Configure your `langgraph-dataplane-values.yaml` file.
|
||||||
|
|
||||||
config:
|
config:
|
||||||
langgraphPlatformLicenseKey: "" # Your LangGraph Platform license key
|
|
||||||
langsmithApiKey: "" # API Key of your Workspace
|
langsmithApiKey: "" # API Key of your Workspace
|
||||||
langsmithWorkspaceId: "" # Workspace ID
|
langsmithWorkspaceId: "" # Workspace ID
|
||||||
hostBackendUrl: "https://api.host.langchain.com" # Only override this if on EU
|
hostBackendUrl: "https://api.host.langchain.com" # Only override this if on EU
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
|||||||
| <span style="white-space: nowrap;">`graphs`</span> | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and returns an instance of `langgraph.graph.state.StateGraph` or `langgraph.graph.state.CompiledStateGraph`. See [how to rebuild a graph at runtime](../../cloud/deployment/graph_rebuild.md) for more details.</li></ul> |
|
| <span style="white-space: nowrap;">`graphs`</span> | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and returns an instance of `langgraph.graph.state.StateGraph` or `langgraph.graph.state.CompiledStateGraph`. See [how to rebuild a graph at runtime](../../cloud/deployment/graph_rebuild.md) for more details.</li></ul> |
|
||||||
| <span style="white-space: nowrap;">`auth`</span> | _(Added in v0.0.11)_ Auth configuration containing the path to your authentication handler. Example: `./your_package/auth.py:auth`, where `auth` is an instance of `langgraph_sdk.Auth`. See [authentication guide](../../concepts/auth.md) for details. |
|
| <span style="white-space: nowrap;">`auth`</span> | _(Added in v0.0.11)_ Auth configuration containing the path to your authentication handler. Example: `./your_package/auth.py:auth`, where `auth` is an instance of `langgraph_sdk.Auth`. See [authentication guide](../../concepts/auth.md) for details. |
|
||||||
| <span style="white-space: nowrap;">`base_image`</span> | Optional. Base image to use for the LangGraph API server. Defaults to `langchain/langgraph-api` or `langchain/langgraphjs-api`. Use this to pin your builds to a particular version of the langgraph API, such as `"langchain/langgraph-server:0.2"`. See https://hub.docker.com/r/langchain/langgraph-server/tags for more details. (added in `langgraph-cli==0.2.8`) |
|
| <span style="white-space: nowrap;">`base_image`</span> | Optional. Base image to use for the LangGraph API server. Defaults to `langchain/langgraph-api` or `langchain/langgraphjs-api`. Use this to pin your builds to a particular version of the langgraph API, such as `"langchain/langgraph-server:0.2"`. See https://hub.docker.com/r/langchain/langgraph-server/tags for more details. (added in `langgraph-cli==0.2.8`) |
|
||||||
|
| <span style="white-space: nowrap;">`image_distro`</span> | Optional. Linux distribution for the base image. Must be either `"debian"` or `"wolfi"`. If omitted, defaults to `"debian"`. Available in `langgraph-cli>=0.2.11`.|
|
||||||
| <span style="white-space: nowrap;">`env`</span> | Path to `.env` file or a mapping from environment variable to its value. |
|
| <span style="white-space: nowrap;">`env`</span> | Path to `.env` file or a mapping from environment variable to its value. |
|
||||||
| <span style="white-space: nowrap;">`store`</span> | Configuration for adding semantic search and/or time-to-live (TTL) to the BaseStore. Contains the following fields: <ul><li>`index` (optional): Configuration for semantic search indexing with fields `embed`, `dims`, and optional `fields`.</li><li>`ttl` (optional): Configuration for item expiration. An object with optional fields: `refresh_on_read` (boolean, defaults to `true`), `default_ttl` (float, lifespan in **minutes**, defaults to no expiration), and `sweep_interval_minutes` (integer, how often to check for expired items, defaults to no sweeping).</li></ul> |
|
| <span style="white-space: nowrap;">`store`</span> | Configuration for adding semantic search and/or time-to-live (TTL) to the BaseStore. Contains the following fields: <ul><li>`index` (optional): Configuration for semantic search indexing with fields `embed`, `dims`, and optional `fields`.</li><li>`ttl` (optional): Configuration for item expiration. An object with optional fields: `refresh_on_read` (boolean, defaults to `true`), `default_ttl` (float, lifespan in **minutes**, defaults to no expiration), and `sweep_interval_minutes` (integer, how often to check for expired items, defaults to no sweeping).</li></ul> |
|
||||||
| <span style="white-space: nowrap;">`ui`</span> | Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file. (added in `langgraph-cli==0.1.84`) |
|
| <span style="white-space: nowrap;">`ui`</span> | Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file. (added in `langgraph-cli==0.1.84`) |
|
||||||
@@ -79,6 +80,20 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Using Wolfi Base Images
|
||||||
|
|
||||||
|
You can specify the Linux distribution for your base image using the `image_distro` field. Valid options are `debian` or `wolfi`. Wolfi is the recommended option as it provides smaller and more secure images. This is available in `langgraph-cli>=0.2.11`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dependencies": ["."],
|
||||||
|
"graphs": {
|
||||||
|
"chat": "./chat/graph.py:graph"
|
||||||
|
},
|
||||||
|
"image_distro": "wolfi"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Adding semantic search to the store
|
#### Adding semantic search to the store
|
||||||
|
|
||||||
All deployments come with a DB-backed BaseStore. Adding an "index" configuration to your `langgraph.json` will enable [semantic search](../deployment/semantic_search.md) within the BaseStore of your deployment.
|
All deployments come with a DB-backed BaseStore. Adding an "index" configuration to your `langgraph.json` will enable [semantic search](../deployment/semantic_search.md) within the BaseStore of your deployment.
|
||||||
|
|||||||
@@ -123,3 +123,12 @@ Defaults to `''`.
|
|||||||
Set `REDIS_CLUSTER` to `True` to enable Redis Cluster mode. When enabled, the system will connect to Redis using cluster mode. This is useful when connecting to a Redis Cluster deployment.
|
Set `REDIS_CLUSTER` to `True` to enable Redis Cluster mode. When enabled, the system will connect to Redis using cluster mode. This is useful when connecting to a Redis Cluster deployment.
|
||||||
|
|
||||||
Defaults to `False`.
|
Defaults to `False`.
|
||||||
|
|
||||||
|
## `MOUNT_PREFIX`
|
||||||
|
|
||||||
|
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||||
|
The `MOUNT_PREFIX` environment variable is only allowed in Self-Hosted Deployment models, LangGraph Platform SaaS will not allow this environment variable.
|
||||||
|
|
||||||
|
Set `MOUNT_PREFIX` to serve the LangGraph Server under a specific path prefix. This is useful for deployments where the server is behind a reverse proxy or load balancer that requires a specific path prefix.
|
||||||
|
|
||||||
|
For example, if the server is to be served under `https://example.com/langgraph`, set `MOUNT_PREFIX` to `/langgraph`.
|
||||||
|
|||||||
@@ -197,19 +197,25 @@ In LangGraph, nodes are typically python functions (sync or async) where the **f
|
|||||||
Similar to `NetworkX`, you add these nodes to a graph using the [add_node][langgraph.graph.StateGraph.add_node] method:
|
Similar to `NetworkX`, you add these nodes to a graph using the [add_node][langgraph.graph.StateGraph.add_node] method:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
from langchain_core.runnables import RunnableConfig
|
from langchain_core.runnables import RunnableConfig
|
||||||
from langgraph.graph import StateGraph
|
from langgraph.graph import StateGraph
|
||||||
|
|
||||||
builder = StateGraph(dict)
|
class State(TypedDict):
|
||||||
|
input: str
|
||||||
|
results: str
|
||||||
|
|
||||||
|
builder = StateGraph(State)
|
||||||
|
|
||||||
|
|
||||||
def my_node(state: dict, config: RunnableConfig):
|
def my_node(state: State, config: RunnableConfig):
|
||||||
print("In node: ", config["configurable"]["user_id"])
|
print("In node: ", config["configurable"]["user_id"])
|
||||||
return {"results": f"Hello, {state['input']}!"}
|
return {"results": f"Hello, {state['input']}!"}
|
||||||
|
|
||||||
|
|
||||||
# The second argument is optional
|
# The second argument is optional
|
||||||
def my_other_node(state: dict):
|
def my_other_node(state: State):
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -470,9 +470,34 @@ If the checkpointer is used with asynchronous graph execution (i.e. executing th
|
|||||||
|
|
||||||
### Serializer
|
### Serializer
|
||||||
|
|
||||||
When checkpointers save the graph state, they need to serialize the channel values in the state. This is done using serializer objects.
|
When checkpointers save the graph state, they need to serialize the channel values in the state. This is done using serializer objects.
|
||||||
`langgraph_checkpoint` defines [protocol][langgraph.checkpoint.serde.base.SerializerProtocol] for implementing serializers provides a default implementation ([JsonPlusSerializer][langgraph.checkpoint.serde.jsonplus.JsonPlusSerializer]) that handles a wide variety of types, including LangChain and LangGraph primitives, datetimes, enums and more.
|
`langgraph_checkpoint` defines [protocol][langgraph.checkpoint.serde.base.SerializerProtocol] for implementing serializers provides a default implementation ([JsonPlusSerializer][langgraph.checkpoint.serde.jsonplus.JsonPlusSerializer]) that handles a wide variety of types, including LangChain and LangGraph primitives, datetimes, enums and more.
|
||||||
|
|
||||||
|
#### Encryption
|
||||||
|
|
||||||
|
Checkpointers can optionally encrypt all persisted state. To enable this, pass an instance of [`EncryptedSerializer`][langgraph.checkpoint.serde.encrypted.EncryptedSerializer] to the `serde` argument of any `BaseCheckpointSaver` implementation. The easiest way to create an encrypted serializer is via [`from_pycryptodome_aes`][langgraph.checkpoint.serde.encrypted.EncryptedSerializer.from_pycryptodome_aes], which reads the AES key from the `LANGGRAPH_AES_KEY` environment variable (or accepts a `key` argument):
|
||||||
|
|
||||||
|
```python
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
from langgraph.checkpoint.serde.encrypted import EncryptedSerializer
|
||||||
|
from langgraph.checkpoint.sqlite import SqliteSaver
|
||||||
|
|
||||||
|
serde = EncryptedSerializer.from_pycryptodome_aes() # reads LANGGRAPH_AES_KEY
|
||||||
|
checkpointer = SqliteSaver(sqlite3.connect("checkpoint.db"), serde=serde)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
from langgraph.checkpoint.serde.encrypted import EncryptedSerializer
|
||||||
|
from langgraph.checkpoint.postgres import PostgresSaver
|
||||||
|
|
||||||
|
serde = EncryptedSerializer.from_pycryptodome_aes()
|
||||||
|
checkpointer = PostgresSaver.from_conn_string("postgresql://...", serde=serde)
|
||||||
|
checkpointer.setup()
|
||||||
|
```
|
||||||
|
|
||||||
|
When running on LangGraph Platform, encryption is automatically enabled whenever `LANGGRAPH_AES_KEY` is present, so you only need to provide the environment variable. Other encryption schemes can be used by implementing [`CipherProtocol`][langgraph.checkpoint.serde.base.CipherProtocol] and supplying it to `EncryptedSerializer`.
|
||||||
|
|
||||||
## Capabilities
|
## Capabilities
|
||||||
|
|
||||||
### Human-in-the-loop
|
### Human-in-the-loop
|
||||||
|
|||||||
@@ -12,12 +12,18 @@
|
|||||||
options:
|
options:
|
||||||
members:
|
members:
|
||||||
- SerializerProtocol
|
- SerializerProtocol
|
||||||
|
- CipherProtocol
|
||||||
|
|
||||||
::: langgraph.checkpoint.serde.jsonplus
|
::: langgraph.checkpoint.serde.jsonplus
|
||||||
options:
|
options:
|
||||||
members:
|
members:
|
||||||
- JsonPlusSerializer
|
- JsonPlusSerializer
|
||||||
|
|
||||||
|
::: langgraph.checkpoint.serde.encrypted
|
||||||
|
options:
|
||||||
|
members:
|
||||||
|
- EncryptedSerializer
|
||||||
|
|
||||||
::: langgraph.checkpoint.memory
|
::: langgraph.checkpoint.memory
|
||||||
|
|
||||||
::: langgraph.checkpoint.sqlite
|
::: langgraph.checkpoint.sqlite
|
||||||
@@ -32,4 +38,4 @@
|
|||||||
::: langgraph.checkpoint.postgres.aio
|
::: langgraph.checkpoint.postgres.aio
|
||||||
options:
|
options:
|
||||||
members:
|
members:
|
||||||
- AsyncPostgresSaver
|
- AsyncPostgresSaver
|
||||||
|
|||||||
@@ -381,25 +381,6 @@ extra:
|
|||||||
link: https://github.com/langchain-ai/langgraph
|
link: https://github.com/langchain-ai/langgraph
|
||||||
- icon: fontawesome/brands/twitter
|
- icon: fontawesome/brands/twitter
|
||||||
link: https://twitter.com/LangChainAI
|
link: https://twitter.com/LangChainAI
|
||||||
analytics:
|
|
||||||
provider: google
|
|
||||||
property: G-G8X6ELZYE0
|
|
||||||
feedback:
|
|
||||||
title: Was this page helpful?
|
|
||||||
ratings:
|
|
||||||
- icon: material/emoticon-happy-outline
|
|
||||||
name: This page was helpful
|
|
||||||
data: 1
|
|
||||||
note: >-
|
|
||||||
Thanks for your feedback!
|
|
||||||
- icon: material/emoticon-sad-outline
|
|
||||||
name: This page could be improved
|
|
||||||
data: 0
|
|
||||||
note: >-
|
|
||||||
Thanks for your feedback! Please help us improve this page by adding to the discussion below.
|
|
||||||
shared_analytics:
|
|
||||||
provider: google
|
|
||||||
property: G-47WX3HKKY2
|
|
||||||
validation:
|
validation:
|
||||||
# https://www.mkdocs.org/user-guide/configuration/
|
# https://www.mkdocs.org/user-guide/configuration/
|
||||||
# We are still raising for omitted files because they determine the breadcrumbs for pages.
|
# We are still raising for omitted files because they determine the breadcrumbs for pages.
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block analytics %}
|
||||||
|
<!-- Google Tag Manager -->
|
||||||
|
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||||
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||||
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||||
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||||
|
})(window,document,'script','dataLayer','GTM-T35S4S46');</script>
|
||||||
|
<!-- End Google Tag Manager -->
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
<meta name="algolia-site-verification" content="165B7E7C89E49946" />
|
<meta name="algolia-site-verification" content="165B7E7C89E49946" />
|
||||||
<style>
|
<style>
|
||||||
@@ -185,7 +196,6 @@
|
|||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="notebook-links">
|
<div class="notebook-links">
|
||||||
{% if page.nb_url %}
|
{% if page.nb_url %}
|
||||||
@@ -209,7 +219,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block announce %}
|
{% block announce %}
|
||||||
<strong>We are growing and hiring for multiple roles for LangChain, LangGraph and LangSmith. <a href="https://www.langchain.com/careers" target="_blank" rel="noopener noreferrer"> Join our team!</a></strong>
|
<strong>We are growing and hiring for multiple roles for LangChain, LangGraph and LangSmith. <a href="https://www.langchain.com/careers" target="_blank" rel="noopener noreferrer"> Join our team!</a></strong>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -319,12 +319,18 @@ class StateGraph(Graph):
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
|
from langchain_core.runnables import RunnableConfig
|
||||||
from langgraph.graph import START, StateGraph
|
from langgraph.graph import START, StateGraph
|
||||||
|
|
||||||
def my_node(state, config):
|
class State(TypedDict):
|
||||||
|
x: int
|
||||||
|
|
||||||
|
def my_node(state: State, config: RunnableConfig) -> State:
|
||||||
return {"x": state["x"] + 1}
|
return {"x": state["x"] + 1}
|
||||||
|
|
||||||
builder = StateGraph(dict)
|
builder = StateGraph(State)
|
||||||
builder.add_node(my_node) # node name will be 'my_node'
|
builder.add_node(my_node) # node name will be 'my_node'
|
||||||
builder.add_edge(START, "my_node")
|
builder.add_edge(START, "my_node")
|
||||||
graph = builder.compile()
|
graph = builder.compile()
|
||||||
@@ -334,7 +340,7 @@ class StateGraph(Graph):
|
|||||||
|
|
||||||
Example: Customize the name:
|
Example: Customize the name:
|
||||||
```python
|
```python
|
||||||
builder = StateGraph(dict)
|
builder = StateGraph(State)
|
||||||
builder.add_node("my_fair_node", my_node)
|
builder.add_node("my_fair_node", my_node)
|
||||||
builder.add_edge(START, "my_fair_node")
|
builder.add_edge(START, "my_fair_node")
|
||||||
graph = builder.compile()
|
graph = builder.compile()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def run_with_retry(
|
|||||||
except ParentCommand as exc:
|
except ParentCommand as exc:
|
||||||
ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]
|
ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]
|
||||||
cmd = exc.args[0]
|
cmd = exc.args[0]
|
||||||
if cmd.graph == ns:
|
if cmd.graph in (ns, task.name):
|
||||||
# this command is for the current graph, handle it
|
# this command is for the current graph, handle it
|
||||||
for w in task.writers:
|
for w in task.writers:
|
||||||
w.invoke(cmd, config)
|
w.invoke(cmd, config)
|
||||||
@@ -137,7 +137,7 @@ async def arun_with_retry(
|
|||||||
except ParentCommand as exc:
|
except ParentCommand as exc:
|
||||||
ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]
|
ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]
|
||||||
cmd = exc.args[0]
|
cmd = exc.args[0]
|
||||||
if cmd.graph == ns:
|
if cmd.graph in (ns, task.name):
|
||||||
# this command is for the current graph, handle it
|
# this command is for the current graph, handle it
|
||||||
for w in task.writers:
|
for w in task.writers:
|
||||||
w.invoke(cmd, config)
|
w.invoke(cmd, config)
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ class PregelRunner:
|
|||||||
writes.extend(resumes)
|
writes.extend(resumes)
|
||||||
self.put_writes()(task.id, writes) # type: ignore[misc]
|
self.put_writes()(task.id, writes) # type: ignore[misc]
|
||||||
elif isinstance(exception, GraphBubbleUp):
|
elif isinstance(exception, GraphBubbleUp):
|
||||||
raise exception
|
pass
|
||||||
else:
|
else:
|
||||||
# save error to checkpointer
|
# save error to checkpointer
|
||||||
task.writes.append((ERROR, exception))
|
task.writes.append((ERROR, exception))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "langgraph"
|
name = "langgraph"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
description = "Building stateful, multi-actor applications with LLMs"
|
description = "Building stateful, multi-actor applications with LLMs"
|
||||||
authors = []
|
authors = []
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
|
|||||||
@@ -5514,8 +5514,11 @@ def test_runnable_passthrough_node_graph() -> None:
|
|||||||
assert graph.get_graph(xray=True).to_json() == graph.get_graph(xray=False).to_json()
|
assert graph.get_graph(xray=True).to_json() == graph.get_graph(xray=False).to_json()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("subgraph_persist", [True, False])
|
||||||
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_SYNC)
|
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_SYNC)
|
||||||
def test_parent_command(request: pytest.FixtureRequest, checkpointer_name: str) -> None:
|
def test_parent_command(
|
||||||
|
request: pytest.FixtureRequest, checkpointer_name: str, subgraph_persist: bool
|
||||||
|
) -> None:
|
||||||
from langchain_core.messages import BaseMessage
|
from langchain_core.messages import BaseMessage
|
||||||
from langchain_core.tools import tool
|
from langchain_core.tools import tool
|
||||||
|
|
||||||
@@ -5527,7 +5530,7 @@ def test_parent_command(request: pytest.FixtureRequest, checkpointer_name: str)
|
|||||||
subgraph_builder = StateGraph(MessagesState)
|
subgraph_builder = StateGraph(MessagesState)
|
||||||
subgraph_builder.add_node("tool", get_user_name)
|
subgraph_builder.add_node("tool", get_user_name)
|
||||||
subgraph_builder.add_edge(START, "tool")
|
subgraph_builder.add_edge(START, "tool")
|
||||||
subgraph = subgraph_builder.compile()
|
subgraph = subgraph_builder.compile(checkpointer=subgraph_persist)
|
||||||
|
|
||||||
class CustomParentState(TypedDict):
|
class CustomParentState(TypedDict):
|
||||||
messages: Annotated[list[BaseMessage], add_messages]
|
messages: Annotated[list[BaseMessage], add_messages]
|
||||||
@@ -8802,3 +8805,43 @@ def test_imp_exception(
|
|||||||
{"my_task": 2},
|
{"my_task": 2},
|
||||||
{"my_workflow": "done"},
|
{"my_workflow": "done"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("subgraph_persist", [True, False])
|
||||||
|
def test_parent_command_goto(
|
||||||
|
sync_checkpointer: BaseCheckpointSaver, subgraph_persist: bool
|
||||||
|
) -> None:
|
||||||
|
class State(TypedDict):
|
||||||
|
dialog_state: Annotated[list[str], operator.add]
|
||||||
|
|
||||||
|
def node_a_child(state):
|
||||||
|
return {"dialog_state": ["a_child_state"]}
|
||||||
|
|
||||||
|
def node_b_child(state):
|
||||||
|
return Command(
|
||||||
|
graph=Command.PARENT,
|
||||||
|
goto="node_b_parent",
|
||||||
|
update={"dialog_state": ["b_child_state"]},
|
||||||
|
)
|
||||||
|
|
||||||
|
sub_builder = StateGraph(State)
|
||||||
|
sub_builder.add_node(node_a_child)
|
||||||
|
sub_builder.add_node(node_b_child)
|
||||||
|
sub_builder.add_edge(START, "node_a_child")
|
||||||
|
sub_builder.add_edge("node_a_child", "node_b_child")
|
||||||
|
sub_graph = sub_builder.compile(checkpointer=subgraph_persist)
|
||||||
|
|
||||||
|
def node_b_parent(state):
|
||||||
|
return {"dialog_state": ["node_b_parent"]}
|
||||||
|
|
||||||
|
main_builder = StateGraph(State)
|
||||||
|
main_builder.add_node(node_b_parent)
|
||||||
|
main_builder.add_edge(START, "subgraph_node")
|
||||||
|
main_builder.add_node("subgraph_node", sub_graph, destinations=("node_b_parent",))
|
||||||
|
|
||||||
|
main_graph = main_builder.compile(sync_checkpointer, name="parent")
|
||||||
|
config = {"configurable": {"thread_id": 1}}
|
||||||
|
|
||||||
|
assert main_graph.invoke(input={"dialog_state": ["init_state"]}, config=config) == {
|
||||||
|
"dialog_state": ["init_state", "b_child_state", "node_b_parent"]
|
||||||
|
}
|
||||||
|
|||||||
@@ -6772,8 +6772,9 @@ async def test_debug_nested_subgraphs(async_checkpointer: BaseCheckpointSaver):
|
|||||||
assert stream_task.get("state") == history_task.state
|
assert stream_task.get("state") == history_task.state
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("subgraph_persist", [True, False])
|
||||||
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_ASYNC)
|
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_ASYNC)
|
||||||
async def test_parent_command(checkpointer_name: str) -> None:
|
async def test_parent_command(checkpointer_name: str, subgraph_persist: bool) -> None:
|
||||||
from langchain_core.messages import BaseMessage
|
from langchain_core.messages import BaseMessage
|
||||||
from langchain_core.tools import tool
|
from langchain_core.tools import tool
|
||||||
|
|
||||||
@@ -6785,7 +6786,7 @@ async def test_parent_command(checkpointer_name: str) -> None:
|
|||||||
subgraph_builder = StateGraph(MessagesState)
|
subgraph_builder = StateGraph(MessagesState)
|
||||||
subgraph_builder.add_node("tool", get_user_name)
|
subgraph_builder.add_node("tool", get_user_name)
|
||||||
subgraph_builder.add_edge(START, "tool")
|
subgraph_builder.add_edge(START, "tool")
|
||||||
subgraph = subgraph_builder.compile()
|
subgraph = subgraph_builder.compile(checkpointer=subgraph_persist)
|
||||||
|
|
||||||
class CustomParentState(TypedDict):
|
class CustomParentState(TypedDict):
|
||||||
messages: Annotated[list[BaseMessage], add_messages]
|
messages: Annotated[list[BaseMessage], add_messages]
|
||||||
@@ -9446,3 +9447,43 @@ async def test_imp_exception(
|
|||||||
"parent_ids": [],
|
"parent_ids": [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("subgraph_persist", [True, False])
|
||||||
|
async def test_parent_command_goto(
|
||||||
|
async_checkpointer: BaseCheckpointSaver, subgraph_persist: bool
|
||||||
|
) -> None:
|
||||||
|
class State(TypedDict):
|
||||||
|
dialog_state: Annotated[list[str], operator.add]
|
||||||
|
|
||||||
|
async def node_a_child(state):
|
||||||
|
return {"dialog_state": ["a_child_state"]}
|
||||||
|
|
||||||
|
async def node_b_child(state):
|
||||||
|
return Command(
|
||||||
|
graph=Command.PARENT,
|
||||||
|
goto="node_b_parent",
|
||||||
|
update={"dialog_state": ["b_child_state"]},
|
||||||
|
)
|
||||||
|
|
||||||
|
sub_builder = StateGraph(State)
|
||||||
|
sub_builder.add_node(node_a_child)
|
||||||
|
sub_builder.add_node(node_b_child)
|
||||||
|
sub_builder.add_edge(START, "node_a_child")
|
||||||
|
sub_builder.add_edge("node_a_child", "node_b_child")
|
||||||
|
sub_graph = sub_builder.compile(checkpointer=subgraph_persist)
|
||||||
|
|
||||||
|
async def node_b_parent(state):
|
||||||
|
return {"dialog_state": ["node_b_parent"]}
|
||||||
|
|
||||||
|
main_builder = StateGraph(State)
|
||||||
|
main_builder.add_node(node_b_parent)
|
||||||
|
main_builder.add_edge(START, "subgraph_node")
|
||||||
|
main_builder.add_node("subgraph_node", sub_graph, destinations=("node_b_parent",))
|
||||||
|
|
||||||
|
main_graph = main_builder.compile(async_checkpointer, name="parent")
|
||||||
|
config = {"configurable": {"thread_id": 1}}
|
||||||
|
|
||||||
|
assert await main_graph.ainvoke(
|
||||||
|
input={"dialog_state": ["init_state"]}, config=config
|
||||||
|
) == {"dialog_state": ["init_state", "b_child_state", "node_b_parent"]}
|
||||||
|
|||||||
Generated
+1565
-1565
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user