mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-18 13:45:44 +02:00
Compare commits
28
Commits
cli==0.3.1
...
0.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13d66e0ebb | ||
|
|
9fcd0d47f5 | ||
|
|
4749a168a9 | ||
|
|
8f285a7463 | ||
|
|
98c369a0cf | ||
|
|
f45ee34ca3 | ||
|
|
bd206c2fcd | ||
|
|
14ec895046 | ||
|
|
fb66736ccb | ||
|
|
c3544024b9 | ||
|
|
b0d1234737 | ||
|
|
53e1a238db | ||
|
|
fcdeafd0d1 | ||
|
|
28c529feb2 | ||
|
|
91ebc8d3ed | ||
|
|
6e08f4c12e | ||
|
|
f2dc0653f1 | ||
|
|
67177a5610 | ||
|
|
b1b238c7ea | ||
|
|
598796ef86 | ||
|
|
3c7981201e | ||
|
|
109c0dfb93 | ||
|
|
d7c364c5bb | ||
|
|
746142fb07 | ||
|
|
b9c9c32c31 | ||
|
|
c89fe4c45d | ||
|
|
b0e28851a6 | ||
|
|
48fb91deda |
@@ -13,7 +13,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v0'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -4,9 +4,11 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v0
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v0
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -82,9 +84,9 @@ jobs:
|
||||
run: make llms-text
|
||||
- name: Build site
|
||||
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
|
||||
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
|
||||
if [ "${{ github.ref }}" == "refs/heads/v0" ]; then
|
||||
DOWNLOAD_STATS=true make build-docs
|
||||
else
|
||||
make build-docs
|
||||
@@ -144,8 +146,8 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Configure GitHub Pages
|
||||
if: github.ref == 'refs/heads/main'
|
||||
uses: actions/configure-pages@v4
|
||||
if: github.ref == 'refs/heads/v0'
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload Pages Artifact
|
||||
# if: github.ref == 'refs/heads/main'
|
||||
@@ -154,6 +156,6 @@ jobs:
|
||||
path: ./docs/site/
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
if: github.ref == 'refs/heads/main'
|
||||
if: github.ref == 'refs/heads/v0'
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
@@ -13,7 +13,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v0'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
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 os
|
||||
import posixpath
|
||||
import re
|
||||
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.pages import Page
|
||||
|
||||
@@ -101,8 +108,7 @@ REDIRECT_MAP = {
|
||||
"how-tos/deploy-self-hosted.md": "cloud/deployment/self_hosted_data_plane.md",
|
||||
"concepts/self_hosted.md": "concepts/langgraph_self_hosted_data_plane.md",
|
||||
# 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"""
|
||||
# Determine all relevant paths
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
def on_post_build(config):
|
||||
use_directory_urls = config.get("use_directory_urls")
|
||||
@@ -324,4 +376,4 @@ def on_post_build(config):
|
||||
+ hash
|
||||
+ suffix
|
||||
)
|
||||
write_html(config["site_dir"], old_html_path, new_html_path)
|
||||
_write_html(config["site_dir"], old_html_path, new_html_path)
|
||||
|
||||
@@ -62,6 +62,15 @@ Starting from the `LangGraph Platform` view...
|
||||
1. In the panel, select the `Server` tab to view server logs for the revision. Server logs are only available after a revision has been deployed.
|
||||
1. Within the `Server` tab, adjust the date/time range picker as needed. By default, the date/time range picker is set to the `Last 7 days`.
|
||||
|
||||
## View Deployment Metrics
|
||||
|
||||
Starting from the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>...
|
||||
|
||||
1. In the left-hand navigation panel, select `LangGraph Platform`. The `LangGraph Platform` view contains a list of existing LangGraph Platform deployments.
|
||||
1. Select an existing deployment to monitor.
|
||||
1. Select the `Monitoring` tab to view the deployment metrics. See a list of [all available metrics](../../concepts/langgraph_control_plane.md#monitoring).
|
||||
1. Within the `Monitoring` tab, use the date/time range picker as needed. By default, the date/time range picker is set to the `Last 15 minutes`.
|
||||
|
||||
## Interrupt Revision
|
||||
|
||||
Interrupting a revision will stop deployment of the revision.
|
||||
|
||||
@@ -30,18 +30,17 @@ Before deploying, review the [conceptual guide for the Self-Hosted Control Plane
|
||||
1. `LangGraphPlatform CRD`: A CRD for LangGraph Platform deployments. This contains the spec for managing an instance of a LangGraph platform deployment.
|
||||
1. `operator`: This operator handles changes to your LangGraph Platform CRDs.
|
||||
1. `host-backend`: This is the [control plane](../../concepts/langgraph_control_plane.md).
|
||||
1. Two additional images will be used by the chart.
|
||||
1. Two additional images will be used by the chart. Use the images that are specified in the latest release.
|
||||
|
||||
hostBackendImage:
|
||||
repository: "docker.io/langchain/hosted-langserve-backend"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "0.9.80"
|
||||
operatorImage:
|
||||
repository: "docker.io/langchain/langgraph-operator"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "aa9dff4"
|
||||
|
||||
1. In your `values.yaml` file, enable the `langgraphPlatform` option. Note that you must also have a valid ingress setup:
|
||||
1. In your config file for langsmith (usually `langsmith_config.yaml`, enable the `langgraphPlatform` option. Note that you must also have a valid ingress setup:
|
||||
|
||||
config:
|
||||
langgraphPlatform:
|
||||
enabled: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Assistants Overview](../../concepts/assistants.md)
|
||||
- [Assistants Overview](../../../concepts/assistants.md)
|
||||
|
||||
LangGraph Studio lets you view, edit, and update your assistants, and allows you to run your graph using these assistant configurations.
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Run experiments over a dataset
|
||||
|
||||
LangGraph Studio supports evaluations by allowing you to run your assistant over a pre-defined LangSmith dataset. This enables you to understand how your application performs over a variety of inputs, compare the results to reference outputs, and score the results using [evaluators](../../../agents/evals.md).
|
||||
|
||||
This guide shows you how to run an experiment end-to-end from Studio.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running an experiment, ensure you have the following:
|
||||
|
||||
1. **A LangSmith dataset**: Your dataset should contain the inputs you want to test and optionally, reference outputs for comparison.
|
||||
|
||||
- The schema for the inputs must match the required input schema for the assistant. For more information on schemas, see [here](../../../concepts/low_level.md#schema).
|
||||
- For more on creating datasets, see [How to Manage Datasets](https://docs.smith.langchain.com/evaluation/how_to_guides/manage_datasets_in_application#set-up-your-dataset).
|
||||
|
||||
2. **(Optional) Evaluators**: You can attach evaluators (e.g., LLM-as-a-Judge, heuristics, or custom functions) to your dataset in LangSmith. These will run automatically after the graph has processed all inputs.
|
||||
|
||||
- To learn more, read about [Evaluation Concepts](https://docs.smith.langchain.com/evaluation/concepts#evaluators).
|
||||
|
||||
3. **A running application**: The experiment can be run against:
|
||||
- An application deployed on [LangGraph Platform](../../quick_start.md).
|
||||
- A locally running application started via the [langgraph-cli](../../../tutorials/langgraph-platform/local-server.md).
|
||||
|
||||
---
|
||||
|
||||
## Step-by-step guide
|
||||
|
||||
### 1. Launch the experiment
|
||||
|
||||
Click the **Run experiment** button in the top right corner of the Studio page.
|
||||
|
||||
### 2. Select your dataset
|
||||
|
||||
In the modal that appears, select the dataset (or a specific dataset split) to use for the experiment and click **Start**.
|
||||
|
||||
### 3. Monitor the progress
|
||||
|
||||
All of the inputs in the dataset will now be run against the active assistant. Monitor the experiment's progress via the badge in the top right corner.
|
||||
|
||||
You can continue to work in Studio while the experiment runs in the background. Click the arrow icon button at any time to navigate to LangSmith and view the detailed experiment results.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Run experiment" button is disabled
|
||||
|
||||
If the "Run experiment" button is disabled, check the following:
|
||||
|
||||
- **Deployed application**: If your application is deployed on LangGraph Platform, you may need to create a new revision to enable this feature.
|
||||
- **Local development server**: If you are running your application locally, make sure you have upgraded to the latest version of the `langgraph-cli` (`pip install -U langgraph-cli`). Additionally, ensure you have tracing enabled by setting the `LANGSMITH_API_KEY` in your project's `.env` file.
|
||||
|
||||
### Evaluator results are missing
|
||||
|
||||
When you run an experiment, any attached evaluators are scheduled for execution in a queue. If you don't see results immediately, it likely means they are still pending.
|
||||
@@ -43,12 +43,14 @@ 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;">`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;">`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;">`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;">`python_version`</span> | `3.11`, `3.12`, or `3.13`. Defaults to `3.11`. |
|
||||
| <span style="white-space: nowrap;">`node_version`</span> | Specify `node_version: 20` to use LangGraph.js. |
|
||||
| <span style="white-space: nowrap;">`pip_config_file`</span> | Path to `pip` config file. |
|
||||
| <span style="white-space: nowrap;">`pip_installer`</span> | _(Added in v0.3)_ Optional. Python package installer selector. It can be set to `"auto"`, `"pip"`, or `"uv"`. From version 0.3 onward the default strategy is to run `uv pip`, which typically delivers faster builds while remaining a drop-in replacement. In the uncommon situation where `uv` cannot handle your dependency graph or the structure of your `pyproject.toml`, specify `"pip"` here to revert to the earlier behaviour. |
|
||||
| <span style="white-space: nowrap;">`dockerfile_lines`</span> | Array of additional lines to add to Dockerfile following the import from parent image. |
|
||||
| <span style="white-space: nowrap;">`checkpointer`</span> | Configuration for the checkpointer. Contains a `ttl` field which is an object with the following keys: <ul><li>`strategy`: How to handle expired checkpoints (e.g., `"delete"`).</li><li>`sweep_interval_minutes`: How often to check for expired checkpoints (integer).</li><li>`default_ttl`: Default time-to-live for checkpoints in **minutes** (integer). Defines how long checkpoints are kept before the specified strategy is applied.</li></ul> |
|
||||
| <span style="white-space: nowrap;">`http`</span> | HTTP server configuration with the following fields: <ul><li>`app`: Path to custom Starlette/FastAPI app (e.g., `"./src/agent/webapp.py:app"`). See [custom routes guide](../../how-tos/http/custom_routes.md).</li><li>`disable_assistants`: Disable `/assistants` routes</li><li>`disable_threads`: Disable `/threads` routes</li><li>`disable_runs`: Disable `/runs` routes</li><li>`disable_store`: Disable `/store` routes</li><li>`disable_meta`: Disable `/ok`, `/info`, `/metrics`, and `/docs` routes</li><li>`cors`: CORS configuration with fields for `allow_origins`, `allow_methods`, `allow_headers`, etc.</li><li>`configurable_headers`: Define which request headers to exclude or include as a run's configurable values.</li></ul> |
|
||||
@@ -79,6 +81,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
|
||||
|
||||
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.
|
||||
@@ -113,7 +129,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
- `cohere:embed-english-v3.0`: 1024
|
||||
- `cohere:embed-english-light-v3.0`: 384
|
||||
- `cohere:embed-multilingual-v3.0`: 1024
|
||||
- `cohere:embed-multilingual-light-v3.0`: 384
|
||||
- `cohere:embed-multilingual-light-v3.0`: 384
|
||||
|
||||
#### Semantic search with a custom embedding function
|
||||
|
||||
@@ -346,8 +362,8 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `--platform TEXT` | | Target platform(s) to build the Docker image for. Example: `langgraph build --platform linux/amd64,linux/arm64` |
|
||||
| `-t, --tag TEXT` | | **Required**. Tag for the Docker image. Example: `langgraph build -t my-image` |
|
||||
| `--pull / --no-pull` | `--pull` | Build with latest remote Docker image. Use `--no-pull` for running the LangGraph Platform API server with locally built images. |
|
||||
@@ -366,8 +382,8 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Option | Default | Description |
|
||||
| -------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `--platform TEXT` | | Target platform(s) to build the Docker image for. Example: `langgraph build --platform linux/amd64,linux/arm64` |
|
||||
| `-t, --tag TEXT` | | **Required**. Tag for the Docker image. Example: `langgraph build -t my-image` |
|
||||
| `--no-pull` | | Use locally built images. Defaults to `false` to build with latest remote Docker image. |
|
||||
|
||||
@@ -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.
|
||||
|
||||
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`.
|
||||
|
||||
@@ -63,4 +63,8 @@ Yes! LangGraph is totally ambivalent to what LLMs are used under the hood. The m
|
||||
|
||||
Yes! You can use the [development version of LangGraph Server](../tutorials/langgraph-platform/local-server.md) to run the backend locally.
|
||||
This will connect to the studio frontend hosted as part of LangSmith.
|
||||
If you set an environment variable of `LANGSMITH_TRACING=false` then no traces will be sent to LangSmith.
|
||||
If you set an environment variable of `LANGSMITH_TRACING=false`, then no traces will be sent to LangSmith.
|
||||
|
||||
## What does "nodes executed" mean for LangGraph Platform usage?
|
||||
|
||||
**Nodes Executed** is the aggregate number of nodes in a LangGraph application that are called and completed successfully during an invocation of the application. If a node in the graph is not called during execution or ends in an error state, these nodes will not be counted. If a node is called and completes successfully multiple times, each occurrence will be counted.
|
||||
|
||||
@@ -19,6 +19,7 @@ From the control plane UI, you can:
|
||||
- Update a deployment.
|
||||
- Update environment variables for a deployment.
|
||||
- View build and server logs of a deployment.
|
||||
- View deployment metrics such as CPU and memory usage.
|
||||
- Delete a deployment.
|
||||
|
||||
The Control Plane UI is embedded in [LangSmith](https://docs.smith.langchain.com/langgraph_cloud).
|
||||
@@ -88,6 +89,17 @@ Infrastructure for deployments and revisions are provisioned and deployed asynch
|
||||
|
||||
The control plane and [LangGraph Data Plane](./langgraph_data_plane.md) "listener" application coordinate to achieve asynchronous deployments.
|
||||
|
||||
### Monitoring
|
||||
|
||||
After a deployment is ready, the control plane monitors the deployment and records various metrics, such as:
|
||||
|
||||
- CPU and memory usage of the deployment.
|
||||
- Number of container restarts.
|
||||
- Number of replicas (this will increase with [autoscaling](../concepts/langgraph_data_plane.md#autoscaling)).
|
||||
- [Postgres](../concepts/langgraph_data_plane.md#postgres) CPU, memory usage, and disk usage.
|
||||
|
||||
These metrics are displayed as charts in the Control Plane UI.
|
||||
|
||||
### LangSmith Integration
|
||||
|
||||
A [LangSmith](https://docs.smith.langchain.com/) tracing project is automatically created for each deployment. The tracing project has the same name as the deployment. When creating a deployment, the `LANGCHAIN_TRACING` and `LANGSMITH_API_KEY`/`LANGCHAIN_API_KEY` environment variables do not need to be specified; they are set automatically by the control plane.
|
||||
|
||||
@@ -17,7 +17,7 @@ Use LangGraph Server to create and manage [assistants](assistants.md), [threads]
|
||||
|
||||
There are two versions of LangGraph Server:
|
||||
|
||||
- `Lite` is a limited version of the LangGraph Server that you can run locally or in a self-hosted manner (up to 1 million nodes executed per year).
|
||||
- `Lite` is a limited version of the LangGraph Server that you can run locally or in a self-hosted manner (up to 1 million [nodes executed](../concepts/faq.md#what-does-nodes-executed-mean-for-langgraph-platform-usage) per year).
|
||||
- `Enterprise` is the full version of the LangGraph Server. To use the `Enterprise` version, you must acquire a license key that you will need to specify when running the Docker image. To acquire a license key, please email sales@langchain.dev.
|
||||
|
||||
Feature Differences:
|
||||
|
||||
@@ -21,9 +21,10 @@ Key features of LangGraph Studio:
|
||||
|
||||
- Visualize your graph architecture
|
||||
- [Run and interact with your agent](../cloud/how-tos/invoke_studio.md)
|
||||
- [Manage assistants](../cloud/how-tos/studio/manage_assistants.md.md)
|
||||
- [Manage assistants](../cloud/how-tos/studio/manage_assistants.md)
|
||||
- [Manage threads](../cloud/how-tos/threads_studio.md)
|
||||
- [Iterate on prompts](../cloud/how-tos/iterate_graph_studio.md)
|
||||
- [Run experiments over a dataset](../cloud/how-tos/studio/run_evals.md)
|
||||
- Manage [long term memory](memory.md)
|
||||
- Debug agent state via [time travel](time-travel.md)
|
||||
|
||||
@@ -41,4 +42,4 @@ Chat mode is a simpler UI for iterating on and testing chat-specific agents. It
|
||||
|
||||
## Learn more
|
||||
|
||||
- See this guide on how to [get started](../cloud/how-tos/studio/quick_start.md) with LangGraph Studio.
|
||||
- See this guide on how to [get started](../cloud/how-tos/studio/quick_start.md) with LangGraph Studio.
|
||||
|
||||
@@ -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:
|
||||
|
||||
```python
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
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"])
|
||||
return {"results": f"Hello, {state['input']}!"}
|
||||
|
||||
|
||||
# The second argument is optional
|
||||
def my_other_node(state: dict):
|
||||
def my_other_node(state: State):
|
||||
return state
|
||||
|
||||
|
||||
|
||||
@@ -470,9 +470,34 @@ If the checkpointer is used with asynchronous graph execution (i.e. executing th
|
||||
|
||||
### 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.
|
||||
|
||||
#### 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
|
||||
|
||||
### Human-in-the-loop
|
||||
|
||||
@@ -20,7 +20,7 @@ There are three different plans for using it.
|
||||
| | Developer | Plus | Enterprise |
|
||||
|------------------------------------------------------------------|---------------------------------------------|-------------------------------------------------------|-----------------------------------------------------|
|
||||
| Deployment Options | Standalone Container (Lite) | Cloud SaaS | <ul><li>Cloud SaaS</li><li>Self-Hosted Data Plane</li><li>Self-Hosted Control Plane</li><li>Standalone Container (Enterprise)</li></ul> |
|
||||
| Usage | Free, limited to 1M nodes executed per year | Free while in Beta, will be charged per node executed | Custom |
|
||||
| Usage | Free, limited to 1M [nodes executed](../concepts/faq.md#what-does-nodes-executed-mean-for-langgraph-platform-usage) per year | See [Pricing](https://www.langchain.com/langgraph-platform-pricing) | Custom |
|
||||
| APIs for retrieving and updating state and conversational history | ✅ | ✅ | ✅ |
|
||||
| APIs for retrieving and updating long-term memory | ✅ | ✅ | ✅ |
|
||||
| Horizontally scalable task queues and servers | ✅ | ✅ | ✅ |
|
||||
|
||||
@@ -5,7 +5,7 @@ search:
|
||||
|
||||
# LangGraph SDK
|
||||
|
||||
LangGraph Platform provides both a Python SDK for interacting with [LangGraph Server](./langgraph_server.md).
|
||||
LangGraph Platform provides both a Python and JS SDK for interacting with [LangGraph Server](./langgraph_server.md).
|
||||
|
||||
!!! tip "Python SDK reference"
|
||||
|
||||
|
||||
@@ -12,12 +12,18 @@
|
||||
options:
|
||||
members:
|
||||
- SerializerProtocol
|
||||
- CipherProtocol
|
||||
|
||||
::: langgraph.checkpoint.serde.jsonplus
|
||||
options:
|
||||
members:
|
||||
- JsonPlusSerializer
|
||||
|
||||
::: langgraph.checkpoint.serde.encrypted
|
||||
options:
|
||||
members:
|
||||
- EncryptedSerializer
|
||||
|
||||
::: langgraph.checkpoint.memory
|
||||
|
||||
::: langgraph.checkpoint.sqlite
|
||||
@@ -32,4 +38,4 @@
|
||||
::: langgraph.checkpoint.postgres.aio
|
||||
options:
|
||||
members:
|
||||
- AsyncPostgresSaver
|
||||
- AsyncPostgresSaver
|
||||
|
||||
+1
-29
@@ -179,6 +179,7 @@ nav:
|
||||
- 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
|
||||
@@ -364,16 +365,6 @@ markdown_extensions:
|
||||
hooks:
|
||||
- _scripts/notebook_hooks.py
|
||||
extra:
|
||||
consent:
|
||||
title: Cookie consent
|
||||
actions:
|
||||
- accept
|
||||
- reject
|
||||
description: >-
|
||||
We use cookies to recognize your repeated visits and preferences, as well
|
||||
as to measure the effectiveness of our documentation and whether users
|
||||
find what they're searching for. <strong>Clicking "Accept" makes our
|
||||
documentation better. Thank you!</strong> ❤️
|
||||
social:
|
||||
- icon: fontawesome/brands/js
|
||||
link: https://langchain-ai.github.io/langgraphjs/
|
||||
@@ -381,25 +372,6 @@ extra:
|
||||
link: https://github.com/langchain-ai/langgraph
|
||||
- icon: fontawesome/brands/twitter
|
||||
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:
|
||||
# https://www.mkdocs.org/user-guide/configuration/
|
||||
# We are still raising for omitted files because they determine the breadcrumbs for pages.
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
{% 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 %}
|
||||
<meta name="algolia-site-verification" content="165B7E7C89E49946" />
|
||||
<style>
|
||||
@@ -185,7 +196,6 @@
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="notebook-links">
|
||||
{% if page.nb_url %}
|
||||
@@ -209,7 +219,6 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% 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>
|
||||
{% endblock %}
|
||||
|
||||
@@ -319,12 +319,18 @@ class StateGraph(Graph):
|
||||
|
||||
Example:
|
||||
```python
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
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}
|
||||
|
||||
builder = StateGraph(dict)
|
||||
builder = StateGraph(State)
|
||||
builder.add_node(my_node) # node name will be 'my_node'
|
||||
builder.add_edge(START, "my_node")
|
||||
graph = builder.compile()
|
||||
@@ -334,7 +340,7 @@ class StateGraph(Graph):
|
||||
|
||||
Example: Customize the name:
|
||||
```python
|
||||
builder = StateGraph(dict)
|
||||
builder = StateGraph(State)
|
||||
builder.add_node("my_fair_node", my_node)
|
||||
builder.add_edge(START, "my_fair_node")
|
||||
graph = builder.compile()
|
||||
|
||||
@@ -275,7 +275,7 @@ def apply_writes(
|
||||
)
|
||||
|
||||
# clear pending sends
|
||||
if checkpoint["pending_sends"] and bump_step:
|
||||
if checkpoint.get("pending_sends") and bump_step:
|
||||
checkpoint["pending_sends"].clear()
|
||||
|
||||
# Group writes by channel
|
||||
@@ -286,7 +286,7 @@ def apply_writes(
|
||||
if chan in (NO_WRITES, PUSH, RESUME, INTERRUPT, RETURN, ERROR):
|
||||
pass
|
||||
elif chan == TASKS:
|
||||
checkpoint["pending_sends"].append(val)
|
||||
checkpoint.setdefault("pending_sends", []).append(val)
|
||||
elif chan in channels:
|
||||
pending_writes_by_channel[chan].append(val)
|
||||
else:
|
||||
@@ -327,7 +327,7 @@ def apply_writes(
|
||||
# If this is (tentatively) the last superstep, notify all channels of finish
|
||||
if (
|
||||
bump_step
|
||||
and not checkpoint["pending_sends"]
|
||||
and not checkpoint.get("pending_sends")
|
||||
and updated_channels.isdisjoint(trigger_to_nodes)
|
||||
):
|
||||
for chan in channels:
|
||||
@@ -344,17 +344,6 @@ def apply_writes(
|
||||
return pending_writes_by_managed, updated_channels
|
||||
|
||||
|
||||
def has_next_tasks(
|
||||
trigger_to_nodes: Mapping[str, Sequence[str]],
|
||||
updated_channels: set[str],
|
||||
checkpoint: Checkpoint,
|
||||
) -> bool:
|
||||
"""Check if there are any tasks that should be run in the next step."""
|
||||
return bool(checkpoint["pending_sends"]) or not updated_channels.isdisjoint(
|
||||
trigger_to_nodes
|
||||
)
|
||||
|
||||
|
||||
@overload
|
||||
def prepare_next_tasks(
|
||||
checkpoint: Checkpoint,
|
||||
@@ -446,7 +435,7 @@ def prepare_next_tasks(
|
||||
null_version = checkpoint_null_version(checkpoint)
|
||||
tasks: list[Union[PregelTask, PregelExecutableTask]] = []
|
||||
# Consume pending_sends from previous step
|
||||
for idx, _ in enumerate(checkpoint["pending_sends"]):
|
||||
for idx, _ in enumerate(checkpoint.get("pending_sends", ())):
|
||||
if task := prepare_single_task(
|
||||
(PUSH, idx),
|
||||
None,
|
||||
@@ -648,7 +637,7 @@ def prepare_single_task(
|
||||
# SEND tasks, executed in superstep n+1
|
||||
# (PUSH, idx of pending send)
|
||||
idx = cast(int, task_path[1])
|
||||
if idx >= len(checkpoint["pending_sends"]):
|
||||
if idx >= len(checkpoint.get("pending_sends", ())):
|
||||
return
|
||||
packet = checkpoint["pending_sends"][idx]
|
||||
if not isinstance(packet, Send):
|
||||
|
||||
@@ -41,7 +41,7 @@ def run_with_retry(
|
||||
except ParentCommand as exc:
|
||||
ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]
|
||||
cmd = exc.args[0]
|
||||
if cmd.graph == ns:
|
||||
if cmd.graph in (ns, task.name):
|
||||
# this command is for the current graph, handle it
|
||||
for w in task.writers:
|
||||
w.invoke(cmd, config)
|
||||
@@ -137,7 +137,7 @@ async def arun_with_retry(
|
||||
except ParentCommand as exc:
|
||||
ns: str = config[CONF][CONFIG_KEY_CHECKPOINT_NS]
|
||||
cmd = exc.args[0]
|
||||
if cmd.graph == ns:
|
||||
if cmd.graph in (ns, task.name):
|
||||
# this command is for the current graph, handle it
|
||||
for w in task.writers:
|
||||
w.invoke(cmd, config)
|
||||
|
||||
@@ -104,7 +104,8 @@ class FuturesDict(Generic[F, E], dict[F, Optional[PregelExecutableTask]]):
|
||||
fut: F,
|
||||
) -> None:
|
||||
try:
|
||||
self.callback()(task, _exception(fut)) # type: ignore[misc]
|
||||
if cb := self.callback():
|
||||
cb(task, _exception(fut))
|
||||
finally:
|
||||
with self.lock:
|
||||
self.done.add(fut)
|
||||
@@ -431,7 +432,7 @@ class PregelRunner:
|
||||
writes.extend(resumes)
|
||||
self.put_writes()(task.id, writes) # type: ignore[misc]
|
||||
elif isinstance(exception, GraphBubbleUp):
|
||||
raise exception
|
||||
pass
|
||||
else:
|
||||
# save error to checkpointer
|
||||
task.writes.append((ERROR, exception))
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph"
|
||||
version = "0.4.7"
|
||||
version = "0.4.10"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
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()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("subgraph_persist", [True, False])
|
||||
@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.tools import tool
|
||||
|
||||
@@ -5527,7 +5530,7 @@ def test_parent_command(request: pytest.FixtureRequest, checkpointer_name: str)
|
||||
subgraph_builder = StateGraph(MessagesState)
|
||||
subgraph_builder.add_node("tool", get_user_name)
|
||||
subgraph_builder.add_edge(START, "tool")
|
||||
subgraph = subgraph_builder.compile()
|
||||
subgraph = subgraph_builder.compile(checkpointer=subgraph_persist)
|
||||
|
||||
class CustomParentState(TypedDict):
|
||||
messages: Annotated[list[BaseMessage], add_messages]
|
||||
@@ -8802,3 +8805,43 @@ def test_imp_exception(
|
||||
{"my_task": 2},
|
||||
{"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
|
||||
|
||||
|
||||
@pytest.mark.parametrize("subgraph_persist", [True, False])
|
||||
@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.tools import tool
|
||||
|
||||
@@ -6785,7 +6786,7 @@ async def test_parent_command(checkpointer_name: str) -> None:
|
||||
subgraph_builder = StateGraph(MessagesState)
|
||||
subgraph_builder.add_node("tool", get_user_name)
|
||||
subgraph_builder.add_edge(START, "tool")
|
||||
subgraph = subgraph_builder.compile()
|
||||
subgraph = subgraph_builder.compile(checkpointer=subgraph_persist)
|
||||
|
||||
class CustomParentState(TypedDict):
|
||||
messages: Annotated[list[BaseMessage], add_messages]
|
||||
@@ -9446,3 +9447,43 @@ async def test_imp_exception(
|
||||
"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
Generated
+742
-742
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user