Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3150cc41d8 | ||
|
|
f563688274 | ||
|
|
fa5407d60b | ||
|
|
e982f60db1 |
@@ -127,6 +127,30 @@ REDIRECT_MAP = {
|
||||
"how-tos/human_in_the_loop/breakpoints.md": "how-tos/human_in_the_loop/add-human-in-the-loop.md",
|
||||
"cloud/how-tos/human_in_the_loop_breakpoint.md": "cloud/how-tos/add-human-in-the-loop.md",
|
||||
"how-tos/human_in_the_loop/edit-graph-state.ipynb": "how-tos/human_in_the_loop/time-travel.md",
|
||||
# LGP migration-related redirects - once LG is also migrated, we can add a redirect for the whole site
|
||||
"concepts/langgraph_platform.md": "https://docs.langchain.com/langgraph-platform",
|
||||
"concepts/langgraph_components.md": "https://docs.langchain.com/langgraph-platform/components",
|
||||
"concepts/langgraph_server.md": "https://docs.langchain.com/langgraph-platform/langgraph-server",
|
||||
"concepts/langgraph_studio.md": "https://docs.langchain.com/langgraph-platform/langgraph-studio",
|
||||
"cloud/how-tos/invoke_studio.md": "https://docs.langchain.com/langgraph-platform/invoke-studio",
|
||||
"concepts/langgraph_cli.md": "https://docs.langchain.com/langgraph-platform/langgraph-cli",
|
||||
"cloud/how-tos/studio/quick_start.md": "https://docs.langchain.com/langgraph-platform/quick-start-studio",
|
||||
"concepts/sdk.md": "https://docs.langchain.com/langgraph-platform/sdk",
|
||||
"concepts/auth.md": "https://docs.langchain.com/langgraph-platform/auth",
|
||||
"concepts/assistants.md": "https://docs.langchain.com/langgraph-platform/assistants",
|
||||
"concepts/deployment_options.md": "https://docs.langchain.com/langgraph-platform/deployment-options",
|
||||
"cloud/quick_start.md": "https://docs.langchain.com/langgraph-platform/deployment-quickstart",
|
||||
"cloud/deployment/setup.md": "https://docs.langchain.com/langgraph-platform/setup-app-requirements-txt",
|
||||
"concepts/langgraph_self_hosted_data_plane.md": "https://docs.langchain.com/langgraph-platform/self-hosted-data-plane",
|
||||
"concepts/langgraph_self_hosted_control_plane.md": "https://docs.langchain.com/langgraph-platform/self-hosted-control-plane",
|
||||
"concepts/langgraph_standalone_container.md": "https://docs.langchain.com/langgraph-platform/standalone-container",
|
||||
"cloud/deployment/self_hosted_data_plane.md": "https://docs.langchain.com/langgraph-platform/deploy-self-hosted-data-plane",
|
||||
"cloud/deployment/self_hosted_control_plane.md": "https://docs.langchain.com/langgraph-platform/deploy-self-hosted-control-plane",
|
||||
"cloud/deployment/standalone_container.md": "https://docs.langchain.com/langgraph-platform/deploy-standalone-container",
|
||||
"concepts/server-mcp.md": "https://docs.langchain.com/langgraph-platform/server-mcp",
|
||||
"cloud/reference/cli.md": "https://docs.langchain.com/langgraph-platform/cli",
|
||||
"cloud/how-tos/use_stream_react.md": "https://docs.langchain.com/langgraph-platform/use-stream-react",
|
||||
"cloud/how-tos/generative-ui-react.md": "https://docs.langchain.com/langgraph-platform/generative-ui-react",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
## Cron jobs
|
||||
|
||||
There are many situations in which it is useful to run an assistant on a schedule.
|
||||
|
||||
For example, say that you're building an assistant that runs daily and sends an email summary
|
||||
of the day's news. You could use a cron job to run the assistant every day at 8:00 PM.
|
||||
|
||||
LangGraph Platform supports cron jobs, which run on a user-defined schedule. The user specifies a schedule, an assistant, and some input. After that, on the specified schedule, the server will:
|
||||
|
||||
- Create a new thread with the specified assistant
|
||||
- Send the specified input to that thread
|
||||
|
||||
Note that this sends the same input to the thread every time. See the [how-to guide](../../cloud/how-tos/cron_jobs.md) for creating cron jobs.
|
||||
|
||||
The LangGraph Platform API provides several endpoints for creating and managing cron jobs. See the [API reference](../../cloud/reference/api/api_ref.html#tag/runscreate/POST/threads/{thread_id}/runs/crons) for more details.
|
||||
@@ -1,54 +0,0 @@
|
||||
# Data Storage and Privacy
|
||||
|
||||
This document describes how data is processed in the LangGraph CLI and the LangGraph Server for both the in-memory server (`langgraph dev`) and the local Docker server (`langgraph up`). It also describes what data is tracked when interacting with the hosted LangGraph Studio frontend.
|
||||
|
||||
## CLI
|
||||
|
||||
LangGraph **CLI** is the command-line interface for building and running LangGraph applications; see the [CLI guide](../../concepts/langgraph_cli.md) to learn more.
|
||||
|
||||
By default, calls to most CLI commands log a single analytics event upon invocation. This helps us better prioritize improvements to the CLI experience. Each telemetry event contains the calling process's OS, OS version, Python version, the CLI version, the command name (`dev`, `up`, `run`, etc.), and booleans representing whether a flag was passed to the command. You can see the full analytics logic [here](https://github.com/langchain-ai/langgraph/blob/main/libs/cli/langgraph_cli/analytics.py).
|
||||
|
||||
You can disable all CLI telemetry by setting `LANGGRAPH_CLI_NO_ANALYTICS=1`.
|
||||
|
||||
## LangGraph Server (in-memory & docker)
|
||||
|
||||
The [LangGraph Server](../../concepts/langgraph_server.md) provides a durable execution runtime that relies on persisting checkpoints of your application state, long-term memories, thread metadata, assistants, and similar resources to the local file system or a database. Unless you have deliberately customized the storage location, this information is either written to local disk (for `langgraph dev`) or a PostgreSQL database (for `langgraph up` and in all deployments).
|
||||
|
||||
### LangSmith Tracing
|
||||
|
||||
When running the LangGraph server (either in-memory or in Docker), LangSmith tracing may be enabled to facilitate faster debugging and offer observability of graph state and LLM prompts in production. You can always disable tracing by setting `LANGSMITH_TRACING=false` in your server's runtime environment.
|
||||
|
||||
### In-memory development server (`langgraph dev`)
|
||||
|
||||
`langgraph dev` runs an [in-memory development server](../../tutorials/langgraph-platform/local-server.md) as a single Python process, designed for quick development and testing. It saves all checkpointing and memory data to disk within a `.langgraph_api` directory in the current working directory. Apart from the telemetry data described in the [CLI](#cli) section, no data leaves the machine unless you have enabled tracing or your graph code explicitly contacts an external service.
|
||||
|
||||
### Standalone Container (`langgraph up`)
|
||||
|
||||
`langgraph up` builds your local package into a Docker image and runs the server as a [standalone container](../../concepts/deployment_options.md#standalone-container) consisting of three containers: the API server, a PostgreSQL container, and a Redis container. All persistent data (checkpoints, assistants, etc.) are stored in the PostgreSQL database. Redis is used as a pubsub connection for real-time streaming of events. You can encrypt all checkpoints before saving to the database by setting a valid `LANGGRAPH_AES_KEY` environment variable. You can also specify [TTLs](../../how-tos/ttl/configure_ttl.md) for checkpoints and cross-thread memories in `langgraph.json` to control how long data is stored. All persisted threads, memories, and other data can be deleted via the relevant API endpoints.
|
||||
|
||||
Additional API calls are made to confirm that the server has a valid license and to track the number of executed runs and tasks. Periodically, the API server validates the provided license key (or API key).
|
||||
|
||||
If you've disabled [tracing](#langsmith-tracing), no user data is persisted externally unless your graph code explicitly contacts an external service.
|
||||
|
||||
## Studio
|
||||
|
||||
[LangGraph Studio](../../concepts/langgraph_studio.md) is a graphical interface for interacting with your LangGraph server. It does not persist any private data (the data you send to your server is not sent to LangSmith). Though the studio interface is served at [smith.langchain.com](https://smith.langchain.com), it is run in your browser and connects directly to your local LangGraph server so that no data needs to be sent to LangSmith.
|
||||
|
||||
If you are logged in, LangSmith does collect some usage analytics to help improve studio's user experience. This includes:
|
||||
|
||||
- Page visits and navigation patterns
|
||||
- User actions (button clicks)
|
||||
- Browser type and version
|
||||
- Screen resolution and viewport size
|
||||
|
||||
Importantly, no application data or code (or other sensitive configuration details) are collected. All of that is stored in the persistence layer of your LangGraph server. When using Studio anonymously, no account creation is required and usage analytics are not collected.
|
||||
|
||||
## Quick reference
|
||||
|
||||
In summary, you can opt-out of server-side telemetry by turning off CLI analytics and disabling tracing.
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| ------------------------------ | ------------------------- | -------------------------------- |
|
||||
| `LANGGRAPH_CLI_NO_ANALYTICS=1` | Disable CLI analytics | Analytics enabled |
|
||||
| `LANGSMITH_API_KEY` | Enable LangSmith tracing | Tracing disabled |
|
||||
| `LANGSMITH_TRACING=false` | Disable LangSmith tracing | Depends on environment |
|
||||
@@ -1,7 +0,0 @@
|
||||
# Webhooks
|
||||
|
||||
Webhooks enable event-driven communication from your LangGraph Platform application to external services. For example, you may want to issue an update to a separate service once an API call to LangGraph Platform has finished running.
|
||||
|
||||
Many LangGraph Platform endpoints accept a `webhook` parameter. If this parameter is specified by an endpoint that can accept POST requests, LangGraph Platform will send a request at the completion of a run.
|
||||
|
||||
See the corresponding [how-to guide](../../cloud/how-tos/webhooks.md) for more detail.
|
||||
@@ -1,128 +0,0 @@
|
||||
# How to Deploy to Cloud SaaS
|
||||
|
||||
Before deploying, review the [conceptual guide for the Cloud SaaS](../../concepts/langgraph_cloud.md) deployment option.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. LangGraph Platform applications are deployed from GitHub repositories. Configure and upload a LangGraph Platform application to a GitHub repository in order to deploy it to LangGraph Platform.
|
||||
1. [Verify that the LangGraph API runs locally](../../tutorials/langgraph-platform/local-server.md). If the API does not run successfully (i.e. `langgraph dev`), deploying to LangGraph Platform will fail as well.
|
||||
|
||||
## Create New Deployment
|
||||
|
||||
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. In the top-right corner, select `+ New Deployment` to create a new deployment.
|
||||
1. In the `Create New Deployment` panel, fill out the required fields.
|
||||
1. `Deployment details`
|
||||
1. Select `Import from GitHub` and follow the GitHub OAuth workflow to install and authorize LangChain's `hosted-langserve` GitHub app to access the selected repositories. After installation is complete, return to the `Create New Deployment` panel and select the GitHub repository to deploy from the dropdown menu. **Note**: The GitHub user installing LangChain's `hosted-langserve` GitHub app must be an [owner](https://docs.github.com/en/organizations/managing-peoples-access-to-your-organization-with-roles/roles-in-an-organization#organization-owners) of the organization or account.
|
||||
1. Specify a name for the deployment.
|
||||
1. Specify the desired `Git Branch`. A deployment is linked to a branch. When a new revision is created, code for the linked branch will be deployed. The branch can be updated later in the [Deployment Settings](#deployment-settings).
|
||||
1. Specify the full path to the [LangGraph API config file](../reference/cli.md#configuration-file) including the file name. For example, if the file `langgraph.json` is in the root of the repository, simply specify `langgraph.json`.
|
||||
1. Check/uncheck checkbox to `Automatically update deployment on push to branch`. If checked, the deployment will automatically be updated when changes are pushed to the specified `Git Branch`. This setting can be enabled/disabled later in the [Deployment Settings](#deployment-settings).
|
||||
1. Select the desired `Deployment Type`.
|
||||
1. `Development` deployments are meant for non-production use cases and are provisioned with minimal resources.
|
||||
1. `Production` deployments can serve up to 500 requests/second and are provisioned with highly available storage with automatic backups.
|
||||
1. Determine if the deployment should be `Shareable through LangGraph Studio`.
|
||||
1. If unchecked, the deployment will only be accessible with a valid LangSmith API key for the workspace.
|
||||
1. If checked, the deployment will be accessible through LangGraph Studio to any LangSmith user. A direct URL to LangGraph Studio for the deployment will be provided to share with other LangSmith users.
|
||||
1. Specify `Environment Variables` and secrets. See the [Environment Variables reference](../reference/env_var.md) to configure additional variables for the deployment.
|
||||
1. Sensitive values such as API keys (e.g. `OPENAI_API_KEY`) should be specified as secrets.
|
||||
1. Additional non-secret environment variables can be specified as well.
|
||||
1. A new LangSmith `Tracing Project` is automatically created with the same name as the deployment.
|
||||
1. In the top-right corner, select `Submit`. After a few seconds, the `Deployment` view appears and the new deployment will be queued for provisioning.
|
||||
|
||||
## Create New Revision
|
||||
|
||||
When [creating a new deployment](#create-new-deployment), a new revision is created by default. Subsequent revisions can be created to deploy new code changes.
|
||||
|
||||
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 create a new revision for.
|
||||
1. In the `Deployment` view, in the top-right corner, select `+ New Revision`.
|
||||
1. In the `New Revision` modal, fill out the required fields.
|
||||
1. Specify the full path to the [LangGraph API config file](../reference/cli.md#configuration-file) including the file name. For example, if the file `langgraph.json` is in the root of the repository, simply specify `langgraph.json`.
|
||||
1. Determine if the deployment should be `Shareable through LangGraph Studio`.
|
||||
1. If unchecked, the deployment will only be accessible with a valid LangSmith API key for the workspace.
|
||||
1. If checked, the deployment will be accessible through LangGraph Studio to any LangSmith user. A direct URL to LangGraph Studio for the deployment will be provided to share with other LangSmith users.
|
||||
1. Specify `Environment Variables` and secrets. Existing secrets and environment variables are prepopulated. See the [Environment Variables reference](../reference/env_var.md) to configure additional variables for the revision.
|
||||
1. Add new secrets or environment variables.
|
||||
1. Remove existing secrets or environment variables.
|
||||
1. Update the value of existing secrets or environment variables.
|
||||
1. Select `Submit`. After a few seconds, the `New Revision` modal will close and the new revision will be queued for deployment.
|
||||
|
||||
## View Build and Server Logs
|
||||
|
||||
Build and server logs are available for each revision.
|
||||
|
||||
Starting from the `LangGraph Platform` view...
|
||||
|
||||
1. Select the desired revision from the `Revisions` table. A panel slides open from the right-hand side and the `Build` tab is selected by default, which displays build logs for the revision.
|
||||
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.
|
||||
|
||||
!!! warning "Undefined Behavior"
|
||||
Interrupted revisions have undefined behavior. This is only useful if you need to deploy a new revision and you already have a revision "stuck" in progress. In the future, this feature may be removed.
|
||||
|
||||
Starting from the `LangGraph Platform` view...
|
||||
|
||||
1. Select the menu icon (three dots) on the right-hand side of the row for the desired revision from the `Revisions` table.
|
||||
1. Select `Interrupt` from the menu.
|
||||
1. A modal will appear. Review the confirmation message. Select `Interrupt revision`.
|
||||
|
||||
## Delete Deployment
|
||||
|
||||
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 the menu icon (three dots) on the right-hand side of the row for the desired deployment and select `Delete`.
|
||||
1. A `Confirmation` modal will appear. Select `Delete`.
|
||||
|
||||
## Deployment Settings
|
||||
|
||||
Starting from the `LangGraph Platform` view...
|
||||
|
||||
1. In the top-right corner, select the gear icon (`Deployment Settings`).
|
||||
1. Update the `Git Branch` to the desired branch.
|
||||
1. Check/uncheck checkbox to `Automatically update deployment on push to branch`.
|
||||
1. Branch creation/deletion and tag creation/deletion events will not trigger an update. Only pushes to an existing branch will trigger an update.
|
||||
1. Pushes in quick succession to a branch will not trigger subsequent updates. In the future, this functionality may be changed/improved.
|
||||
|
||||
## Add or Remove GitHub Repositories
|
||||
|
||||
After installing and authorizing LangChain's `hosted-langserve` GitHub app, repository access for the app can be modified to add new repositories or remove existing repositories. If a new repository is created, it may need to be added explicitly.
|
||||
|
||||
1. From the GitHub profile, navigate to `Settings` > `Applications` > `hosted-langserve` > click `Configure`.
|
||||
1. Under `Repository access`, select `All repositories` or `Only select repositories`. If `Only select repositories` is selected, new repositories must be explicitly added.
|
||||
1. Click `Save`.
|
||||
1. When creating a new deployment, the list of GitHub repositories in the dropdown menu will be updated to reflect the repository access changes.
|
||||
|
||||
## Whitelisting IP Addresses
|
||||
|
||||
All traffic from `LangGraph Platform` deployments created after January 6th 2025 will come through a NAT gateway.
|
||||
This NAT gateway will have several static ip addresses depending on the region you are deploying in. Refer to the table below for the list of IP addresses to whitelist:
|
||||
|
||||
| US | EU |
|
||||
|----------------|-----------------|
|
||||
| 35.197.29.146 | 34.90.213.236 |
|
||||
| 34.145.102.123 | 34.13.244.114 |
|
||||
| 34.169.45.153 | 34.32.180.189 |
|
||||
| 34.82.222.17 | 34.34.69.108 |
|
||||
| 35.227.171.135 | 34.32.145.240 |
|
||||
| 34.169.88.30 | 34.90.157.44 |
|
||||
| 34.19.93.202 | 34.141.242.180 |
|
||||
| 34.19.34.50 | 34.32.141.108 |
|
||||
@@ -1,19 +0,0 @@
|
||||
# How to customize Dockerfile
|
||||
|
||||
Users can add an array of additional lines to add to the Dockerfile following the import from the parent LangGraph image. In order to do this, you simply need to modify your `langgraph.json` file by passing in the commands you want run to the `dockerfile_lines` key. For example, if we wanted to use `Pillow` in our graph you would need to add the following dependencies:
|
||||
|
||||
```
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"openai_agent": "./openai_agent.py:agent",
|
||||
},
|
||||
"env": "./.env",
|
||||
"dockerfile_lines": [
|
||||
"RUN apt-get update && apt-get install -y libjpeg-dev zlib1g-dev libpng-dev",
|
||||
"RUN pip install Pillow"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This would install the system packages required to use Pillow if we were working with `jpeg` or `png` image formats.
|
||||
@@ -1,119 +0,0 @@
|
||||
# Egress for Subscription Metrics and Operational Metadata
|
||||
|
||||
> **Important: Self Hosted Only**
|
||||
> This section only applies to customers who are not running in offline mode and assumes you are using a self-hosted LangGraph Platform instance.
|
||||
> This does not apply to SaaS or Hybrid deployments.
|
||||
|
||||
Self-Hosted LangGraph Platform instances store all information locally and will never send sensitive information outside of your network. We currently only track platform usage for billing purposes according to the entitlements in your order. In order to better remotely support our customers, we do require egress to `https://beacon.langchain.com`.
|
||||
|
||||
In the future, we will be introducing support diagnostics to help us ensure that the LangGraph Platform is running at an optimal level within your environment.
|
||||
|
||||
> **Warning**
|
||||
> **This will require egress to `https://beacon.langchain.com` from your network.**
|
||||
> **If using an API key, you will also need to allow egress to `https://api.smith.langchain.com` or `https://eu.api.smith.langchain.com` for API key verification.**
|
||||
|
||||
Generally, data that we send to Beacon can be categorized as follows:
|
||||
|
||||
- **Subscription Metrics**
|
||||
- Subscription metrics are used to determine level of access and utilization of LangSmith. This includes, but are not limited to:
|
||||
- Nodes Executed
|
||||
- Runs Executed
|
||||
- License Key Verification
|
||||
- **Operational Metadata**
|
||||
- This metadata will contain and collect the above subscription metrics to assist with remote support, allowing the LangChain team to diagnose and troubleshoot performance issues more effectively and proactively.
|
||||
|
||||
## Example Payloads
|
||||
|
||||
In an effort to maximize transparency, we provide sample payloads here:
|
||||
|
||||
### License Verification (If using an Enterprise License)
|
||||
|
||||
**Endpoint:**
|
||||
|
||||
`POST beacon.langchain.com/v1/beacon/verify`
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"license": "<YOUR_LICENSE_KEY>"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"token": "Valid JWT" // Short-lived JWT token to avoid repeated license checks
|
||||
}
|
||||
```
|
||||
|
||||
### Api Key Verification (If using a LangSmith API Key)
|
||||
|
||||
**Endpoint:**
|
||||
`POST api.smith.langchain.com/auth`
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
"Headers": {
|
||||
X-Api-Key: <YOUR_API_KEY>
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"org_config": {
|
||||
"org_id": "3a1c2b6f-4430-4b92-8a5b-79b8b567bbc1",
|
||||
... // Additional organization details
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Reporting
|
||||
|
||||
**Endpoint:**
|
||||
|
||||
`POST beacon.langchain.com/v1/metadata/submit`
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"license": "<YOUR_LICENSE_KEY>",
|
||||
"from_timestamp": "2025-01-06T09:00:00Z",
|
||||
"to_timestamp": "2025-01-06T10:00:00Z",
|
||||
"tags": {
|
||||
"langgraph.python.version": "0.1.0",
|
||||
"langgraph_api.version": "0.2.0",
|
||||
"langgraph.platform.revision": "abc123",
|
||||
"langgraph.platform.variant": "standard",
|
||||
"langgraph.platform.host": "host-1",
|
||||
"langgraph.platform.tenant_id": "3a1c2b6f-4430-4b92-8a5b-79b8b567bbc1",
|
||||
"langgraph.platform.project_id": "c5b5f53a-4716-4326-8967-d4f7f7799735",
|
||||
"langgraph.platform.plan": "enterprise",
|
||||
"user_app.uses_indexing": "true",
|
||||
"user_app.uses_custom_app": "false",
|
||||
"user_app.uses_custom_auth": "true",
|
||||
"user_app.uses_thread_ttl": "true",
|
||||
"user_app.uses_store_ttl": "false"
|
||||
},
|
||||
"measures": {
|
||||
"langgraph.platform.runs": 150,
|
||||
"langgraph.platform.nodes": 450
|
||||
},
|
||||
"logs": []
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
"204 No Content"
|
||||
```
|
||||
|
||||
## Our Commitment
|
||||
|
||||
LangChain will not store any sensitive information in the Subscription Metrics or Operational Metadata. Any data collected will not be shared with a third party. If you have any concerns about the data being sent, please reach out to your account team.
|
||||
@@ -1,147 +0,0 @@
|
||||
# Rebuild Graph at Runtime
|
||||
|
||||
You might need to rebuild your graph with a different configuration for a new run. For example, you might need to use a different graph state or graph structure depending on the config. This guide shows how you can do this.
|
||||
|
||||
!!! note "Note"
|
||||
In most cases, customizing behavior based on the config should be handled by a single graph where each node can read a config and change its behavior based on it
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Make sure to check out [this how-to guide](./setup.md) on setting up your app for deployment first.
|
||||
|
||||
## Define graphs
|
||||
|
||||
Let's say you have an app with a simple graph that calls an LLM and returns the response to the user. The app file directory looks like the following:
|
||||
|
||||
```
|
||||
my-app/
|
||||
|-- requirements.txt
|
||||
|-- .env
|
||||
|-- openai_agent.py # code for your graph
|
||||
```
|
||||
|
||||
where the graph is defined in `openai_agent.py`.
|
||||
|
||||
### No rebuild
|
||||
|
||||
In the standard LangGraph API configuration, the server uses the compiled graph instance that's defined at the top level of `openai_agent.py`, which looks like the following:
|
||||
|
||||
```python
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, START, MessageGraph
|
||||
|
||||
model = ChatOpenAI(temperature=0)
|
||||
|
||||
graph_workflow = MessageGraph()
|
||||
|
||||
graph_workflow.add_node("agent", model)
|
||||
graph_workflow.add_edge("agent", END)
|
||||
graph_workflow.add_edge(START, "agent")
|
||||
|
||||
agent = graph_workflow.compile()
|
||||
```
|
||||
|
||||
To make the server aware of your graph, you need to specify a path to the variable that contains the `CompiledStateGraph` instance in your LangGraph API configuration (`langgraph.json`), e.g.:
|
||||
|
||||
```
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"openai_agent": "./openai_agent.py:agent",
|
||||
},
|
||||
"env": "./.env"
|
||||
}
|
||||
```
|
||||
|
||||
### Rebuild
|
||||
|
||||
To make your graph rebuild on each new run with custom configuration, you need to rewrite `openai_agent.py` to instead provide a _function_ that takes a config and returns a graph (or compiled graph) instance. Let's say we want to return our existing graph for user ID '1', and a tool-calling agent for other users. We can modify `openai_agent.py` as follows:
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from typing_extensions import TypedDict
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, START, MessageGraph
|
||||
from langgraph.graph.state import StateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.prebuilt import ToolNode
|
||||
from langchain_core.tools import tool
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
|
||||
class State(TypedDict):
|
||||
messages: Annotated[list[BaseMessage], add_messages]
|
||||
|
||||
|
||||
model = ChatOpenAI(temperature=0)
|
||||
|
||||
def make_default_graph():
|
||||
"""Make a simple LLM agent"""
|
||||
graph_workflow = StateGraph(State)
|
||||
def call_model(state):
|
||||
return {"messages": [model.invoke(state["messages"])]}
|
||||
|
||||
graph_workflow.add_node("agent", call_model)
|
||||
graph_workflow.add_edge("agent", END)
|
||||
graph_workflow.add_edge(START, "agent")
|
||||
|
||||
agent = graph_workflow.compile()
|
||||
return agent
|
||||
|
||||
|
||||
def make_alternative_graph():
|
||||
"""Make a tool-calling agent"""
|
||||
|
||||
@tool
|
||||
def add(a: float, b: float):
|
||||
"""Adds two numbers."""
|
||||
return a + b
|
||||
|
||||
tool_node = ToolNode([add])
|
||||
model_with_tools = model.bind_tools([add])
|
||||
def call_model(state):
|
||||
return {"messages": [model_with_tools.invoke(state["messages"])]}
|
||||
|
||||
def should_continue(state: State):
|
||||
if state["messages"][-1].tool_calls:
|
||||
return "tools"
|
||||
else:
|
||||
return END
|
||||
|
||||
graph_workflow = StateGraph(State)
|
||||
|
||||
graph_workflow.add_node("agent", call_model)
|
||||
graph_workflow.add_node("tools", tool_node)
|
||||
graph_workflow.add_edge("tools", "agent")
|
||||
graph_workflow.add_edge(START, "agent")
|
||||
graph_workflow.add_conditional_edges("agent", should_continue)
|
||||
|
||||
agent = graph_workflow.compile()
|
||||
return agent
|
||||
|
||||
|
||||
# this is the graph making function that will decide which graph to
|
||||
# build based on the provided config
|
||||
def make_graph(config: RunnableConfig):
|
||||
user_id = config.get("configurable", {}).get("user_id")
|
||||
# route to different graph state / structure based on the user ID
|
||||
if user_id == "1":
|
||||
return make_default_graph()
|
||||
else:
|
||||
return make_alternative_graph()
|
||||
```
|
||||
|
||||
Finally, you need to specify the path to your graph-making function (`make_graph`) in `langgraph.json`:
|
||||
|
||||
```
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"openai_agent": "./openai_agent.py:make_graph",
|
||||
},
|
||||
"env": "./.env"
|
||||
}
|
||||
```
|
||||
|
||||
See more info on LangGraph API configuration file [here](../reference/cli.md#configuration-file)
|
||||
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 514 KiB |
@@ -1,53 +0,0 @@
|
||||
# How to Deploy Self-Hosted Control Plane
|
||||
|
||||
Before deploying, review the [conceptual guide for the Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployment option.
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. You are using Kubernetes.
|
||||
1. You have self-hosted LangSmith deployed.
|
||||
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to [test your application locally](../../tutorials/langgraph-platform/local-server.md).
|
||||
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to build a Docker image (i.e. `langgraph build`) and push it to a registry your Kubernetes cluster has access to.
|
||||
1. `KEDA` is installed on your cluster.
|
||||
|
||||
helm repo add kedacore https://kedacore.github.io/charts
|
||||
helm install keda kedacore/keda --namespace keda --create-namespace
|
||||
1. Ingress Configuration
|
||||
1. You must set up an ingress for your LangSmith instance. All agents will be deployed as Kubernetes services behind this ingress.
|
||||
1. You can use this guide to [set up an ingress](https://docs.smith.langchain.com/self_hosting/configuration/ingress) for your instance.
|
||||
1. You have slack space in your cluster for multiple deployments. `Cluster-Autoscaler` is recommended to automatically provision new nodes.
|
||||
1. A valid Dynamic PV provisioner or PVs available on your cluster. You can verify this by running:
|
||||
|
||||
kubectl get storageclass
|
||||
|
||||
1. Egress to `https://beacon.langchain.com` from your network. This is required for license verification and usage reporting if not running in air-gapped mode. See the [Egress documentation](../../cloud/deployment/egress.md) for more details.
|
||||
|
||||
## Setup
|
||||
|
||||
1. As part of configuring your Self-Hosted LangSmith instance, you enable the `langgraphPlatform` option. This will provision a few key resources.
|
||||
1. `listener`: This is a service that listens to the [control plane](../../concepts/langgraph_control_plane.md) for changes to your deployments and creates/updates downstream CRDs.
|
||||
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. Use the images that are specified in the latest release.
|
||||
|
||||
hostBackendImage:
|
||||
repository: "docker.io/langchain/hosted-langserve-backend"
|
||||
pullPolicy: IfNotPresent
|
||||
operatorImage:
|
||||
repository: "docker.io/langchain/langgraph-operator"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
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
|
||||
langgraphPlatformLicenseKey: "YOUR_LANGGRAPH_PLATFORM_LICENSE_KEY"
|
||||
1. In your `values.yaml` file, configure the `hostBackendImage` and `operatorImage` options (if you need to mirror images)
|
||||
|
||||
1. You can also configure base templates for your agents by overriding the base templates [here](https://github.com/langchain-ai/helm/blob/main/charts/langsmith/values.yaml#L898).
|
||||
1. You create a deployment from the [control plane UI](../../concepts/langgraph_control_plane.md#control-plane-ui).
|
||||
@@ -1,59 +0,0 @@
|
||||
# How to Deploy Self-Hosted Data Plane
|
||||
|
||||
Before deploying, review the [conceptual guide for the Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) deployment option.
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option requires an [Enterprise](../../concepts/plans.md) plan.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to [test your application locally](../../tutorials/langgraph-platform/local-server.md).
|
||||
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to build a Docker image (i.e. `langgraph build`) and push it to a registry your Kubernetes cluster or Amazon ECS cluster has access to.
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### Prerequisites
|
||||
1. `KEDA` is installed on your cluster.
|
||||
|
||||
helm repo add kedacore https://kedacore.github.io/charts
|
||||
helm install keda kedacore/keda --namespace keda --create-namespace
|
||||
|
||||
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 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
|
||||
|
||||
1. You give us your LangSmith organization ID. We will enable the Self-Hosted Data Plane for your organization.
|
||||
1. We provide you a [Helm chart](https://github.com/langchain-ai/helm/tree/main/charts/langgraph-dataplane) which you run to setup your Kubernetes cluster. This chart contains a few important components.
|
||||
1. `langgraph-listener`: This is a service that listens to LangChain's [control plane](../../concepts/langgraph_control_plane.md) for changes to your deployments and creates/updates downstream CRDs.
|
||||
1. `LangGraphPlatform CRD`: A CRD for LangGraph Platform deployments. This contains the spec for managing an instance of a LangGraph Platform deployment.
|
||||
1. `langgraph-platform-operator`: This operator handles changes to your LangGraph Platform CRDs.
|
||||
1. Configure your `langgraph-dataplane-values.yaml` file.
|
||||
|
||||
config:
|
||||
langsmithApiKey: "" # API Key of your Workspace
|
||||
langsmithWorkspaceId: "" # Workspace ID
|
||||
hostBackendUrl: "https://api.host.langchain.com" # Only override this if on EU
|
||||
smithBackendUrl: "https://api.smith.langchain.com" # Only override this if on EU
|
||||
|
||||
1. Deploy `langgraph-dataplane` Helm chart.
|
||||
|
||||
helm repo add langchain https://langchain-ai.github.io/helm/
|
||||
helm repo update
|
||||
helm upgrade -i langgraph-dataplane langchain/langgraph-dataplane --values langgraph-dataplane-values.yaml
|
||||
|
||||
1. If successful, you will see two services start up in your namespace.
|
||||
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
langgraph-dataplane-listener-7fccd788-wn2dx 0/1 Running 0 9s
|
||||
langgraph-dataplane-redis-0 0/1 ContainerCreating 0 9s
|
||||
|
||||
1. You create a deployment from the [control plane UI](../../concepts/langgraph_control_plane.md#control-plane-ui).
|
||||
|
||||
## Amazon ECS
|
||||
|
||||
Coming soon!
|
||||
@@ -1,123 +0,0 @@
|
||||
# How to add semantic search to your LangGraph deployment
|
||||
|
||||
This guide explains how to add semantic search to your LangGraph deployment's cross-thread [store](../../concepts/persistence.md#memory-store), so that your agent can search for memories and other documents by semantic similarity.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A LangGraph deployment (see [how to deploy](setup_pyproject.md))
|
||||
- API keys for your embedding provider (in this case, OpenAI)
|
||||
- `langchain >= 0.3.8` (if you specify using the string format below)
|
||||
|
||||
## Steps
|
||||
|
||||
1. Update your `langgraph.json` configuration file to include the store configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"store": {
|
||||
"index": {
|
||||
"embed": "openai:text-embedding-3-small",
|
||||
"dims": 1536,
|
||||
"fields": ["$"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration:
|
||||
|
||||
- Uses OpenAI's text-embedding-3-small model for generating embeddings
|
||||
- Sets the embedding dimension to 1536 (matching the model's output)
|
||||
- Indexes all fields in your stored data (`["$"]` means index everything, or specify specific fields like `["text", "metadata.title"]`)
|
||||
|
||||
2. To use the string embedding format above, make sure your dependencies include `langchain >= 0.3.8`:
|
||||
|
||||
```toml
|
||||
# In pyproject.toml
|
||||
[project]
|
||||
dependencies = [
|
||||
"langchain>=0.3.8"
|
||||
]
|
||||
```
|
||||
|
||||
Or if using requirements.txt:
|
||||
|
||||
```
|
||||
langchain>=0.3.8
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Once configured, you can use semantic search in your LangGraph nodes. The store requires a namespace tuple to organize memories:
|
||||
|
||||
```python
|
||||
def search_memory(state: State, *, store: BaseStore):
|
||||
# Search the store using semantic similarity
|
||||
# The namespace tuple helps organize different types of memories
|
||||
# e.g., ("user_facts", "preferences") or ("conversation", "summaries")
|
||||
results = store.search(
|
||||
namespace=("memory", "facts"), # Organize memories by type
|
||||
query="your search query",
|
||||
limit=3 # number of results to return
|
||||
)
|
||||
return results
|
||||
```
|
||||
|
||||
## Custom Embeddings
|
||||
|
||||
If you want to use custom embeddings, you can pass a path to a custom embedding function:
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"store": {
|
||||
"index": {
|
||||
"embed": "path/to/embedding_function.py:embed",
|
||||
"dims": 1536,
|
||||
"fields": ["$"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The deployment will look for the function in the specified path. The function must be async and accept a list of strings:
|
||||
|
||||
```python
|
||||
# path/to/embedding_function.py
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
client = AsyncOpenAI()
|
||||
|
||||
async def aembed_texts(texts: list[str]) -> list[list[float]]:
|
||||
"""Custom embedding function that must:
|
||||
1. Be async
|
||||
2. Accept a list of strings
|
||||
3. Return a list of float arrays (embeddings)
|
||||
"""
|
||||
response = await client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input=texts
|
||||
)
|
||||
return [e.embedding for e in response.data]
|
||||
```
|
||||
|
||||
## Querying via the API
|
||||
|
||||
You can also query the store using the LangGraph SDK. Since the SDK uses async operations:
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
async def search_store():
|
||||
client = get_client()
|
||||
results = await client.store.search_items(
|
||||
("memory", "facts"),
|
||||
query="your search query",
|
||||
limit=3 # number of results to return
|
||||
)
|
||||
return results
|
||||
|
||||
# Use in an async context
|
||||
results = await search_store()
|
||||
```
|
||||
@@ -1,188 +0,0 @@
|
||||
# How to Set Up a LangGraph Application with requirements.txt
|
||||
|
||||
A LangGraph application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Platform (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph application for deployment using `requirements.txt` to specify project dependencies.
|
||||
|
||||
This walkthrough is based on [this repository](https://github.com/langchain-ai/langgraph-example), which you can play around with to learn more about how to setup your LangGraph application for deployment.
|
||||
|
||||
!!! tip "Setup with pyproject.toml"
|
||||
If you prefer using poetry for dependency management, check out [this how-to guide](./setup_pyproject.md) on using `pyproject.toml` for LangGraph Platform.
|
||||
|
||||
!!! tip "Setup with a Monorepo"
|
||||
If you are interested in deploying a graph located inside a monorepo, take a look at [this repository](https://github.com/langchain-ai/langgraph-example-monorepo) for an example of how to do so.
|
||||
|
||||
The final repository structure will look something like this:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for you graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── requirements.txt # package dependencies
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
├── .env # environment variables
|
||||
└── langgraph.json # configuration file for LangGraph
|
||||
```
|
||||
|
||||
After each step, an example file directory is provided to demonstrate how code can be organized.
|
||||
|
||||
## Specify Dependencies
|
||||
|
||||
Dependencies can optionally be specified in one of the following files: `pyproject.toml`, `setup.py`, or `requirements.txt`. If none of these files is created, then dependencies can be specified later in the [LangGraph configuration file](#create-langgraph-configuration-file).
|
||||
|
||||
The dependencies below will be included in the image, you can also use them in your code, as long as with a compatible version range:
|
||||
|
||||
```
|
||||
langgraph>=0.3.27
|
||||
langgraph-sdk>=0.1.66
|
||||
langgraph-checkpoint>=2.0.23
|
||||
langchain-core>=0.2.38
|
||||
langsmith>=0.1.63
|
||||
orjson>=3.9.7,<3.10.17
|
||||
httpx>=0.25.0
|
||||
tenacity>=8.0.0
|
||||
uvicorn>=0.26.0
|
||||
sse-starlette>=2.1.0,<2.2.0
|
||||
uvloop>=0.18.0
|
||||
httptools>=0.5.0
|
||||
jsonschema-rs>=0.20.0
|
||||
structlog>=24.1.0
|
||||
cloudpickle>=3.0.0
|
||||
```
|
||||
|
||||
Example `requirements.txt` file:
|
||||
|
||||
```
|
||||
langgraph
|
||||
langchain_anthropic
|
||||
tavily-python
|
||||
langchain_community
|
||||
langchain_openai
|
||||
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ └── requirements.txt # package dependencies
|
||||
```
|
||||
|
||||
## Specify Environment Variables
|
||||
|
||||
Environment variables can optionally be specified in a file (e.g. `.env`). See the [Environment Variables reference](../reference/env_var.md) to configure additional variables for a deployment.
|
||||
|
||||
Example `.env` file:
|
||||
|
||||
```
|
||||
MY_ENV_VAR_1=foo
|
||||
MY_ENV_VAR_2=bar
|
||||
OPENAI_API_KEY=key
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ └── requirements.txt # package dependencies
|
||||
└── .env # environment variables
|
||||
```
|
||||
|
||||
## Define Graphs
|
||||
|
||||
Implement your graphs! Graphs can be defined in a single file or multiple files. Make note of the variable names of each [CompiledStateGraph][langgraph.graph.state.CompiledStateGraph] to be included in the LangGraph application. The variable names will be used later when creating the [LangGraph configuration file](../reference/cli.md#configuration-file).
|
||||
|
||||
Example `agent.py` file, which shows how to import from other modules you define (code for the modules is not shown here, please see [this repository](https://github.com/langchain-ai/langgraph-example) to see their implementation):
|
||||
|
||||
```python
|
||||
# my_agent/agent.py
|
||||
from typing import Literal
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langgraph.graph import StateGraph, END, START
|
||||
from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes
|
||||
from my_agent.utils.state import AgentState # import state
|
||||
|
||||
# Define the config
|
||||
class GraphConfig(TypedDict):
|
||||
model_name: Literal["anthropic", "openai"]
|
||||
|
||||
workflow = StateGraph(AgentState, config_schema=GraphConfig)
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("action", tool_node)
|
||||
workflow.add_edge(START, "agent")
|
||||
workflow.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
{
|
||||
"continue": "action",
|
||||
"end": END,
|
||||
},
|
||||
)
|
||||
workflow.add_edge("action", "agent")
|
||||
|
||||
graph = workflow.compile()
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for you graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── requirements.txt # package dependencies
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
└── .env # environment variables
|
||||
```
|
||||
|
||||
## Create LangGraph Configuration File
|
||||
|
||||
Create a [LangGraph configuration file](../reference/cli.md#configuration-file) called `langgraph.json`. See the [LangGraph configuration file reference](../reference/cli.md#configuration-file) for detailed explanations of each key in the JSON object of the configuration file.
|
||||
|
||||
Example `langgraph.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["./my_agent"],
|
||||
"graphs": {
|
||||
"agent": "./my_agent/agent.py:graph"
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
```
|
||||
|
||||
Note that the variable name of the `CompiledGraph` appears at the end of the value of each subkey in the top-level `graphs` key (i.e. `:<variable_name>`).
|
||||
|
||||
!!! warning "Configuration File Location"
|
||||
The LangGraph configuration file must be placed in a directory that is at the same level or higher than the Python files that contain compiled graphs and associated dependencies.
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for you graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── requirements.txt # package dependencies
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
├── .env # environment variables
|
||||
└── langgraph.json # configuration file for LangGraph
|
||||
```
|
||||
|
||||
## Next
|
||||
|
||||
After you setup your project and place it in a GitHub repository, it's time to [deploy your app](./cloud.md).
|
||||
@@ -1,199 +0,0 @@
|
||||
# How to Set Up a LangGraph.js Application
|
||||
|
||||
A [LangGraph.js](https://langchain-ai.github.io/langgraphjs/) application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Platform (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph.js application for deployment using `package.json` to specify project dependencies.
|
||||
|
||||
This walkthrough is based on [this repository](https://github.com/langchain-ai/langgraphjs-studio-starter), which you can play around with to learn more about how to setup your LangGraph application for deployment.
|
||||
|
||||
The final repository structure will look something like this:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── src # all project code lies within here
|
||||
│ ├── utils # optional utilities for your graph
|
||||
│ │ ├── tools.ts # tools for your graph
|
||||
│ │ ├── nodes.ts # node functions for you graph
|
||||
│ │ └── state.ts # state definition of your graph
|
||||
│ └── agent.ts # code for constructing your graph
|
||||
├── package.json # package dependencies
|
||||
├── .env # environment variables
|
||||
└── langgraph.json # configuration file for LangGraph
|
||||
```
|
||||
|
||||
After each step, an example file directory is provided to demonstrate how code can be organized.
|
||||
|
||||
## Specify Dependencies
|
||||
|
||||
Dependencies can be specified in a `package.json`. If none of these files is created, then dependencies can be specified later in the [LangGraph configuration file](#create-langgraph-api-config).
|
||||
|
||||
Example `package.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "langgraphjs-studio-starter",
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"dependencies": {
|
||||
"@langchain/community": "^0.2.31",
|
||||
"@langchain/core": "^0.2.31",
|
||||
"@langchain/langgraph": "^0.2.0",
|
||||
"@langchain/openai": "^0.2.8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When deploying your app, the dependencies will be installed using the package manager of your choice, provided they adhere to the compatible version ranges listed below:
|
||||
|
||||
```
|
||||
"@langchain/core": "^0.3.42",
|
||||
"@langchain/langgraph": "^0.2.57",
|
||||
"@langchain/langgraph-checkpoint": "~0.0.16",
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
└── package.json # package dependencies
|
||||
```
|
||||
|
||||
## Specify Environment Variables
|
||||
|
||||
Environment variables can optionally be specified in a file (e.g. `.env`). See the [Environment Variables reference](../reference/env_var.md) to configure additional variables for a deployment.
|
||||
|
||||
Example `.env` file:
|
||||
|
||||
```
|
||||
MY_ENV_VAR_1=foo
|
||||
MY_ENV_VAR_2=bar
|
||||
OPENAI_API_KEY=key
|
||||
TAVILY_API_KEY=key_2
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── package.json
|
||||
└── .env # environment variables
|
||||
```
|
||||
|
||||
## Define Graphs
|
||||
|
||||
Implement your graphs! Graphs can be defined in a single file or multiple files. Make note of the variable names of each compiled graph to be included in the LangGraph application. The variable names will be used later when creating the [LangGraph configuration file](../reference/cli.md#configuration-file).
|
||||
|
||||
Here is an example `agent.ts`:
|
||||
|
||||
```ts
|
||||
import type { AIMessage } from "@langchain/core/messages";
|
||||
import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
|
||||
import { MessagesAnnotation, StateGraph } from "@langchain/langgraph";
|
||||
import { ToolNode } from "@langchain/langgraph/prebuilt";
|
||||
|
||||
const tools = [new TavilySearchResults({ maxResults: 3 })];
|
||||
|
||||
// Define the function that calls the model
|
||||
async function callModel(state: typeof MessagesAnnotation.State) {
|
||||
/**
|
||||
* Call the LLM powering our agent.
|
||||
* Feel free to customize the prompt, model, and other logic!
|
||||
*/
|
||||
const model = new ChatOpenAI({
|
||||
model: "gpt-4o",
|
||||
}).bindTools(tools);
|
||||
|
||||
const response = await model.invoke([
|
||||
{
|
||||
role: "system",
|
||||
content: `You are a helpful assistant. The current date is ${new Date().getTime()}.`,
|
||||
},
|
||||
...state.messages,
|
||||
]);
|
||||
|
||||
// MessagesAnnotation supports returning a single message or array of messages
|
||||
return { messages: response };
|
||||
}
|
||||
|
||||
// Define the function that determines whether to continue or not
|
||||
function routeModelOutput(state: typeof MessagesAnnotation.State) {
|
||||
const messages = state.messages;
|
||||
const lastMessage: AIMessage = messages[messages.length - 1];
|
||||
// If the LLM is invoking tools, route there.
|
||||
if ((lastMessage?.tool_calls?.length ?? 0) > 0) {
|
||||
return "tools";
|
||||
}
|
||||
// Otherwise end the graph.
|
||||
return "__end__";
|
||||
}
|
||||
|
||||
// Define a new graph.
|
||||
// See https://langchain-ai.github.io/langgraphjs/how-tos/define-state/#getting-started for
|
||||
// more on defining custom graph states.
|
||||
const workflow = new StateGraph(MessagesAnnotation)
|
||||
// Define the two nodes we will cycle between
|
||||
.addNode("callModel", callModel)
|
||||
.addNode("tools", new ToolNode(tools))
|
||||
// Set the entrypoint as `callModel`
|
||||
// This means that this node is the first one called
|
||||
.addEdge("__start__", "callModel")
|
||||
.addConditionalEdges(
|
||||
// First, we define the edges' source node. We use `callModel`.
|
||||
// This means these are the edges taken after the `callModel` node is called.
|
||||
"callModel",
|
||||
// Next, we pass in the function that will determine the sink node(s), which
|
||||
// will be called after the source node is called.
|
||||
routeModelOutput,
|
||||
// List of the possible destinations the conditional edge can route to.
|
||||
// Required for conditional edges to properly render the graph in Studio
|
||||
["tools", "__end__"]
|
||||
)
|
||||
// This means that after `tools` is called, `callModel` node is called next.
|
||||
.addEdge("tools", "callModel");
|
||||
|
||||
// Finally, we compile it!
|
||||
// This compiles it into a graph you can invoke and deploy.
|
||||
export const graph = workflow.compile();
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── src # all project code lies within here
|
||||
│ ├── utils # optional utilities for your graph
|
||||
│ │ ├── tools.ts # tools for your graph
|
||||
│ │ ├── nodes.ts # node functions for you graph
|
||||
│ │ └── state.ts # state definition of your graph
|
||||
│ └── agent.ts # code for constructing your graph
|
||||
├── package.json # package dependencies
|
||||
├── .env # environment variables
|
||||
└── langgraph.json # configuration file for LangGraph
|
||||
```
|
||||
|
||||
## Create LangGraph API Config
|
||||
|
||||
Create a [LangGraph configuration file](../reference/cli.md#configuration-file) called `langgraph.json`. See the [LangGraph configuration file reference](../reference/cli.md#configuration-file) for detailed explanations of each key in the JSON object of the configuration file.
|
||||
|
||||
Example `langgraph.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"node_version": "20",
|
||||
"dockerfile_lines": [],
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"agent": "./src/agent.ts:graph"
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
```
|
||||
|
||||
Note that the variable name of the `CompiledGraph` appears at the end of the value of each subkey in the top-level `graphs` key (i.e. `:<variable_name>`).
|
||||
|
||||
!!! info "Configuration Location"
|
||||
|
||||
The LangGraph configuration file must be placed in a directory that is at the same level or higher than the TypeScript files that contain compiled graphs and associated dependencies.
|
||||
|
||||
## Next
|
||||
|
||||
After you setup your project and place it in a GitHub repository, it's time to [deploy your app](./cloud.md).
|
||||
@@ -1,201 +0,0 @@
|
||||
# How to Set Up a LangGraph Application with pyproject.toml
|
||||
|
||||
A LangGraph application must be configured with a [LangGraph configuration file](../reference/cli.md#configuration-file) in order to be deployed to LangGraph Platform (or to be self-hosted). This how-to guide discusses the basic steps to setup a LangGraph application for deployment using `pyproject.toml` to define your package's dependencies.
|
||||
|
||||
This walkthrough is based on [this repository](https://github.com/langchain-ai/langgraph-example-pyproject), which you can play around with to learn more about how to setup your LangGraph application for deployment.
|
||||
|
||||
!!! tip "Setup with requirements.txt"
|
||||
If you prefer using `requirements.txt` for dependency management, check out [this how-to guide](./setup.md).
|
||||
|
||||
!!! tip "Setup with a Monorepo"
|
||||
If you are interested in deploying a graph located inside a monorepo, take a look at [this](https://github.com/langchain-ai/langgraph-example-monorepo) repository for an example of how to do so.
|
||||
|
||||
The final repository structure will look something like this:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for you graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
├── .env # environment variables
|
||||
├── langgraph.json # configuration file for LangGraph
|
||||
└── pyproject.toml # dependencies for your project
|
||||
```
|
||||
|
||||
After each step, an example file directory is provided to demonstrate how code can be organized.
|
||||
|
||||
## Specify Dependencies
|
||||
|
||||
Dependencies can optionally be specified in one of the following files: `pyproject.toml`, `setup.py`, or `requirements.txt`. If none of these files is created, then dependencies can be specified later in the [LangGraph configuration file](#create-langgraph-configuration-file).
|
||||
|
||||
The dependencies below will be included in the image, you can also use them in your code, as long as with a compatible version range:
|
||||
|
||||
```
|
||||
langgraph>=0.3.27
|
||||
langgraph-sdk>=0.1.66
|
||||
langgraph-checkpoint>=2.0.23
|
||||
langchain-core>=0.2.38
|
||||
langsmith>=0.1.63
|
||||
orjson>=3.9.7,<3.10.17
|
||||
httpx>=0.25.0
|
||||
tenacity>=8.0.0
|
||||
uvicorn>=0.26.0
|
||||
sse-starlette>=2.1.0,<2.2.0
|
||||
uvloop>=0.18.0
|
||||
httptools>=0.5.0
|
||||
jsonschema-rs>=0.20.0
|
||||
structlog>=24.1.0
|
||||
cloudpickle>=3.0.0
|
||||
```
|
||||
|
||||
Example `pyproject.toml` file:
|
||||
|
||||
```toml
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "my-agent"
|
||||
version = "0.0.1"
|
||||
description = "An excellent agent build for LangGraph Platform."
|
||||
authors = [
|
||||
{name = "Polly the parrot", email = "1223+polly@users.noreply.github.com"}
|
||||
]
|
||||
license = {text = "MIT"}
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"langgraph>=0.2.0",
|
||||
"langchain-fireworks>=0.1.3"
|
||||
]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["my_agent"]
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
└── pyproject.toml # Python packages required for your graph
|
||||
```
|
||||
|
||||
## Specify Environment Variables
|
||||
|
||||
Environment variables can optionally be specified in a file (e.g. `.env`). See the [Environment Variables reference](../reference/env_var.md) to configure additional variables for a deployment.
|
||||
|
||||
Example `.env` file:
|
||||
|
||||
```
|
||||
MY_ENV_VAR_1=foo
|
||||
MY_ENV_VAR_2=bar
|
||||
FIREWORKS_API_KEY=key
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── .env # file with environment variables
|
||||
└── pyproject.toml
|
||||
```
|
||||
|
||||
## Define Graphs
|
||||
|
||||
Implement your graphs! Graphs can be defined in a single file or multiple files. Make note of the variable names of each [CompiledStateGraph][langgraph.graph.state.CompiledStateGraph] to be included in the LangGraph application. The variable names will be used later when creating the [LangGraph configuration file](../reference/cli.md#configuration-file).
|
||||
|
||||
Example `agent.py` file, which shows how to import from other modules you define (code for the modules is not shown here, please see [this repository](https://github.com/langchain-ai/langgraph-example-pyproject) to see their implementation):
|
||||
|
||||
```python
|
||||
# my_agent/agent.py
|
||||
from typing import Literal
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from langgraph.graph import StateGraph, END, START
|
||||
from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes
|
||||
from my_agent.utils.state import AgentState # import state
|
||||
|
||||
# Define the config
|
||||
class GraphConfig(TypedDict):
|
||||
model_name: Literal["anthropic", "openai"]
|
||||
|
||||
workflow = StateGraph(AgentState, config_schema=GraphConfig)
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("action", tool_node)
|
||||
workflow.add_edge(START, "agent")
|
||||
workflow.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
{
|
||||
"continue": "action",
|
||||
"end": END,
|
||||
},
|
||||
)
|
||||
workflow.add_edge("action", "agent")
|
||||
|
||||
graph = workflow.compile()
|
||||
```
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for you graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
├── .env
|
||||
└── pyproject.toml
|
||||
```
|
||||
|
||||
## Create LangGraph Configuration File
|
||||
|
||||
Create a [LangGraph configuration file](../reference/cli.md#configuration-file) called `langgraph.json`. See the [LangGraph configuration file reference](../reference/cli.md#configuration-file) for detailed explanations of each key in the JSON object of the configuration file.
|
||||
|
||||
Example `langgraph.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"agent": "./my_agent/agent.py:graph"
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
```
|
||||
|
||||
Note that the variable name of the `CompiledGraph` appears at the end of the value of each subkey in the top-level `graphs` key (i.e. `:<variable_name>`).
|
||||
|
||||
!!! warning "Configuration File Location"
|
||||
The LangGraph configuration file must be placed in a directory that is at the same level or higher than the Python files that contain compiled graphs and associated dependencies.
|
||||
|
||||
Example file directory:
|
||||
|
||||
```bash
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for you graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
├── .env # environment variables
|
||||
├── langgraph.json # configuration file for LangGraph
|
||||
└── pyproject.toml # dependencies for your project
|
||||
```
|
||||
|
||||
## Next
|
||||
|
||||
After you setup your project and place it in a GitHub repository, it's time to [deploy your app](./cloud.md).
|
||||
@@ -1,111 +0,0 @@
|
||||
# How to Deploy a Standalone Container
|
||||
|
||||
Before deploying, review the [conceptual guide for the Standalone Container](../../concepts/langgraph_standalone_container.md) deployment option.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to [test your application locally](../../tutorials/langgraph-platform/local-server.md).
|
||||
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to build a Docker image (i.e. `langgraph build`).
|
||||
1. The following environment variables are needed for a standalone container deployment.
|
||||
1. `REDIS_URI`: Connection details to a Redis instance. Redis will be used as a pub-sub broker to enable streaming real time output from background runs. The value of `REDIS_URI` must be a valid [Redis connection URI](https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url).
|
||||
|
||||
!!! Note "Shared Redis Instance"
|
||||
Multiple self-hosted deployments can share the same Redis instance. For example, for `Deployment A`, `REDIS_URI` can be set to `redis://<hostname_1>:<port>/1` and for `Deployment B`, `REDIS_URI` can be set to `redis://<hostname_1>:<port>/2`.
|
||||
|
||||
`1` and `2` are different database numbers within the same instance, but `<hostname_1>` is shared. **The same database number cannot be used for separate deployments**.
|
||||
|
||||
1. `DATABASE_URI`: Postgres connection details. Postgres will be used to store assistants, threads, runs, persist thread state and long term memory, and to manage the state of the background task queue with 'exactly once' semantics. The value of `DATABASE_URI` must be a valid [Postgres connection URI](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS).
|
||||
|
||||
!!! Note "Shared Postgres Instance"
|
||||
Multiple self-hosted deployments can share the same Postgres instance. For example, for `Deployment A`, `DATABASE_URI` can be set to `postgres://<user>:<password>@/<database_name_1>?host=<hostname_1>` and for `Deployment B`, `DATABASE_URI` can be set to `postgres://<user>:<password>@/<database_name_2>?host=<hostname_1>`.
|
||||
|
||||
`<database_name_1>` and `database_name_2` are different databases within the same instance, but `<hostname_1>` is shared. **The same database cannot be used for separate deployments**.
|
||||
|
||||
1. `LANGSMITH_API_KEY`: (if using [Lite](../../concepts/langgraph_server.md#server-versions)) LangSmith API key. This will be used to authenticate ONCE at server start up.
|
||||
1. `LANGGRAPH_CLOUD_LICENSE_KEY`: (if using [Enterprise](../../concepts/langgraph_data_plane.md#licensing)) LangGraph Platform license key. This will be used to authenticate ONCE at server start up.
|
||||
1. `LANGSMITH_ENDPOINT`: To send traces to a [self-hosted LangSmith](https://docs.smith.langchain.com/self_hosting) instance, set `LANGSMITH_ENDPOINT` to the hostname of the self-hosted LangSmith instance.
|
||||
1. Egress to `https://beacon.langchain.com` from your network. This is required for license verification and usage reporting if not running in air-gapped mode. See the [Egress documentation](../../cloud/deployment/egress.md) for more details.
|
||||
|
||||
## Kubernetes (Helm)
|
||||
|
||||
Use this [Helm chart](https://github.com/langchain-ai/helm/blob/main/charts/langgraph-cloud/README.md) to deploy a LangGraph Server to a Kubernetes cluster.
|
||||
|
||||
## Docker
|
||||
|
||||
Run the following `docker` command:
|
||||
```shell
|
||||
docker run \
|
||||
--env-file .env \
|
||||
-p 8123:8000 \
|
||||
-e REDIS_URI="foo" \
|
||||
-e DATABASE_URI="bar" \
|
||||
-e LANGSMITH_API_KEY="baz" \
|
||||
my-image
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
* You need to replace `my-image` with the name of the image you built in the prerequisite steps (from `langgraph build`)
|
||||
and you should provide appropriate values for `REDIS_URI`, `DATABASE_URI`, and `LANGSMITH_API_KEY`.
|
||||
* If your application requires additional environment variables, you can pass them in a similar way.
|
||||
|
||||
## Docker Compose
|
||||
|
||||
Docker Compose YAML file:
|
||||
```yml
|
||||
volumes:
|
||||
langgraph-data:
|
||||
driver: local
|
||||
services:
|
||||
langgraph-redis:
|
||||
image: redis:6
|
||||
healthcheck:
|
||||
test: redis-cli ping
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
langgraph-postgres:
|
||||
image: postgres:16
|
||||
ports:
|
||||
- "5433:5432"
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- langgraph-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: pg_isready -U postgres
|
||||
start_period: 10s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
interval: 5s
|
||||
langgraph-api:
|
||||
image: ${IMAGE_NAME}
|
||||
ports:
|
||||
- "8123:8000"
|
||||
depends_on:
|
||||
langgraph-redis:
|
||||
condition: service_healthy
|
||||
langgraph-postgres:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
REDIS_URI: redis://langgraph-redis:6379
|
||||
LANGSMITH_API_KEY: ${LANGSMITH_API_KEY}
|
||||
POSTGRES_URI: postgres://postgres:postgres@langgraph-postgres:5432/postgres?sslmode=disable
|
||||
```
|
||||
|
||||
You can run the command `docker compose up` with this Docker Compose file in the same folder.
|
||||
|
||||
This will launch a LangGraph Server on port `8123` (if you want to change this, you can change this by changing the ports in the `langgraph-api` volume). You can test if the application is healthy by running:
|
||||
|
||||
```shell
|
||||
curl --request GET --url 0.0.0.0:8123/ok
|
||||
```
|
||||
Assuming everything is running correctly, you should see a response like:
|
||||
|
||||
```shell
|
||||
{"ok":true}
|
||||
```
|
||||
@@ -1,490 +0,0 @@
|
||||
# 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.
|
||||
|
||||
## Dynamic interrupts
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# Run the graph until the interrupt is hit.
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"some_text": "original text"} # (1)!
|
||||
)
|
||||
|
||||
print(result['__interrupt__']) # (2)!
|
||||
# > [
|
||||
# > {
|
||||
# > 'value': {'text_to_revise': 'original text'},
|
||||
# > 'resumable': True,
|
||||
# > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
# > 'when': 'during'
|
||||
# > }
|
||||
# > ]
|
||||
|
||||
|
||||
# Resume the graph
|
||||
print(await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(resume="Edited text") # (3)!
|
||||
))
|
||||
# > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `Command(resume=...)`, injecting the human's input and continuing execution.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// Run the graph until the interrupt is hit.
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: { "some_text": "original text" } } // (1)!
|
||||
);
|
||||
|
||||
console.log(result['__interrupt__']); // (2)!
|
||||
// > [
|
||||
// > {
|
||||
// > 'value': {'text_to_revise': 'original text'},
|
||||
// > 'resumable': True,
|
||||
// > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
// > 'when': 'during'
|
||||
// > }
|
||||
// > ]
|
||||
|
||||
// Resume the graph
|
||||
console.log(await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
// highlight-next-line
|
||||
{ command: { resume: "Edited text" }} // (3)!
|
||||
));
|
||||
// > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `{ resume: ... }` command object, injecting the human's input and continuing execution.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph until the interrupt is hit.:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"some_text\": \"original text\"}
|
||||
}"
|
||||
```
|
||||
|
||||
Resume the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": \"Edited text\"
|
||||
}
|
||||
}"
|
||||
```
|
||||
|
||||
??? example "Extended example: using `interrupt`"
|
||||
|
||||
This is an example graph you can run in the LangGraph API server.
|
||||
See [LangGraph Platform quickstart](../quick_start.md) for more details.
|
||||
|
||||
```python
|
||||
from typing import TypedDict
|
||||
import uuid
|
||||
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
from langgraph.constants import START
|
||||
from langgraph.graph import StateGraph
|
||||
# highlight-next-line
|
||||
from langgraph.types import interrupt, Command
|
||||
|
||||
class State(TypedDict):
|
||||
some_text: str
|
||||
|
||||
def human_node(state: State):
|
||||
# highlight-next-line
|
||||
value = interrupt( # (1)!
|
||||
{
|
||||
"text_to_revise": state["some_text"] # (2)!
|
||||
}
|
||||
)
|
||||
return {
|
||||
"some_text": value # (3)!
|
||||
}
|
||||
|
||||
|
||||
# Build the graph
|
||||
graph_builder = StateGraph(State)
|
||||
graph_builder.add_node("human_node", human_node)
|
||||
graph_builder.add_edge(START, "human_node")
|
||||
|
||||
graph = graph_builder.compile()
|
||||
```
|
||||
|
||||
1. `interrupt(...)` pauses execution at `human_node`, surfacing the given payload to a human.
|
||||
2. Any JSON serializable value can be passed to the `interrupt` function. Here, a dict containing the text to revise.
|
||||
3. Once resumed, the return value of `interrupt(...)` is the human-provided input, which is used to update the state.
|
||||
|
||||
Once you have a running LangGraph API server, you can interact with it using
|
||||
[LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/)
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
# highlight-next-line
|
||||
from langgraph_sdk.schema import Command
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# Run the graph until the interrupt is hit.
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"some_text": "original text"} # (1)!
|
||||
)
|
||||
|
||||
print(result['__interrupt__']) # (2)!
|
||||
# > [
|
||||
# > {
|
||||
# > 'value': {'text_to_revise': 'original text'},
|
||||
# > 'resumable': True,
|
||||
# > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
# > 'when': 'during'
|
||||
# > }
|
||||
# > ]
|
||||
|
||||
|
||||
# Resume the graph
|
||||
print(await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
command=Command(resume="Edited text") # (3)!
|
||||
))
|
||||
# > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `Command(resume=...)`, injecting the human's input and continuing execution.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// Run the graph until the interrupt is hit.
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: { "some_text": "original text" } } // (1)!
|
||||
);
|
||||
|
||||
console.log(result['__interrupt__']); // (2)!
|
||||
// > [
|
||||
// > {
|
||||
// > 'value': {'text_to_revise': 'original text'},
|
||||
// > 'resumable': True,
|
||||
// > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'],
|
||||
// > 'when': 'during'
|
||||
// > }
|
||||
// > ]
|
||||
|
||||
// Resume the graph
|
||||
console.log(await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
// highlight-next-line
|
||||
{ command: { resume: "Edited text" }} // (3)!
|
||||
));
|
||||
// > {'some_text': 'Edited text'}
|
||||
```
|
||||
|
||||
1. The graph is invoked with some initial state.
|
||||
2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata.
|
||||
3. The graph is resumed with a `{ resume: ... }` command object, injecting the human's input and continuing execution.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph until the interrupt is hit:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"some_text\": \"original text\"}
|
||||
}"
|
||||
```
|
||||
|
||||
Resume the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"command\": {
|
||||
\"resume\": \"Edited text\"
|
||||
}
|
||||
}"
|
||||
```
|
||||
|
||||
## Static interrupts
|
||||
|
||||
Static interrupts (also known as static breakpoints) are triggered either before or after a node executes.
|
||||
|
||||
!!! warning
|
||||
|
||||
Static interrupts are **not** recommended for human-in-the-loop workflows. They are best used for debugging and testing.
|
||||
|
||||
You can set static interrupts by specifying `interrupt_before` and `interrupt_after` at 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)!
|
||||
)
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Alternatively, you can set static interrupts at run time:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# highlight-next-line
|
||||
await client.runs.wait( # (1)!
|
||||
thread_id,
|
||||
assistant_id,
|
||||
inputs=inputs,
|
||||
# highlight-next-line
|
||||
interrupt_before=["node_a"], # (2)!
|
||||
# highlight-next-line
|
||||
interrupt_after=["node_b", "node_c"] # (3)!
|
||||
)
|
||||
```
|
||||
|
||||
1. `client.runs.wait` 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.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
// highlight-next-line
|
||||
await client.runs.wait( // (1)!
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: input,
|
||||
// highlight-next-line
|
||||
interruptBefore: ["node_a"], // (2)!
|
||||
// highlight-next-line
|
||||
interruptAfter: ["node_b", "node_c"] // (3)!
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
1. `client.runs.wait` is called with the `interruptBefore` and `interruptAfter` parameters. This is a run-time configuration and can be changed for every invocation.
|
||||
2. `interruptBefore` specifies the nodes where execution should pause before the node is executed.
|
||||
3. `interruptAfter` specifies the nodes where execution should pause after the node is executed.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"interrupt_before\": [\"node_a\"],
|
||||
\"interrupt_after\": [\"node_b\", \"node_c\"],
|
||||
\"input\": <INPUT>
|
||||
}"
|
||||
```
|
||||
|
||||
The following example shows how to add static interrupts:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# Run the graph until the breakpoint
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=inputs # (1)!
|
||||
)
|
||||
|
||||
# Resume the graph
|
||||
await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=None # (2)!
|
||||
)
|
||||
```
|
||||
|
||||
1. The graph is run until the first breakpoint is hit.
|
||||
2. The graph is resumed by passing in `None` for the input. This will run the graph until the next breakpoint is hit.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// Run the graph until the breakpoint
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: input } // (1)!
|
||||
);
|
||||
|
||||
// Resume the graph
|
||||
await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: null } // (2)!
|
||||
);
|
||||
```
|
||||
|
||||
1. The graph is run until the first breakpoint is hit.
|
||||
2. The graph is resumed by passing in `null` for the input. This will run the graph until the next breakpoint is hit.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph until the breakpoint:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <INPUT>
|
||||
}"
|
||||
```
|
||||
|
||||
Resume the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\"
|
||||
}"
|
||||
```
|
||||
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Human-in-the-loop conceptual guide](../../concepts/human_in_the_loop.md): learn more about LangGraph human-in-the-loop features.
|
||||
- [Common patterns](../../how-tos/human_in_the_loop/add-human-in-the-loop.md#common-patterns): learn how to implement patterns like approving/rejecting actions, requesting user input, tool call review, and validating human input.
|
||||
@@ -1,451 +0,0 @@
|
||||
# How to kick off background runs
|
||||
|
||||
|
||||
This guide covers how to kick off background runs for your agent.
|
||||
This can be useful for long running jobs.
|
||||
|
||||
## Setup
|
||||
|
||||
First let's set up our client and thread:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
# create thread
|
||||
thread = await client.threads.create()
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
// create thread
|
||||
const thread = await client.threads.create();
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
'thread_id': '5cb1e8a1-34b3-4a61-a34e-71a9799bd00d',
|
||||
'created_at': '2024-08-30T20:35:52.062934+00:00',
|
||||
'updated_at': '2024-08-30T20:35:52.062934+00:00',
|
||||
'metadata': {},
|
||||
'status': 'idle',
|
||||
'config': {},
|
||||
'values': None
|
||||
}
|
||||
|
||||
## Check runs on thread
|
||||
|
||||
If we list the current runs on this thread, we will see that it's empty:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
runs = await client.runs.list(thread["thread_id"])
|
||||
print(runs)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
let runs = await client.runs.list(thread['thread_id']);
|
||||
console.log(runs);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[]
|
||||
|
||||
## Start runs on thread
|
||||
|
||||
Now let's kick off a run:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]}
|
||||
run = await client.runs.create(thread["thread_id"], assistant_id, input=input)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
let input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]};
|
||||
let run = await client.runs.create(thread["thread_id"], assistantID, { input });
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <ASSISTANT_ID>
|
||||
}'
|
||||
```
|
||||
|
||||
The first time we poll it, we can see `status=pending`:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(await client.runs.get(thread["thread_id"], run["run_id"]))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(await client.runs.get(thread["thread_id"], run["run_id"]));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"run_id": "1ef6a5f8-bd86-6763-bbd6-bff042db7b1b",
|
||||
"thread_id": "7885f0cf-94ad-4040-91d7-73f7ba007c8a",
|
||||
"assistant_id": "fe096781-5601-53d2-b2f6-0d3403f7e9ca",
|
||||
"created_at": "2024-09-04T01:46:47.244887+00:00",
|
||||
"updated_at": "2024-09-04T01:46:47.244887+00:00",
|
||||
"metadata": {},
|
||||
"status": "pending",
|
||||
"kwargs": {
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "what's the weather in sf"
|
||||
}
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"metadata": {
|
||||
"created_by": "system"
|
||||
},
|
||||
"configurable": {
|
||||
"run_id": "1ef6a5f8-bd86-6763-bbd6-bff042db7b1b",
|
||||
"user_id": "",
|
||||
"graph_id": "agent",
|
||||
"thread_id": "7885f0cf-94ad-4040-91d7-73f7ba007c8a",
|
||||
"assistant_id": "fe096781-5601-53d2-b2f6-0d3403f7e9ca",
|
||||
"checkpoint_id": null
|
||||
}
|
||||
},
|
||||
"webhook": null,
|
||||
"temporary": false,
|
||||
"stream_mode": [
|
||||
"values"
|
||||
],
|
||||
"feedback_keys": null,
|
||||
"interrupt_after": null,
|
||||
"interrupt_before": null
|
||||
},
|
||||
"multitask_strategy": "reject"
|
||||
}
|
||||
|
||||
|
||||
|
||||
Now we can join the run, wait for it to finish and check that status again:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.runs.join(thread["thread_id"], run["run_id"])
|
||||
print(await client.runs.get(thread["thread_id"], run["run_id"]))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.runs.join(thread["thread_id"], run["run_id"]);
|
||||
console.log(await client.runs.get(thread["thread_id"], run["run_id"]));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/join &&
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"run_id": "1ef6a5f8-bd86-6763-bbd6-bff042db7b1b",
|
||||
"thread_id": "7885f0cf-94ad-4040-91d7-73f7ba007c8a",
|
||||
"assistant_id": "fe096781-5601-53d2-b2f6-0d3403f7e9ca",
|
||||
"created_at": "2024-09-04T01:46:47.244887+00:00",
|
||||
"updated_at": "2024-09-04T01:46:47.244887+00:00",
|
||||
"metadata": {},
|
||||
"status": "success",
|
||||
"kwargs": {
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "what's the weather in sf"
|
||||
}
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"metadata": {
|
||||
"created_by": "system"
|
||||
},
|
||||
"configurable": {
|
||||
"run_id": "1ef6a5f8-bd86-6763-bbd6-bff042db7b1b",
|
||||
"user_id": "",
|
||||
"graph_id": "agent",
|
||||
"thread_id": "7885f0cf-94ad-4040-91d7-73f7ba007c8a",
|
||||
"assistant_id": "fe096781-5601-53d2-b2f6-0d3403f7e9ca",
|
||||
"checkpoint_id": null
|
||||
}
|
||||
},
|
||||
"webhook": null,
|
||||
"temporary": false,
|
||||
"stream_mode": [
|
||||
"values"
|
||||
],
|
||||
"feedback_keys": null,
|
||||
"interrupt_after": null,
|
||||
"interrupt_before": null
|
||||
},
|
||||
"multitask_strategy": "reject"
|
||||
}
|
||||
|
||||
|
||||
Perfect! The run succeeded as we would expect. We can double check that the run worked as expected by printing out the final state:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
final_result = await client.threads.get_state(thread["thread_id"])
|
||||
print(final_result)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
let finalResult = await client.threads.getState(thread["thread_id"]);
|
||||
console.log(finalResult);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"values": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "what's the weather in sf",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "human",
|
||||
"name": null,
|
||||
"id": "beba31bf-320d-4125-9c37-cadf526ac47a",
|
||||
"example": false
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"id": "toolu_01AaNPSPzqia21v7aAKwbKYm",
|
||||
"input": {},
|
||||
"name": "tavily_search_results_json",
|
||||
"type": "tool_use",
|
||||
"index": 0,
|
||||
"partial_json": "{\"query\": \"weather in san francisco\"}"
|
||||
}
|
||||
],
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {
|
||||
"stop_reason": "tool_use",
|
||||
"stop_sequence": null
|
||||
},
|
||||
"type": "ai",
|
||||
"name": null,
|
||||
"id": "run-f220faf8-1d27-4f73-ad91-6bb3f47e8639",
|
||||
"example": false,
|
||||
"tool_calls": [
|
||||
{
|
||||
"name": "tavily_search_results_json",
|
||||
"args": {
|
||||
"query": "weather in san francisco"
|
||||
},
|
||||
"id": "toolu_01AaNPSPzqia21v7aAKwbKYm",
|
||||
"type": "tool_call"
|
||||
}
|
||||
],
|
||||
"invalid_tool_calls": [],
|
||||
"usage_metadata": {
|
||||
"input_tokens": 273,
|
||||
"output_tokens": 61,
|
||||
"total_tokens": 334
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": "[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1725052131, 'localtime': '2024-08-30 14:08'}, 'current': {'last_updated_epoch': 1725051600, 'last_updated': '2024-08-30 14:00', 'temp_c': 21.1, 'temp_f': 70.0, 'is_day': 1, 'condition': {'text': 'Partly cloudy', 'icon': '//cdn.weatherapi.com/weather/64x64/day/116.png', 'code': 1003}, 'wind_mph': 11.9, 'wind_kph': 19.1, 'wind_degree': 290, 'wind_dir': 'WNW', 'pressure_mb': 1018.0, 'pressure_in': 30.07, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 59, 'cloud': 25, 'feelslike_c': 21.1, 'feelslike_f': 70.0, 'windchill_c': 18.6, 'windchill_f': 65.5, 'heatindex_c': 18.6, 'heatindex_f': 65.5, 'dewpoint_c': 12.2, 'dewpoint_f': 54.0, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 5.0, 'gust_mph': 15.0, 'gust_kph': 24.2}}\"}]",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "tool",
|
||||
"name": "tavily_search_results_json",
|
||||
"id": "686b2487-f332-4e58-9508-89b3a814cd81",
|
||||
"tool_call_id": "toolu_01AaNPSPzqia21v7aAKwbKYm",
|
||||
"artifact": {
|
||||
"query": "weather in san francisco",
|
||||
"follow_up_questions": null,
|
||||
"answer": null,
|
||||
"images": [],
|
||||
"results": [
|
||||
{
|
||||
"title": "Weather in San Francisco",
|
||||
"url": "https://www.weatherapi.com/",
|
||||
"content": "{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1725052131, 'localtime': '2024-08-30 14:08'}, 'current': {'last_updated_epoch': 1725051600, 'last_updated': '2024-08-30 14:00', 'temp_c': 21.1, 'temp_f': 70.0, 'is_day': 1, 'condition': {'text': 'Partly cloudy', 'icon': '//cdn.weatherapi.com/weather/64x64/day/116.png', 'code': 1003}, 'wind_mph': 11.9, 'wind_kph': 19.1, 'wind_degree': 290, 'wind_dir': 'WNW', 'pressure_mb': 1018.0, 'pressure_in': 30.07, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 59, 'cloud': 25, 'feelslike_c': 21.1, 'feelslike_f': 70.0, 'windchill_c': 18.6, 'windchill_f': 65.5, 'heatindex_c': 18.6, 'heatindex_f': 65.5, 'dewpoint_c': 12.2, 'dewpoint_f': 54.0, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 5.0, 'gust_mph': 15.0, 'gust_kph': 24.2}}",
|
||||
"score": 0.976148,
|
||||
"raw_content": null
|
||||
}
|
||||
],
|
||||
"response_time": 3.07
|
||||
},
|
||||
"status": "success"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "\n\nThe search results provide the current weather conditions in San Francisco. According to the data, as of 2:00 PM on August 30, 2024, the temperature in San Francisco is 70\u00b0F (21.1\u00b0C) with partly cloudy skies. The wind is blowing from the west-northwest at around 12 mph (19 km/h). The humidity is 59% and visibility is 9 miles (16 km). Overall, it looks like a nice late summer day in San Francisco with comfortable temperatures and partly sunny conditions.",
|
||||
"type": "text",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {
|
||||
"stop_reason": "end_turn",
|
||||
"stop_sequence": null
|
||||
},
|
||||
"type": "ai",
|
||||
"name": null,
|
||||
"id": "run-8fecc61d-3d9f-4e16-8e8a-92f702be498a",
|
||||
"example": false,
|
||||
"tool_calls": [],
|
||||
"invalid_tool_calls": [],
|
||||
"usage_metadata": {
|
||||
"input_tokens": 837,
|
||||
"output_tokens": 124,
|
||||
"total_tokens": 961
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"next": [],
|
||||
"tasks": [],
|
||||
"metadata": {
|
||||
"step": 3,
|
||||
"run_id": "1ef67140-eb23-684b-8253-91d4c90bb05e",
|
||||
"source": "loop",
|
||||
"writes": {
|
||||
"agent": {
|
||||
"messages": [
|
||||
{
|
||||
"id": "run-8fecc61d-3d9f-4e16-8e8a-92f702be498a",
|
||||
"name": null,
|
||||
"type": "ai",
|
||||
"content": [
|
||||
{
|
||||
"text": "\n\nThe search results provide the current weather conditions in San Francisco. According to the data, as of 2:00 PM on August 30, 2024, the temperature in San Francisco is 70\u00b0F (21.1\u00b0C) with partly cloudy skies. The wind is blowing from the west-northwest at around 12 mph (19 km/h). The humidity is 59% and visibility is 9 miles (16 km). Overall, it looks like a nice late summer day in San Francisco with comfortable temperatures and partly sunny conditions.",
|
||||
"type": "text",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"example": false,
|
||||
"tool_calls": [],
|
||||
"usage_metadata": {
|
||||
"input_tokens": 837,
|
||||
"total_tokens": 961,
|
||||
"output_tokens": 124
|
||||
},
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {
|
||||
"stop_reason": "end_turn",
|
||||
"stop_sequence": null
|
||||
},
|
||||
"invalid_tool_calls": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"user_id": "",
|
||||
"graph_id": "agent",
|
||||
"thread_id": "5cb1e8a1-34b3-4a61-a34e-71a9799bd00d",
|
||||
"created_by": "system",
|
||||
"assistant_id": "fe096781-5601-53d2-b2f6-0d3403f7e9ca"
|
||||
},
|
||||
"created_at": "2024-08-30T21:09:00.079909+00:00",
|
||||
"checkpoint_id": "1ef67141-3ca2-6fae-8003-fe96832e57d6",
|
||||
"parent_checkpoint_id": "1ef67141-2129-6b37-8002-61fc3bf69cb5"
|
||||
}
|
||||
|
||||
We can also just print the content of the last AIMessage:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(final_result['values']['messages'][-1]['content'][0]['text'])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(finalResult['values']['messages'][finalResult['values']['messages'].length-1]['content'][0]['text']);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state | jq -r '.values.messages[-1].content.[0].text'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
The search results provide the current weather conditions in San Francisco. According to the data, as of 2:00 PM on August 30, 2024, the temperature in San Francisco is 70°F (21.1°C) with partly cloudy skies. The wind is blowing from the west-northwest at around 12 mph (19 km/h). The humidity is 59% and visibility is 9 miles (16 km). Overall, it looks like a nice late summer day in San Francisco with comfortable temperatures and partly sunny conditions.
|
||||
@@ -1,36 +0,0 @@
|
||||
# Debug LangSmith traces
|
||||
|
||||
This guide explains how to open LangSmith traces in LangGraph Studio for interactive investigation and debugging.
|
||||
|
||||
## Open deployed threads
|
||||
|
||||
1. Open the LangSmith trace, selecting the root run.
|
||||
2. Click "Run in Studio".
|
||||
|
||||
This will open LangGraph Studio connected to the associated LangGraph Platform deployment with the trace's parent thread selected.
|
||||
|
||||
## Testing local agents with remote traces
|
||||
|
||||
This section explains how to test a local agent against remote traces from LangSmith. This enables you to use production traces as input for local testing, allowing you to debug and verify agent modifications in your development environment.
|
||||
|
||||
### Requirements
|
||||
|
||||
- A LangSmith traced thread
|
||||
- A locally running agent. See [here](../how-tos/studio/quick_start.md#local-development-server) for setup
|
||||
instructions.
|
||||
|
||||
!!! info "Local agent requirements"
|
||||
|
||||
- langgraph>=0.3.18
|
||||
- langgraph-api>=0.0.32
|
||||
- Contains the same set of nodes present in the remote trace
|
||||
|
||||
### Cloning Thread
|
||||
|
||||
1. Open the LangSmith trace, selecting the root run.
|
||||
2. Click the dropdown next to "Run in Studio".
|
||||
3. Enter your local agent's URL.
|
||||
4. Select "Clone thread locally".
|
||||
5. If multiple graphs exist, select the target graph.
|
||||
|
||||
A new thread will be created in your local agent with the thread history inferred and copied from the remote thread, and you will be navigated to LangGraph Studio for your locally running application.
|
||||
@@ -1,84 +0,0 @@
|
||||
# Configurable Headers
|
||||
|
||||
LangGraph allows runtime configuration to modify agent behavior and permissions dynamically. When using the [LangGraph Platform](../quick_start.md), you can pass this configuration in the request body (`config`) or specific request headers. This enables adjustments based on user identity or other request data.
|
||||
|
||||
For privacy, control which headers are passed to the runtime configuration via the `http.configurable_headers` section in your `langgraph.json` file.
|
||||
|
||||
Here's how to customize the included and excluded headers:
|
||||
|
||||
```json
|
||||
{
|
||||
"http": {
|
||||
"configurable_headers": {
|
||||
"include": ["x-user-id", "x-organization-id", "my-prefix-*"],
|
||||
"exclude": ["authorization", "x-api-key"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
The `include` and `exclude` lists accept exact header names or patterns using `*` to match any number of characters. For your security, no other regex patterns are supported.
|
||||
|
||||
## Using within your graph
|
||||
|
||||
You can access the included headers in your graph using the `config` argument of any node.
|
||||
|
||||
```python
|
||||
def my_node(state, config):
|
||||
organization_id = config["configurable"].get("x-organization-id")
|
||||
...
|
||||
```
|
||||
|
||||
Or by fetching from context (useful in tools and or within other nested functions).
|
||||
|
||||
```python
|
||||
from langgraph.config import get_config
|
||||
|
||||
def search_everything(query: str):
|
||||
organization_id = get_config()["configurable"].get("x-organization-id")
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
You can even use this to dynamically compile the graph.
|
||||
|
||||
```python
|
||||
# my_graph.py.
|
||||
import contextlib
|
||||
|
||||
@contextlib.asynccontextmanager
|
||||
async def generate_agent(config):
|
||||
organization_id = config["configurable"].get("x-organization-id")
|
||||
if organization_id == "org1":
|
||||
graph = ...
|
||||
yield graph
|
||||
else:
|
||||
graph = ...
|
||||
yield graph
|
||||
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"graphs": {"agent": "my_grph.py:generate_agent"}
|
||||
}
|
||||
```
|
||||
|
||||
### Opt-out of configurable headers
|
||||
|
||||
If you'd like to opt-out of configurable headers, you can simply set a wildcard pattern in the `exclude` list:
|
||||
|
||||
```json
|
||||
{
|
||||
"http": {
|
||||
"configurable_headers": {
|
||||
"exclude": ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will exclude all headers from being added to your run's configuration.
|
||||
|
||||
Note that exclusions take precedence over inclusions.
|
||||
@@ -1,331 +0,0 @@
|
||||
# Manage assistants
|
||||
|
||||
In this guide we will show how to create, configure, and manage an [assistant](../../concepts/assistants.md).
|
||||
|
||||
First, as a brief refresher on the concept of configurations, consider the following simple `call_model` node and configuration schema. Observe that this node tries to read and use the `model_name` as defined by the `config` object's `configurable`.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
|
||||
class ConfigSchema(TypedDict):
|
||||
model_name: str
|
||||
|
||||
builder = StateGraph(AgentState, config_schema=ConfigSchema)
|
||||
|
||||
def call_model(state, config):
|
||||
messages = state["messages"]
|
||||
model_name = config.get('configurable', {}).get("model_name", "anthropic")
|
||||
model = _get_model(model_name)
|
||||
response = model.invoke(messages)
|
||||
# We return a list, because this will get added to the existing list
|
||||
return {"messages": [response]}
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Annotation } from "@langchain/langgraph";
|
||||
|
||||
const ConfigSchema = Annotation.Root({
|
||||
model_name: Annotation<string>,
|
||||
system_prompt:
|
||||
});
|
||||
|
||||
const builder = new StateGraph(AgentState, ConfigSchema)
|
||||
|
||||
function callModel(state: State, config: RunnableConfig) {
|
||||
const messages = state.messages;
|
||||
const modelName = config.configurable?.model_name ?? "anthropic";
|
||||
const model = _getModel(modelName);
|
||||
const response = model.invoke(messages);
|
||||
// We return a list, because this will get added to the existing list
|
||||
return { messages: [response] };
|
||||
}
|
||||
```
|
||||
|
||||
For more information on configurations, [see here](../../concepts/low_level.md#configuration).
|
||||
|
||||
## Create an assistant
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
To create an assistant, use the [LangGraph SDK](../../concepts/sdk.md) `create` method. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.AssistantsClient.create) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#create) SDK reference docs for more information.
|
||||
|
||||
This example uses the same configuration schema as above, and creates an assistant with `model_name` set to `openai`.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
openai_assistant = await client.assistants.create(
|
||||
# "agent" is the name of a graph we deployed
|
||||
"agent", config={"configurable": {"model_name": "openai"}}, name="Open AI Assistant"
|
||||
)
|
||||
|
||||
print(openai_assistant)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const openAIAssistant = await client.assistants.create({
|
||||
graphId: 'agent',
|
||||
name: "Open AI Assistant",
|
||||
config: { "configurable": { "model_name": "openai" } },
|
||||
});
|
||||
|
||||
console.log(openAIAssistant);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"graph_id":"agent", "config":{"configurable":{"model_name":"openai"}}, "name": "Open AI Assistant"}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"assistant_id": "62e209ca-9154-432a-b9e9-2d75c7a9219b",
|
||||
"graph_id": "agent",
|
||||
"name": "Open AI Assistant"
|
||||
"config": {
|
||||
"configurable": {
|
||||
"model_name": "openai"
|
||||
}
|
||||
},
|
||||
"metadata": {}
|
||||
"created_at": "2024-08-31T03:09:10.230718+00:00",
|
||||
"updated_at": "2024-08-31T03:09:10.230718+00:00",
|
||||
}
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also create assistants from the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Assistants" tab. This will load a table of all of the assistants in your deployment, across all graphs.
|
||||
|
||||
To create a new assistant, select the "+ New assistant" button. This will open a form where you can specify the graph this assistant is for, as well as provide a name, description, and the desired configuration for the assistant based on the configuration schema for that graph.
|
||||
|
||||
To confirm, click "Create assistant". This will take you to [LangGraph Studio](../../concepts/langgraph_studio.md) where you can test the assistant. If you go back to the "Assistants" tab in the deployment, you will see the newly created assistant in the table.
|
||||
|
||||
## Use an assistant
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
We have now created an assistant called "Open AI Assistant" that has `model_name` defined as `openai`. We can now use this assistant with this configuration:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
thread = await client.threads.create()
|
||||
input = {"messages": [{"role": "user", "content": "who made you?"}]}
|
||||
async for event in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
# this is where we specify the assistant id to use
|
||||
openai_assistant["assistant_id"],
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
print(f"Receiving event of type: {event.event}")
|
||||
print(event.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const thread = await client.threads.create();
|
||||
const input = { "messages": [{ "role": "user", "content": "who made you?" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
// this is where we specify the assistant id to use
|
||||
openAIAssistant["assistant_id"],
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const event of streamResponse) {
|
||||
console.log(`Receiving event of type: ${event.event}`);
|
||||
console.log(event.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
thread_id=$(curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}' | jq -r '.thread_id') && \
|
||||
curl --request POST \
|
||||
--url "<DEPLOYMENT_URL>/threads/${thread_id}/runs/stream" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <OPENAI_ASSISTANT_ID>,
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "who made you?"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stream_mode": [
|
||||
"updates"
|
||||
]
|
||||
}' | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
Receiving event of type: metadata
|
||||
{'run_id': '1ef6746e-5893-67b1-978a-0f1cd4060e16'}
|
||||
|
||||
|
||||
|
||||
Receiving event of type: updates
|
||||
{'agent': {'messages': [{'content': 'I was created by OpenAI, a research organization focused on developing and advancing artificial intelligence technology.', 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-e1a6b25c-8416-41f2-9981-f9cfe043f414', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
```
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
Inside your deployment, select the "Assistants" tab. For the assistant you would like to use, click the "Studio" button. This will open LangGraph Studio with the selected assistant. When you submit an input (either in Graph or Chat mode), the selected assistant and its configuration will be used.
|
||||
|
||||
## Create a new version for your assistant
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
To edit the assistant, use the `update` method. This will create a new version of the assistant with the provided edits. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.AssistantsClient.update) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#update) SDK reference docs for more information.
|
||||
|
||||
!!! note "Note"
|
||||
|
||||
You must pass in the ENTIRE config (and metadata if you are using it). The update endpoint creates new versions completely from scratch and does not rely on previous versions.
|
||||
|
||||
For example, to update your assistant's system prompt:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
openai_assistant_v2 = await client.assistants.update(
|
||||
openai_assistant["assistant_id"],
|
||||
config={
|
||||
"configurable": {
|
||||
"model_name": "openai",
|
||||
"system_prompt": "You are an unhelpful assistant!",
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const openaiAssistantV2 = await client.assistants.update(
|
||||
openai_assistant["assistant_id"],
|
||||
{
|
||||
config: {
|
||||
configurable: {
|
||||
model_name: 'openai',
|
||||
system_prompt: 'You are an unhelpful assistant!',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request PATCH \
|
||||
--url <DEPOLYMENT_URL>/assistants/<ASSISTANT_ID> \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"config": {"model_name": "openai", "system_prompt": "You are an unhelpful assistant!"}
|
||||
}'
|
||||
```
|
||||
|
||||
This will create a new version of the assistant with the updated parameters and set this as the active version of your assistant. If you now run your graph and pass in this assistant id, it will use this latest version.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also edit assistants from the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Assistants" tab. This will load a table of all of the assistants in your deployment, across all graphs.
|
||||
|
||||
To edit an existing assistant, select the "Edit" button for the specified assistant. This will open a form where you can edit the assistant's name, description, and configuration.
|
||||
|
||||
Additionally, if using LangGraph Studio, you can edit the assistants and create new versions via the "Manage Assistants" button.
|
||||
|
||||
## Use a previous assistant version
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
You can also change the active version of your assistant. To do so, use the `setLatest` method.
|
||||
|
||||
In the example above, to rollback to the first version of the assistant:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.assistants.set_latest(openai_assistant['assistant_id'], 1)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.assistants.setLatest(openaiAssistant['assistant_id'], 1);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/<ASSISTANT_ID>/latest \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"version": 1
|
||||
}'
|
||||
```
|
||||
|
||||
If you now run your graph and pass in this assistant id, it will use the first version of the assistant.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
If using LangGraph Studio, to set the active version of your assistant, click the "Manage Assistants" button and locate the assistant you would like to use. Select the assistant and the version, and then click the "Active" toggle. This will update the assistant to make the selected version active.
|
||||
|
||||
!!! warning "Deleting Assistants"
|
||||
Deleting as assistant will delete ALL of its versions. There is currently no way to delete a single version, but by pointing your assistant to the correct version you can skip any versions that you don't wish to use.
|
||||
@@ -1,184 +0,0 @@
|
||||
# Use cron jobs
|
||||
|
||||
Sometimes you don't want to run your graph based on user interaction, but rather you would like to schedule your graph to run on a schedule - for example if you wish for your graph to compose and send out a weekly email of to-dos for your team. LangGraph Platform allows you to do this without having to write your own script by using the `Crons` client. To schedule a graph job, you need to pass a [cron expression](https://crontab.cronhub.io/) to inform the client when you want to run the graph. `Cron` jobs are run in the background and do not interfere with normal invocations of the graph.
|
||||
|
||||
## Setup
|
||||
|
||||
First, let's set up our SDK client, assistant, and thread:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
# create thread
|
||||
thread = await client.threads.create()
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantId = "agent";
|
||||
// create thread
|
||||
const thread = await client.threads.create();
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}' | jq -c 'map(select(.config == null or .config == {})) | .[0].graph_id' && \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
'thread_id': '9dde5490-2b67-47c8-aa14-4bfec88af217',
|
||||
'created_at': '2024-08-30T23:07:38.242730+00:00',
|
||||
'updated_at': '2024-08-30T23:07:38.242730+00:00',
|
||||
'metadata': {},
|
||||
'status': 'idle',
|
||||
'config': {},
|
||||
'values': None
|
||||
}
|
||||
|
||||
## Cron job on a thread
|
||||
|
||||
To create a cron job associated with a specific thread, you can write:
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# This schedules a job to run at 15:27 (3:27PM) every day
|
||||
cron_job = await client.crons.create_for_thread(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
schedule="27 15 * * *",
|
||||
input={"messages": [{"role": "user", "content": "What time is it?"}]},
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// This schedules a job to run at 15:27 (3:27PM) every day
|
||||
const cronJob = await client.crons.create_for_thread(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
schedule: "27 15 * * *",
|
||||
input: { messages: [{ role: "user", content: "What time is it?" }] }
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/crons \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <ASSISTANT_ID>,
|
||||
}'
|
||||
```
|
||||
|
||||
Note that it is **very** important to delete `Cron` jobs that are no longer useful. Otherwise you could rack up unwanted API charges to the LLM! You can delete a `Cron` job using the following code:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.crons.delete(cron_job["cron_id"])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.crons.delete(cronJob["cron_id"]);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request DELETE \
|
||||
--url <DEPLOYMENT_URL>/runs/crons/<CRON_ID>
|
||||
```
|
||||
|
||||
## Cron job stateless
|
||||
|
||||
You can also create stateless cron jobs by using the following code:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# This schedules a job to run at 15:27 (3:27PM) every day
|
||||
cron_job_stateless = await client.crons.create(
|
||||
assistant_id,
|
||||
schedule="27 15 * * *",
|
||||
input={"messages": [{"role": "user", "content": "What time is it?"}]},
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// This schedules a job to run at 15:27 (3:27PM) every day
|
||||
const cronJobStateless = await client.crons.create(
|
||||
assistantId,
|
||||
{
|
||||
schedule: "27 15 * * *",
|
||||
input: { messages: [{ role: "user", content: "What time is it?" }] }
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/runs/crons \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <ASSISTANT_ID>,
|
||||
}'
|
||||
```
|
||||
|
||||
Again, remember to delete your job once you are done with it!
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.crons.delete(cron_job_stateless["cron_id"])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.crons.delete(cronJobStateless["cron_id"]);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request DELETE \
|
||||
--url <DEPLOYMENT_URL>/runs/crons/<CRON_ID>
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
# Add node to dataset
|
||||
|
||||
This guide shows how to add examples to [LangSmith datasets](https://docs.smith.langchain.com/evaluation/how_to_guides#dataset-management) from nodes in the thread log. This is useful to evaluate individual steps of the agent.
|
||||
|
||||
1. Select a thread.
|
||||
2. Click on the `Add to Dataset` button.
|
||||
3. Select nodes whose input/output you want to add to a dataset.
|
||||
4. For each selected node, select the target dataset to create the example in. By default a dataset for the specific assistant and node will be selected. If this dataset does not yet exist, it will be created.
|
||||
5. Edit the example's input/output as needed before adding it to the dataset.
|
||||
6. Select "Add to dataset" at the bottom of the page to add all selected nodes to their respective datasets.
|
||||
|
||||
See [Evaluating intermediate steps](https://docs.smith.langchain.com/evaluation/how_to_guides/langgraph#evaluating-intermediate-steps) for more details on how to evaluate intermediate steps.
|
||||
@@ -1,255 +0,0 @@
|
||||
# Enqueue
|
||||
|
||||
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
|
||||
|
||||
The guide covers the `enqueue` option for double texting, which adds the interruptions to a queue and executes them in the order they are received by the client. Below is a quick example of using the `enqueue` option.
|
||||
|
||||
## Setup
|
||||
|
||||
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
function prettyPrint(m) {
|
||||
const padded = " " + m['type'] + " ";
|
||||
const sepLen = Math.floor((80 - padded.length) / 2);
|
||||
const sep = "=".repeat(sepLen);
|
||||
const secondSep = sep + (padded.length % 2 ? "=" : "");
|
||||
|
||||
console.log(`${sep}${padded}${secondSep}`);
|
||||
console.log("\n\n");
|
||||
console.log(m.content);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
# PLACE THIS IN A FILE CALLED pretty_print.sh
|
||||
pretty_print() {
|
||||
local type="$1"
|
||||
local content="$2"
|
||||
local padded=" $type "
|
||||
local total_width=80
|
||||
local sep_len=$(( (total_width - ${#padded}) / 2 ))
|
||||
local sep=$(printf '=%.0s' $(eval "echo {1.."${sep_len}"}"))
|
||||
local second_sep=$sep
|
||||
if (( (total_width - ${#padded}) % 2 )); then
|
||||
second_sep="${second_sep}="
|
||||
fi
|
||||
|
||||
echo "${sep}${padded}${second_sep}"
|
||||
echo
|
||||
echo "$content"
|
||||
}
|
||||
```
|
||||
|
||||
Then, let's import our required packages and instantiate our client, assistant, and thread.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
import httpx
|
||||
from langchain_core.messages import convert_to_messages
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantId = "agent";
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Create runs
|
||||
|
||||
Now let's start two runs, with the second interrupting the first one with a multitask strategy of "enqueue":
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
first_run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
|
||||
)
|
||||
second_run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
|
||||
multitask_strategy="enqueue",
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const firstRun = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
|
||||
)
|
||||
|
||||
const secondRun = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
|
||||
multitask_strategy="enqueue",
|
||||
)
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in sf?\"}]},
|
||||
}" && curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in nyc?\"}]},
|
||||
\"multitask_strategy\": \"enqueue\"
|
||||
}"
|
||||
```
|
||||
|
||||
## View run results
|
||||
|
||||
Verify that the thread has data from both runs:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# wait until the second run completes
|
||||
await client.runs.join(thread["thread_id"], second_run["run_id"])
|
||||
|
||||
state = await client.threads.get_state(thread["thread_id"])
|
||||
|
||||
for m in convert_to_messages(state["values"]["messages"]):
|
||||
m.pretty_print()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.runs.join(thread["thread_id"], secondRun["run_id"]);
|
||||
|
||||
const state = await client.threads.getState(thread["thread_id"]);
|
||||
|
||||
for (const m of state["values"]["messages"]) {
|
||||
prettyPrint(m);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
source pretty_print.sh && curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/join && \
|
||||
curl --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state | \
|
||||
jq -c '.values.messages[]' | while read -r element; do
|
||||
type=$(echo "$element" | jq -r '.type')
|
||||
content=$(echo "$element" | jq -r '.content | if type == "array" then tostring else . end')
|
||||
pretty_print "$type" "$content"
|
||||
done
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
================================ Human Message =================================
|
||||
|
||||
what's the weather in sf?
|
||||
================================== Ai Message ==================================
|
||||
|
||||
[{'id': 'toolu_01Dez1sJre4oA2Y7NsKJV6VT', 'input': {'query': 'weather in san francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
tavily_search_results_json (toolu_01Dez1sJre4oA2Y7NsKJV6VT)
|
||||
Call ID: toolu_01Dez1sJre4oA2Y7NsKJV6VT
|
||||
Args:
|
||||
query: weather in san francisco
|
||||
================================= Tool Message =================================
|
||||
Name: tavily_search_results_json
|
||||
|
||||
[{"url": "https://www.accuweather.com/en/us/san-francisco/94103/weather-forecast/347629", "content": "Get the current and future weather conditions for San Francisco, CA, including temperature, precipitation, wind, air quality and more. See the hourly and 10-day outlook, radar maps, alerts and allergy information."}]
|
||||
================================== Ai Message ==================================
|
||||
|
||||
According to AccuWeather, the current weather conditions in San Francisco are:
|
||||
|
||||
Temperature: 57°F (14°C)
|
||||
Conditions: Mostly Sunny
|
||||
Wind: WSW 10 mph
|
||||
Humidity: 72%
|
||||
|
||||
The forecast for the next few days shows partly sunny skies with highs in the upper 50s to mid 60s F (14-18°C) and lows in the upper 40s to low 50s F (9-11°C). Typical mild, dry weather for San Francisco this time of year.
|
||||
|
||||
Some key details from the AccuWeather forecast:
|
||||
|
||||
Today: Mostly sunny, high of 62°F (17°C)
|
||||
Tonight: Partly cloudy, low of 49°F (9°C)
|
||||
Tomorrow: Partly sunny, high of 59°F (15°C)
|
||||
Saturday: Mostly sunny, high of 64°F (18°C)
|
||||
Sunday: Partly sunny, high of 61°F (16°C)
|
||||
|
||||
So in summary, expect seasonable spring weather in San Francisco over the next several days, with a mix of sun and clouds and temperatures ranging from the upper 40s at night to the low 60s during the days. Typical dry conditions with no rain in the forecast.
|
||||
================================ Human Message =================================
|
||||
|
||||
what's the weather in nyc?
|
||||
================================== Ai Message ==================================
|
||||
|
||||
[{'text': 'Here are the current weather conditions and forecast for New York City:', 'type': 'text'}, {'id': 'toolu_01FFft5Sx9oS6AdVJuRWWcGp', 'input': {'query': 'weather in new york city'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
tavily_search_results_json (toolu_01FFft5Sx9oS6AdVJuRWWcGp)
|
||||
Call ID: toolu_01FFft5Sx9oS6AdVJuRWWcGp
|
||||
Args:
|
||||
query: weather in new york city
|
||||
================================= Tool Message =================================
|
||||
Name: tavily_search_results_json
|
||||
|
||||
[{"url": "https://www.weatherapi.com/", "content": "{'location': {'name': 'New York', 'region': 'New York', 'country': 'United States of America', 'lat': 40.71, 'lon': -74.01, 'tz_id': 'America/New_York', 'localtime_epoch': 1718734479, 'localtime': '2024-06-18 14:14'}, 'current': {'last_updated_epoch': 1718733600, 'last_updated': '2024-06-18 14:00', 'temp_c': 29.4, 'temp_f': 84.9, 'is_day': 1, 'condition': {'text': 'Sunny', 'icon': '//cdn.weatherapi.com/weather/64x64/day/113.png', 'code': 1000}, 'wind_mph': 2.2, 'wind_kph': 3.6, 'wind_degree': 158, 'wind_dir': 'SSE', 'pressure_mb': 1025.0, 'pressure_in': 30.26, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 63, 'cloud': 0, 'feelslike_c': 31.3, 'feelslike_f': 88.3, 'windchill_c': 28.3, 'windchill_f': 82.9, 'heatindex_c': 29.6, 'heatindex_f': 85.3, 'dewpoint_c': 18.4, 'dewpoint_f': 65.2, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 7.0, 'gust_mph': 16.5, 'gust_kph': 26.5}}"}]
|
||||
================================== Ai Message ==================================
|
||||
|
||||
According to the weather data from WeatherAPI:
|
||||
|
||||
Current Conditions in New York City (as of 2:00 PM local time):
|
||||
- Temperature: 85°F (29°C)
|
||||
- Conditions: Sunny
|
||||
- Wind: 2 mph (4 km/h) from the SSE
|
||||
- Humidity: 63%
|
||||
- Heat Index: 85°F (30°C)
|
||||
|
||||
The forecast shows sunny and warm conditions persisting over the next few days:
|
||||
|
||||
Today: Sunny, high of 85°F (29°C)
|
||||
Tonight: Clear, low of 68°F (20°C)
|
||||
Tomorrow: Sunny, high of 88°F (31°C)
|
||||
Thursday: Mostly sunny, high of 90°F (32°C)
|
||||
Friday: Partly cloudy, high of 87°F (31°C)
|
||||
|
||||
So New York City is experiencing beautiful sunny weather with seasonably warm temperatures in the mid-to-upper 80s Fahrenheit (around 30°C). Humidity is moderate in the 60% range. Overall, ideal late spring/early summer conditions for being outdoors in the city over the next several days.
|
||||
|
||||
@@ -1,522 +0,0 @@
|
||||
# How to implement Generative User Interfaces with LangGraph
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [LangGraph Platform](../../concepts/langgraph_platform.md)
|
||||
- [LangGraph Server](../../concepts/langgraph_server.md)
|
||||
- [`useStream()` React Hook](./use_stream_react.md)
|
||||
|
||||
Generative user interfaces (Generative UI) allows agents to go beyond text and generate rich user interfaces. This enables creating more interactive and context-aware applications where the UI adapts based on the conversation flow and AI responses.
|
||||
|
||||

|
||||
|
||||
LangGraph Platform supports colocating your React components with your graph code. This allows you to focus on building specific UI components for your graph while easily plugging into existing chat interfaces such as [Agent Chat](https://agentchat.vercel.app) and loading the code only when actually needed.
|
||||
|
||||
## Tutorial
|
||||
|
||||
### 1. Define and configure UI components
|
||||
|
||||
First, create your first UI component. For each component you need to provide an unique identifier that will be used to reference the component in your graph code.
|
||||
|
||||
```tsx title="src/agent/ui.tsx"
|
||||
const WeatherComponent = (props: { city: string }) => {
|
||||
return <div>Weather for {props.city}</div>;
|
||||
};
|
||||
|
||||
export default {
|
||||
weather: WeatherComponent,
|
||||
};
|
||||
```
|
||||
|
||||
Next, define your UI components in your `langgraph.json` configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": {
|
||||
"agent": "./src/agent/index.ts:graph"
|
||||
},
|
||||
"ui": {
|
||||
"agent": "./src/agent/ui.tsx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `ui` section points to the UI components that will be used by graphs. By default, we recommend using the same key as the graph name, but you can split out the components however you like, see [Customise the namespace of UI components](#customise-the-namespace-of-ui-components) for more details.
|
||||
|
||||
LangGraph Platform will automatically bundle your UI components code and styles and serve them as external assets that can be loaded by the `LoadExternalComponent` component. Some dependencies such as `react` and `react-dom` will be automatically excluded from the bundle.
|
||||
|
||||
CSS and Tailwind 4.x is also supported out of the box, so you can freely use Tailwind classes as well as `shadcn/ui` in your UI components.
|
||||
|
||||
=== "`src/agent/ui.tsx`"
|
||||
|
||||
```tsx
|
||||
import "./styles.css";
|
||||
|
||||
const WeatherComponent = (props: { city: string }) => {
|
||||
return <div className="bg-red-500">Weather for {props.city}</div>;
|
||||
};
|
||||
|
||||
export default {
|
||||
weather: WeatherComponent,
|
||||
};
|
||||
```
|
||||
|
||||
=== "`src/agent/styles.css`"
|
||||
|
||||
```css
|
||||
@import "tailwindcss";
|
||||
```
|
||||
|
||||
### 2. Send the UI components in your graph
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="src/agent.py"
|
||||
import uuid
|
||||
from typing import Annotated, Sequence, TypedDict
|
||||
|
||||
from langchain_core.messages import AIMessage, BaseMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import StateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.graph.ui import AnyUIMessage, ui_message_reducer, push_ui_message
|
||||
|
||||
|
||||
class AgentState(TypedDict): # noqa: D101
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
ui: Annotated[Sequence[AnyUIMessage], ui_message_reducer]
|
||||
|
||||
|
||||
async def weather(state: AgentState):
|
||||
class WeatherOutput(TypedDict):
|
||||
city: str
|
||||
|
||||
weather: WeatherOutput = (
|
||||
await ChatOpenAI(model="gpt-4o-mini")
|
||||
.with_structured_output(WeatherOutput)
|
||||
.with_config({"tags": ["nostream"]})
|
||||
.ainvoke(state["messages"])
|
||||
)
|
||||
|
||||
message = AIMessage(
|
||||
id=str(uuid.uuid4()),
|
||||
content=f"Here's the weather for {weather['city']}",
|
||||
)
|
||||
|
||||
# Emit UI elements associated with the message
|
||||
push_ui_message("weather", weather, message=message)
|
||||
return {"messages": [message]}
|
||||
|
||||
|
||||
workflow = StateGraph(AgentState)
|
||||
workflow.add_node(weather)
|
||||
workflow.add_edge("__start__", "weather")
|
||||
graph = workflow.compile()
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
Use the `typedUi` utility to emit UI elements from your agent nodes:
|
||||
|
||||
```typescript title="src/agent/index.ts"
|
||||
import {
|
||||
typedUi,
|
||||
uiMessageReducer,
|
||||
} from "@langchain/langgraph-sdk/react-ui/server";
|
||||
|
||||
import { ChatOpenAI } from "@langchain/openai";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
import type ComponentMap from "./ui.js";
|
||||
|
||||
import {
|
||||
Annotation,
|
||||
MessagesAnnotation,
|
||||
StateGraph,
|
||||
type LangGraphRunnableConfig,
|
||||
} from "@langchain/langgraph";
|
||||
|
||||
const AgentState = Annotation.Root({
|
||||
...MessagesAnnotation.spec,
|
||||
ui: Annotation({ reducer: uiMessageReducer, default: () => [] }),
|
||||
});
|
||||
|
||||
export const graph = new StateGraph(AgentState)
|
||||
.addNode("weather", async (state, config) => {
|
||||
// Provide the type of the component map to ensure
|
||||
// type safety of `ui.push()` calls as well as
|
||||
// pushing the messages to the `ui` and sending a custom event as well.
|
||||
const ui = typedUi<typeof ComponentMap>(config);
|
||||
|
||||
const weather = await new ChatOpenAI({ model: "gpt-4o-mini" })
|
||||
.withStructuredOutput(z.object({ city: z.string() }))
|
||||
.withConfig({ tags: ["nostream"] })
|
||||
.invoke(state.messages);
|
||||
|
||||
const response = {
|
||||
id: uuidv4(),
|
||||
type: "ai",
|
||||
content: `Here's the weather for ${weather.city}`,
|
||||
};
|
||||
|
||||
// Emit UI elements associated with the AI message
|
||||
ui.push({ name: "weather", props: weather }, { message: response });
|
||||
|
||||
return { messages: [response] };
|
||||
})
|
||||
.addEdge("__start__", "weather")
|
||||
.compile();
|
||||
```
|
||||
|
||||
### 3. Handle UI elements in your React application
|
||||
|
||||
On the client side, you can use `useStream()` and `LoadExternalComponent` to display the UI elements.
|
||||
|
||||
```tsx title="src/app/page.tsx"
|
||||
"use client";
|
||||
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui";
|
||||
|
||||
export default function Page() {
|
||||
const { thread, values } = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{thread.messages.map((message) => (
|
||||
<div key={message.id}>
|
||||
{message.content}
|
||||
{values.ui
|
||||
?.filter((ui) => ui.metadata?.message_id === message.id)
|
||||
.map((ui) => (
|
||||
<LoadExternalComponent key={ui.id} stream={thread} message={ui} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Behind the scenes, `LoadExternalComponent` will fetch the JS and CSS for the UI components from LangGraph Platform and render them in a shadow DOM, thus ensuring style isolation from the rest of your application.
|
||||
|
||||
## How-to guides
|
||||
|
||||
### Provide custom components on the client side
|
||||
|
||||
If you already have the components loaded in your client application, you can provide a map of such components to be rendered directly without fetching the UI code from LangGraph Platform.
|
||||
|
||||
```tsx
|
||||
const clientComponents = {
|
||||
weather: WeatherComponent,
|
||||
};
|
||||
|
||||
<LoadExternalComponent
|
||||
stream={thread}
|
||||
message={ui}
|
||||
components={clientComponents}
|
||||
/>;
|
||||
```
|
||||
|
||||
### Show loading UI when components are loading
|
||||
|
||||
You can provide a fallback UI to be rendered when the components are loading.
|
||||
|
||||
```tsx
|
||||
<LoadExternalComponent
|
||||
stream={thread}
|
||||
message={ui}
|
||||
fallback={<div>Loading...</div>}
|
||||
/>
|
||||
```
|
||||
|
||||
### Customise the namespace of UI components.
|
||||
|
||||
By default `LoadExternalComponent` will use the `assistantId` from `useStream()` hook to fetch the code for UI components. You can customise this by providing a `namespace` prop to the `LoadExternalComponent` component.
|
||||
|
||||
=== "`src/app/page.tsx`"
|
||||
|
||||
```tsx
|
||||
<LoadExternalComponent
|
||||
stream={thread}
|
||||
message={ui}
|
||||
namespace="custom-namespace"
|
||||
/>
|
||||
```
|
||||
|
||||
=== "`langgraph.json`"
|
||||
|
||||
```json
|
||||
{
|
||||
"ui": {
|
||||
"custom-namespace": "./src/agent/ui.tsx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Access and interact with the thread state from the UI component
|
||||
|
||||
You can access the thread state inside the UI component by using the `useStreamContext` hook.
|
||||
|
||||
```tsx
|
||||
import { useStreamContext } from "@langchain/langgraph-sdk/react-ui";
|
||||
|
||||
const WeatherComponent = (props: { city: string }) => {
|
||||
const { thread, submit } = useStreamContext();
|
||||
return (
|
||||
<>
|
||||
<div>Weather for {props.city}</div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
const newMessage = {
|
||||
type: "human",
|
||||
content: `What's the weather in ${props.city}?`,
|
||||
};
|
||||
|
||||
submit({ messages: [newMessage] });
|
||||
}}
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Pass additional context to the client components
|
||||
|
||||
You can pass additional context to the client components by providing a `meta` prop to the `LoadExternalComponent` component.
|
||||
|
||||
```tsx
|
||||
<LoadExternalComponent stream={thread} message={ui} meta={{ userId: "123" }} />
|
||||
```
|
||||
|
||||
Then, you can access the `meta` prop in the UI component by using the `useStreamContext` hook.
|
||||
|
||||
```tsx
|
||||
import { useStreamContext } from "@langchain/langgraph-sdk/react-ui";
|
||||
|
||||
const WeatherComponent = (props: { city: string }) => {
|
||||
const { meta } = useStreamContext<
|
||||
{ city: string },
|
||||
{ MetaType: { userId?: string } }
|
||||
>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
Weather for {props.city} (user: {meta?.userId})
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Streaming UI messages from the server
|
||||
|
||||
You can stream UI messages before the node execution is finished by using the `onCustomEvent` callback of the `useStream()` hook. This is especially useful when updating the UI component as the LLM is generating the response.
|
||||
|
||||
```tsx
|
||||
import { uiMessageReducer } from "@langchain/langgraph-sdk/react-ui";
|
||||
|
||||
const { thread, submit } = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
onCustomEvent: (event, options) => {
|
||||
options.mutate((prev) => {
|
||||
const ui = uiMessageReducer(prev.ui ?? [], event);
|
||||
return { ...prev, ui };
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Then you can push updates to the UI component by calling `ui.push()` / `push_ui_message()` with the same ID as the UI message you wish to update.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from typing import Annotated, Sequence, TypedDict
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage
|
||||
from langgraph.graph import StateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.graph.ui import AnyUIMessage, push_ui_message, ui_message_reducer
|
||||
|
||||
|
||||
class AgentState(TypedDict): # noqa: D101
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
ui: Annotated[Sequence[AnyUIMessage], ui_message_reducer]
|
||||
|
||||
|
||||
class CreateTextDocument(TypedDict):
|
||||
"""Prepare a document heading for the user."""
|
||||
|
||||
title: str
|
||||
|
||||
|
||||
async def writer_node(state: AgentState):
|
||||
model = ChatAnthropic(model="claude-3-5-sonnet-latest")
|
||||
message: AIMessage = await model.bind_tools(
|
||||
tools=[CreateTextDocument],
|
||||
tool_choice={"type": "tool", "name": "CreateTextDocument"},
|
||||
).ainvoke(state["messages"])
|
||||
|
||||
tool_call = next(
|
||||
(x["args"] for x in message.tool_calls if x["name"] == "CreateTextDocument"),
|
||||
None,
|
||||
)
|
||||
|
||||
if tool_call:
|
||||
ui_message = push_ui_message("writer", tool_call, message=message)
|
||||
ui_message_id = ui_message["id"]
|
||||
|
||||
# We're already streaming the LLM response to the client through UI messages
|
||||
# so we don't need to stream it again to the `messages` stream mode.
|
||||
content_stream = model.with_config({"tags": ["nostream"]}).astream(
|
||||
f"Create a document with the title: {tool_call['title']}"
|
||||
)
|
||||
|
||||
content: AIMessageChunk | None = None
|
||||
async for chunk in content_stream:
|
||||
content = content + chunk if content else chunk
|
||||
|
||||
push_ui_message(
|
||||
"writer",
|
||||
{"content": content.text()},
|
||||
id=ui_message_id,
|
||||
message=message,
|
||||
# Use `merge=rue` to merge props with the existing UI message
|
||||
merge=True,
|
||||
)
|
||||
|
||||
return {"messages": [message]}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Annotation,
|
||||
MessagesAnnotation,
|
||||
type LangGraphRunnableConfig,
|
||||
} from "@langchain/langgraph";
|
||||
import { z } from "zod";
|
||||
import { ChatAnthropic } from "@langchain/anthropic";
|
||||
import {
|
||||
typedUi,
|
||||
uiMessageReducer,
|
||||
} from "@langchain/langgraph-sdk/react-ui/server";
|
||||
import type { AIMessageChunk } from "@langchain/core/messages";
|
||||
|
||||
import type ComponentMap from "./ui";
|
||||
|
||||
const AgentState = Annotation.Root({
|
||||
...MessagesAnnotation.spec,
|
||||
ui: Annotation({ reducer: uiMessageReducer, default: () => [] }),
|
||||
});
|
||||
|
||||
async function writerNode(
|
||||
state: typeof AgentState.State,
|
||||
config: LangGraphRunnableConfig
|
||||
): Promise<typeof AgentState.Update> {
|
||||
const ui = typedUi<typeof ComponentMap>(config);
|
||||
|
||||
const model = new ChatAnthropic({ model: "claude-3-5-sonnet-latest" });
|
||||
const message = await model
|
||||
.bindTools(
|
||||
[
|
||||
{
|
||||
name: "create_text_document",
|
||||
description: "Prepare a document heading for the user.",
|
||||
schema: z.object({ title: z.string() }),
|
||||
},
|
||||
],
|
||||
{ tool_choice: { type: "tool", name: "create_text_document" } }
|
||||
)
|
||||
.invoke(state.messages);
|
||||
|
||||
type ToolCall = { name: "create_text_document"; args: { title: string } };
|
||||
const toolCall = message.tool_calls?.find(
|
||||
(tool): tool is ToolCall => tool.name === "create_text_document"
|
||||
);
|
||||
|
||||
if (toolCall) {
|
||||
const { id, name } = ui.push(
|
||||
{ name: "writer", props: { title: toolCall.args.title } },
|
||||
{ message }
|
||||
);
|
||||
|
||||
const contentStream = await model
|
||||
// We're already streaming the LLM response to the client through UI messages
|
||||
// so we don't need to stream it again to the `messages` stream mode.
|
||||
.withConfig({ tags: ["nostream"] })
|
||||
.stream(`Create a short poem with the topic: ${message.text}`);
|
||||
|
||||
let content: AIMessageChunk | undefined;
|
||||
for await (const chunk of contentStream) {
|
||||
content = content?.concat(chunk) ?? chunk;
|
||||
|
||||
ui.push(
|
||||
{ id, name, props: { content: content?.text } },
|
||||
// Use `merge: true` to merge props with the existing UI message
|
||||
{ message, merge: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { messages: [message] };
|
||||
}
|
||||
```
|
||||
|
||||
=== "`ui.tsx`"
|
||||
|
||||
```tsx
|
||||
function WriterComponent(props: { title: string; content?: string }) {
|
||||
return (
|
||||
<article>
|
||||
<h2>{props.title}</h2>
|
||||
<p style={{ whiteSpace: "pre-wrap" }}>{props.content}</p>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
weather: WriterComponent,
|
||||
};
|
||||
```
|
||||
|
||||
### Remove UI messages from state
|
||||
|
||||
Similar to how messages can be removed from the state by appending a RemoveMessage you can remove an UI message from the state by calling `remove_ui_message` / `ui.delete` with the ID of the UI message.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph.graph.ui import push_ui_message, delete_ui_message
|
||||
|
||||
# push message
|
||||
message = push_ui_message("weather", {"city": "London"})
|
||||
|
||||
# remove said message
|
||||
delete_ui_message(message["id"])
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```tsx
|
||||
// push message
|
||||
const message = ui.push({ name: "weather", props: { city: "London" } });
|
||||
|
||||
// remove said message
|
||||
ui.delete(message.id);
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
- [JS/TS SDK Reference](../reference/sdk/js_ts_sdk_ref.md)
|
||||
@@ -1,238 +0,0 @@
|
||||
# Time travel using Server API
|
||||
|
||||
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.
|
||||
|
||||
To time travel using the LangGraph Server API (via the LangGraph SDK):
|
||||
|
||||
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`.
|
||||
Alternatively, set a [breakpoint](./human_in_the_loop_breakpoint.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. **(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`.
|
||||
|
||||
## Use time travel in a workflow
|
||||
|
||||
??? example "Example graph"
|
||||
|
||||
```python
|
||||
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
|
||||
builder = StateGraph(State)
|
||||
|
||||
# Add nodes
|
||||
builder.add_node("generate_topic", generate_topic)
|
||||
builder.add_node("write_joke", write_joke)
|
||||
|
||||
# Add edges to connect nodes
|
||||
builder.add_edge(START, "generate_topic")
|
||||
builder.add_edge("generate_topic", "write_joke")
|
||||
|
||||
# Compile
|
||||
graph = builder.compile()
|
||||
```
|
||||
|
||||
### 1. Run the graph
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# Run the graph
|
||||
result = await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={}
|
||||
)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// Run the graph
|
||||
const result = await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{ input: {}}
|
||||
);
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Run the graph:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {}
|
||||
}"
|
||||
```
|
||||
|
||||
### 2. Identify a checkpoint
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# The states are returned in reverse chronological order.
|
||||
states = await client.threads.get_history(thread_id)
|
||||
selected_state = states[1]
|
||||
print(selected_state)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
// The states are returned in reverse chronological order.
|
||||
const states = await client.threads.getHistory(threadID);
|
||||
const selectedState = states[1];
|
||||
console.log(selectedState);
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/history \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
### 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"
|
||||
|
||||
```python
|
||||
new_config = await client.threads.update_state(
|
||||
thread_id,
|
||||
{"topic": "chickens"},
|
||||
# highlight-next-line
|
||||
checkpoint_id=selected_state["checkpoint_id"]
|
||||
)
|
||||
print(new_config)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const newConfig = await client.threads.updateState(
|
||||
threadID,
|
||||
{
|
||||
values: { "topic": "chickens" },
|
||||
checkpointId: selectedState["checkpoint_id"]
|
||||
}
|
||||
);
|
||||
console.log(newConfig);
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"checkpoint_id\": <CHECKPOINT_ID>,
|
||||
\"values\": {\"topic\": \"chickens\"}
|
||||
}"
|
||||
```
|
||||
|
||||
### 4. Resume execution from the checkpoint
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
await client.runs.wait(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
# highlight-next-line
|
||||
input=None,
|
||||
# highlight-next-line
|
||||
checkpoint_id=new_config["checkpoint_id"]
|
||||
)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
await client.runs.wait(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
// highlight-next-line
|
||||
input: null,
|
||||
// highlight-next-line
|
||||
checkpointId: newConfig["checkpoint_id"]
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"checkpoint_id\": <CHECKPOINT_ID>
|
||||
}"
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
- [**LangGraph time travel guide**](../../how-tos/human_in_the_loop/time-travel.md): learn more about using time travel in LangGraph.
|
||||
|
Before Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 430 KiB |
|
Before Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 367 KiB |
|
Before Width: | Height: | Size: 392 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 332 KiB |
|
Before Width: | Height: | Size: 356 KiB |
|
Before Width: | Height: | Size: 380 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 721 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 578 KiB |
|
Before Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 267 KiB |
|
Before Width: | Height: | Size: 355 KiB |
@@ -1,253 +0,0 @@
|
||||
# How to use the interrupt option
|
||||
|
||||
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
|
||||
|
||||
The guide covers the `interrupt` option for double texting, which interrupts the prior run of the graph and starts a new one with the double-text. This option does not delete the first run, but rather keeps it in the database but sets its status to `interrupted`. Below is a quick example of using the `interrupt` option.
|
||||
|
||||
## Setup
|
||||
|
||||
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
function prettyPrint(m) {
|
||||
const padded = " " + m['type'] + " ";
|
||||
const sepLen = Math.floor((80 - padded.length) / 2);
|
||||
const sep = "=".repeat(sepLen);
|
||||
const secondSep = sep + (padded.length % 2 ? "=" : "");
|
||||
|
||||
console.log(`${sep}${padded}${secondSep}`);
|
||||
console.log("\n\n");
|
||||
console.log(m.content);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
# PLACE THIS IN A FILE CALLED pretty_print.sh
|
||||
pretty_print() {
|
||||
local type="$1"
|
||||
local content="$2"
|
||||
local padded=" $type "
|
||||
local total_width=80
|
||||
local sep_len=$(( (total_width - ${#padded}) / 2 ))
|
||||
local sep=$(printf '=%.0s' $(eval "echo {1.."${sep_len}"}"))
|
||||
local second_sep=$sep
|
||||
if (( (total_width - ${#padded}) % 2 )); then
|
||||
second_sep="${second_sep}="
|
||||
fi
|
||||
|
||||
echo "${sep}${padded}${second_sep}"
|
||||
echo
|
||||
echo "$content"
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's import our required packages and instantiate our client, assistant, and thread.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from langchain_core.messages import convert_to_messages
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantId = "agent";
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Create runs
|
||||
|
||||
Now we can start our two runs and join the second one until it has completed:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# the first run will be interrupted
|
||||
interrupted_run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
|
||||
)
|
||||
# sleep a bit to get partial outputs from the first run
|
||||
await asyncio.sleep(2)
|
||||
run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
|
||||
multitask_strategy="interrupt",
|
||||
)
|
||||
# wait until the second run completes
|
||||
await client.runs.join(thread["thread_id"], run["run_id"])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// the first run will be interrupted
|
||||
let interruptedRun = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{ input: { messages: [{ role: "human", content: "what's the weather in sf?" }] } }
|
||||
);
|
||||
// sleep a bit to get partial outputs from the first run
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
let run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: { messages: [{ role: "human", content: "what's the weather in nyc?" }] },
|
||||
multitaskStrategy: "interrupt"
|
||||
}
|
||||
);
|
||||
|
||||
// wait until the second run completes
|
||||
await client.runs.join(thread["thread_id"], run["run_id"]);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in sf?\"}]},
|
||||
}" && sleep 2 && curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in nyc?\"}]},
|
||||
\"multitask_strategy\": \"interrupt\"
|
||||
}" && curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/join
|
||||
```
|
||||
|
||||
## View run results
|
||||
|
||||
We can see that the thread has partial data from the first run + data from the second run
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
state = await client.threads.get_state(thread["thread_id"])
|
||||
|
||||
for m in convert_to_messages(state["values"]["messages"]):
|
||||
m.pretty_print()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const state = await client.threads.getState(thread["thread_id"]);
|
||||
|
||||
for (const m of state['values']['messages']) {
|
||||
prettyPrint(m);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
source pretty_print.sh && curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state | \
|
||||
jq -c '.values.messages[]' | while read -r element; do
|
||||
type=$(echo "$element" | jq -r '.type')
|
||||
content=$(echo "$element" | jq -r '.content | if type == "array" then tostring else . end')
|
||||
pretty_print "$type" "$content"
|
||||
done
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
================================ Human Message =================================
|
||||
|
||||
what's the weather in sf?
|
||||
================================== Ai Message ==================================
|
||||
|
||||
[{'id': 'toolu_01MjNtVJwEcpujRGrf3x6Pih', 'input': {'query': 'weather in san francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
tavily_search_results_json (toolu_01MjNtVJwEcpujRGrf3x6Pih)
|
||||
Call ID: toolu_01MjNtVJwEcpujRGrf3x6Pih
|
||||
Args:
|
||||
query: weather in san francisco
|
||||
================================= Tool Message =================================
|
||||
Name: tavily_search_results_json
|
||||
|
||||
[{"url": "https://www.wunderground.com/hourly/us/ca/san-francisco/KCASANFR2002/date/2024-6-18", "content": "High 64F. Winds W at 10 to 20 mph. A few clouds from time to time. Low 49F. Winds W at 10 to 20 mph. Temp. San Francisco Weather Forecasts. Weather Underground provides local & long-range weather ..."}]
|
||||
================================ Human Message =================================
|
||||
|
||||
what's the weather in nyc?
|
||||
================================== Ai Message ==================================
|
||||
|
||||
[{'id': 'toolu_01KtE1m1ifPLQAx4fQLyZL9Q', 'input': {'query': 'weather in new york city'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
tavily_search_results_json (toolu_01KtE1m1ifPLQAx4fQLyZL9Q)
|
||||
Call ID: toolu_01KtE1m1ifPLQAx4fQLyZL9Q
|
||||
Args:
|
||||
query: weather in new york city
|
||||
================================= Tool Message =================================
|
||||
Name: tavily_search_results_json
|
||||
|
||||
[{"url": "https://www.accuweather.com/en/us/new-york/10021/june-weather/349727", "content": "Get the monthly weather forecast for New York, NY, including daily high/low, historical averages, to help you plan ahead."}]
|
||||
================================== Ai Message ==================================
|
||||
|
||||
The search results provide weather forecasts and information for New York City. Based on the top result from AccuWeather, here are some key details about the weather in NYC:
|
||||
|
||||
- This is a monthly weather forecast for New York City for the month of June.
|
||||
- It includes daily high and low temperatures to help plan ahead.
|
||||
- Historical averages for June in NYC are also provided as a reference point.
|
||||
- More detailed daily or hourly forecasts with precipitation chances, humidity, wind, etc. can be found by visiting the AccuWeather page.
|
||||
|
||||
So in summary, the search provides a convenient overview of the expected weather conditions in New York City over the next month to give you an idea of what to prepare for if traveling or making plans there. Let me know if you need any other details!
|
||||
|
||||
|
||||
Verify that the original, interrupted run was interrupted
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.runs.get(thread["thread_id"], interrupted_run["run_id"]))["status"])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.runs.get(thread['thread_id'], interruptedRun["run_id"]))["status"])
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
'interrupted'
|
||||
```
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# Run application
|
||||
|
||||
!!!info "Prerequisites"
|
||||
- [Running agents](../../agents/run_agents.md#running-agents)
|
||||
|
||||
This guide shows how to submit a [run](../../concepts/assistants.md#execution) to your application.
|
||||
|
||||
## Graph mode
|
||||
|
||||
### Specify input
|
||||
First define the input to your graph with in the "Input" section on the left side of the page, below the graph interface.
|
||||
|
||||
Studio will attempt to render a form for your input based on the graph's defined [state schema](../../concepts/low_level.md/#schema). To disable this, click the "View Raw" button, which will present you with a JSON editor.
|
||||
|
||||
Click the up/down arrows at the top of the "Input" section to toggle through and use previously submitted inputs.
|
||||
|
||||
### Run settings
|
||||
|
||||
#### Assistant
|
||||
|
||||
To specify the [assistant](../../concepts/assistants.md) that is used for the run click the settings button in the bottom left corner. If an assistant is currently selected the button will also list the assistant name. If no assistant is selected it will say "Manage Assistants".
|
||||
|
||||
Select the assistant to run and click the "Active" toggle at the top of the modal to activate it. [See here](./studio/manage_assistants.md) for more information on managing assistants.
|
||||
|
||||
#### Streaming
|
||||
Click the dropdown next to "Submit" and click the toggle to enable/disable streaming.
|
||||
|
||||
#### Breakpoints
|
||||
To run your graph with breakpoints, click the "Interrupt" button. Select a node and whether to pause before and/or after that node has executed. Click "Continue" in the thread log to resume execution.
|
||||
|
||||
|
||||
For more information on breakpoints see [here](../../concepts/human_in_the_loop.md).
|
||||
|
||||
### Submit run
|
||||
|
||||
To submit the run with the specified input and run settings, click the "Submit" button. This will add a [run](../../concepts/assistants.md#execution) to the existing selected [thread](../../concepts/persistence.md#threads). If no thread is currently selected, a new one will be created.
|
||||
|
||||
To cancel the ongoing run, click the "Cancel" button.
|
||||
|
||||
|
||||
## Chat mode
|
||||
Specify the input to your chat application in the bottom of the conversation panel. Click the "Send message" button to submit the input as a Human message and have the response streamed back.
|
||||
|
||||
To cancel the ongoing run, click the "Cancel" button. Click the "Show tool calls" toggle to hide/show tool calls in the conversation.
|
||||
|
||||
## Learn more
|
||||
|
||||
To run your application from a specific checkpoint in an existing thread, see [this guide](./threads_studio.md#edit-thread-history).
|
||||
@@ -1,134 +0,0 @@
|
||||
# Iterate on prompts
|
||||
|
||||
## Overview
|
||||
|
||||
LangGraph Studio supports two methods for modifying prompts in your graph: direct node editing and the LangSmith Playground interface.
|
||||
|
||||
## Direct Node Editing
|
||||
|
||||
Studio allows you to edit prompts used inside individual nodes, directly from the graph interface.
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [Assistants overview](../../concepts/assistants.md)
|
||||
|
||||
### Graph Configuration
|
||||
|
||||
Define your [configuration](https://langchain-ai.github.io/langgraph/how-tos/configuration/) to specify prompt fields and their associated nodes using `langgraph_nodes` and `langgraph_type` keys.
|
||||
|
||||
#### Configuration Reference
|
||||
|
||||
##### `langgraph_nodes`
|
||||
|
||||
- **Description**: Specifies which nodes of the graph a configuration field is associated with.
|
||||
- **Value Type**: Array of strings, where each string is the name of a node in your graph.
|
||||
- **Usage Context**: Include in the `json_schema_extra` dictionary for Pydantic models or the `metadata["json_schema_extra"]` dictionary for dataclasses.
|
||||
- **Example**:
|
||||
```python
|
||||
system_prompt: str = Field(
|
||||
default="You are a helpful AI assistant.",
|
||||
json_schema_extra={"langgraph_nodes": ["call_model", "other_node"]},
|
||||
)
|
||||
```
|
||||
|
||||
##### `langgraph_type`
|
||||
|
||||
- **Description**: Specifies the type of configuration field, which determines how it's handled in the UI.
|
||||
- **Value Type**: String
|
||||
- **Supported Values**:
|
||||
- `"prompt"`: Indicates the field contains prompt text that should be treated specially in the UI.
|
||||
- **Usage Context**: Include in the `json_schema_extra` dictionary for Pydantic models or the `metadata["json_schema_extra"]` dictionary for dataclasses.
|
||||
- **Example**:
|
||||
```python
|
||||
system_prompt: str = Field(
|
||||
default="You are a helpful AI assistant.",
|
||||
json_schema_extra={
|
||||
"langgraph_nodes": ["call_model"],
|
||||
"langgraph_type": "prompt",
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
#### Example Configuration
|
||||
|
||||
```python
|
||||
## Using Pydantic
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Annotated, Literal
|
||||
|
||||
class Configuration(BaseModel):
|
||||
"""The configuration for the agent."""
|
||||
|
||||
system_prompt: str = Field(
|
||||
default="You are a helpful AI assistant.",
|
||||
description="The system prompt to use for the agent's interactions. "
|
||||
"This prompt sets the context and behavior for the agent.",
|
||||
json_schema_extra={
|
||||
"langgraph_nodes": ["call_model"],
|
||||
"langgraph_type": "prompt",
|
||||
},
|
||||
)
|
||||
|
||||
model: Annotated[
|
||||
Literal[
|
||||
"anthropic/claude-3-7-sonnet-latest",
|
||||
"anthropic/claude-3-5-haiku-latest",
|
||||
"openai/o1",
|
||||
"openai/gpt-4o-mini",
|
||||
"openai/o1-mini",
|
||||
"openai/o3-mini",
|
||||
],
|
||||
{"__template_metadata__": {"kind": "llm"}},
|
||||
] = Field(
|
||||
default="openai/gpt-4o-mini",
|
||||
description="The name of the language model to use for the agent's main interactions. "
|
||||
"Should be in the form: provider/model-name.",
|
||||
json_schema_extra={"langgraph_nodes": ["call_model"]},
|
||||
)
|
||||
|
||||
## Using Dataclasses
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Configuration:
|
||||
"""The configuration for the agent."""
|
||||
|
||||
system_prompt: str = field(
|
||||
default="You are a helpful AI assistant.",
|
||||
metadata={
|
||||
"description": "The system prompt to use for the agent's interactions. "
|
||||
"This prompt sets the context and behavior for the agent.",
|
||||
"json_schema_extra": {"langgraph_nodes": ["call_model"]},
|
||||
},
|
||||
)
|
||||
|
||||
model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
|
||||
default="anthropic/claude-3-5-sonnet-20240620",
|
||||
metadata={
|
||||
"description": "The name of the language model to use for the agent's main interactions. "
|
||||
"Should be in the form: provider/model-name.",
|
||||
"json_schema_extra": {"langgraph_nodes": ["call_model"]},
|
||||
},
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### Editing prompts in UI
|
||||
|
||||
1. Locate the gear icon on nodes with associated configuration fields
|
||||
2. Click to open the configuration modal
|
||||
3. Edit the values
|
||||
4. Save to update the current assistant version or create a new one
|
||||
|
||||
## LangSmith Playground
|
||||
|
||||
The [LangSmith Playground](https://
|
||||
docs.smith.langchain.com/prompt_engineering/how_to_guides#playground) interface allows testing individual LLM calls without running the full graph:
|
||||
|
||||
1. Select a thread
|
||||
2. Click "View LLM Runs" on a node. This lists all the LLM calls (if any) made inside the node.
|
||||
3. Select an LLM run to open in Playground
|
||||
4. Modify prompts and test different model and tool settings
|
||||
5. Copy updated prompts back to your graph
|
||||
|
||||
For advanced Playground features, click the expand button in the top right corner.
|
||||
@@ -1,229 +0,0 @@
|
||||
# Reject
|
||||
|
||||
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
|
||||
|
||||
The guide covers the `reject` option for double texting, which rejects the new run of the graph by throwing an error and continues with the original run until completion. Below is a quick example of using the `reject` option.
|
||||
|
||||
## Setup
|
||||
|
||||
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
function prettyPrint(m) {
|
||||
const padded = " " + m['type'] + " ";
|
||||
const sepLen = Math.floor((80 - padded.length) / 2);
|
||||
const sep = "=".repeat(sepLen);
|
||||
const secondSep = sep + (padded.length % 2 ? "=" : "");
|
||||
|
||||
console.log(`${sep}${padded}${secondSep}`);
|
||||
console.log("\n\n");
|
||||
console.log(m.content);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
# PLACE THIS IN A FILE CALLED pretty_print.sh
|
||||
pretty_print() {
|
||||
local type="$1"
|
||||
local content="$2"
|
||||
local padded=" $type "
|
||||
local total_width=80
|
||||
local sep_len=$(( (total_width - ${#padded}) / 2 ))
|
||||
local sep=$(printf '=%.0s' $(eval "echo {1.."${sep_len}"}"))
|
||||
local second_sep=$sep
|
||||
if (( (total_width - ${#padded}) % 2 )); then
|
||||
second_sep="${second_sep}="
|
||||
fi
|
||||
|
||||
echo "${sep}${padded}${second_sep}"
|
||||
echo
|
||||
echo "$content"
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's import our required packages and instantiate our client, assistant, and thread.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
import httpx
|
||||
from langchain_core.messages import convert_to_messages
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantId = "agent";
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Create runs
|
||||
|
||||
Now we can run a thread and try to run a second one with the "reject" option, which should fail since we have already started a run:
|
||||
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
|
||||
)
|
||||
try:
|
||||
await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={
|
||||
"messages": [{"role": "user", "content": "what's the weather in nyc?"}]
|
||||
},
|
||||
multitask_strategy="reject",
|
||||
)
|
||||
except httpx.HTTPStatusError as e:
|
||||
print("Failed to start concurrent run", e)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
|
||||
);
|
||||
|
||||
try {
|
||||
await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{
|
||||
input: {"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
|
||||
multitask_strategy:"reject"
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Failed to start concurrent run", e);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in sf?\"}]},
|
||||
}" && curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in nyc?\"}]},
|
||||
\"multitask_strategy\": \"reject\"
|
||||
}" || { echo "Failed to start concurrent run"; echo "Error: $?" >&2; }
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Failed to start concurrent run Client error '409 Conflict' for url 'http://localhost:8123/threads/f9e7088b-8028-4e5c-88d2-9cc9a2870e50/runs'
|
||||
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409
|
||||
|
||||
## View run results
|
||||
|
||||
We can verify that the original thread finished executing:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# wait until the original run completes
|
||||
await client.runs.join(thread["thread_id"], run["run_id"])
|
||||
|
||||
state = await client.threads.get_state(thread["thread_id"])
|
||||
|
||||
for m in convert_to_messages(state["values"]["messages"]):
|
||||
m.pretty_print()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
await client.runs.join(thread["thread_id"], run["run_id"]);
|
||||
|
||||
const state = await client.threads.getState(thread["thread_id"]);
|
||||
|
||||
for (const m of state["values"]["messages"]) {
|
||||
prettyPrint(m);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
source pretty_print.sh && curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/join && \
|
||||
curl --request GET --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state | \
|
||||
jq -c '.values.messages[]' | while read -r element; do
|
||||
type=$(echo "$element" | jq -r '.type')
|
||||
content=$(echo "$element" | jq -r '.content | if type == "array" then tostring else . end')
|
||||
pretty_print "$type" "$content"
|
||||
done
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
================================ Human Message =================================
|
||||
|
||||
what's the weather in sf?
|
||||
================================== Ai Message ==================================
|
||||
|
||||
[{'id': 'toolu_01CyewEifV2Kmi7EFKHbMDr1', 'input': {'query': 'weather in san francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
tavily_search_results_json (toolu_01CyewEifV2Kmi7EFKHbMDr1)
|
||||
Call ID: toolu_01CyewEifV2Kmi7EFKHbMDr1
|
||||
Args:
|
||||
query: weather in san francisco
|
||||
================================= Tool Message =================================
|
||||
Name: tavily_search_results_json
|
||||
|
||||
[{"url": "https://www.accuweather.com/en/us/san-francisco/94103/june-weather/347629", "content": "Get the monthly weather forecast for San Francisco, CA, including daily high/low, historical averages, to help you plan ahead."}]
|
||||
================================== Ai Message ==================================
|
||||
|
||||
According to the search results from Tavily, the current weather in San Francisco is:
|
||||
|
||||
The average high temperature in San Francisco in June is around 65°F (18°C), with average lows around 54°F (12°C). June tends to be one of the cooler and foggier months in San Francisco due to the marine layer of fog that often blankets the city during the summer months.
|
||||
|
||||
Some key points about the typical June weather in San Francisco:
|
||||
|
||||
- Mild temperatures with highs in the 60s F and lows in the 50s F
|
||||
- Foggy mornings that often burn off to sunny afternoons
|
||||
- Little to no rainfall, as June falls in the dry season
|
||||
- Breezy conditions, with winds off the Pacific Ocean
|
||||
- Layers are recommended for changing weather conditions
|
||||
|
||||
So in summary, you can expect mild, foggy mornings giving way to sunny but cool afternoons in San Francisco this time of year. The marine layer keeps temperatures moderate compared to other parts of California in June.
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
# How to use the Rollback option
|
||||
|
||||
|
||||
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../../concepts/double_texting.md).
|
||||
|
||||
The guide covers the `rollback` option for double texting, which interrupts the prior run of the graph and starts a new one with the double-text. This option is very similar to the `interrupt` option, but in this case the first run is completely deleted from the database and cannot be restarted. Below is a quick example of using the `rollback` option.
|
||||
|
||||
## Setup
|
||||
|
||||
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
function prettyPrint(m) {
|
||||
const padded = " " + m['type'] + " ";
|
||||
const sepLen = Math.floor((80 - padded.length) / 2);
|
||||
const sep = "=".repeat(sepLen);
|
||||
const secondSep = sep + (padded.length % 2 ? "=" : "");
|
||||
|
||||
console.log(`${sep}${padded}${secondSep}`);
|
||||
console.log("\n\n");
|
||||
console.log(m.content);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
# PLACE THIS IN A FILE CALLED pretty_print.sh
|
||||
pretty_print() {
|
||||
local type="$1"
|
||||
local content="$2"
|
||||
local padded=" $type "
|
||||
local total_width=80
|
||||
local sep_len=$(( (total_width - ${#padded}) / 2 ))
|
||||
local sep=$(printf '=%.0s' $(eval "echo {1.."${sep_len}"}"))
|
||||
local second_sep=$sep
|
||||
if (( (total_width - ${#padded}) % 2 )); then
|
||||
second_sep="${second_sep}="
|
||||
fi
|
||||
|
||||
echo "${sep}${padded}${second_sep}"
|
||||
echo
|
||||
echo "$content"
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's import our required packages and instantiate our client, assistant, and thread.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
import httpx
|
||||
from langchain_core.messages import convert_to_messages
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantId = "agent";
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Create runs
|
||||
|
||||
Now let's run a thread with the multitask parameter set to "rollback":
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
# the first run will be rolled back
|
||||
rolled_back_run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
|
||||
)
|
||||
run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
|
||||
multitask_strategy="rollback",
|
||||
)
|
||||
# wait until the second run completes
|
||||
await client.runs.join(thread["thread_id"], run["run_id"])
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
// the first run will be interrupted
|
||||
let rolledBackRun = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistantId,
|
||||
{ input: { messages: [{ role: "human", content: "what's the weather in sf?" }] } }
|
||||
);
|
||||
|
||||
let run = await client.runs.create(
|
||||
thread["thread_id"],
|
||||
assistant_id,
|
||||
{
|
||||
input: { messages: [{ role: "human", content: "what's the weather in nyc?" }] },
|
||||
multitaskStrategy: "rollback"
|
||||
}
|
||||
);
|
||||
|
||||
// wait until the second run completes
|
||||
await client.runs.join(thread["thread_id"], run["run_id"]);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in sf?\"}]},
|
||||
}" && curl --request POST \
|
||||
--url <DEPLOY<ENT_URL>>/threads/<THREAD_ID>/runs \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what\'s the weather in nyc?\"}]},
|
||||
\"multitask_strategy\": \"rollback\"
|
||||
}" && curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/join
|
||||
```
|
||||
|
||||
## View run results
|
||||
|
||||
We can see that the thread has data only from the second run
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
state = await client.threads.get_state(thread["thread_id"])
|
||||
|
||||
for m in convert_to_messages(state["values"]["messages"]):
|
||||
m.pretty_print()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const state = await client.threads.getState(thread["thread_id"]);
|
||||
|
||||
for (const m of state['values']['messages']) {
|
||||
prettyPrint(m);
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
source pretty_print.sh && curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state | \
|
||||
jq -c '.values.messages[]' | while read -r element; do
|
||||
type=$(echo "$element" | jq -r '.type')
|
||||
content=$(echo "$element" | jq -r '.content | if type == "array" then tostring else . end')
|
||||
pretty_print "$type" "$content"
|
||||
done
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
================================ Human Message =================================
|
||||
|
||||
what's the weather in nyc?
|
||||
================================== Ai Message ==================================
|
||||
|
||||
[{'id': 'toolu_01JzPqefao1gxwajHQ3Yh3JD', 'input': {'query': 'weather in nyc'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
|
||||
Tool Calls:
|
||||
tavily_search_results_json (toolu_01JzPqefao1gxwajHQ3Yh3JD)
|
||||
Call ID: toolu_01JzPqefao1gxwajHQ3Yh3JD
|
||||
Args:
|
||||
query: weather in nyc
|
||||
================================= Tool Message =================================
|
||||
Name: tavily_search_results_json
|
||||
|
||||
[{"url": "https://www.weatherapi.com/", "content": "{'location': {'name': 'New York', 'region': 'New York', 'country': 'United States of America', 'lat': 40.71, 'lon': -74.01, 'tz_id': 'America/New_York', 'localtime_epoch': 1718734479, 'localtime': '2024-06-18 14:14'}, 'current': {'last_updated_epoch': 1718733600, 'last_updated': '2024-06-18 14:00', 'temp_c': 29.4, 'temp_f': 84.9, 'is_day': 1, 'condition': {'text': 'Sunny', 'icon': '//cdn.weatherapi.com/weather/64x64/day/113.png', 'code': 1000}, 'wind_mph': 2.2, 'wind_kph': 3.6, 'wind_degree': 158, 'wind_dir': 'SSE', 'pressure_mb': 1025.0, 'pressure_in': 30.26, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 63, 'cloud': 0, 'feelslike_c': 31.3, 'feelslike_f': 88.3, 'windchill_c': 28.3, 'windchill_f': 82.9, 'heatindex_c': 29.6, 'heatindex_f': 85.3, 'dewpoint_c': 18.4, 'dewpoint_f': 65.2, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 7.0, 'gust_mph': 16.5, 'gust_kph': 26.5}}"}]
|
||||
================================== Ai Message ==================================
|
||||
|
||||
The weather API results show that the current weather in New York City is sunny with a temperature of around 85°F (29°C). The wind is light at around 2-3 mph from the south-southeast. Overall it looks like a nice sunny summer day in NYC.
|
||||
|
||||
|
||||
Verify that the original, rolled back run was deleted
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
try:
|
||||
await client.runs.get(thread["thread_id"], rolled_back_run["run_id"])
|
||||
except httpx.HTTPStatusError as _:
|
||||
print("Original run was correctly deleted")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
try {
|
||||
await client.runs.get(thread["thread_id"], rolledBackRun["run_id"]);
|
||||
} catch (e) {
|
||||
console.log("Original run was correctly deleted");
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Original run was correctly deleted
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
# How to run multiple agents on the same thread
|
||||
|
||||
In LangGraph Platform, a thread is not explicitly associated with a particular agent.
|
||||
This means that you can run multiple agents on the same thread, which allows a different agent to continue from an initial agent's progress.
|
||||
|
||||
In this example, we will create two agents and then call them both on the same thread.
|
||||
You'll see that the second agent will respond using information from the [checkpoint](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer-state) generated in the thread by the first agent as context.
|
||||
|
||||
## Setup
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
openai_assistant = await client.assistants.create(
|
||||
graph_id="agent", config={"configurable": {"model_name": "openai"}}
|
||||
)
|
||||
|
||||
# There should always be a default assistant with no configuration
|
||||
assistants = await client.assistants.search()
|
||||
default_assistant = [a for a in assistants if not a["config"]][0]
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
const openAIAssistant = await client.assistants.create(
|
||||
{ graphId: "agent", config: {"configurable": {"model_name": "openai"}}}
|
||||
);
|
||||
|
||||
const assistants = await client.assistants.search();
|
||||
const defaultAssistant = assistants.find(a => !a.config);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"graph_id": "agent",
|
||||
"config": { "configurable": { "model_name": "openai" } }
|
||||
}' && \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}' | jq -c 'map(select(.config == null or .config == {})) | .[0]'
|
||||
```
|
||||
|
||||
We can see that these agents are different:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(openai_assistant)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(openAIAssistant);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/assistants/<OPENAI_ASSISTANT_ID>
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"assistant_id": "db87f39d-b2b1-4da8-ac65-cf81beb3c766",
|
||||
"graph_id": "agent",
|
||||
"created_at": "2024-08-30T21:18:51.850581+00:00",
|
||||
"updated_at": "2024-08-30T21:18:51.850581+00:00",
|
||||
"config": {
|
||||
"configurable": {
|
||||
"model_name": "openai"
|
||||
}
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(default_assistant)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(defaultAssistant);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/assistants/<DEFAULT_ASSISTANT_ID>
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"assistant_id": "fe096781-5601-53d2-b2f6-0d3403f7e9ca",
|
||||
"graph_id": "agent",
|
||||
"created_at": "2024-08-08T22:45:24.562906+00:00",
|
||||
"updated_at": "2024-08-08T22:45:24.562906+00:00",
|
||||
"config": {},
|
||||
"metadata": {
|
||||
"created_by": "system"
|
||||
}
|
||||
}
|
||||
|
||||
## Run assistants on thread
|
||||
|
||||
### Run OpenAI assistant
|
||||
|
||||
We can now run the OpenAI assistant on the thread first.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
thread = await client.threads.create()
|
||||
input = {"messages": [{"role": "user", "content": "who made you?"}]}
|
||||
async for event in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
openai_assistant["assistant_id"],
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
print(f"Receiving event of type: {event.event}")
|
||||
print(event.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const thread = await client.threads.create();
|
||||
let input = {"messages": [{"role": "user", "content": "who made you?"}]}
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
openAIAssistant["assistant_id"],
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const event of streamResponse) {
|
||||
console.log(`Receiving event of type: ${event.event}`);
|
||||
console.log(event.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
thread_id=$(curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}' | jq -r '.thread_id') && \
|
||||
curl --request POST \
|
||||
--url "<DEPLOYMENT_URL>/threads/${thread_id}/runs/stream" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <OPENAI_ASSISTANT_ID>,
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "who made you?"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stream_mode": [
|
||||
"updates"
|
||||
]
|
||||
}' | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Receiving event of type: metadata
|
||||
{'run_id': '1ef671c5-fb83-6e70-b698-44dba2d9213e'}
|
||||
|
||||
|
||||
Receiving event of type: updates
|
||||
{'agent': {'messages': [{'content': 'I was created by OpenAI, a research organization focused on developing and advancing artificial intelligence technology.', 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-f5735b86-b80d-4c71-8dc3-4782b5a9c7c8', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
|
||||
### Run default assistant
|
||||
|
||||
Now, we can run it on the default assistant and see that this second assistant is aware of the initial question, and can answer the question, "and you?":
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {"messages": [{"role": "user", "content": "and you?"}]}
|
||||
async for event in client.runs.stream(
|
||||
thread["thread_id"],
|
||||
default_assistant["assistant_id"],
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
print(f"Receiving event of type: {event.event}")
|
||||
print(event.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
let input = {"messages": [{"role": "user", "content": "and you?"}]}
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
defaultAssistant["assistant_id"],
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const event of streamResponse) {
|
||||
console.log(`Receiving event of type: ${event.event}`);
|
||||
console.log(event.data);
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <DEFAULT_ASSISTANT_ID>,
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "and you?"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stream_mode": [
|
||||
"updates"
|
||||
]
|
||||
}' | \
|
||||
sed 's/\r$//' | \
|
||||
awk '
|
||||
/^event:/ {
|
||||
if (data_content != "") {
|
||||
print data_content "\n"
|
||||
}
|
||||
sub(/^event: /, "Receiving event of type: ", $0)
|
||||
printf "%s...\n", $0
|
||||
data_content = ""
|
||||
}
|
||||
/^data:/ {
|
||||
sub(/^data: /, "", $0)
|
||||
data_content = $0
|
||||
}
|
||||
END {
|
||||
if (data_content != "") {
|
||||
print data_content "\n\n"
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
Receiving event of type: metadata
|
||||
{'run_id': '1ef6722d-80b3-6fbb-9324-253796b1cd13'}
|
||||
|
||||
|
||||
Receiving event of type: updates
|
||||
{'agent': {'messages': [{'content': [{'text': 'I am an artificial intelligence created by Anthropic, not by OpenAI. I should not have stated that OpenAI created me, as that is incorrect. Anthropic is the company that developed and trained me using advanced language models and AI technology. I will be more careful about providing accurate information regarding my origins in the future.', 'type': 'text', 'index': 0}], 'additional_kwargs': {}, 'response_metadata': {'stop_reason': 'end_turn', 'stop_sequence': None}, 'type': 'ai', 'name': None, 'id': 'run-ebaacf62-9dd9-4165-9535-db432e4793ec', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 302, 'output_tokens': 72, 'total_tokens': 374}}]}}
|
||||
|
||||
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
# Stateless Runs
|
||||
|
||||
Most of the time, you provide a `thread_id` to your client when you run your graph in order to keep track of prior runs through the persistent state implemented in LangGraph Platform. However, if you don't need to persist the runs you don't need to use the built in persistent state and can create stateless runs.
|
||||
|
||||
## Setup
|
||||
|
||||
First, let's setup our client:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
# create thread
|
||||
thread = await client.threads.create()
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantId = "agent";
|
||||
// create thread
|
||||
const thread = await client.threads.create();
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}' | jq -c 'map(select(.config == null or .config == {})) | .[0].graph_id' && \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
## Stateless streaming
|
||||
|
||||
We can stream the results of a stateless run in an almost identical fashion to how we stream from a run with the state attribute, but instead of passing a value to the `thread_id` parameter, we pass `None`:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = {
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello! My name is Bagatur and I am 26 years old."}
|
||||
]
|
||||
}
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
# Don't pass in a thread_id and the stream will be stateless
|
||||
None,
|
||||
assistant_id,
|
||||
input=input,
|
||||
stream_mode="updates",
|
||||
):
|
||||
if chunk.data and "run_id" not in chunk.data:
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
let input = {
|
||||
messages: [
|
||||
{ role: "user", content: "Hello! My name is Bagatur and I am 26 years old." }
|
||||
]
|
||||
};
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
// Don't pass in a thread_id and the stream will be stateless
|
||||
null,
|
||||
assistantId,
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.data && !("run_id" in chunk.data)) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"Hello! My name is Bagatur and I am 26 years old.\"}]},
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}" | jq -c 'select(.data and (.data | has("run_id") | not)) | .data'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{'agent': {'messages': [{'content': "Hello Bagatur! It's nice to meet you. Thank you for introducing yourself and sharing your age. Is there anything specific you'd like to know or discuss? I'm here to help with any questions or topics you're interested in.", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-489ec573-1645-4ce2-a3b8-91b391d50a71', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
|
||||
|
||||
## Waiting for stateless results
|
||||
|
||||
In addition to streaming, you can also wait for a stateless result by using the `.wait` function like follows:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
stateless_run_result = await client.runs.wait(
|
||||
None,
|
||||
assistant_id,
|
||||
input=input,
|
||||
)
|
||||
print(stateless_run_result)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
let statelessRunResult = await client.runs.wait(
|
||||
null,
|
||||
assistantId,
|
||||
{ input: input }
|
||||
);
|
||||
console.log(statelessRunResult);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/runs/wait \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <ASSISTANT_IDD>,
|
||||
}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
'messages': [
|
||||
{
|
||||
'content': 'Hello! My name is Bagatur and I am 26 years old.',
|
||||
'additional_kwargs': {},
|
||||
'response_metadata': {},
|
||||
'type': 'human',
|
||||
'name': None,
|
||||
'id': '5e088543-62c2-43de-9d95-6086ad7f8b48',
|
||||
'example': False}
|
||||
,
|
||||
{
|
||||
'content': "Hello Bagatur! It's nice to meet you. Thank you for introducing yourself and sharing your age. Is there anything specific you'd like to know or discuss? I'm here to help with any questions or topics you'd like to explore.",
|
||||
'additional_kwargs': {},
|
||||
'response_metadata': {},
|
||||
'type': 'ai',
|
||||
'name': None,
|
||||
'id': 'run-d6361e8d-4d4c-45bd-ba47-39520257f773',
|
||||
'example': False,
|
||||
'tool_calls': [],
|
||||
'invalid_tool_calls': [],
|
||||
'usage_metadata': None
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,957 +0,0 @@
|
||||
# Streaming API
|
||||
|
||||
[LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) allows you to [stream outputs](../../concepts/streaming.md) from the LangGraph API server.
|
||||
|
||||
!!! note
|
||||
|
||||
LangGraph SDK and LangGraph Server are a part of [LangGraph Platform](../../concepts/langgraph_platform.md).
|
||||
|
||||
## Basic usage
|
||||
|
||||
Basic usage example:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>, api_key=<API_KEY>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# create a streaming run
|
||||
# highlight-next-line
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=inputs,
|
||||
stream_mode="updates"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <API_KEY> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// create a streaming run
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Create a streaming run:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <API_KEY>'
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <inputs>,
|
||||
\"stream_mode\": \"updates\"
|
||||
}"
|
||||
```
|
||||
|
||||
??? example "Extended example: streaming updates"
|
||||
|
||||
This is an example graph you can run in the LangGraph API server.
|
||||
See [LangGraph Platform quickstart](../quick_start.md) for more details.
|
||||
|
||||
```python
|
||||
# graph.py
|
||||
from typing import TypedDict
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
|
||||
class State(TypedDict):
|
||||
topic: str
|
||||
joke: str
|
||||
|
||||
def refine_topic(state: State):
|
||||
return {"topic": state["topic"] + " and cats"}
|
||||
|
||||
def generate_joke(state: State):
|
||||
return {"joke": f"This is a joke about {state['topic']}"}
|
||||
|
||||
graph = (
|
||||
StateGraph(State)
|
||||
.add_node(refine_topic)
|
||||
.add_node(generate_joke)
|
||||
.add_edge(START, "refine_topic")
|
||||
.add_edge("refine_topic", "generate_joke")
|
||||
.add_edge("generate_joke", END)
|
||||
.compile()
|
||||
)
|
||||
```
|
||||
|
||||
Once you have a running LangGraph API server, you can interact with it using
|
||||
[LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/)
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
# create a streaming run
|
||||
# highlight-next-line
|
||||
async for chunk in client.runs.stream( # (1)!
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"topic": "ice cream"},
|
||||
# highlight-next-line
|
||||
stream_mode="updates" # (2)!
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
1. The `client.runs.stream()` method returns an iterator that yields streamed outputs.
|
||||
2. Set `stream_mode="updates"` to stream only the updates to the graph state after each node. Other stream modes are also available. See [supported stream modes](#supported-stream-modes) for details.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// create a streaming run
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.stream( // (1)!
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { topic: "ice cream" },
|
||||
// highlight-next-line
|
||||
streamMode: "updates" // (2)!
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
1. The `client.runs.stream()` method returns an iterator that yields streamed outputs.
|
||||
2. Set `streamMode: "updates"` to stream only the updates to the graph state after each node. Other stream modes are also available. See [supported stream modes](#supported-stream-modes) for details.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Create a streaming run:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"topic\": \"ice cream\"},
|
||||
\"stream_mode\": \"updates\"
|
||||
}"
|
||||
```
|
||||
|
||||
```output
|
||||
{'run_id': '1f02c2b3-3cef-68de-b720-eec2a4a8e920', 'attempt': 1}
|
||||
{'refine_topic': {'topic': 'ice cream and cats'}}
|
||||
{'generate_joke': {'joke': 'This is a joke about ice cream and cats'}}
|
||||
```
|
||||
|
||||
|
||||
### Supported stream modes
|
||||
|
||||
| Mode | Description | LangGraph Library Method |
|
||||
|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
|
||||
| [`values`](#stream-graph-state) | Stream the full graph state after each [super-step](../../concepts/low_level.md#graphs). | `.stream()` / `.astream()` with [`stream_mode="values"`](../../how-tos/streaming.md#stream-graph-state) |
|
||||
| [`updates`](#stream-graph-state) | Streams the updates to the state after each step of the graph. If multiple updates are made in the same step (e.g., multiple nodes are run), those updates are streamed separately. | `.stream()` / `.astream()` with [`stream_mode="updates"`](../../how-tos/streaming.md#stream-graph-state) |
|
||||
| [`messages-tuple`](#messages) | Streams LLM tokens and metadata for the graph node where the LLM is invoked (useful for chat apps). | `.stream()` / `.astream()` with [`stream_mode="messages"`](../../how-tos/streaming.md#messages) |
|
||||
| [`debug`](#debug) | Streams as much information as possible throughout the execution of the graph. | `.stream()` / `.astream()` with [`stream_mode="debug"`](../../how-tos/streaming.md#stream-graph-state) |
|
||||
| [`custom`](#stream-custom-data) | Streams custom data from inside your graph | `.stream()` / `.astream()` with [`stream_mode="custom"`](../../how-tos/streaming.md#stream-custom-data) |
|
||||
| [`events`](#stream-events) | Stream all events (including the state of the graph); mainly useful when migrating large LCEL apps. | `.astream_events()` |
|
||||
|
||||
### Stream multiple modes
|
||||
|
||||
You can pass a list as the `stream_mode` parameter to stream multiple modes at once.
|
||||
|
||||
The streamed outputs will be tuples of `(mode, chunk)` where `mode` is the name of the stream mode and `chunk` is the data streamed by that mode.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input=inputs,
|
||||
stream_mode=["updates", "custom"]
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
streamMode: ["updates", "custom"]
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <inputs>,
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
\"custom\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
## Stream graph state
|
||||
|
||||
Use the stream modes `updates` and `values` to stream the state of the graph as it executes.
|
||||
|
||||
* `updates` streams the **updates** to the state after each step of the graph.
|
||||
* `values` streams the **full value** of the state after each step of the graph.
|
||||
|
||||
??? example "Example graph"
|
||||
|
||||
```python
|
||||
from typing import TypedDict
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
|
||||
class State(TypedDict):
|
||||
topic: str
|
||||
joke: str
|
||||
|
||||
def refine_topic(state: State):
|
||||
return {"topic": state["topic"] + " and cats"}
|
||||
|
||||
def generate_joke(state: State):
|
||||
return {"joke": f"This is a joke about {state['topic']}"}
|
||||
|
||||
graph = (
|
||||
StateGraph(State)
|
||||
.add_node(refine_topic)
|
||||
.add_node(generate_joke)
|
||||
.add_edge(START, "refine_topic")
|
||||
.add_edge("refine_topic", "generate_joke")
|
||||
.add_edge("generate_joke", END)
|
||||
.compile()
|
||||
)
|
||||
```
|
||||
|
||||
!!! note "Stateful runs"
|
||||
|
||||
Examples below assume that you want to **persist the outputs** of a streaming run in the [checkpointer](../../concepts/persistence.md) DB and have created a thread. To create a thread:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"]
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
If you don't need to persist the outputs of a run, you can pass `None` instead of `thread_id` when streaming.
|
||||
|
||||
=== "updates"
|
||||
|
||||
Use this to stream only the **state updates** returned by the nodes after each step. The streamed outputs include the name of the node as well as the update.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"topic": "ice cream"},
|
||||
# highlight-next-line
|
||||
stream_mode="updates"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { topic: "ice cream" },
|
||||
// highlight-next-line
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"topic\": \"ice cream\"},
|
||||
\"stream_mode\": \"updates\"
|
||||
}"
|
||||
```
|
||||
|
||||
=== "values"
|
||||
|
||||
Use this to stream the **full state** of the graph after each step.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"topic": "ice cream"},
|
||||
# highlight-next-line
|
||||
stream_mode="values"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { topic: "ice cream" },
|
||||
// highlight-next-line
|
||||
streamMode: "values"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"topic\": \"ice cream\"},
|
||||
\"stream_mode\": \"values\"
|
||||
}"
|
||||
```
|
||||
|
||||
|
||||
## Subgraphs
|
||||
|
||||
To include outputs from [subgraphs](../../concepts/subgraphs.md) in the streamed outputs, you can set `subgraphs=True` in the `.stream()` method of the parent graph. This will stream outputs from both the parent graph and any subgraphs.
|
||||
|
||||
```python
|
||||
for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"foo": "foo"},
|
||||
# highlight-next-line
|
||||
stream_subgraphs=True, # (1)!
|
||||
stream_mode="updates",
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
1. Set `stream_subgraphs=True` to stream outputs from subgraphs.
|
||||
|
||||
??? example "Extended example: streaming from subgraphs"
|
||||
|
||||
This is an example graph you can run in the LangGraph API server.
|
||||
See [LangGraph Platform quickstart](../quick_start.md) for more details.
|
||||
|
||||
```python
|
||||
# graph.py
|
||||
from langgraph.graph import START, StateGraph
|
||||
from typing import TypedDict
|
||||
|
||||
# Define subgraph
|
||||
class SubgraphState(TypedDict):
|
||||
foo: str # note that this key is shared with the parent graph state
|
||||
bar: str
|
||||
|
||||
def subgraph_node_1(state: SubgraphState):
|
||||
return {"bar": "bar"}
|
||||
|
||||
def subgraph_node_2(state: SubgraphState):
|
||||
return {"foo": state["foo"] + state["bar"]}
|
||||
|
||||
subgraph_builder = StateGraph(SubgraphState)
|
||||
subgraph_builder.add_node(subgraph_node_1)
|
||||
subgraph_builder.add_node(subgraph_node_2)
|
||||
subgraph_builder.add_edge(START, "subgraph_node_1")
|
||||
subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")
|
||||
subgraph = subgraph_builder.compile()
|
||||
|
||||
# Define parent graph
|
||||
class ParentState(TypedDict):
|
||||
foo: str
|
||||
|
||||
def node_1(state: ParentState):
|
||||
return {"foo": "hi! " + state["foo"]}
|
||||
|
||||
builder = StateGraph(ParentState)
|
||||
builder.add_node("node_1", node_1)
|
||||
builder.add_node("node_2", subgraph)
|
||||
builder.add_edge(START, "node_1")
|
||||
builder.add_edge("node_1", "node_2")
|
||||
graph = builder.compile()
|
||||
```
|
||||
|
||||
Once you have a running LangGraph API server, you can interact with it using
|
||||
[LangGraph SDK](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/)
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
|
||||
# Using the graph deployed with the name "agent"
|
||||
assistant_id = "agent"
|
||||
|
||||
# create a thread
|
||||
thread = await client.threads.create()
|
||||
thread_id = thread["thread_id"]
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"foo": "foo"},
|
||||
# highlight-next-line
|
||||
stream_subgraphs=True, # (1)!
|
||||
stream_mode="updates",
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
1. Set `stream_subgraphs=True` to stream outputs from subgraphs.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
|
||||
// Using the graph deployed with the name "agent"
|
||||
const assistantID = "agent";
|
||||
|
||||
// create a thread
|
||||
const thread = await client.threads.create();
|
||||
const threadID = thread["thread_id"];
|
||||
|
||||
// create a streaming run
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { foo: "foo" },
|
||||
// highlight-next-line
|
||||
streamSubgraphs: true, // (1)!
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
1. Set `streamSubgraphs: true` to stream outputs from subgraphs.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
Create a thread:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Create a streaming run:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"foo\": \"foo\"},
|
||||
\"stream_subgraphs\": true,
|
||||
\"stream_mode\": [
|
||||
\"updates\"
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
**Note** that we are receiving not just the node updates, but we also the namespaces which tell us what graph (or subgraph) we are streaming from.
|
||||
|
||||
## Debugging {#debug}
|
||||
|
||||
Use the `debug` streaming mode to stream as much information as possible throughout the execution of the graph. The streamed outputs include the name of the node as well as the full state.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"topic": "ice cream"},
|
||||
# highlight-next-line
|
||||
stream_mode="debug"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { topic: "ice cream" },
|
||||
// highlight-next-line
|
||||
streamMode: "debug"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"topic\": \"ice cream\"},
|
||||
\"stream_mode\": \"debug\"
|
||||
}"
|
||||
```
|
||||
|
||||
## LLM tokens {#messages}
|
||||
|
||||
Use the `messages-tuple` streaming mode to stream Large Language Model (LLM) outputs **token by token** from any part of your graph, including nodes, tools, subgraphs, or tasks.
|
||||
|
||||
The streamed output from [`messages-tuple` mode](#supported-stream-modes) is a tuple `(message_chunk, metadata)` where:
|
||||
|
||||
- `message_chunk`: the token or message segment from the LLM.
|
||||
- `metadata`: a dictionary containing details about the graph node and LLM invocation.
|
||||
|
||||
??? example "Example graph"
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langgraph.graph import StateGraph, START
|
||||
|
||||
@dataclass
|
||||
class MyState:
|
||||
topic: str
|
||||
joke: str = ""
|
||||
|
||||
llm = init_chat_model(model="openai:gpt-4o-mini")
|
||||
|
||||
def call_model(state: MyState):
|
||||
"""Call the LLM to generate a joke about a topic"""
|
||||
# highlight-next-line
|
||||
llm_response = llm.invoke( # (1)!
|
||||
[
|
||||
{"role": "user", "content": f"Generate a joke about {state.topic}"}
|
||||
]
|
||||
)
|
||||
return {"joke": llm_response.content}
|
||||
|
||||
graph = (
|
||||
StateGraph(MyState)
|
||||
.add_node(call_model)
|
||||
.add_edge(START, "call_model")
|
||||
.compile()
|
||||
)
|
||||
```
|
||||
|
||||
1. Note that the message events are emitted even when the LLM is run using `.invoke` rather than `.stream`.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"topic": "ice cream"},
|
||||
# highlight-next-line
|
||||
stream_mode="messages-tuple",
|
||||
):
|
||||
if chunk.event != "messages":
|
||||
continue
|
||||
|
||||
message_chunk, metadata = chunk.data # (1)!
|
||||
if message_chunk["content"]:
|
||||
print(message_chunk["content"], end="|", flush=True)
|
||||
```
|
||||
|
||||
1. The "messages-tuple" stream mode returns an iterator of tuples `(message_chunk, metadata)` where `message_chunk` is the token streamed by the LLM and `metadata` is a dictionary with information about the graph node where the LLM was called and other information.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { topic: "ice cream" },
|
||||
// highlight-next-line
|
||||
streamMode: "messages-tuple"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
if (chunk.event !== "messages") {
|
||||
continue;
|
||||
}
|
||||
console.log(chunk.data[0]["content"]); // (1)!
|
||||
}
|
||||
```
|
||||
|
||||
1. The "messages-tuple" stream mode returns an iterator of tuples `(message_chunk, metadata)` where `message_chunk` is the token streamed by the LLM and `metadata` is a dictionary with information about the graph node where the LLM was called and other information.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"topic\": \"ice cream\"},
|
||||
\"stream_mode\": \"messages-tuple\"
|
||||
}"
|
||||
```
|
||||
|
||||
### Filter LLM tokens
|
||||
|
||||
* To filter the streamed tokens by LLM invocation, you can [associate `tags` with LLM invocations](../../how-tos/streaming.md#filter-by-llm-invocation).
|
||||
* To stream tokens only from specific nodes, use `stream_mode="messages"` and [filter the outputs by the `langgraph_node` field](../../how-tos/streaming.md#filter-by-node) in the streamed metadata.
|
||||
|
||||
## Stream custom data
|
||||
|
||||
To send **custom user-defined data**:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"query": "example"},
|
||||
# highlight-next-line
|
||||
stream_mode="custom"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { query: "example" },
|
||||
// highlight-next-line
|
||||
streamMode: "custom"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"query\": \"example\"},
|
||||
\"stream_mode\": \"custom\"
|
||||
}"
|
||||
```
|
||||
|
||||
## Stream events
|
||||
|
||||
To stream all events, including the state of the graph:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id,
|
||||
assistant_id,
|
||||
input={"topic": "ice cream"},
|
||||
# highlight-next-line
|
||||
stream_mode="events"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const streamResponse = client.runs.stream(
|
||||
threadID,
|
||||
assistantID,
|
||||
{
|
||||
input: { topic: "ice cream" },
|
||||
// highlight-next-line
|
||||
streamMode: "events"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {\"topic\": \"ice cream\"},
|
||||
\"stream_mode\": \"events\"
|
||||
}"
|
||||
```
|
||||
|
||||
## Stateless runs
|
||||
|
||||
If you don't want to **persist the outputs** of a streaming run in the [checkpointer](../../concepts/persistence.md) DB, you can create a stateless run without creating a thread:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>, api_key=<API_KEY>)
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
# highlight-next-line
|
||||
None, # (1)!
|
||||
assistant_id,
|
||||
input=inputs,
|
||||
stream_mode="updates"
|
||||
):
|
||||
print(chunk.data)
|
||||
```
|
||||
|
||||
1. We are passing `None` instead of a `thread_id` UUID.
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <API_KEY> });
|
||||
|
||||
// create a streaming run
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.stream(
|
||||
// highlight-next-line
|
||||
null, // (1)!
|
||||
assistantID,
|
||||
{
|
||||
input,
|
||||
streamMode: "updates"
|
||||
}
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk.data);
|
||||
}
|
||||
```
|
||||
|
||||
1. We are passing `None` instead of a `thread_id` UUID.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <API_KEY>'
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": <inputs>,
|
||||
\"stream_mode\": \"updates\"
|
||||
}"
|
||||
```
|
||||
|
||||
## Join and stream
|
||||
|
||||
LangGraph Platform allows you to join an active [background run](../how-tos/background_run.md) and stream outputs from it. To do so, you can use [LangGraph SDK's](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/) `client.runs.join_stream` method:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
client = get_client(url=<DEPLOYMENT_URL>, api_key=<API_KEY>)
|
||||
|
||||
# highlight-next-line
|
||||
async for chunk in client.runs.join_stream(
|
||||
thread_id,
|
||||
# highlight-next-line
|
||||
run_id, # (1)!
|
||||
):
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
1. This is the `run_id` of an existing run you want to join.
|
||||
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL>, apiKey: <API_KEY> });
|
||||
|
||||
// highlight-next-line
|
||||
const streamResponse = client.runs.joinStream(
|
||||
threadID,
|
||||
// highlight-next-line
|
||||
runId // (1)!
|
||||
);
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
1. This is the `run_id` of an existing run you want to join.
|
||||
|
||||
=== "cURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <API_KEY>'
|
||||
```
|
||||
|
||||
!!! warning "Outputs not buffered"
|
||||
|
||||
When you use `.join_stream`, output is not buffered, so any output produced before joining will not be received.
|
||||
|
||||
## API Reference
|
||||
|
||||
For API usage and implementation, refer to the [API reference](../reference/api/api_ref.html#tag/thread-runs/POST/threads/{thread_id}/runs/stream).
|
||||
@@ -1,19 +0,0 @@
|
||||
# Manage assistants
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [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.
|
||||
|
||||
## Graph mode
|
||||
|
||||
To view your assistants, click the "Manage Assistants" button in the bottom left corner.
|
||||
|
||||
This opens a modal for you to view all the assistants for the selected graph. Specify the assistant and its version you would like to mark as "Active", and this assistant will be used when submitting runs.
|
||||
|
||||
By default, the "Default configuration" option will be active. This option reflects the default configuration defined in your graph. Edits made to this configuration will be used to update the run-time configuration, but will not update or create a new assistant unless you click "Create new assistant".
|
||||
|
||||
## Chat mode
|
||||
|
||||
Chat mode enables you to switch through the different assistants in your graph via the dropdown selector at the top of the page. To create, edit, or delete assistants, use Graph mode.
|
||||
@@ -1,112 +0,0 @@
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [LangGraph Studio Overview](../../../concepts/langgraph_studio.md)
|
||||
|
||||
LangGraph Studio supports connecting to two types of graphs:
|
||||
|
||||
- Graphs deployed on [LangGraph Platform](../../../cloud/quick_start.md)
|
||||
- Graphs running locally via the [LangGraph Server](../../../tutorials/langgraph-platform/local-server.md).
|
||||
|
||||
LangGraph Studio is accessed from the LangSmith UI, within the LangGraph Platform Deployments tab.
|
||||
|
||||
## Deployed application
|
||||
|
||||
For applications that are [deployed](../../quick_start.md) on LangGraph Platform, you can access Studio as part of that deployment. To do so, navigate to the deployment in LangGraph Platform within the LangSmith UI and click the "LangGraph Studio" button.
|
||||
|
||||
This will load the Studio UI connected to your live deployment, allowing you to create, read, and update the [threads](../../../concepts/persistence.md#threads), [assistants](../../../concepts/assistants.md), and [memory](../../../concepts//memory.md) in that deployment.
|
||||
|
||||
## Local development server
|
||||
|
||||
To test your locally running application using LangGraph Studio, ensure your application is set up following [this guide](https://langchain-ai.github.io/langgraph/cloud/deployment/setup/).
|
||||
|
||||
!!! info "LangSmith Tracing"
|
||||
For local development, if you do not wish to have data traced to LangSmith, set `LANGSMITH_TRACING=false` in your application's `.env` file. With tracing disabled, no data will leave your local server.
|
||||
|
||||
Next, install the [LangGraph CLI](../../../concepts/langgraph_cli.md):
|
||||
|
||||
```
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
```
|
||||
|
||||
and run:
|
||||
|
||||
```
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
!!! warning "Browser Compatibility"
|
||||
Safari blocks `localhost` connections to Studio. To work around this, run the above command with `--tunnel` to access Studio via a secure tunnel.
|
||||
|
||||
This will start the LangGraph Server locally, running in-memory. The server will run in watch mode, listening for and automatically restarting on code changes. Read this [reference](https://langchain-ai.github.io/langgraph/cloud/reference/cli/#dev) to learn about all the options for starting the API server.
|
||||
|
||||
If successful, you will see the following logs:
|
||||
|
||||
> 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
|
||||
|
||||
Once running, you will automatically be directed to LangGraph Studio.
|
||||
|
||||
For an already running server, access Studio by either:
|
||||
|
||||
1. Directly navigate to the following URL: `https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024`.
|
||||
2. Within LangSmith, navigate to the LangGraph Platform Deployments tab, click the "LangGraph Studio" button, enter `http://127.0.0.1:2024` and click "Connect".
|
||||
|
||||
If running your server at a different host or port, simply update the `baseUrl` to match.
|
||||
|
||||
### (Optional) Attach a debugger
|
||||
|
||||
For step-by-step debugging with breakpoints and variable inspection:
|
||||
|
||||
```bash
|
||||
# Install debugpy package
|
||||
pip install debugpy
|
||||
|
||||
# Start server with debugging enabled
|
||||
langgraph dev --debug-port 5678
|
||||
```
|
||||
|
||||
Then attach your preferred debugger:
|
||||
|
||||
=== "VS Code"
|
||||
|
||||
Add this configuration to `launch.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Attach to LangGraph",
|
||||
"type": "debugpy",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 5678
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "PyCharm"
|
||||
|
||||
1. Go to Run → Edit Configurations
|
||||
2. Click + and select "Python Debug Server"
|
||||
3. Set IDE host name: `localhost`
|
||||
4. Set port: `5678` (or the port number you chose in the previous step)
|
||||
5. Click "OK" and start debugging
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For issues getting started, please see this [troubleshooting guide](../../../troubleshooting/studio.md).
|
||||
|
||||
## Next steps
|
||||
|
||||
See the following guides for more information on how to use Studio:
|
||||
|
||||
- [Run application](../invoke_studio.md)
|
||||
- [Manage assistants](./manage_assistants.md)
|
||||
- [Manage threads](../threads_studio.md)
|
||||
- [Iterate on prompts](../iterate_graph_studio.md)
|
||||
- [Debug LangSmith traces](../clone_traces_studio.md)
|
||||
- [Add node to dataset](../datasets_studio.md)
|
||||
@@ -1,57 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,37 +0,0 @@
|
||||
# Manage threads
|
||||
|
||||
Studio allows you to view [threads](../../concepts/persistence.md#threads) from the server and edit their state.
|
||||
|
||||
## View threads
|
||||
|
||||
### Graph mode
|
||||
|
||||
1. In the top of the right-hand pane, select the dropdown menu to view existing threads.
|
||||
1. Select the desired thread, and the thread history will populate in the right-hand side of the page.
|
||||
1. To create a new thread, click `+ New Thread` and [submit a run](../how-tos/invoke_studio.md#graph-mode).
|
||||
|
||||
To view more granular information in the thread, drag the slider at the top of the page to the right. To view less information, drag the slider to the left. Additionally, collapse or expand individual turns, nodes, and keys of the state.
|
||||
|
||||
Switch between `Pretty` and `JSON` mode for different rendering formats.
|
||||
|
||||
### Chat mode
|
||||
|
||||
1. View all threads in the right-hand pane of the page.
|
||||
2. Select the desired thread and the thread history will populate in the center panel.
|
||||
3. To create a new thread, click the plus button and [submit a run](../how-tos/invoke_studio.md#chat-mode).
|
||||
|
||||
## Edit thread history
|
||||
|
||||
### Graph mode
|
||||
|
||||
To edit the state of the thread, select "edit node state" next to the desired node. Edit the node's output as desired and click "fork" to confirm. This will create a new forked run from the checkpoint of the selected node.
|
||||
|
||||
If you instead want to re-run the thread from a given checkpoint without editing the state, click the "Re-run from here". This will again create a new forked run from the selected checkpoint. This is useful for re-running with changes that are not specific to the state, such as the selected assistant.
|
||||
|
||||
### Chat mode
|
||||
|
||||
To edit a human message in the thread, click the edit button below the human message. Edit the message as desired and submit. This will create a new fork of the conversation history. To re-generate an AI message, click the retry icon below the AI message.
|
||||
|
||||
## Learn more
|
||||
|
||||
For more information about time travel, [see here](../../concepts/time-travel.md).
|
||||
@@ -1,661 +0,0 @@
|
||||
# How to integrate LangGraph into your React application
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [LangGraph Platform](../../concepts/langgraph_platform.md)
|
||||
- [LangGraph Server](../../concepts/langgraph_server.md)
|
||||
|
||||
The `useStream()` React hook provides a seamless way to integrate LangGraph into your React applications. It handles all the complexities of streaming, state management, and branching logic, letting you focus on building great chat experiences.
|
||||
|
||||
Key features:
|
||||
|
||||
- Messages streaming: Handle a stream of message chunks to form a complete message
|
||||
- Automatic state management for messages, interrupts, loading states, and errors
|
||||
- Conversation branching: Create alternate conversation paths from any point in the chat history
|
||||
- UI-agnostic design: bring your own components and styling
|
||||
|
||||
Let's explore how to use `useStream()` in your React application.
|
||||
|
||||
The `useStream()` provides a solid foundation for creating bespoke chat experiences. For pre-built chat components and interfaces, we also recommend checking out [CopilotKit](https://docs.copilotkit.ai/coagents/quickstart/langgraph) and [assistant-ui](https://www.assistant-ui.com/docs/runtimes/langgraph).
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @langchain/langgraph-sdk @langchain/core
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
|
||||
export default function App() {
|
||||
const thread = useStream<{ messages: Message[] }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{thread.messages.map((message) => (
|
||||
<div key={message.id}>{message.content as string}</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const form = e.target as HTMLFormElement;
|
||||
const message = new FormData(form).get("message") as string;
|
||||
|
||||
form.reset();
|
||||
thread.submit({ messages: [{ type: "human", content: message }] });
|
||||
}}
|
||||
>
|
||||
<input type="text" name="message" />
|
||||
|
||||
{thread.isLoading ? (
|
||||
<button key="stop" type="button" onClick={() => thread.stop()}>
|
||||
Stop
|
||||
</button>
|
||||
) : (
|
||||
<button keytype="submit">Send</button>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Customizing Your UI
|
||||
|
||||
The `useStream()` hook takes care of all the complex state management behind the scenes, providing you with simple interfaces to build your UI. Here's what you get out of the box:
|
||||
|
||||
- Thread state management
|
||||
- Loading and error states
|
||||
- Interrupts
|
||||
- Message handling and updates
|
||||
- Branching support
|
||||
|
||||
Here are some examples on how to use these features effectively:
|
||||
|
||||
### Loading States
|
||||
|
||||
The `isLoading` property tells you when a stream is active, enabling you to:
|
||||
|
||||
- Show a loading indicator
|
||||
- Disable input fields during processing
|
||||
- Display a cancel button
|
||||
|
||||
```tsx
|
||||
export default function App() {
|
||||
const { isLoading, stop } = useStream<{ messages: Message[] }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
return (
|
||||
<form>
|
||||
{isLoading && (
|
||||
<button key="stop" type="button" onClick={() => stop()}>
|
||||
Stop
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Resume a stream after page refresh
|
||||
|
||||
The `useStream()` hook can automatically resume an ongoing run upon mounting by setting `reconnectOnMount: true`. This is useful for continuing a stream after a page refresh, ensuring no messages and events generated during the downtime are lost.
|
||||
|
||||
```tsx
|
||||
const thread = useStream<{ messages: Message[] }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
reconnectOnMount: true,
|
||||
});
|
||||
```
|
||||
|
||||
By default the ID of the created run is stored in `window.sessionStorage`, which can be swapped by passing a custom storage in `reconnectOnMount` instead. The storage is used to persist the in-flight run ID for a thread (under `lg:stream:${threadId}` key).
|
||||
|
||||
```tsx
|
||||
const thread = useStream<{ messages: Message[] }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
reconnectOnMount: () => window.localStorage,
|
||||
});
|
||||
```
|
||||
|
||||
You can also manually manage the resuming process by using the run callbacks to persist the run metadata and the `joinStream` function to resume the stream. Make sure to pass `streamResumable: true` when creating the run; otherwise some events might be lost.
|
||||
|
||||
````tsx
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { useCallback, useState, useEffect, useRef } from "react";
|
||||
|
||||
export default function App() {
|
||||
const [threadId, onThreadId] = useSearchParam("threadId");
|
||||
|
||||
const thread = useStream<{ messages: Message[] }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
|
||||
threadId,
|
||||
onThreadId,
|
||||
|
||||
onCreated: (run) => {
|
||||
window.sessionStorage.setItem(`resume:${run.thread_id}`, run.run_id);
|
||||
},
|
||||
onFinish: (_, run) => {
|
||||
window.sessionStorage.removeItem(`resume:${run?.thread_id}`);
|
||||
},
|
||||
});
|
||||
|
||||
// Ensure that we only join the stream once per thread.
|
||||
const joinedThreadId = useRef<string | null>(null);
|
||||
useEffect(() => {
|
||||
if (!threadId) return;
|
||||
|
||||
const resume = window.sessionStorage.getItem(`resume:${threadId}`);
|
||||
if (resume && joinedThreadId.current !== threadId) {
|
||||
thread.joinStream(resume);
|
||||
joinedThreadId.current = threadId;
|
||||
}
|
||||
}, [threadId]);
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const form = e.target as HTMLFormElement;
|
||||
const message = new FormData(form).get("message") as string;
|
||||
thread.submit(
|
||||
{ messages: [{ type: "human", content: message }] },
|
||||
{ streamResumable: true }
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{thread.messages.map((message) => (
|
||||
<div key={message.id}>{message.content as string}</div>
|
||||
))}
|
||||
</div>
|
||||
<input type="text" name="message" />
|
||||
<button type="submit">Send</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
// Utility method to retrieve and persist data in URL as search param
|
||||
function useSearchParam(key: string) {
|
||||
const [value, setValue] = useState<string | null>(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get(key) ?? null;
|
||||
});
|
||||
|
||||
const update = useCallback(
|
||||
(value: string | null) => {
|
||||
setValue(value);
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
if (value == null) {
|
||||
url.searchParams.delete(key);
|
||||
} else {
|
||||
url.searchParams.set(key, value);
|
||||
}
|
||||
|
||||
window.history.pushState({}, "", url.toString());
|
||||
},
|
||||
[key]
|
||||
);
|
||||
|
||||
return [value, update] as const;
|
||||
}
|
||||
```
|
||||
|
||||
### Thread Management
|
||||
|
||||
Keep track of conversations with built-in thread management. You can access the current thread ID and get notified when new threads are created:
|
||||
|
||||
```tsx
|
||||
const [threadId, setThreadId] = useState<string | null>(null);
|
||||
|
||||
const thread = useStream<{ messages: Message[] }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
|
||||
threadId: threadId,
|
||||
onThreadId: setThreadId,
|
||||
});
|
||||
````
|
||||
|
||||
We recommend storing the `threadId` in your URL's query parameters to let users resume conversations after page refreshes.
|
||||
|
||||
### Messages Handling
|
||||
|
||||
The `useStream()` hook will keep track of the message chunks received from the server and concatenate them together to form a complete message. The completed message chunks can be retrieved via the `messages` property.
|
||||
|
||||
By default, the `messagesKey` is set to `messages`, where it will append the new messages chunks to `values["messages"]`. If you store messages in a different key, you can change the value of `messagesKey`.
|
||||
|
||||
```tsx
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
|
||||
export default function HomePage() {
|
||||
const thread = useStream<{ messages: Message[] }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{thread.messages.map((message) => (
|
||||
<div key={message.id}>{message.content as string}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Under the hood, the `useStream()` hook will use the `streamMode: "messages-tuple"` to receive a stream of messages (i.e. individual LLM tokens) from any LangChain chat model invocations inside your graph nodes. Learn more about messages streaming in the [streaming](../how-tos/streaming.md#messages) guide.
|
||||
|
||||
### Interrupts
|
||||
|
||||
The `useStream()` hook exposes the `interrupt` property, which will be filled with the last interrupt from the thread. You can use interrupts to:
|
||||
|
||||
- Render a confirmation UI before executing a node
|
||||
- Wait for human input, allowing agent to ask the user with clarifying questions
|
||||
|
||||
Learn more about interrupts in the [How to handle interrupts](../../how-tos/human_in_the_loop/wait-user-input.ipynb) guide.
|
||||
|
||||
```tsx
|
||||
const thread = useStream<{ messages: Message[] }, { InterruptType: string }>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
if (thread.interrupt) {
|
||||
return (
|
||||
<div>
|
||||
Interrupted! {thread.interrupt.value}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
// `resume` can be any value that the agent accepts
|
||||
thread.submit(undefined, { command: { resume: true } });
|
||||
}}
|
||||
>
|
||||
Resume
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Branching
|
||||
|
||||
For each message, you can use `getMessagesMetadata()` to get the first checkpoint from which the message has been first seen. You can then create a new run from the checkpoint preceding the first seen checkpoint to create a new branch in a thread.
|
||||
|
||||
A branch can be created in following ways:
|
||||
|
||||
1. Edit a previous user message.
|
||||
2. Request a regeneration of a previous assistant message.
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { useState } from "react";
|
||||
|
||||
function BranchSwitcher({
|
||||
branch,
|
||||
branchOptions,
|
||||
onSelect,
|
||||
}: {
|
||||
branch: string | undefined;
|
||||
branchOptions: string[] | undefined;
|
||||
onSelect: (branch: string) => void;
|
||||
}) {
|
||||
if (!branchOptions || !branch) return null;
|
||||
const index = branchOptions.indexOf(branch);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const prevBranch = branchOptions[index - 1];
|
||||
if (!prevBranch) return;
|
||||
onSelect(prevBranch);
|
||||
}}
|
||||
>
|
||||
Prev
|
||||
</button>
|
||||
<span>
|
||||
{index + 1} / {branchOptions.length}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const nextBranch = branchOptions[index + 1];
|
||||
if (!nextBranch) return;
|
||||
onSelect(nextBranch);
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EditMessage({
|
||||
message,
|
||||
onEdit,
|
||||
}: {
|
||||
message: Message;
|
||||
onEdit: (message: Message) => void;
|
||||
}) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
if (!editing) {
|
||||
return (
|
||||
<button type="button" onClick={() => setEditing(true)}>
|
||||
Edit
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const form = e.target as HTMLFormElement;
|
||||
const content = new FormData(form).get("content") as string;
|
||||
|
||||
form.reset();
|
||||
onEdit({ type: "human", content });
|
||||
setEditing(false);
|
||||
}}
|
||||
>
|
||||
<input name="content" defaultValue={message.content as string} />
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const thread = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{thread.messages.map((message) => {
|
||||
const meta = thread.getMessagesMetadata(message);
|
||||
const parentCheckpoint = meta?.firstSeenState?.parent_checkpoint;
|
||||
|
||||
return (
|
||||
<div key={message.id}>
|
||||
<div>{message.content as string}</div>
|
||||
|
||||
{message.type === "human" && (
|
||||
<EditMessage
|
||||
message={message}
|
||||
onEdit={(message) =>
|
||||
thread.submit(
|
||||
{ messages: [message] },
|
||||
{ checkpoint: parentCheckpoint }
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{message.type === "ai" && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
thread.submit(undefined, { checkpoint: parentCheckpoint })
|
||||
}
|
||||
>
|
||||
<span>Regenerate</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<BranchSwitcher
|
||||
branch={meta?.branch}
|
||||
branchOptions={meta?.branchOptions}
|
||||
onSelect={(branch) => thread.setBranch(branch)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const form = e.target as HTMLFormElement;
|
||||
const message = new FormData(form).get("message") as string;
|
||||
|
||||
form.reset();
|
||||
thread.submit({ messages: [message] });
|
||||
}}
|
||||
>
|
||||
<input type="text" name="message" />
|
||||
|
||||
{thread.isLoading ? (
|
||||
<button key="stop" type="button" onClick={() => thread.stop()}>
|
||||
Stop
|
||||
</button>
|
||||
) : (
|
||||
<button key="submit" type="submit">
|
||||
Send
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
For advanced use cases you can use the `experimental_branchTree` property to get the tree representation of the thread, which can be used to render branching controls for non-message based graphs.
|
||||
|
||||
### Optimistic Updates
|
||||
|
||||
You can optimistically update the client state before performing a network request to the agent, allowing you to provide immediate feedback to the user, such as showing the user message immediately before the agent has seen the request.
|
||||
|
||||
```tsx
|
||||
const stream = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
const handleSubmit = (text: string) => {
|
||||
const newMessage = { type: "human" as const, content: text };
|
||||
|
||||
stream.submit(
|
||||
{ messages: [newMessage] },
|
||||
{
|
||||
optimisticValues(prev) {
|
||||
const prevMessages = prev.messages ?? [];
|
||||
const newMessages = [...prevMessages, newMessage];
|
||||
return { ...prev, messages: newMessages };
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Cached Thread Display
|
||||
|
||||
Use the `initialValues` option to display cached thread data immediately while the history is being loaded from the server. This improves user experience by showing cached data instantly when navigating to existing threads.
|
||||
|
||||
```tsx
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
|
||||
const CachedThreadExample = ({ threadId, cachedThreadData }) => {
|
||||
const stream = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
threadId,
|
||||
// Show cached data immediately while history loads
|
||||
initialValues: cachedThreadData?.values,
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{stream.messages.map((message) => (
|
||||
<div key={message.id}>{message.content as string}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Optimistic Thread Creation
|
||||
|
||||
Use the `threadId` option in `submit` function to enable optimistic UI patterns where you need to know the thread ID before the thread is actually created.
|
||||
|
||||
```tsx
|
||||
import { useState } from "react";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
|
||||
const OptimisticThreadExample = () => {
|
||||
const [threadId, setThreadId] = useState<string | null>(null);
|
||||
const [optimisticThreadId] = useState(() => crypto.randomUUID());
|
||||
|
||||
const stream = useStream({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
threadId,
|
||||
onThreadId: setThreadId, // (3) Updated after thread has been created.
|
||||
messagesKey: "messages",
|
||||
});
|
||||
|
||||
const handleSubmit = (text: string) => {
|
||||
// (1) Perform a soft navigation to /threads/${optimisticThreadId}
|
||||
// without waiting for thread creation.
|
||||
window.history.pushState({}, "", `/threads/${optimisticThreadId}`);
|
||||
|
||||
// (2) Submit message to create thread with the predetermined ID.
|
||||
stream.submit(
|
||||
{ messages: [{ type: "human", content: text }] },
|
||||
{ threadId: optimisticThreadId }
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Thread ID: {threadId ?? optimisticThreadId}</p>
|
||||
{/* Rest of component */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
The `useStream()` hook is friendly for apps written in TypeScript and you can specify types for the state to get better type safety and IDE support.
|
||||
|
||||
```tsx
|
||||
// Define your types
|
||||
type State = {
|
||||
messages: Message[];
|
||||
context?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
// Use them with the hook
|
||||
const thread = useStream<State>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
```
|
||||
|
||||
You can also optionally specify types for different scenarios, such as:
|
||||
|
||||
- `ConfigurableType`: Type for the `config.configurable` property (default: `Record<string, unknown>`)
|
||||
- `InterruptType`: Type for the interrupt value - i.e. contents of `interrupt(...)` function (default: `unknown`)
|
||||
- `CustomEventType`: Type for the custom events (default: `unknown`)
|
||||
- `UpdateType`: Type for the submit function (default: `Partial<State>`)
|
||||
|
||||
```tsx
|
||||
const thread = useStream<
|
||||
State,
|
||||
{
|
||||
UpdateType: {
|
||||
messages: Message[] | Message;
|
||||
context?: Record<string, unknown>;
|
||||
};
|
||||
InterruptType: string;
|
||||
CustomEventType: {
|
||||
type: "progress" | "debug";
|
||||
payload: unknown;
|
||||
};
|
||||
ConfigurableType: {
|
||||
model: string;
|
||||
};
|
||||
}
|
||||
>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
```
|
||||
|
||||
If you're using LangGraph.js, you can also reuse your graph's annotation types. However, make sure to only import the types of the annotation schema in order to avoid importing the entire LangGraph.js runtime (i.e. via `import type { ... }` directive).
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Annotation,
|
||||
MessagesAnnotation,
|
||||
type StateType,
|
||||
type UpdateType,
|
||||
} from "@langchain/langgraph/web";
|
||||
|
||||
const AgentState = Annotation.Root({
|
||||
...MessagesAnnotation.spec,
|
||||
context: Annotation<string>(),
|
||||
});
|
||||
|
||||
const thread = useStream<
|
||||
StateType<typeof AgentState.spec>,
|
||||
{ UpdateType: UpdateType<typeof AgentState.spec> }
|
||||
>({
|
||||
apiUrl: "http://localhost:2024",
|
||||
assistantId: "agent",
|
||||
messagesKey: "messages",
|
||||
});
|
||||
```
|
||||
|
||||
## Event Handling
|
||||
|
||||
The `useStream()` hook provides several callback options to help you respond to different events:
|
||||
|
||||
- `onError`: Called when an error occurs.
|
||||
- `onFinish`: Called when the stream is finished.
|
||||
- `onUpdateEvent`: Called when an update event is received.
|
||||
- `onCustomEvent`: Called when a custom event is received. See the [streaming](../../how-tos/streaming.md#stream-custom-data) guide to learn how to stream custom events.
|
||||
- `onMetadataEvent`: Called when a metadata event is received, which contains the Run ID and Thread ID.
|
||||
|
||||
## Learn More
|
||||
|
||||
- [JS/TS SDK Reference](../reference/sdk/js_ts_sdk_ref.md)
|
||||
@@ -1,487 +0,0 @@
|
||||
# Use threads
|
||||
|
||||
In this guide, we will show how to create, view, and inspect [threads](../../concepts/persistence.md#threads).
|
||||
|
||||
## Create a thread
|
||||
|
||||
To run your graph and the state persisted, you must first create a thread.
|
||||
|
||||
### Empty thread
|
||||
|
||||
To create a new thread, use the [LangGraph SDK](../../concepts/sdk.md) `create` method. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.ThreadsClient.create) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#create_3) SDK reference docs for more information.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
thread = await client.threads.create()
|
||||
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const thread = await client.threads.create();
|
||||
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"thread_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"created_at": "2025-05-12T14:04:08.268Z",
|
||||
"updated_at": "2025-05-12T14:04:08.268Z",
|
||||
"metadata": {},
|
||||
"status": "idle",
|
||||
"values": {}
|
||||
}
|
||||
|
||||
### Copy thread
|
||||
|
||||
Alternatively, if you already have a thread in your application whose state you wish to copy, you can use the `copy` method. This will create an independent thread whose history is identical to the original thread at the time of the operation. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.ThreadsClient.copy) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#copy) SDK reference docs for more information.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
copied_thread = await client.threads.copy(<THREAD_ID>)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const copiedThread = await client.threads.copy(<THREAD_ID>);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/copy \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
### Prepopulated State
|
||||
|
||||
Finally, you can create a thread with an arbitrary pre-defined state by providing a list of `supersteps` into the `create` method. The `supersteps` describe a list of a sequence of state updates. For example:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
thread = await client.threads.create(
|
||||
graph_id="agent",
|
||||
supersteps=[
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {},
|
||||
as_node: '__input__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
type: 'human',
|
||||
content: 'hello',
|
||||
},
|
||||
],
|
||||
},
|
||||
as_node: '__start__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
content: 'Hello! How can I assist you today?',
|
||||
type: 'ai',
|
||||
},
|
||||
],
|
||||
},
|
||||
as_node: 'call_model',
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const thread = await client.threads.create({
|
||||
graphId: 'agent',
|
||||
supersteps: [
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {},
|
||||
asNode: '__input__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
type: 'human',
|
||||
content: 'hello',
|
||||
},
|
||||
],
|
||||
},
|
||||
asNode: '__start__',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
updates: [
|
||||
{
|
||||
values: {
|
||||
messages: [
|
||||
{
|
||||
content: 'Hello! How can I assist you today?',
|
||||
type: 'ai',
|
||||
},
|
||||
],
|
||||
},
|
||||
asNode: 'call_model',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"metadata":{"graph_id":"agent"},"supersteps":[{"updates":[{"values":{},"as_node":"__input__"}]},{"updates":[{"values":{"messages":[{"type":"human","content":"hello"}]},"as_node":"__start__"}]},{"updates":[{"values":{"messages":[{"content":"Hello\u0021 How can I assist you today?","type":"ai"}]},"as_node":"call_model"}]}]}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"created_at": "2025-05-12T15:37:08.935038+00:00",
|
||||
"updated_at": "2025-05-12T15:37:08.935046+00:00",
|
||||
"metadata": {"graph_id": "agent"},
|
||||
"status": "idle",
|
||||
"config": {},
|
||||
"values": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "hello",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "human",
|
||||
"name": null,
|
||||
"id": "8701f3be-959c-4b7c-852f-c2160699b4ab",
|
||||
"example": false
|
||||
},
|
||||
{
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "ai",
|
||||
"name": null,
|
||||
"id": "4d8ea561-7ca1-409a-99f7-6b67af3e1aa3",
|
||||
"example": false,
|
||||
"tool_calls": [],
|
||||
"invalid_tool_calls": [],
|
||||
"usage_metadata": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
## List threads
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
To list threads, use the [LangGraph SDK](../../concepts/sdk.md) `search` method. This will list the threads in the application that match the provided filters. See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.ThreadsClient.search) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#search_2) SDK reference docs for more information.
|
||||
|
||||
#### Filter by thread status
|
||||
|
||||
Use the `status` field to filter threads based on their status. Supported values are `idle`, `busy`, `interrupted`, and `error`. See [here](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/?h=thread+status#langgraph_sdk.auth.types.ThreadStatus) for information on each status. For example, to view `idle` threads:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print(await client.threads.search(status="idle",limit=1))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log(await client.threads.search({ status: "idle", limit: 1 }));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"status": "idle", "limit": 1}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[
|
||||
{
|
||||
'thread_id': 'cacf79bb-4248-4d01-aabc-938dbd60ed2c',
|
||||
'created_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'updated_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'idle',
|
||||
'config': {'configurable': {}}
|
||||
}
|
||||
]
|
||||
|
||||
#### Filter by metadata
|
||||
|
||||
The `search` method allows you to filter on metadata:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.search(metadata={"graph_id":"agent"},limit=1)))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.search({ metadata: { "graph_id": "agent" }, limit: 1 })));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"metadata": {"graph_id":"agent"}, "limit": 1}'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
[
|
||||
{
|
||||
'thread_id': 'cacf79bb-4248-4d01-aabc-938dbd60ed2c',
|
||||
'created_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'updated_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'idle',
|
||||
'config': {'configurable': {}}
|
||||
}
|
||||
]
|
||||
|
||||
#### Sorting
|
||||
|
||||
The SDK also supports sorting threads by `thread_id`, `status`, `created_at`, and `updated_at` using the `sort_by` and `sort_order` params.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also view threads in a deployment via the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Threads" tab. This will load a table of all of the threads in your deployment.
|
||||
|
||||
To filter by thread status, select a status in the top bar. To sort by a supported property, click on the arrow icon for the desired column.
|
||||
|
||||
## Inspect threads
|
||||
|
||||
### LangGraph SDK
|
||||
|
||||
#### Get Thread
|
||||
|
||||
To view a specific thread given its `thread_id`, use the `get` method:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.get(<THREAD_ID>)))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.get(<THREAD_ID>)));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID> \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
'thread_id': 'cacf79bb-4248-4d01-aabc-938dbd60ed2c',
|
||||
'created_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'updated_at': '2024-08-14T17:36:38.921660+00:00',
|
||||
'metadata': {'graph_id': 'agent'},
|
||||
'status': 'idle',
|
||||
'config': {'configurable': {}}
|
||||
}
|
||||
|
||||
#### Inspect Thread State
|
||||
|
||||
To view the current state of a given thread, use the `get_state` method:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
print((await client.threads.get_state(<THREAD_ID>)))
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
console.log((await client.threads.getState(<THREAD_ID>)));
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"values": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "hello",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "human",
|
||||
"name": null,
|
||||
"id": "8701f3be-959c-4b7c-852f-c2160699b4ab",
|
||||
"example": false
|
||||
},
|
||||
{
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"additional_kwargs": {},
|
||||
"response_metadata": {},
|
||||
"type": "ai",
|
||||
"name": null,
|
||||
"id": "4d8ea561-7ca1-409a-99f7-6b67af3e1aa3",
|
||||
"example": false,
|
||||
"tool_calls": [],
|
||||
"invalid_tool_calls": [],
|
||||
"usage_metadata": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"next": [],
|
||||
"tasks": [],
|
||||
"metadata": {
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"checkpoint_id": "1f02f46f-7308-616c-8000-1b158a9a6955",
|
||||
"graph_id": "agent_with_quite_a_long_name",
|
||||
"source": "update",
|
||||
"step": 1,
|
||||
"writes": {
|
||||
"call_model": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "Hello! How can I assist you today?",
|
||||
"type": "ai"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"parents": {}
|
||||
},
|
||||
"created_at": "2025-05-12T15:37:09.008055+00:00",
|
||||
"checkpoint": {
|
||||
"checkpoint_id": "1f02f46f-733f-6b58-8001-ea90dcabb1bd",
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"checkpoint_ns": ""
|
||||
},
|
||||
"parent_checkpoint": {
|
||||
"checkpoint_id": "1f02f46f-7308-616c-8000-1b158a9a6955",
|
||||
"thread_id": "f15d70a1-27d4-4793-a897-de5609920b7d",
|
||||
"checkpoint_ns": ""
|
||||
},
|
||||
"checkpoint_id": "1f02f46f-733f-6b58-8001-ea90dcabb1bd",
|
||||
"parent_checkpoint_id": "1f02f46f-7308-616c-8000-1b158a9a6955"
|
||||
}
|
||||
|
||||
Optionally, to view the state of a thread at a given checkpoint, simply pass in the checkpoint id (or the entire checkpoint object):
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
thread_state = await client.threads.get_state(
|
||||
thread_id=<THREAD_ID>
|
||||
checkpoint_id=<CHECKPOINT_ID>
|
||||
)
|
||||
```
|
||||
|
||||
=== "Javascript"
|
||||
|
||||
```js
|
||||
const threadState = await client.threads.getState(<THREAD_ID>, <CHECKPOINT_ID>);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/state/<CHECKPOINT_ID> \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
#### Inspect Full Thread History
|
||||
|
||||
To view a thread's history, use the `get_history` method. This returns a list of every state the thread experienced. For more information see the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/?h=thread+status#langgraph_sdk.client.ThreadsClient.get_history) and [JS](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/js_ts_sdk_ref/#gethistory) reference docs.
|
||||
|
||||
### LangGraph Platform UI
|
||||
|
||||
You can also view threads in a deployment via the LangGraph Platform UI.
|
||||
|
||||
Inside your deployment, select the "Threads" tab. This will load a table of all of the threads in your deployment.
|
||||
|
||||
Select a thread to inspect its current state. To view its full history and for further debugging, open the thread in [LangGraph Studio](../../concepts//langgraph_studio.md).
|
||||
@@ -1,166 +0,0 @@
|
||||
# Use webhooks
|
||||
|
||||
When working with LangGraph Platform, you may want to use webhooks to receive updates after an API call completes. Webhooks are useful for triggering actions in your service once a run has finished processing. To implement this, you need to expose an endpoint that can accept `POST` requests and pass this endpoint as a `webhook` parameter in your API request.
|
||||
|
||||
Currently, the SDK does not provide built-in support for defining webhook endpoints, but you can specify them manually using API requests.
|
||||
|
||||
## Supported endpoints
|
||||
|
||||
The following API endpoints accept a `webhook` parameter:
|
||||
|
||||
| Operation | HTTP Method | Endpoint |
|
||||
|----------------------|-------------|-----------------------------------|
|
||||
| Create Run | `POST` | `/thread/{thread_id}/runs` |
|
||||
| Create Thread Cron | `POST` | `/thread/{thread_id}/runs/crons` |
|
||||
| Stream Run | `POST` | `/thread/{thread_id}/runs/stream` |
|
||||
| Wait Run | `POST` | `/thread/{thread_id}/runs/wait` |
|
||||
| Create Cron | `POST` | `/runs/crons` |
|
||||
| Stream Run Stateless | `POST` | `/runs/stream` |
|
||||
| Wait Run Stateless | `POST` | `/runs/wait` |
|
||||
|
||||
In this guide, we’ll show how to trigger a webhook after streaming a run.
|
||||
|
||||
## Set up your assistant and thread
|
||||
|
||||
Before making API calls, set up your assistant and thread.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=<DEPLOYMENT_URL>)
|
||||
assistant_id = "agent"
|
||||
thread = await client.threads.create()
|
||||
print(thread)
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
import { Client } from "@langchain/langgraph-sdk";
|
||||
|
||||
const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
|
||||
const assistantID = "agent";
|
||||
const thread = await client.threads.create();
|
||||
console.log(thread);
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/assistants/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{ "limit": 10, "offset": 0 }' | jq -c 'map(select(.config == null or .config == {})) | .[0]' && \
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"thread_id": "9dde5490-2b67-47c8-aa14-4bfec88af217",
|
||||
"created_at": "2024-08-30T23:07:38.242730+00:00",
|
||||
"updated_at": "2024-08-30T23:07:38.242730+00:00",
|
||||
"metadata": {},
|
||||
"status": "idle",
|
||||
"config": {},
|
||||
"values": null
|
||||
}
|
||||
```
|
||||
|
||||
## Use a webhook with a graph run
|
||||
|
||||
To use a webhook, specify the `webhook` parameter in your API request. When the run completes, LangGraph Platform sends a `POST` request to the specified webhook URL.
|
||||
|
||||
For example, if your server listens for webhook events at `https://my-server.app/my-webhook-endpoint`, include this in your request:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python
|
||||
input = { "messages": [{ "role": "user", "content": "Hello!" }] }
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
thread_id=thread["thread_id"],
|
||||
assistant_id=assistant_id,
|
||||
input=input,
|
||||
stream_mode="events",
|
||||
webhook="https://my-server.app/my-webhook-endpoint"
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```js
|
||||
const input = { messages: [{ role: "human", content: "Hello!" }] };
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
thread["thread_id"],
|
||||
assistantID,
|
||||
{
|
||||
input: input,
|
||||
webhook: "https://my-server.app/my-webhook-endpoint"
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
// Handle stream output
|
||||
}
|
||||
```
|
||||
|
||||
=== "CURL"
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"assistant_id": <ASSISTANT_ID>,
|
||||
"input": {"messages": [{"role": "user", "content": "Hello!"}]},
|
||||
"webhook": "https://my-server.app/my-webhook-endpoint"
|
||||
}'
|
||||
```
|
||||
|
||||
## Webhook payload
|
||||
|
||||
LangGraph Platform sends webhook notifications in the format of a [Run](../../concepts/assistants.md#execution). See the [API Reference](https://langchain-ai.github.io/langgraph/cloud/reference/api/api_ref.html#model/run) for details. The request payload includes run input, configuration, and other metadata in the `kwargs` field.
|
||||
|
||||
## Secure webhooks
|
||||
|
||||
To ensure only authorized requests hit your webhook endpoint, consider adding a security token as a query parameter:
|
||||
|
||||
```
|
||||
https://my-server.app/my-webhook-endpoint?token=YOUR_SECRET_TOKEN
|
||||
```
|
||||
|
||||
Your server should extract and validate this token before processing requests.
|
||||
|
||||
## Disable webhooks
|
||||
|
||||
As of `langgraph-api>=0.2.78`, developers can disable webhooks in the `langgraph.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"http": {
|
||||
"disable_webhooks": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This feature is primarily intended for self-hosted deployments, where platform administrators or developers may prefer to disable webhooks to simplify their security posture—especially if they are not configuring firewall rules or other network controls. Disabling webhooks helps prevent untrusted payloads from being sent to internal endpoints.
|
||||
|
||||
For full configuration details, refer to the [configuration file reference](https://langchain-ai.github.io/langgraph/cloud/reference/cli/?h=disable_webhooks#configuration-file).
|
||||
|
||||
## Test webhooks
|
||||
|
||||
You can test your webhook using online services like:
|
||||
|
||||
- **[Beeceptor](https://beeceptor.com/)** – Quickly create a test endpoint and inspect incoming webhook payloads.
|
||||
- **[Webhook.site](https://webhook.site/)** – View, debug, and log incoming webhook requests in real time.
|
||||
|
||||
These tools help you verify that LangGraph Platform is correctly triggering and sending webhooks to your service.
|
||||
@@ -1,184 +0,0 @@
|
||||
# Deployment quickstart
|
||||
|
||||
This guide shows you how to set up and use LangGraph Platform for a cloud deployment.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following:
|
||||
|
||||
- A [GitHub account](https://github.com/)
|
||||
- A [LangSmith account](https://smith.langchain.com/) – free to sign up
|
||||
|
||||
## 1. Create a repository on GitHub
|
||||
|
||||
To deploy an application to **LangGraph Platform**, your application code must reside in a GitHub repository. Both public and private repositories are supported. For this quickstart, use the [`new-langgraph-project` template](https://github.com/langchain-ai/react-agent) for your application:
|
||||
|
||||
1. Go to the [`new-langgraph-project` repository](https://github.com/langchain-ai/new-langgraph-project) or [`new-langgraphjs-project` template](https://github.com/langchain-ai/new-langgraphjs-project).
|
||||
1. Click the `Fork` button in the top right corner to fork the repository to your GitHub account.
|
||||
1. Click **Create fork**.
|
||||
|
||||
## 2. Deploy to LangGraph Platform
|
||||
|
||||
1. Log in to [LangSmith](https://smith.langchain.com/).
|
||||
1. In the left sidebar, select **Deployments**.
|
||||
1. Click the **+ New Deployment** button. A pane will open where you can fill in the required fields.
|
||||
1. If you are a first time user or adding a private repository that has not been previously connected, click the **Import from GitHub** button and follow the instructions to connect your GitHub account.
|
||||
1. Select your New LangGraph Project repository.
|
||||
1. Click **Submit** to deploy.
|
||||
|
||||
This may take about 15 minutes to complete. You can check the status in the **Deployment details** view.
|
||||
|
||||
## 3. Test your application in LangGraph Studio
|
||||
|
||||
Once your application is deployed:
|
||||
|
||||
1. Select the deployment you just created to view more details.
|
||||
1. Click the **LangGraph Studio** button in the top right corner.
|
||||
|
||||
LangGraph Studio will open to display your graph.
|
||||
|
||||
<figure markdown="1">
|
||||
[{: style="max-height:400px"}](deployment/img/langgraph_studio.png)
|
||||
<figcaption>
|
||||
Sample graph run in LangGraph Studio.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
## 4. Get the API URL for your deployment
|
||||
|
||||
1. In the **Deployment details** view in LangGraph, click the **API URL** to copy it to your clipboard.
|
||||
1. Click the `URL` to copy it to the clipboard.
|
||||
|
||||
## 5. Test the API
|
||||
|
||||
You can now test the API:
|
||||
|
||||
=== "Python SDK (Async)"
|
||||
|
||||
1. Install the LangGraph Python SDK:
|
||||
|
||||
```shell
|
||||
pip install langgraph-sdk
|
||||
```
|
||||
|
||||
1. Send a message to the assistant (threadless run):
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url="your-deployment-url", api_key="your-langsmith-api-key")
|
||||
|
||||
async for chunk in client.runs.stream(
|
||||
None, # Threadless run
|
||||
"agent", # Name of assistant. Defined in langgraph.json.
|
||||
input={
|
||||
"messages": [{
|
||||
"role": "human",
|
||||
"content": "What is LangGraph?",
|
||||
}],
|
||||
},
|
||||
stream_mode="updates",
|
||||
):
|
||||
print(f"Receiving new event of type: {chunk.event}...")
|
||||
print(chunk.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "Python SDK (Sync)"
|
||||
|
||||
1. Install the LangGraph Python SDK:
|
||||
|
||||
```shell
|
||||
pip install langgraph-sdk
|
||||
```
|
||||
|
||||
1. Send a message to the assistant (threadless run):
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_sync_client
|
||||
|
||||
client = get_sync_client(url="your-deployment-url", api_key="your-langsmith-api-key")
|
||||
|
||||
for chunk in client.runs.stream(
|
||||
None, # Threadless run
|
||||
"agent", # Name of assistant. Defined in langgraph.json.
|
||||
input={
|
||||
"messages": [{
|
||||
"role": "human",
|
||||
"content": "What is LangGraph?",
|
||||
}],
|
||||
},
|
||||
stream_mode="updates",
|
||||
):
|
||||
print(f"Receiving new event of type: {chunk.event}...")
|
||||
print(chunk.data)
|
||||
print("\n\n")
|
||||
```
|
||||
|
||||
=== "JavaScript SDK"
|
||||
|
||||
1. Install the LangGraph JS SDK
|
||||
|
||||
```shell
|
||||
npm install @langchain/langgraph-sdk
|
||||
```
|
||||
|
||||
1. Send a message to the assistant (threadless run):
|
||||
|
||||
```js
|
||||
const { Client } = await import("@langchain/langgraph-sdk");
|
||||
|
||||
const client = new Client({ apiUrl: "your-deployment-url", apiKey: "your-langsmith-api-key" });
|
||||
|
||||
const streamResponse = client.runs.stream(
|
||||
null, // Threadless run
|
||||
"agent", // Assistant ID
|
||||
{
|
||||
input: {
|
||||
"messages": [
|
||||
{ "role": "user", "content": "What is LangGraph?"}
|
||||
]
|
||||
},
|
||||
streamMode: "messages",
|
||||
}
|
||||
);
|
||||
|
||||
for await (const chunk of streamResponse) {
|
||||
console.log(`Receiving new event of type: ${chunk.event}...`);
|
||||
console.log(JSON.stringify(chunk.data));
|
||||
console.log("\n\n");
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rest API"
|
||||
|
||||
```bash
|
||||
curl -s --request POST \
|
||||
--url <DEPLOYMENT_URL>/runs/stream \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "X-Api-Key: <LANGSMITH API KEY> \
|
||||
--data "{
|
||||
\"assistant_id\": \"agent\",
|
||||
\"input\": {
|
||||
\"messages\": [
|
||||
{
|
||||
\"role\": \"human\",
|
||||
\"content\": \"What is LangGraph?\"
|
||||
}
|
||||
]
|
||||
},
|
||||
\"stream_mode\": \"updates\"
|
||||
}"
|
||||
```
|
||||
|
||||
|
||||
## Next steps
|
||||
|
||||
Congratulations! You have deployed an application using LangGraph Platform.
|
||||
|
||||
Here are some other resources to check out:
|
||||
|
||||
- [LangGraph Platform overview](../concepts/langgraph_platform.md)
|
||||
- [Deployment options](../concepts/deployment_options.md)
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LangGraph Platform API Reference</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<script id="api-reference" data-url="./openapi.json"></script>
|
||||
<script>
|
||||
var configuration = {}
|
||||
document.getElementById('api-reference').dataset.configuration =
|
||||
JSON.stringify(configuration)
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,22 +0,0 @@
|
||||
# LangGraph Server API Reference
|
||||
|
||||
The LangGraph Server API reference is available within each deployment at the `/docs` endpoint (e.g. `http://localhost:8124/docs`).
|
||||
|
||||
Click <a href="/langgraph/cloud/reference/api/api_ref.html" target="_blank">here</a> to view the API reference.
|
||||
|
||||
## Authentication
|
||||
|
||||
For deployments to LangGraph Platform, authentication is required. Pass the `X-Api-Key` header with each request to the LangGraph Server. The value of the header should be set to a valid LangSmith API key for the organization where the LangGraph Server is deployed.
|
||||
|
||||
Example `curl` command:
|
||||
```shell
|
||||
curl --request POST \
|
||||
--url http://localhost:8124/assistants/search \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'X-Api-Key: LANGSMITH_API_KEY' \
|
||||
--data '{
|
||||
"metadata": {},
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}'
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LangGraph Platform API Reference</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<script id="api-reference" data-url="./openapi_control_plane.json"></script>
|
||||
<script>
|
||||
var configuration = {}
|
||||
document.getElementById('api-reference').dataset.configuration =
|
||||
JSON.stringify(configuration)
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,247 +0,0 @@
|
||||
# LangGraph Control Plane API Reference
|
||||
|
||||
The LangGraph Control Plane API is used to programmatically create and manage LangGraph Server deployments. For example, the APIs can be orchestrated to create custom CI/CD workflows.
|
||||
|
||||
Click <a href="https://api.host.langchain.com/docs" target="_blank">here</a> to view the API reference.
|
||||
|
||||
## Host
|
||||
|
||||
LangGraph Control Plane hosts for Cloud SaaS data regions:
|
||||
|
||||
| US | EU |
|
||||
|----|----|
|
||||
| `https://api.host.langchain.com` | `https://eu.api.host.langchain.com` |
|
||||
|
||||
**Note**: Self-hosted deployments of LangGraph Platform will have a custom host for the LangGraph Control Plane.
|
||||
|
||||
## Authentication
|
||||
|
||||
To authenticate with the LangGraph Control Plane API, set the `X-Api-Key` header to a valid LangSmith API key.
|
||||
|
||||
Example `curl` command:
|
||||
```shell
|
||||
curl --request GET \
|
||||
--url http://localhost:8124/v2/deployments \
|
||||
--header 'X-Api-Key: LANGSMITH_API_KEY'
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
Each endpoint path is prefixed with a version (e.g. `v1`, `v2`).
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Call `POST /v2/deployments` to create a new Deployment. The response body contains the Deployment ID (`id`) and the ID of the latest (and first) revision (`latest_revision_id`).
|
||||
1. Call `GET /v2/deployments/{deployment_id}` to retrieve the Deployment. Set `deployment_id` in the URL to the value of Deployment ID (`id`).
|
||||
1. Poll for revision `status` until `status` is `DEPLOYED` by calling `GET /v2/deployments/{deployment_id}/revisions/{latest_revision_id}`.
|
||||
1. Call `PATCH /v2/deployments/{deployment_id}` to update the deployment.
|
||||
|
||||
## Example Code
|
||||
Below is example Python code that demonstrates how to orchestrate the LangGraph Control Plane APIs to create a deployment, update the deployment, and delete the deployment.
|
||||
```python
|
||||
import os
|
||||
import time
|
||||
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# required environment variables
|
||||
CONTROL_PLANE_HOST = os.getenv("CONTROL_PLANE_HOST")
|
||||
LANGSMITH_API_KEY = os.getenv("LANGSMITH_API_KEY")
|
||||
INTEGRATION_ID = os.getenv("INTEGRATION_ID")
|
||||
MAX_WAIT_TIME = 1800 # 30 mins
|
||||
|
||||
|
||||
def get_headers() -> dict:
|
||||
"""Return common headers for requests to LangGraph Control Plane API."""
|
||||
return {
|
||||
"X-Api-Key": LANGSMITH_API_KEY,
|
||||
}
|
||||
|
||||
|
||||
def create_deployment() -> str:
|
||||
"""Create deployment. Return deployment ID."""
|
||||
headers = get_headers()
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
deployment_name = "my_deployment"
|
||||
|
||||
request_body = {
|
||||
"name": deployment_name,
|
||||
"source": "github",
|
||||
"source_config": {
|
||||
"integration_id": INTEGRATION_ID,
|
||||
"repo_url": "https://github.com/langchain-ai/langgraph-example",
|
||||
"deployment_type": "dev",
|
||||
"build_on_push": False,
|
||||
"custom_url": None,
|
||||
"resource_spec": None,
|
||||
},
|
||||
"source_revision_config": {
|
||||
"repo_ref": "main",
|
||||
"langgraph_config_path": "langgraph.json",
|
||||
"image_uri": None,
|
||||
},
|
||||
"secrets": [
|
||||
{
|
||||
"name": "OPENAI_API_KEY",
|
||||
"value": "test_openai_api_key",
|
||||
},
|
||||
{
|
||||
"name": "ANTHROPIC_API_KEY",
|
||||
"value": "test_anthropic_api_key",
|
||||
},
|
||||
{
|
||||
"name": "TAVILY_API_KEY",
|
||||
"value": "test_tavily_api_key",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments",
|
||||
headers=headers,
|
||||
json=request_body,
|
||||
)
|
||||
|
||||
if response.status_code != 201:
|
||||
raise Exception(f"Failed to create deployment: {response.text}")
|
||||
|
||||
deployment_id = response.json()["id"]
|
||||
print(f"Created deployment {deployment_name} ({deployment_id})")
|
||||
return deployment_id
|
||||
|
||||
|
||||
def get_deployment(deployment_id: str) -> dict:
|
||||
"""Get deployment."""
|
||||
response = requests.get(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get deployment ID {deployment_id}: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def list_revisions(deployment_id: str) -> list[dict]:
|
||||
"""List revisions.
|
||||
|
||||
Return list is sorted by created_at in descending order (latest first).
|
||||
"""
|
||||
response = requests.get(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(
|
||||
f"Failed to list revisions for deployment ID {deployment_id}: {response.text}"
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def get_revision(
|
||||
deployment_id: str,
|
||||
revision_id: str,
|
||||
) -> dict:
|
||||
"""Get revision."""
|
||||
response = requests.get(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions/{revision_id}",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to get revision ID {revision_id}: {response.text}")
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
def patch_deployment(deployment_id: str) -> None:
|
||||
"""Patch deployment."""
|
||||
headers = get_headers()
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
response = requests.patch(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
|
||||
headers=headers,
|
||||
json={
|
||||
"source_config": {
|
||||
"build_on_push": True,
|
||||
},
|
||||
"source_revision_config": {
|
||||
"repo_ref": "main",
|
||||
"langgraph_config_path": "langgraph.json",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to patch deployment: {response.text}")
|
||||
|
||||
print(f"Patched deployment ID {deployment_id}")
|
||||
|
||||
|
||||
def wait_for_deployment(deployment_id: str, revision_id: str) -> None:
|
||||
"""Wait for revision status to be DEPLOYED."""
|
||||
start_time = time.time()
|
||||
revision, status = None, None
|
||||
while time.time() - start_time < MAX_WAIT_TIME:
|
||||
revision = get_revision(deployment_id, revision_id)
|
||||
status = revision["status"]
|
||||
if status == "DEPLOYED":
|
||||
break
|
||||
elif "FAILED" in status:
|
||||
raise Exception(f"Revision ID {revision_id} failed: {revision}")
|
||||
|
||||
print(f"Waiting for revision ID {revision_id} to be DEPLOYED...")
|
||||
time.sleep(60)
|
||||
|
||||
if status != "DEPLOYED":
|
||||
raise Exception(
|
||||
f"Timeout waiting for revision ID {revision_id} to be DEPLOYED: {revision}"
|
||||
)
|
||||
|
||||
|
||||
def delete_deployment(deployment_id: str) -> None:
|
||||
"""Delete deployment."""
|
||||
response = requests.delete(
|
||||
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
|
||||
headers=get_headers(),
|
||||
)
|
||||
|
||||
if response.status_code != 204:
|
||||
raise Exception(
|
||||
f"Failed to delete deployment ID {deployment_id}: {response.text}"
|
||||
)
|
||||
|
||||
print(f"Deployment ID {deployment_id} deleted")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# create deployment and get the latest revision
|
||||
deployment_id = create_deployment()
|
||||
revisions = list_revisions(deployment_id)
|
||||
latest_revision = revisions["resources"][0]
|
||||
latest_revision_id = latest_revision["id"]
|
||||
|
||||
# wait for latest revision to be DEPLOYED
|
||||
wait_for_deployment(deployment_id, latest_revision_id)
|
||||
|
||||
# patch the deployment and get the latest revision
|
||||
patch_deployment(deployment_id)
|
||||
revisions = list_revisions(deployment_id)
|
||||
latest_revision = revisions["resources"][0]
|
||||
latest_revision_id = latest_revision["id"]
|
||||
|
||||
# wait for latest revision to be DEPLOYED
|
||||
wait_for_deployment(deployment_id, latest_revision_id)
|
||||
|
||||
# delete the deployment
|
||||
delete_deployment(deployment_id)
|
||||
```
|
||||
@@ -1,873 +0,0 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "LangGraph Control Plane API (Beta)",
|
||||
"version": "0.0.1",
|
||||
"description": "The LangGraph Control Plane API is used to programmatically create and manage LangGraph Server deployments. For example, the APIs can be orchestrated to create custom CI/CD workflows.\n\n### Beta\nThis API is currently in beta and may change or break without notice. This API documentation may not be up-to-date with actual API functionality.\n### Host\nhttps://api.host.langchain.com/\n\n### Authentication\nTo authenticate with the LangGraph Control Plane API, set the `X-Api-Key` header to a valid LangSmith API key for each request.\n\n### Versioning\nEach endpoint path is prefixed with a version (e.g. `v1`).\n\n### Quick Start\n\n1. Call `POST /{version}/projects` to create a new `Project`.\n2. Call `GET /{version}/projects` to retrieve the `Project` `id`. The `Project` `id` is needed in subsequent API calls.\n3. Call `POST /{version}/projects/{project_id}/revisions` to create a new `Revision` for the `Project`.\n4. Call `GET /{version}/projects/{project_id}/revisions` to get the latest `Revision` (first element in returned list). Get the `Revision` `id`.\n5. Poll for `Revision` `status` until `status` is `DEPLOYED` by calling `GET /{version}/projects/{project_id}/revisions/{revision_id}`."
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://api.host.langchain.com"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "Projects (v1)",
|
||||
"description": "A project corresponds to a LangGraph Server deployment and the associated LangSmith tracing project.\n\nCreating a project via API is not currently supported/documented."
|
||||
},
|
||||
{
|
||||
"name": "Revisions (v1)",
|
||||
"description": "A revision is a version of a LangGraph Server deployment. Different revisions may contain different code and/or environment variables. A project can have many revisions."
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/v1/projects": {
|
||||
"post": {
|
||||
"tags": ["Projects (v1)"],
|
||||
"summary": "Create Project",
|
||||
"description": "Create a new project.",
|
||||
"operationId": "create_project_projects_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateProjectRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"tags": ["Projects (v1)"],
|
||||
"summary": "List Projects",
|
||||
"description": "List all projects.",
|
||||
"operationId": "list_projects_projects_get",
|
||||
"parameters": [
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"title": "Limit",
|
||||
"description": "Maximum number of results to return. Minimum: 1. Maximum: 100.",
|
||||
"default": 20
|
||||
},
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"title": "Offset",
|
||||
"description": "Pagination offset value. Pass this value in subsequent requests to retrieve the next page of results. Minimum: 0.",
|
||||
"default": 0
|
||||
},
|
||||
"name": "offset",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"title": "Name Contains",
|
||||
"description": "Filter string to filter projects by `name`."
|
||||
},
|
||||
"name": "name_contains",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/projects/{project_id}": {
|
||||
"get": {
|
||||
"tags": ["Projects (v1)"],
|
||||
"summary": "Get Project",
|
||||
"description": "Get project by ID.",
|
||||
"operationId": "get_project_projects__project_id__get",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": ["Projects (v1)"],
|
||||
"summary": "Delete Project",
|
||||
"description": "Delete project by ID.",
|
||||
"operationId": "delete_project_projects__project_id__delete",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/projects/{project_id}/revisions": {
|
||||
"get": {
|
||||
"tags": ["Revisions (v1)"],
|
||||
"summary": "List Revisions",
|
||||
"description": "List revisions of a project.",
|
||||
"operationId": "list_revisions_projects__project_id__revisions_get",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"title": "Limit",
|
||||
"description": "Maximum number of results to return. Minimum: 1. Maximum: 100.",
|
||||
"default": 20
|
||||
},
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"title": "Offset",
|
||||
"description": "Pagination offset value. Pass this value in subsequent requests to retrieve the next page of results. Minimum: 0.",
|
||||
"default": 0
|
||||
},
|
||||
"name": "offset",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Revision"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": ["Revisions (v1)"],
|
||||
"summary": "Create Revision",
|
||||
"description": "Create a new revision for a project.",
|
||||
"operationId": "create_revision_projects__project_id__revisions_post",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateRevisionRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/projects/{project_id}/revisions/{revision_id}": {
|
||||
"get": {
|
||||
"tags": ["Revisions (v1)"],
|
||||
"summary": "Get Revision",
|
||||
"description": "Get revision by ID.",
|
||||
"operationId": "get_revision_projects__project_id__revisions__revision_id__get",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Revision ID"
|
||||
},
|
||||
"name": "revision_id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Revision"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/projects/{project_id}/revisions/{revision_id}/deploy": {
|
||||
"post": {
|
||||
"tags": ["Revisions (v1)"],
|
||||
"summary": "Deploy Revision",
|
||||
"description": "Deploy revision by ID.\n\nThis endpoint redeploys the deployment of a revision without rebuilding the image for the deployment. Redeploying the deployment of a revision may mitigate intermittent issues with a deployment.\n\nThe revision must be in the `DEPLOYED` status and must be the latest revision of the project.",
|
||||
"operationId": "deploy_revision_projects__project_id__revisions__revision_id__deploy_post",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Revision ID"
|
||||
},
|
||||
"name": "revision_id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"400": {
|
||||
"description": "Revision is not in DEPLOYED status or revision is not the latest revision for the project.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Revision not found.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/projects/{project_id}/revisions/{revision_id}/interrupt": {
|
||||
"post": {
|
||||
"tags": ["Revisions (v1)"],
|
||||
"summary": "Interrupt Revision",
|
||||
"description": "Interrupt revision by ID.\n\nIf the deployment of a revision appears \"stuck\", the revision may need to be interrupted. A new revision cannot be created if the latest revision is in a non-terminal `status`. In this scenario, the revision may need to be interrupted.",
|
||||
"operationId": "interrupt_revision_projects__project_id__revisions__revision_id__interrupt_post",
|
||||
"parameters": [
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Project ID"
|
||||
},
|
||||
"name": "project_id",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Revision ID"
|
||||
},
|
||||
"name": "revision_id",
|
||||
"in": "path"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"apiKeyAuth": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "X-Api-Key"
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"ContainerSpec": {
|
||||
"type": "object",
|
||||
"description": "Container specification for a revision's deployment.\n\nIf any field is omitted or set to `null`, the internal default value is used depending on the deployment type (`dev` or `prod`).",
|
||||
"properties": {
|
||||
"min_scale": {
|
||||
"type": ["integer", "null"],
|
||||
"description": "Minimum number of replicas in deployment.",
|
||||
"default": "null"
|
||||
},
|
||||
"max_scale": {
|
||||
"type": ["integer", "null"],
|
||||
"description": "Maximum number of replicas in deployment.",
|
||||
"default": "null"
|
||||
},
|
||||
"cpu": {
|
||||
"type": ["integer", "null"],
|
||||
"description": "Number of vCPU cores per replica.",
|
||||
"default": "null"
|
||||
},
|
||||
"memory_mb": {
|
||||
"type": ["integer", "null"],
|
||||
"description": "Amount of memory in MB per replica.",
|
||||
"default": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateProjectRequest":{
|
||||
"type": "object",
|
||||
"description": "Object for creating a new project.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the project.",
|
||||
"required": true
|
||||
},
|
||||
"lc_hosted": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the project is hosted on LangChain's cloud (i.e. Cloud SaaS deployment option). Set to `false` for Self-Hosted Data Plane and Self-Hosted Control Plane deployment options.",
|
||||
"default": true
|
||||
},
|
||||
"repo_url": {
|
||||
"type": ["string", "null"],
|
||||
"description": "URL of the GitHub repository to use for the project. Omit this field if creating a new project from a Docker image.",
|
||||
"default": "null"
|
||||
},
|
||||
"repo_path": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Path to `langgraph.json` configuration file. For example, `langgraph.json` or `src/langgraph.json`.\n\nIf this field is omitted or set to `null`, the previous revision's `repo_path` value is used. Set this field for deployments from a GitHub repository. Omit this field if creating a new revision from a Docker image.",
|
||||
"default": "null"
|
||||
},
|
||||
"repo_commit": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Git branch name of deployment.\n\nThis field only applies to deployments from a GitHub repository.",
|
||||
"default": "null"
|
||||
},
|
||||
"env_vars": {
|
||||
"type": "array",
|
||||
"description": "List of environment variables or secrets.\n\nIf this field is omitted or set to `null`, the previous revision's `env_vars` value is used.",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/EnvVar"
|
||||
},
|
||||
"default": "null"
|
||||
},
|
||||
"host_integration_id": {
|
||||
"type": ["string", "null"],
|
||||
"format": "uuid",
|
||||
"description": "Do not use."
|
||||
},
|
||||
"deployment_type": {
|
||||
"type": "string",
|
||||
"description": "Development (`dev`) or Production (`prod`) type deployment.",
|
||||
"enum": [
|
||||
"dev",
|
||||
"prod"
|
||||
]
|
||||
},
|
||||
"shareable": {
|
||||
"type": ["boolean", "null"],
|
||||
"description": "Boolean flag to configure if a deployment is shareable through LangGraph Studio.\n\nIf this field is omitted or set to `null`, the previous revision's `shareable` value is used. This field does not apply to BYOC deployments.",
|
||||
"default": "null"
|
||||
},
|
||||
"platform": {
|
||||
"type": "object",
|
||||
"description": "Do not use.",
|
||||
"default": "null"
|
||||
},
|
||||
"image_path": {
|
||||
"type": ["string", "null"],
|
||||
"description": "URI of the Docker image to deploy.\n\nIf this field is omitted or set to `null`, the previous revision's `image_path` value is used. Set this field for BYOC deployments. Omit this field if creating a new revision from a GitHub repository.",
|
||||
"default": "null"
|
||||
},
|
||||
"build_on_push": {
|
||||
"type": "boolean",
|
||||
"description": "Boolean flag to indicate if a new revision is automatically created on push to GitHub branch (`repo_branch`).\n\nThis field does not apply for BYOC deployments.",
|
||||
"default": false
|
||||
},
|
||||
"container_spec": {
|
||||
"description": "If this field is omitted or set to `null`, the previous revision's `container_spec` value is used.",
|
||||
"$ref": "#/components/schemas/ContainerSpec",
|
||||
"default": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateRevisionRequest": {
|
||||
"type": "object",
|
||||
"description": "Object for creating a new revision.",
|
||||
"properties": {
|
||||
"image_path": {
|
||||
"type": ["string", "null"],
|
||||
"description": "URI of the Docker image to deploy.\n\nIf this field is omitted or set to `null`, the previous revision's `image_path` value is used. Set this field for BYOC deployments. Omit this field if creating a new revision from a GitHub repository.",
|
||||
"default": "null"
|
||||
},
|
||||
"repo_path": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Path to `langgraph.json` configuration file. For example, `langgraph.json` or `src/langgraph.json`.\n\nIf this field is omitted or set to `null`, the previous revision's `repo_path` value is used. Set this field for deployments from a GitHub repository. Omit this field if creating a new revision from a Docker image.",
|
||||
"default": "null"
|
||||
},
|
||||
"env_vars": {
|
||||
"type": "array",
|
||||
"description": "List of environment variables or secrets.\n\nIf this field is omitted or set to `null`, the previous revision's `env_vars` value is used.",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/EnvVar"
|
||||
},
|
||||
"default": "null"
|
||||
},
|
||||
"shareable": {
|
||||
"type": ["boolean", "null"],
|
||||
"description": "Boolean flag to configure if a deployment is shareable through LangGraph Studio.\n\nIf this field is omitted or set to `null`, the previous revision's `shareable` value is used. This field does not apply to BYOC deployments.",
|
||||
"default": "null"
|
||||
},
|
||||
"container_spec": {
|
||||
"description": "If this field is omitted or set to `null`, the previous revision's `container_spec` value is used.",
|
||||
"$ref": "#/components/schemas/ContainerSpec",
|
||||
"default": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EnvVar": {
|
||||
"type": "object",
|
||||
"description": "An environment variable or secret.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Environment variable or secret name.",
|
||||
"required": true
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"description": "Environment variable or secret value.",
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"secret"
|
||||
],
|
||||
"description": "Field to designate type of the environment variable (default) or secret.",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ErrorResponse": {
|
||||
"type": "object",
|
||||
"description": "Error response.",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"description": "Error details.",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"Project": {
|
||||
"type": "object",
|
||||
"description": "A project corresponds to a LangGraph Server deployment and the associated LangSmith tracing project.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "ID of the project.",
|
||||
"required": true
|
||||
},
|
||||
"tool_name": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Do not use."
|
||||
},
|
||||
"display_name": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Do not use."
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Do not use."
|
||||
},
|
||||
"example_input": {
|
||||
"type": ["object", "null"],
|
||||
"description": "Do not use."
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "ID of the tenant/workspace of the project.",
|
||||
"required": true
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of when the project was created.",
|
||||
"required": true
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of when the project was updated.",
|
||||
"required": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the project.\n\nThis is also the name of the LangSmith tracing project for the LangGraph deployment.",
|
||||
"required": true
|
||||
},
|
||||
"lc_hosted": {
|
||||
"type": "boolean",
|
||||
"description": "Boolean flag to indicate if the deployment is hosted in LangChain's cloud or an external cloud (e.g. BYOC).",
|
||||
"required": true
|
||||
},
|
||||
"repo_url": {
|
||||
"type": ["string", "null"],
|
||||
"description": "URL of the GitHub repository.\n\nThis field is not used for deployments from a Docker image."
|
||||
},
|
||||
"repo_branch": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Branch of the GitHub repository.\n\nThis field is not used for deployments from a Docker image."
|
||||
},
|
||||
"tracer_session_id": {
|
||||
"type": ["string", "null"],
|
||||
"format": "uuid",
|
||||
"description": "Do not use."
|
||||
},
|
||||
"api_key_id": {
|
||||
"type": ["string", "null"],
|
||||
"format": "uuid",
|
||||
"description": "Do not use."
|
||||
},
|
||||
"build_on_push": {
|
||||
"type": "boolean",
|
||||
"description": "Boolean flag to indicate if a new revision is automatically created on push to GitHub branch (`repo_branch`).\n\nThis field does not apply for BYOC deployments."
|
||||
},
|
||||
"input_json_schemas": {
|
||||
"type": ["object", "null"],
|
||||
"description": "Do not use."
|
||||
},
|
||||
"output_json_schemas": {
|
||||
"type": ["object", "null"],
|
||||
"description": "Do not use."
|
||||
},
|
||||
"host_integration_id": {
|
||||
"type": ["string", "null"],
|
||||
"format": "uuid",
|
||||
"description": "Do not use."
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "#/components/schemas/ProjectMetadata"
|
||||
},
|
||||
"resource": {
|
||||
"$ref": "#/components/schemas/ResourceService"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"AWAITING_DATABASE",
|
||||
"READY",
|
||||
"AWAITING_DELETE",
|
||||
"UNKNOWN"
|
||||
],
|
||||
"description": "Deployment status of the project.\n\nNon-terminal statuses: `AWAITING_DATABASE`, `AWAITING_DELETE`. All other statuses are terminal."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ProjectMetadata": {
|
||||
"type": "object",
|
||||
"description": "Metadata associated with a `Project`.",
|
||||
"properties": {
|
||||
"deployment_type": {
|
||||
"type": "string",
|
||||
"description": "Development (`dev`) or Production (`prod`) type deployment.",
|
||||
"enum": [
|
||||
"dev",
|
||||
"prod"
|
||||
]
|
||||
},
|
||||
"image_source": {
|
||||
"type": "string",
|
||||
"description": "Do not use.",
|
||||
"enum": [
|
||||
"github",
|
||||
"internal_docker",
|
||||
"external_docker"
|
||||
]
|
||||
},
|
||||
"shareable": {
|
||||
"type": "boolean",
|
||||
"description": "Boolean flag to configure if a deployment is shareable through LangGraph Studio.\n\nThis field does not apply to BYOC deployments."
|
||||
},
|
||||
"region": {
|
||||
"type": "string",
|
||||
"description": "Region of deployment.\n\nRegion value is cloud provider specific."
|
||||
},
|
||||
"aws_account_id": {
|
||||
"type": "string",
|
||||
"description": "AWS account ID of BYOC deployment.\n\nThis field does not apply to non-BYOC deployments."
|
||||
},
|
||||
"aws_external_id": {
|
||||
"type": "string",
|
||||
"description": "Do not use."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResourceId": {
|
||||
"type": "object",
|
||||
"description": "Internal identifier for a `ResourceRevision` or `ResourceService`.",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"revisions",
|
||||
"services"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResourceRevision": {
|
||||
"type": "object",
|
||||
"description": "Internal revision resource for a `ResourceService`.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/components/schemas/ResourceId"
|
||||
},
|
||||
"env_vars": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/EnvVar"
|
||||
}
|
||||
},
|
||||
"hosted_langserve_revision_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "References `id` of a `Revision`."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResourceService": {
|
||||
"type": "object",
|
||||
"description": "Internal service resource for a `Project`.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/components/schemas/ResourceId"
|
||||
},
|
||||
"url": {
|
||||
"type": ["string", "null"],
|
||||
"description": "URL of LangGraph Server deployment."
|
||||
},
|
||||
"latest_revision": {
|
||||
"description": "References latest `ResourceRevision`.\n\nThe latest `ResourceRevision` may not be active if it's currently being deployed.",
|
||||
"$ref": "#/components/schemas/ResourceRevision"
|
||||
},
|
||||
"latest_active_revision": {
|
||||
"description": "References latest active `ResourceRevision`.\n\nThe latest active `ResourceRevision` is not always the latest `ResourceRevision`.",
|
||||
"$ref": "#/components/schemas/ResourceRevision"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Revision": {
|
||||
"type": "object",
|
||||
"description": "A revision is a version of a LangGraph Server deployment.\n\nDifferent revisions may contain different code and/or environment variables. A project can have many revisions.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "ID of the revision.",
|
||||
"required": true
|
||||
},
|
||||
"project_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "References `id` of `Project`.",
|
||||
"required": true
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of when the revision was created.",
|
||||
"required": true
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of when the revision was updated.",
|
||||
"required": true
|
||||
},
|
||||
"repo_path": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Path to `langgraph.json` configuration file. For example, `langgraph.json` or `src/langgraph.json`.\n\nThis field only applies to deployments from a GitHub repository.",
|
||||
"default": "null"
|
||||
},
|
||||
"repo_commit": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Git branch name of deployment.\n\nThis field only applies to deployments from a GitHub repository.",
|
||||
"default": "null"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"CREATING",
|
||||
"AWAITING_BUILD",
|
||||
"BUILDING",
|
||||
"AWAITING_DEPLOY",
|
||||
"DEPLOYING",
|
||||
"CREATE_FAILED",
|
||||
"BUILD_FAILED",
|
||||
"DEPLOY_FAILED",
|
||||
"DEPLOYED",
|
||||
"INTERRUPTED",
|
||||
"UNKNOWN"
|
||||
],
|
||||
"description": "Deployment status of the revision.\n\nNon-terminal statuses: `CREATING`, `AWAITING_BUILD`, `BUILDING`, `AWAITING_DEPLOY`, `DEPLOYING`. All other statuses are terminal."
|
||||
},
|
||||
"status_message": {
|
||||
"type": "string",
|
||||
"description": "Message associated with the `status`."
|
||||
},
|
||||
"gcp_build_name": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Do not use."
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "#/components/schemas/RevisionMetadata"
|
||||
},
|
||||
"image_path": {
|
||||
"type": ["string", "null"],
|
||||
"description": "URI of the Docker image to deploy.\n\nThis field does not apply to deployments from a GitHub repository.",
|
||||
"default": "null"
|
||||
},
|
||||
"container_spec": {
|
||||
"$ref": "#/components/schemas/ContainerSpec"
|
||||
},
|
||||
"resource": {
|
||||
"$ref": "#/components/schemas/ResourceRevision"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RevisionMetadata": {
|
||||
"type": "object",
|
||||
"description": "Metadata associated with a `Revision`.",
|
||||
"properties": {
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"description": "Do not use."
|
||||
},
|
||||
"repo_commit_sha": {
|
||||
"type": "string",
|
||||
"description": "Git commit SHA of the deployment.\n\nThis field only applies to deployments from a GitHub repository."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,544 +0,0 @@
|
||||
# LangGraph CLI
|
||||
|
||||
The LangGraph command line interface includes commands to build and run a LangGraph Platform API server locally in [Docker](https://www.docker.com/). For development and testing, you can use the CLI to deploy a local API server.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Ensure that Docker is installed (e.g. `docker --version`).
|
||||
2. Install the CLI package:
|
||||
|
||||
=== "Python"
|
||||
```bash
|
||||
pip install langgraph-cli
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
```bash
|
||||
npx @langchain/langgraph-cli
|
||||
|
||||
# Install globally, will be available as `langgraphjs`
|
||||
npm install -g @langchain/langgraph-cli
|
||||
```
|
||||
|
||||
3. Run the command `langgraph --help` or `npx @langchain/langgraph-cli --help` to confirm that the CLI is working correctly.
|
||||
|
||||
[](){#langgraph.json}
|
||||
|
||||
## Configuration File {#configuration-file}
|
||||
|
||||
The LangGraph CLI requires a JSON configuration file that follows this [schema](https://raw.githubusercontent.com/langchain-ai/langgraph/refs/heads/main/libs/cli/schemas/schema.json). It contains the following properties:
|
||||
|
||||
<div class="admonition tip">
|
||||
<p class="admonition-title">Note</p>
|
||||
<p>
|
||||
The LangGraph CLI defaults to using the configuration file <strong>langgraph.json</strong> in the current directory.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
=== "Python"
|
||||
|
||||
| Key | Description |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| <span style="white-space: nowrap;">`dependencies`</span> | **Required**. Array of dependencies for LangGraph Platform API server. Dependencies can be one of the following: <ul><li>A single period (`"."`), which will look for local Python packages.</li><li>The directory path where `pyproject.toml`, `setup.py` or `requirements.txt` is located.</br></br>For example, if `requirements.txt` is located in the root of the project directory, specify `"./"`. If it's located in a subdirectory called `local_package`, specify `"./local_package"`. Do not specify the string `"requirements.txt"` itself.</li><li>A Python package name.</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;">`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;">`keep_pkg_tools`</span> | _(Added in v0.3.4)_ Optional. Control whether to retain Python packaging tools (`pip`, `setuptools`, `wheel`) in the final image. Accepted values: <ul><li><code>true</code> : Keep all three tools (skip uninstall).</li><li><code>false</code> / omitted : Uninstall all three tools (default behaviour).</li><li><code>list[str]</code> : Names of tools <strong>to retain</strong>. Each value must be one of "pip", "setuptools", "wheel".</li></ul>. By default, all three tools are uninstalled. |
|
||||
| <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>`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><li>`disable_assistants`: Disable `/assistants` routes</li><li>`disable_mcp`: Disable `/mcp` routes</li><li>`disable_meta`: Disable `/ok`, `/info`, `/metrics`, and `/docs` routes</li><li>`disable_runs`: Disable `/runs` routes</li><li>`disable_store`: Disable `/store` routes</li><li>`disable_threads`: Disable `/threads` routes</li><li>`disable_ui`: Disable `/ui` routes</li><li>`disable_webhooks`: Disable webhooks calls on run completion in all routes</li><li>`mount_prefix`: Prefix for mounted routes (e.g., "/my-deployment/api")</li></ul> |
|
||||
|
||||
=== "JS"
|
||||
|
||||
| Key | Description |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| <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>`./src/graph.ts:variable`, where `variable` is an instance of `CompiledStateGraph`</li><li>`./src/graph.ts:makeGraph`, where `makeGraph` is a function that takes a config dictionary (`LangGraphRunnableConfig`) and returns an instance of `StateGraph` or `CompiledStateGraph`. See [how to rebuild a graph at runtime](../../cloud/deployment/graph_rebuild.md) for more details.</li></ul> |
|
||||
| <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;">`node_version`</span> | Specify `node_version: 20` to use LangGraph.js. |
|
||||
| <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> |
|
||||
|
||||
### Examples
|
||||
|
||||
=== "Python"
|
||||
|
||||
#### Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"chat": "./chat/graph.py:graph"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 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.
|
||||
|
||||
The `index.fields` configuration determines which parts of your documents to embed:
|
||||
|
||||
- If omitted or set to `["$"]`, the entire document will be embedded
|
||||
- To embed specific fields, use JSON path notation: `["metadata.title", "content.text"]`
|
||||
- Documents missing specified fields will still be stored but won't have embeddings for those fields
|
||||
- You can still override which fields to embed on a specific item at `put` time using the `index` parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"memory_agent": "./agent/graph.py:graph"
|
||||
},
|
||||
"store": {
|
||||
"index": {
|
||||
"embed": "openai:text-embedding-3-small",
|
||||
"dims": 1536,
|
||||
"fields": ["$"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! note "Common model dimensions"
|
||||
- `openai:text-embedding-3-large`: 3072
|
||||
- `openai:text-embedding-3-small`: 1536
|
||||
- `openai:text-embedding-ada-002`: 1536
|
||||
- `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
|
||||
|
||||
#### Semantic search with a custom embedding function
|
||||
|
||||
If you want to use semantic search with a custom embedding function, you can pass a path to a custom embedding function:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"memory_agent": "./agent/graph.py:graph"
|
||||
},
|
||||
"store": {
|
||||
"index": {
|
||||
"embed": "./embeddings.py:embed_texts",
|
||||
"dims": 768,
|
||||
"fields": ["text", "summary"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `embed` field in store configuration can reference a custom function that takes a list of strings and returns a list of embeddings. Example implementation:
|
||||
|
||||
```python
|
||||
# embeddings.py
|
||||
def embed_texts(texts: list[str]) -> list[list[float]]:
|
||||
"""Custom embedding function for semantic search."""
|
||||
# Implementation using your preferred embedding model
|
||||
return [[0.1, 0.2, ...] for _ in texts] # dims-dimensional vectors
|
||||
```
|
||||
|
||||
#### Adding custom authentication
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"chat": "./chat/graph.py:graph"
|
||||
},
|
||||
"auth": {
|
||||
"path": "./auth.py:auth",
|
||||
"openapi": {
|
||||
"securitySchemes": {
|
||||
"apiKeyAuth": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "X-API-Key"
|
||||
}
|
||||
},
|
||||
"security": [{ "apiKeyAuth": [] }]
|
||||
},
|
||||
"disable_studio_auth": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See the [authentication conceptual guide](../../concepts/auth.md) for details, and the [setting up custom authentication](../../tutorials/auth/getting_started.md) guide for a practical walk through of the process.
|
||||
|
||||
#### Configuring Store Item Time-to-Live (TTL)
|
||||
|
||||
You can configure default data expiration for items/memories in the BaseStore using the `store.ttl` key. This determines how long items are retained after they are last accessed (with reads potentially refreshing the timer based on `refresh_on_read`). Note that these defaults can be overwritten on a per-call basis by modifying the corresponding arguments in `get`, `search`, etc.
|
||||
|
||||
The `ttl` configuration is an object containing optional fields:
|
||||
|
||||
- `refresh_on_read`: If `true` (the default), accessing an item via `get` or `search` resets its expiration timer. Set to `false` to only refresh TTL on writes (`put`).
|
||||
- `default_ttl`: The default lifespan of an item in **minutes**. If not set, items do not expire by default.
|
||||
- `sweep_interval_minutes`: How frequently (in minutes) the system should run a background process to delete expired items. If not set, sweeping does not occur automatically.
|
||||
|
||||
Here is an example enabling a 7-day TTL (10080 minutes), refreshing on reads, and sweeping every hour:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"memory_agent": "./agent/graph.py:graph"
|
||||
},
|
||||
"store": {
|
||||
"ttl": {
|
||||
"refresh_on_read": true,
|
||||
"sweep_interval_minutes": 60,
|
||||
"default_ttl": 10080
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Configuring Checkpoint Time-to-Live (TTL)
|
||||
|
||||
You can configure the time-to-live (TTL) for checkpoints using the `checkpointer` key. This determines how long checkpoint data is retained before being automatically handled according to the specified strategy (e.g., deletion). The `ttl` configuration is an object containing:
|
||||
|
||||
- `strategy`: The action to take on expired checkpoints (currently `"delete"` is the only accepted option).
|
||||
- `sweep_interval_minutes`: How frequently (in minutes) the system checks for expired checkpoints.
|
||||
- `default_ttl`: The default lifespan of a checkpoint in **minutes**.
|
||||
|
||||
Here's an example setting a default TTL of 30 days (43200 minutes):
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"chat": "./chat/graph.py:graph"
|
||||
},
|
||||
"checkpointer": {
|
||||
"ttl": {
|
||||
"strategy": "delete",
|
||||
"sweep_interval_minutes": 10,
|
||||
"default_ttl": 43200
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, checkpoints older than 30 days will be deleted, and the check runs every 10 minutes.
|
||||
|
||||
|
||||
=== "JS"
|
||||
|
||||
#### Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"graphs": {
|
||||
"chat": "./src/graph.ts:graph"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Commands
|
||||
|
||||
**Usage**
|
||||
|
||||
=== "Python"
|
||||
|
||||
The base command for the LangGraph CLI is `langgraph`.
|
||||
|
||||
```
|
||||
langgraph [OPTIONS] COMMAND [ARGS]
|
||||
```
|
||||
=== "JS"
|
||||
|
||||
The base command for the LangGraph.js CLI is `langgraphjs`.
|
||||
|
||||
```
|
||||
npx @langchain/langgraph-cli [OPTIONS] COMMAND [ARGS]
|
||||
```
|
||||
|
||||
We recommend using `npx` to always use the latest version of the CLI.
|
||||
|
||||
### `dev`
|
||||
|
||||
=== "Python"
|
||||
|
||||
Run LangGraph API server in development mode with hot reloading and debugging capabilities. This lightweight server requires no Docker installation and is suitable for development and testing. State is persisted to a local directory.
|
||||
|
||||
!!! note
|
||||
|
||||
Currently, the CLI only supports Python >= 3.11.
|
||||
|
||||
**Installation**
|
||||
|
||||
This command requires the "inmem" extra to be installed:
|
||||
|
||||
```bash
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
```
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
langgraph dev [OPTIONS]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| ----------------------------- | ---------------- | ----------------------------------------------------------------------------------- |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables |
|
||||
| `--host TEXT` | `127.0.0.1` | Host to bind the server to |
|
||||
| `--port INTEGER` | `2024` | Port to bind the server to |
|
||||
| `--no-reload` | | Disable auto-reload |
|
||||
| `--n-jobs-per-worker INTEGER` | | Number of jobs per worker. Default is 10 |
|
||||
| `--debug-port INTEGER` | | Port for debugger to listen on |
|
||||
| `--wait-for-client` | `False` | Wait for a debugger client to connect to the debug port before starting the server |
|
||||
| `--no-browser` | | Skip automatically opening the browser when the server starts |
|
||||
| `--studio-url TEXT` | | URL of the LangGraph Studio instance to connect to. Defaults to https://smith.langchain.com |
|
||||
| `--allow-blocking` | `False` | Do not raise errors for synchronous I/O blocking operations in your code (added in `0.2.6`) |
|
||||
| `--tunnel` | `False` | Expose the local server via a public tunnel (Cloudflare) for remote frontend access. This avoids issues with browsers like Safari or networks blocking localhost connections |
|
||||
| `--help` | | Display command documentation |
|
||||
|
||||
|
||||
=== "JS"
|
||||
|
||||
Run LangGraph API server in development mode with hot reloading capabilities. This lightweight server requires no Docker installation and is suitable for development and testing. State is persisted to a local directory.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
npx @langchain/langgraph-cli dev [OPTIONS]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| ----------------------------- | ---------------- | ----------------------------------------------------------------------------------- |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables |
|
||||
| `--host TEXT` | `127.0.0.1` | Host to bind the server to |
|
||||
| `--port INTEGER` | `2024` | Port to bind the server to |
|
||||
| `--no-reload` | | Disable auto-reload |
|
||||
| `--n-jobs-per-worker INTEGER` | | Number of jobs per worker. Default is 10 |
|
||||
| `--debug-port INTEGER` | | Port for debugger to listen on |
|
||||
| `--wait-for-client` | `False` | Wait for a debugger client to connect to the debug port before starting the server |
|
||||
| `--no-browser` | | Skip automatically opening the browser when the server starts |
|
||||
| `--studio-url TEXT` | | URL of the LangGraph Studio instance to connect to. Defaults to https://smith.langchain.com |
|
||||
| `--allow-blocking` | `False` | Do not raise errors for synchronous I/O blocking operations in your code |
|
||||
| `--tunnel` | `False` | Expose the local server via a public tunnel (Cloudflare) for remote frontend access. This avoids issues with browsers or networks blocking localhost connections |
|
||||
| `--help` | | Display command documentation |
|
||||
|
||||
### `build`
|
||||
|
||||
=== "Python"
|
||||
|
||||
Build LangGraph Platform API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
langgraph build [OPTIONS]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| 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. |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
|
||||
| `--help` | | Display command documentation. |
|
||||
|
||||
=== "JS"
|
||||
|
||||
Build LangGraph Platform API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
npx @langchain/langgraph-cli build [OPTIONS]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| 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. |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
|
||||
| `--help` | | Display command documentation. |
|
||||
|
||||
|
||||
### `up`
|
||||
|
||||
=== "Python"
|
||||
|
||||
Start LangGraph API server. For local testing, requires a LangSmith API key with access to LangGraph Platform. Requires a license key for production use.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
langgraph up [OPTIONS]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| ---------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--wait` | | Wait for services to start before returning. Implies --detach |
|
||||
| `--base-image TEXT` | `langchain/langgraph-api` | Base image to use for the LangGraph API server. Pin to specific versions using version tags. |
|
||||
| `--image TEXT` | | Docker image to use for the langgraph-api service. If specified, skips building and uses this image directly. |
|
||||
| `--postgres-uri TEXT` | Local database | Postgres URI to use for the database. |
|
||||
| `--watch` | | Restart on file changes |
|
||||
| `--debugger-base-url TEXT` | `http://127.0.0.1:[PORT]` | URL used by the debugger to access LangGraph API. |
|
||||
| `--debugger-port INTEGER` | | Pull the debugger image locally and serve the UI on specified port |
|
||||
| `--verbose` | | Show more output from the server logs. |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
|
||||
| `-d, --docker-compose FILE` | | Path to docker-compose.yml file with additional services to launch. |
|
||||
| `-p, --port INTEGER` | `8123` | Port to expose. Example: `langgraph up --port 8000` |
|
||||
| `--pull / --no-pull` | `pull` | Pull latest images. Use `--no-pull` for running the server with locally-built images. Example: `langgraph up --no-pull` |
|
||||
| `--recreate / --no-recreate` | `no-recreate` | Recreate containers even if their configuration and image haven't changed |
|
||||
| `--help` | | Display command documentation. |
|
||||
|
||||
=== "JS"
|
||||
|
||||
Start LangGraph API server. For local testing, requires a LangSmith API key with access to LangGraph Platform. Requires a license key for production use.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
npx @langchain/langgraph-cli up [OPTIONS]
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| ---------------------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| <span style="white-space: nowrap;">`--wait`</span> | | Wait for services to start before returning. Implies --detach |
|
||||
| <span style="white-space: nowrap;">`--base-image TEXT`</span> | <span style="white-space: nowrap;">`langchain/langgraph-api`</span> | Base image to use for the LangGraph API server. Pin to specific versions using version tags. |
|
||||
| <span style="white-space: nowrap;">`--image TEXT`</span> | | Docker image to use for the langgraph-api service. If specified, skips building and uses this image directly. |
|
||||
| <span style="white-space: nowrap;">`--postgres-uri TEXT`</span> | Local database | Postgres URI to use for the database. |
|
||||
| <span style="white-space: nowrap;">`--watch`</span> | | Restart on file changes |
|
||||
| <span style="white-space: nowrap;">`-c, --config FILE`</span> | `langgraph.json` | Path to configuration file declaring dependencies, graphs and environment variables. |
|
||||
| <span style="white-space: nowrap;">`-d, --docker-compose FILE`</span> | | Path to docker-compose.yml file with additional services to launch. |
|
||||
| <span style="white-space: nowrap;">`-p, --port INTEGER`</span> | `8123` | Port to expose. Example: `langgraph up --port 8000` |
|
||||
| <span style="white-space: nowrap;">`--no-pull`</span> | | Use locally built images. Defaults to `false` to build with latest remote Docker image. |
|
||||
| <span style="white-space: nowrap;">`--recreate`</span> | | Recreate containers even if their configuration and image haven't changed |
|
||||
| <span style="white-space: nowrap;">`--help`</span> | | Display command documentation. |
|
||||
|
||||
### `dockerfile`
|
||||
|
||||
=== "Python"
|
||||
|
||||
Generate a Dockerfile for building a LangGraph Platform API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
langgraph dockerfile [OPTIONS] SAVE_PATH
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| ------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to the [configuration file](#configuration-file) declaring dependencies, graphs and environment variables. |
|
||||
| `--help` | | Show this message and exit. |
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
langgraph dockerfile -c langgraph.json Dockerfile
|
||||
```
|
||||
|
||||
This generates a Dockerfile that looks similar to:
|
||||
|
||||
```dockerfile
|
||||
FROM langchain/langgraph-api:3.11
|
||||
|
||||
ADD ./pipconf.txt /pipconfig.txt
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt langchain_community langchain_anthropic langchain_openai wikipedia scikit-learn
|
||||
|
||||
ADD ./graphs /deps/__outer_graphs/src
|
||||
RUN set -ex && \
|
||||
for line in '[project]' \
|
||||
'name = "graphs"' \
|
||||
'version = "0.1"' \
|
||||
'[tool.setuptools.package-data]' \
|
||||
'"*" = ["**/*"]'; do \
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \
|
||||
done
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
|
||||
```
|
||||
|
||||
???+ note "Updating your langgraph.json file"
|
||||
The `langgraph dockerfile` command translates all the configuration in your `langgraph.json` file into Dockerfile commands. When using this command, you will have to re-run it whenever you update your `langgraph.json` file. Otherwise, your changes will not be reflected when you build or run the dockerfile.
|
||||
|
||||
=== "JS"
|
||||
|
||||
Generate a Dockerfile for building a LangGraph Platform API server Docker image.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
npx @langchain/langgraph-cli dockerfile [OPTIONS] SAVE_PATH
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Default | Description |
|
||||
| ------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `-c, --config FILE` | `langgraph.json` | Path to the [configuration file](#configuration-file) declaring dependencies, graphs and environment variables. |
|
||||
| `--help` | | Show this message and exit. |
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
npx @langchain/langgraph-cli dockerfile -c langgraph.json Dockerfile
|
||||
```
|
||||
|
||||
This generates a Dockerfile that looks similar to:
|
||||
|
||||
```dockerfile
|
||||
FROM langchain/langgraphjs-api:20
|
||||
|
||||
ADD . /deps/agent
|
||||
|
||||
RUN cd /deps/agent && yarn install
|
||||
|
||||
ENV LANGSERVE_GRAPHS='{"agent":"./src/react_agent/graph.ts:graph"}'
|
||||
|
||||
WORKDIR /deps/agent
|
||||
|
||||
RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts
|
||||
```
|
||||
|
||||
???+ note "Updating your langgraph.json file"
|
||||
The `npx @langchain/langgraph-cli dockerfile` command translates all the configuration in your `langgraph.json` file into Dockerfile commands. When using this command, you will have to re-run it whenever you update your `langgraph.json` file. Otherwise, your changes will not be reflected when you build or run the dockerfile.
|
||||
@@ -1,150 +0,0 @@
|
||||
# Environment Variables
|
||||
|
||||
The LangGraph Server supports specific environment variables for configuring a deployment.
|
||||
|
||||
## `BG_JOB_ISOLATED_LOOPS`
|
||||
|
||||
Set `BG_JOB_ISOLATED_LOOPS` to `True` to execute background runs in an isolated event loop separate from the serving API event loop.
|
||||
|
||||
This environment variable should be set to `True` if the implementation of a graph/node contains synchronous code. In this situation, the synchronous code will block the serving API event loop, which may cause the API to be unavailable. A symptom of an unavailable API is continuous application restarts due to failing health checks.
|
||||
|
||||
Defaults to `False`.
|
||||
|
||||
## `BG_JOB_SHUTDOWN_GRACE_PERIOD_SECS`
|
||||
|
||||
Specifies, in seconds, how long the server will wait for background jobs to finish after the queue receives a shutdown signal. After this period, the server will force termination. Defaults to `180` seconds. Set this to ensure jobs have enough time to complete cleanly during shutdown. Added in `langgraph-api==0.2.16`.
|
||||
|
||||
## `BG_JOB_TIMEOUT_SECS`
|
||||
|
||||
The timeout of a background run can be increased. However, the infrastructure for a Cloud SaaS deployment enforces a 1 hour timeout limit for API requests. This means the connection between client and server will timeout after 1 hour. This is not configurable.
|
||||
|
||||
A background run can execute for longer than 1 hour, but a client must reconnect to the server (e.g. join stream via `POST /threads/{thread_id}/runs/{run_id}/stream`) to retrieve output from the run if the run is taking longer than 1 hour.
|
||||
|
||||
Defaults to `3600`.
|
||||
|
||||
## `DD_API_KEY`
|
||||
|
||||
Specify `DD_API_KEY` (your [Datadog API Key](https://docs.datadoghq.com/account_management/api-app-keys/)) to automatically enable Datadog tracing for the deployment. Specify other [`DD_*` environment variables](https://ddtrace.readthedocs.io/en/stable/configuration.html) to configure the tracing instrumentation.
|
||||
|
||||
If `DD_API_KEY` is specified, the application process is wrapped in the [`ddtrace-run` command](https://ddtrace.readthedocs.io/en/stable/installation_quickstart.html). Other `DD_*` environment variables (e.g. `DD_SITE`, `DD_ENV`, `DD_SERVICE`, `DD_TRACE_ENABLED`) are typically needed to properly configure the tracing instrumentation. See [`DD_*` environment variables](https://ddtrace.readthedocs.io/en/stable/configuration.html) for more details.
|
||||
|
||||
!!! note
|
||||
Enabling `DD_API_KEY` (and thus `ddtrace-run`) can override or interfere with other auto-instrumentation solutions (such as OpenTelemetry) that you may have instrumented into your application code.
|
||||
|
||||
## `LANGCHAIN_TRACING_SAMPLING_RATE`
|
||||
|
||||
Sampling rate for traces sent to LangSmith. Valid values: Any float between `0` and `1`.
|
||||
|
||||
See <a href="https://docs.smith.langchain.com/how_to_guides/tracing/sample_traces" target="_blank">LangSmith documentation</a> for more details.
|
||||
|
||||
## `LANGGRAPH_AUTH_TYPE`
|
||||
|
||||
Type of authentication for the LangGraph Server deployment. Valid values: `langsmith`, `noop`.
|
||||
|
||||
For deployments to LangGraph Platform, this environment variable is set automatically. For local development or deployments where authentication is handled externally (e.g. self-hosted), set this environment variable to `noop`.
|
||||
|
||||
## `LANGGRAPH_POSTGRES_POOL_MAX_SIZE`
|
||||
|
||||
Beginning with langgraph-api version `0.2.12`, the maximum size of the Postgres connection pool (per replica) can be controlled using the `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` environment variable. By setting this variable, you can determine the upper bound on the number of simultaneous connections the server will establish with the Postgres database.
|
||||
|
||||
For example, if a deployment is scaled up to 10 replicas and `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` is configured to `150`, then up to `1500` connections to Postgres can be established. This is particularly useful for deployments where database resources are limited (or more available) or where you need to tune connection behavior for performance or scaling reasons.
|
||||
|
||||
Defaults to `150` connections.
|
||||
|
||||
## `LANGSMITH_RUNS_ENDPOINTS`
|
||||
|
||||
For deployments with [self-hosted LangSmith](https://docs.smith.langchain.com/self_hosting) only.
|
||||
|
||||
Set this environment variable to have a deployment send traces to a self-hosted LangSmith instance. The value of `LANGSMITH_RUNS_ENDPOINTS` is a JSON string: `{"<SELF_HOSTED_LANGSMITH_HOSTNAME>":"<LANGSMITH_API_KEY>"}`.
|
||||
|
||||
`SELF_HOSTED_LANGSMITH_HOSTNAME` is the hostname of the self-hosted LangSmith instance. It must be accessible to the deployment. `LANGSMITH_API_KEY` is a LangSmith API generated from the self-hosted LangSmith instance.
|
||||
|
||||
## `LANGSMITH_TRACING`
|
||||
|
||||
Set `LANGSMITH_TRACING` to `false` to disable tracing to LangSmith.
|
||||
|
||||
Defaults to `true`.
|
||||
|
||||
## `LOG_COLOR`
|
||||
|
||||
This is mainly relevant in the context of using the dev server via the `langgraph dev` command. Set `LOG_COLOR` to `true` to enable ANSI-colored console output when using the default console renderer. Disabling color output by setting this variable to `false` produces monochrome logs. Defaults to `true`.
|
||||
|
||||
## `LOG_LEVEL`
|
||||
|
||||
Configure [log level](https://docs.python.org/3/library/logging.html#logging-levels). Defaults to `INFO`.
|
||||
|
||||
## `LOG_JSON`
|
||||
|
||||
Set `LOG_JSON` to `true` to render all log messages as JSON objects using the configured `JSONRenderer`. This produces structured logs that can be easily parsed or ingested by log management systems. 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`.
|
||||
|
||||
## `N_JOBS_PER_WORKER`
|
||||
|
||||
Number of jobs per worker for the LangGraph Server task queue. Defaults to `10`.
|
||||
|
||||
## `POSTGRES_URI_CUSTOM`
|
||||
|
||||
!!! info "Only for Self-Hosted Data Plane and Self-Hosted Control Plane"
|
||||
Custom Postgres instances are only available for [Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
Specify `POSTGRES_URI_CUSTOM` to use a custom Postgres instance. The value of `POSTGRES_URI_CUSTOM` must be a valid [Postgres connection URI](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS).
|
||||
|
||||
Postgres:
|
||||
|
||||
- Version 15.8 or higher.
|
||||
- An initial database must be present and the connection URI must reference the database.
|
||||
|
||||
Control Plane Functionality:
|
||||
|
||||
- If `POSTGRES_URI_CUSTOM` is specified, the LangGraph Control Plane will not provision a database for the server.
|
||||
- If `POSTGRES_URI_CUSTOM` is removed, the LangGraph Control Plane will not provision a database for the server and will not delete the externally managed Postgres instance.
|
||||
- If `POSTGRES_URI_CUSTOM` is removed, deployment of the revision will not succeed. Once `POSTGRES_URI_CUSTOM` is specified, it must always be set for the lifecycle of the deployment.
|
||||
- If the deployment is deleted, the LangGraph Control Plane will not delete the externally managed Postgres instance.
|
||||
- The value of `POSTGRES_URI_CUSTOM` can be updated. For example, a password in the URI can be updated.
|
||||
|
||||
Database Connectivity:
|
||||
|
||||
- The custom Postgres instance must be accessible by the LangGraph Server. The user is responsible for ensuring connectivity.
|
||||
|
||||
## `REDIS_CLUSTER`
|
||||
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
Redis Cluster mode is only available in Self-Hosted Deployment models, LangGraph Platform SaaS will provision a redis instance for you by default.
|
||||
|
||||
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`.
|
||||
|
||||
## `REDIS_KEY_PREFIX`
|
||||
|
||||
!!! info "Available in API Server version 0.1.9+"
|
||||
This environment variable is supported in API Server version 0.1.9 and above.
|
||||
|
||||
Specify a prefix for Redis keys. This allows multiple LangGraph Server instances to share the same Redis instance by using different key prefixes.
|
||||
|
||||
Defaults to `''`.
|
||||
|
||||
## `REDIS_URI_CUSTOM`
|
||||
|
||||
!!! info "Only for Self-Hosted Data Plane and Self-Hosted Control Plane"
|
||||
Custom Redis instances are only available for [Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
Specify `REDIS_URI_CUSTOM` to use a custom Redis instance. The value of `REDIS_URI_CUSTOM` must be a valid [Redis connection URI](https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url).
|
||||
|
||||
## `RESUMABLE_STREAM_TTL_SECONDS`
|
||||
|
||||
Time-to-live in seconds for resumable stream data in Redis.
|
||||
|
||||
When a run is created and the output is streamed, the stream can be configured to be resumable (e.g. `stream_resumable=True`). If a stream is resumable, output from the stream is temporarily stored in Redis. The TTL for this data can be configured by setting `RESUMABLE_STREAM_TTL_SECONDS`.
|
||||
|
||||
See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.RunsClient.stream) and [JS/TS](https://langchain-ai.github.io/langgraphjs/reference/classes/sdk_client.RunsClient.html#stream) SDKs for more details on how to implement resumable streams.
|
||||
|
||||
Defaults to `120` seconds.
|
||||
@@ -1,184 +0,0 @@
|
||||
# LangGraph Server Changelog
|
||||
|
||||
[LangGraph Server](../../concepts/langgraph_server.md) is an API platform for creating and managing agent-based applications. It provides built-in persistence, a task queue, and supports deploying, configuring, and running assistants (agentic workflows) at scale. This changelog documents all notable updates, features, and fixes to LangGraph Server releases.
|
||||
|
||||
---
|
||||
|
||||
## v0.2.96 (2025-07-17)
|
||||
- Added a fallback mechanism for configurable header patterns to handle exclude/include settings more effectively.
|
||||
|
||||
## v0.2.95 (2025-07-17)
|
||||
- Avoided setting the future if it is already done to prevent redundant operations.
|
||||
- Resolved compatibility errors in CI by switching from `typing.TypedDict` to `typing_extensions.TypedDict` for Python versions below 3.12.
|
||||
|
||||
## v0.2.94 (2025-07-16)
|
||||
- Improved performance by omitting pending sends for langgraph versions 0.5 and above.
|
||||
- Improved server startup logs to provide clearer warnings when the DD_API_KEY environment variable is set.
|
||||
|
||||
## v0.2.93 (2025-07-16)
|
||||
- Removed the GIN index for run metadata to improve performance.
|
||||
|
||||
## v0.2.92 (2025-07-16)
|
||||
- Enabled copying functionality for blobs and checkpoints, improving data management flexibility.
|
||||
|
||||
## v0.2.91 (2025-07-16)
|
||||
- Reduced writes to the `checkpoint_blobs` table by inlining small values (null, numeric, str, etc.). This means we don't need to store extra values for channels that haven't been updated.
|
||||
|
||||
## v0.2.90 (2025-07-16)
|
||||
- Improve checkpoint writes via node-local background queueing.
|
||||
|
||||
|
||||
## v0.2.89 (2025-07-15)
|
||||
- Decoupled checkpoint writing from thread/run state by removing foreign keys and updated logger to prevent timeout-related failures.
|
||||
|
||||
## v0.2.88 (2025-07-14)
|
||||
- Removed the foreign key constraint for `thread` in the `run` table to simplify database schema.
|
||||
|
||||
## v0.2.87 (2025-07-14)
|
||||
- Added more detailed logs for Redis worker signaling to improve debugging.
|
||||
|
||||
## v0.2.86 (2025-07-11)
|
||||
- Honored tool descriptions in the `/mcp` endpoint to align with expected functionality.
|
||||
|
||||
## v0.2.85 (2025-07-10)
|
||||
- Added support for the `on_disconnect` field to `runs/wait` and included disconnect logs for better debugging.
|
||||
|
||||
## v0.2.84 (2025-07-09)
|
||||
- Removed unnecessary status updates to streamline thread handling and updated version to 0.2.84.
|
||||
|
||||
## v0.2.83 (2025-07-09)
|
||||
- Reduced the default time-to-live for resumable streams to 2 minutes.
|
||||
- Enhanced data submission logic to send data to both Beacon and LangSmith instance based on license configuration.
|
||||
- Enabled submission of self-hosted data to a Langsmith instance when the endpoint is configured.
|
||||
|
||||
## v0.2.82 (2025-07-03)
|
||||
- Addressed a race condition in background runs by implementing a lock using join, ensuring reliable execution across CTEs.
|
||||
|
||||
## v0.2.81 (2025-07-03)
|
||||
- Optimized run streams by reducing initial wait time to improve responsiveness for older or non-existent runs.
|
||||
|
||||
## v0.2.80 (2025-07-03)
|
||||
- Corrected parameter passing in the `logger.ainfo()` API call to resolve a TypeError.
|
||||
|
||||
## v0.2.79 (2025-07-02)
|
||||
- Fixed a JsonDecodeError in checkpointing with remote graph by correcting JSON serialization to handle trailing slashes properly.
|
||||
- Introduced a configuration flag to disable webhooks globally across all routes.
|
||||
|
||||
## v0.2.78 (2025-07-02)
|
||||
- Added timeout retries to webhook calls to improve reliability.
|
||||
- Added HTTP request metrics, including a request count and latency histogram, for enhanced monitoring capabilities.
|
||||
|
||||
## v0.2.77 (2025-07-02)
|
||||
- Added HTTP metrics to improve performance monitoring.
|
||||
- Changed the Redis cache delimiter to reduce conflicts with subgraph message names and updated caching behavior.
|
||||
|
||||
## v0.2.76 (2025-07-01)
|
||||
- Updated Redis cache delimiter to prevent conflicts with subgraph messages.
|
||||
|
||||
## v0.2.74 (2025-06-30)
|
||||
- Scheduled webhooks in an isolated loop to ensure thread-safe operations and prevent errors with PYTHONASYNCIODEBUG=1.
|
||||
|
||||
## v0.2.73 (2025-06-27)
|
||||
- Fixed an infinite frame loop issue and removed the dict_parser due to structlog's unexpected behavior.
|
||||
- Throw a 409 error on deadlock occurrence during run cancellations to handle lock conflicts gracefully.
|
||||
|
||||
## v0.2.72 (2025-06-27)
|
||||
- Ensured compatibility with future langgraph versions.
|
||||
- Implemented a 409 response status to handle deadlock issues during cancellation.
|
||||
|
||||
## v0.2.71 (2025-06-26)
|
||||
- Improved logging for better clarity and detail regarding log types.
|
||||
|
||||
## v0.2.70 (2025-06-26)
|
||||
- Improved error handling to better distinguish and log TimeoutErrors caused by users from internal run timeouts.
|
||||
|
||||
## v0.2.69 (2025-06-26)
|
||||
- Added sorting and pagination to the crons API and updated schema definitions for improved accuracy.
|
||||
|
||||
## v0.2.66 (2025-06-26)
|
||||
- Fixed a 404 error when creating multiple runs with the same thread_id using `on_not_exist="create"`.
|
||||
|
||||
## v0.2.65 (2025-06-25)
|
||||
- Ensured that only fields from `assistant_versions` are returned when necessary.
|
||||
- Ensured consistent data types for in-memory and PostgreSQL users, improving internal authentication handling.
|
||||
|
||||
## v0.2.64 (2025-06-24)
|
||||
- Added descriptions to version entries for better clarity.
|
||||
|
||||
## v0.2.62 (2025-06-23)
|
||||
- Improved user handling for custom authentication in the JS Studio.
|
||||
- Added Prometheus-format run statistics to the metrics endpoint for better monitoring.
|
||||
- Added run statistics in Prometheus format to the metrics endpoint.
|
||||
|
||||
## v0.2.61 (2025-06-20)
|
||||
- Set a maximum idle time for Redis connections to prevent unnecessary open connections.
|
||||
|
||||
## v0.2.60 (2025-06-20)
|
||||
- Enhanced error logging to include traceback details for dictionary operations.
|
||||
- Added a `/metrics` endpoint to expose queue worker metrics for monitoring.
|
||||
|
||||
## v0.2.57 (2025-06-18)
|
||||
- Removed CancelledError from retriable exceptions to allow local interrupts while maintaining retriability for workers.
|
||||
- Introduced middleware to gracefully shut down the server after completing in-flight requests upon receiving a SIGINT.
|
||||
- Reduced metadata stored in checkpoint to only include necessary information.
|
||||
- Improved error handling in join runs to return error details when present.
|
||||
|
||||
## v0.2.56 (2025-06-17)
|
||||
- Improved application stability by adding a handler for SIGTERM signals.
|
||||
|
||||
## v0.2.55 (2025-06-17)
|
||||
- Improved the handling of cancellations in the queue entrypoint.
|
||||
- Improved cancellation handling in the queue entry point.
|
||||
|
||||
## v0.2.54 (2025-06-16)
|
||||
- Enhanced error message for LuaLock timeout during license validation.
|
||||
- Fixed the $contains filter in custom auth by requiring an explicit ::text cast and updated tests accordingly.
|
||||
- Ensured project and tenant IDs are formatted as UUIDs for consistency.
|
||||
|
||||
## v0.2.53 (2025-06-13)
|
||||
- Resolved a timing issue to ensure the queue starts only after the graph is registered.
|
||||
- Improved performance by setting thread and run status in a single query and enhanced error handling during checkpoint writes.
|
||||
- Reduced the default background grace period to 3 minutes.
|
||||
|
||||
## v0.2.52 (2025-06-12)
|
||||
- Now logging expected graphs when one is omitted to improve traceability.
|
||||
- Implemented a time-to-live (TTL) feature for resumable streams.
|
||||
- Improved query efficiency and consistency by adding a unique index and optimizing row locking.
|
||||
|
||||
## v0.2.51 (2025-06-12)
|
||||
- Handled `CancelledError` by marking tasks as ready to retry, improving error management in worker processes.
|
||||
- Added LG API version and request ID to metadata and logs for better tracking.
|
||||
- Added LG API version and request ID to metadata and logs to improve traceability.
|
||||
- Improved database performance by creating indexes concurrently.
|
||||
- Ensured postgres write is committed only after the Redis running marker is set to prevent race conditions.
|
||||
- Enhanced query efficiency and reliability by adding a unique index on thread_id/running, optimizing row locks, and ensuring deterministic run selection.
|
||||
- Resolved a race condition by ensuring Postgres updates only occur after the Redis running marker is set.
|
||||
|
||||
## v0.2.46 (2025-06-07)
|
||||
- Introduced a new connection for each operation while preserving transaction characteristics in Threads state `update()` and `bulk()` commands.
|
||||
|
||||
## v0.2.45 (2025-06-05)
|
||||
- Enhanced streaming feature by incorporating tracing contexts.
|
||||
- Removed an unnecessary query from the Crons.search function.
|
||||
- Resolved connection reuse issue when scheduling next run for multiple cron jobs.
|
||||
- Removed an unnecessary query in the Crons.search function to improve efficiency.
|
||||
- Resolved an issue with scheduling the next cron run by improving connection reuse.
|
||||
|
||||
## v0.2.44 (2025-06-04)
|
||||
- Enhanced the worker logic to exit the pipeline before continuing when the Redis message limit is reached.
|
||||
- Introduced a ceiling for Redis message size with an option to skip messages larger than 128 MB for improved performance.
|
||||
- Ensured the pipeline always closes properly to prevent resource leaks.
|
||||
|
||||
## v0.2.43 (2025-06-04)
|
||||
- Improved performance by omitting logs in metadata calls and ensuring output schema compliance in value streaming.
|
||||
- Ensured the connection is properly closed after use.
|
||||
- Aligned output format to strictly adhere to the specified schema.
|
||||
- Stopped sending internal logs in metadata requests to improve privacy.
|
||||
|
||||
## v0.2.42 (2025-06-04)
|
||||
- Added timestamps to track the start and end of a request's run.
|
||||
- Added tracer information to the configuration settings.
|
||||
- Added support for streaming with tracing contexts.
|
||||
|
||||
## v0.2.41 (2025-06-03)
|
||||
- Added locking mechanism to prevent errors in pipelined executions.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Python SDK Reference
|
||||
|
||||
::: langgraph_sdk.client
|
||||
handler: python
|
||||
|
||||
|
||||
::: langgraph_sdk.schema
|
||||
handler: python
|
||||
|
||||
::: langgraph_sdk.auth
|
||||
handler: python
|
||||
|
||||
::: langgraph_sdk.auth.types
|
||||
handler: python
|
||||
|
||||
::: langgraph_sdk.auth.exceptions
|
||||
handler: python
|
||||
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 108 KiB |
@@ -1,150 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Application Structure
|
||||
|
||||
## Overview
|
||||
|
||||
A LangGraph application consists of one or more graphs, a configuration file (`langgraph.json`), a file that specifies dependencies, and an optional `.env` file that specifies environment variables.
|
||||
|
||||
This guide shows a typical structure of an application and shows how the required information to deploy an application using the LangGraph Platform is specified.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
To deploy using the LangGraph Platform, the following information should be provided:
|
||||
|
||||
1. A [LangGraph configuration file](#configuration-file-concepts) (`langgraph.json`) that specifies the dependencies, graphs, and environment variables to use for the application.
|
||||
2. The [graphs](#graphs) that implement the logic of the application.
|
||||
3. A file that specifies [dependencies](#dependencies) required to run the application.
|
||||
4. [Environment variables](#environment-variables) that are required for the application to run.
|
||||
|
||||
## File Structure
|
||||
|
||||
Below are examples of directory structures for Python and JavaScript applications:
|
||||
|
||||
=== "Python (requirements.txt)"
|
||||
|
||||
```plaintext
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for your graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
├── .env # environment variables
|
||||
├── requirements.txt # package dependencies
|
||||
└── langgraph.json # configuration file for LangGraph
|
||||
```
|
||||
=== "Python (pyproject.toml)"
|
||||
|
||||
```plaintext
|
||||
my-app/
|
||||
├── my_agent # all project code lies within here
|
||||
│ ├── utils # utilities for your graph
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── tools.py # tools for your graph
|
||||
│ │ ├── nodes.py # node functions for your graph
|
||||
│ │ └── state.py # state definition of your graph
|
||||
│ ├── __init__.py
|
||||
│ └── agent.py # code for constructing your graph
|
||||
├── .env # environment variables
|
||||
├── langgraph.json # configuration file for LangGraph
|
||||
└── pyproject.toml # dependencies for your project
|
||||
```
|
||||
|
||||
=== "JS (package.json)"
|
||||
|
||||
```plaintext
|
||||
my-app/
|
||||
├── src # all project code lies within here
|
||||
│ ├── utils # optional utilities for your graph
|
||||
│ │ ├── tools.ts # tools for your graph
|
||||
│ │ ├── nodes.ts # node functions for your graph
|
||||
│ │ └── state.ts # state definition of your graph
|
||||
│ └── agent.ts # code for constructing your graph
|
||||
├── package.json # package dependencies
|
||||
├── .env # environment variables
|
||||
└── langgraph.json # configuration file for LangGraph
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
The directory structure of a LangGraph application can vary depending on the programming language and the package manager used.
|
||||
|
||||
## Configuration File {#configuration-file-concepts}
|
||||
|
||||
The `langgraph.json` file is a JSON file that specifies the dependencies, graphs, environment variables, and other settings required to deploy a LangGraph application.
|
||||
|
||||
See the [LangGraph configuration file reference](../cloud/reference/cli.md#configuration-file) for details on all supported keys in the JSON file.
|
||||
|
||||
!!! tip
|
||||
|
||||
The [LangGraph CLI](./langgraph_cli.md) defaults to using the configuration file `langgraph.json` in the current directory.
|
||||
|
||||
### Examples
|
||||
|
||||
=== "Python"
|
||||
|
||||
* The dependencies involve a custom local package and the `langchain_openai` package.
|
||||
* A single graph will be loaded from the file `./your_package/your_file.py` with the variable `variable`.
|
||||
* The environment variables are loaded from the `.env` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": [
|
||||
"langchain_openai",
|
||||
"./your_package"
|
||||
],
|
||||
"graphs": {
|
||||
"my_agent": "./your_package/your_file.py:agent"
|
||||
},
|
||||
"env": "./.env"
|
||||
}
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
* The dependencies will be loaded from a dependency file in the local directory (e.g., `package.json`).
|
||||
* A single graph will be loaded from the file `./your_package/your_file.js` with the function `agent`.
|
||||
* The environment variable `OPENAI_API_KEY` is set inline.
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": [
|
||||
"."
|
||||
],
|
||||
"graphs": {
|
||||
"my_agent": "./your_package/your_file.js:agent"
|
||||
},
|
||||
"env": {
|
||||
"OPENAI_API_KEY": "secret-key"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
A LangGraph application may depend on other Python packages or JavaScript libraries (depending on the programming language in which the application is written).
|
||||
|
||||
You will generally need to specify the following information for dependencies to be set up correctly:
|
||||
|
||||
1. A file in the directory that specifies the dependencies (e.g. `requirements.txt`, `pyproject.toml`, or `package.json`).
|
||||
2. A `dependencies` key in the [LangGraph configuration file](#configuration-file-concepts) that specifies the dependencies required to run the LangGraph application.
|
||||
3. Any additional binaries or system libraries can be specified using `dockerfile_lines` key in the [LangGraph configuration file](#configuration-file-concepts).
|
||||
|
||||
## Graphs
|
||||
|
||||
Use the `graphs` key in the [LangGraph configuration file](#configuration-file-concepts) to specify which graphs will be available in the deployed LangGraph application.
|
||||
|
||||
You can specify one or more graphs in the configuration file. Each graph is identified by a name (which should be unique) and a path for either: (1) the compiled graph or (2) a function that makes a graph is defined.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
If you're working with a deployed LangGraph application locally, you can configure environment variables in the `env` key of the [LangGraph configuration file](#configuration-file-concepts).
|
||||
|
||||
For a production deployment, you will typically want to configure the environment variables in the deployment environment.
|
||||
@@ -1,31 +0,0 @@
|
||||
# Assistants
|
||||
|
||||
**Assistants** allow you to manage configurations (like prompts, LLM selection, tools) separately from your graph's core logic, enabling rapid changes that don't alter the graph architecture. It is a way to create multiple specialized versions of the same graph architecture, each optimized for different use cases through configuration variations rather than structural changes.
|
||||
|
||||
For example, imagine a general-purpose writing agent built on a common graph architecture. While the structure remains the same, different writing styles—such as blog posts and tweets—require tailored configurations to optimize performance. To support these variations, you can create multiple assistants (e.g., one for blogs and another for tweets) that share the underlying graph but differ in model selection and system prompt.
|
||||
|
||||

|
||||
|
||||
The LangGraph Cloud API provides several endpoints for creating and managing assistants and their versions. See the [API reference](../cloud/reference/api/api_ref.html#tag/assistants) for more details.
|
||||
|
||||
!!! info
|
||||
|
||||
Assistants are a [LangGraph Platform](langgraph_platform.md) concept. They are not available in the open source LangGraph library.
|
||||
|
||||
## Configuration
|
||||
|
||||
Assistants build on the LangGraph open source concept of [configuration](low_level.md#configuration).
|
||||
While configuration is available in the open source LangGraph library, assistants are only present in [LangGraph Platform](langgraph_platform.md). This is due to the fact that assistants are tightly coupled to your deployed graph. Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default configuration settings.
|
||||
|
||||
In practice, an assistant is just an _instance_ of a graph with a specific configuration. Therefore, multiple assistants can reference the same graph but can contain different configurations (e.g. prompts, models, tools). The LangGraph Server API provides several endpoints for creating and managing assistants. See the [API reference](../cloud/reference/api/api_ref.html) and [this how-to](../cloud/how-tos/configuration_cloud.md) for more details on how to create assistants.
|
||||
|
||||
## Versioning
|
||||
|
||||
Assistants support versioning to track changes over time.
|
||||
Once you've created an assistant, subsequent edits to that assistant will create new versions. See [this how-to](../cloud/how-tos/configuration_cloud.md#create-a-new-version-for-your-assistant) for more details on how to manage assistant versions.
|
||||
|
||||
## Execution
|
||||
|
||||
A **run** is an invocation of an assistant. Each run may have its own input, configuration, and metadata, which may affect execution and output of the underlying graph. A run can optionally be executed on a [thread](./persistence.md#threads).
|
||||
|
||||
The LangGraph Platform API provides several endpoints for creating and managing runs. See the [API reference](../cloud/reference/api/api_ref.html#tag/thread-runs/) for more details.
|
||||
@@ -1,485 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Authentication & Access Control
|
||||
|
||||
LangGraph Platform provides a flexible authentication and authorization system that can integrate with most authentication schemes.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Authentication vs Authorization
|
||||
|
||||
While often used interchangeably, these terms represent distinct security concepts:
|
||||
|
||||
- [**Authentication**](#authentication) ("AuthN") verifies _who_ you are. This runs as middleware for every request.
|
||||
- [**Authorization**](#authorization) ("AuthZ") determines _what you can do_. This validates the user's privileges and roles on a per-resource basis.
|
||||
|
||||
In LangGraph Platform, authentication is handled by your [`@auth.authenticate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.authenticate) handler, and authorization is handled by your [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) handlers.
|
||||
|
||||
## Default Security Models
|
||||
|
||||
LangGraph Platform provides different security defaults:
|
||||
|
||||
### LangGraph Platform
|
||||
|
||||
- Uses LangSmith API keys by default
|
||||
- Requires valid API key in `x-api-key` header
|
||||
- Can be customized with your auth handler
|
||||
|
||||
!!! note "Custom auth"
|
||||
Custom auth **is supported** for all plans in LangGraph Platform.
|
||||
|
||||
### Self-Hosted
|
||||
|
||||
- No default authentication
|
||||
- Complete flexibility to implement your security model
|
||||
- You control all aspects of authentication and authorization
|
||||
|
||||
!!! note "Custom auth"
|
||||
Custom auth is supported for **Enterprise** self-hosted deployments.
|
||||
Standalone Container (Lite) deployments do not support custom auth natively.
|
||||
|
||||
## System Architecture
|
||||
|
||||
A typical authentication setup involves three main components:
|
||||
|
||||
1. **Authentication Provider** (Identity Provider/IdP)
|
||||
|
||||
* A dedicated service that manages user identities and credentials
|
||||
* Handles user registration, login, password resets, etc.
|
||||
* Issues tokens (JWT, session tokens, etc.) after successful authentication
|
||||
* Examples: Auth0, Supabase Auth, Okta, or your own auth server
|
||||
|
||||
2. **LangGraph Backend** (Resource Server)
|
||||
|
||||
* Your LangGraph application that contains business logic and protected resources
|
||||
* Validates tokens with the auth provider
|
||||
* Enforces access control based on user identity and permissions
|
||||
* Doesn't store user credentials directly
|
||||
|
||||
3. **Client Application** (Frontend)
|
||||
|
||||
* Web app, mobile app, or API client
|
||||
* Collects time-sensitive user credentials and sends to auth provider
|
||||
* Receives tokens from auth provider
|
||||
* Includes these tokens in requests to LangGraph backend
|
||||
|
||||
Here's how these components typically interact:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Client App
|
||||
participant Auth as Auth Provider
|
||||
participant LG as LangGraph Backend
|
||||
|
||||
Client->>Auth: 1. Login (username/password)
|
||||
Auth-->>Client: 2. Return token
|
||||
Client->>LG: 3. Request with token
|
||||
Note over LG: 4. Validate token (@auth.authenticate)
|
||||
LG-->>Auth: 5. Fetch user info
|
||||
Auth-->>LG: 6. Confirm validity
|
||||
Note over LG: 7. Apply access control (@auth.on.*)
|
||||
LG-->>Client: 8. Return resources
|
||||
```
|
||||
|
||||
Your [`@auth.authenticate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.authenticate) handler in LangGraph handles steps 4-6, while your [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) handlers implement step 7.
|
||||
|
||||
## Authentication
|
||||
|
||||
Authentication in LangGraph runs as middleware on every request. Your [`@auth.authenticate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.authenticate) handler receives request information and should:
|
||||
|
||||
1. Validate the credentials
|
||||
2. Return [user info](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.MinimalUserDict) containing the user's identity and user information if valid
|
||||
3. Raise an [HTTP exception](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.exceptions.HTTPException) or AssertionError if invalid
|
||||
|
||||
```python
|
||||
from langgraph_sdk import Auth
|
||||
|
||||
auth = Auth()
|
||||
|
||||
@auth.authenticate
|
||||
async def authenticate(headers: dict) -> Auth.types.MinimalUserDict:
|
||||
# Validate credentials (e.g., API key, JWT token)
|
||||
api_key = headers.get("x-api-key")
|
||||
if not api_key or not is_valid_key(api_key):
|
||||
raise Auth.exceptions.HTTPException(
|
||||
status_code=401,
|
||||
detail="Invalid API key"
|
||||
)
|
||||
|
||||
# Return user info - only identity and is_authenticated are required
|
||||
# Add any additional fields you need for authorization
|
||||
return {
|
||||
"identity": "user-123", # Required: unique user identifier
|
||||
"is_authenticated": True, # Optional: assumed True by default
|
||||
"permissions": ["read", "write"] # Optional: for permission-based auth
|
||||
# You can add more custom fields if you want to implement other auth patterns
|
||||
"role": "admin",
|
||||
"org_id": "org-456"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The returned user information is available:
|
||||
|
||||
- To your authorization handlers via [`ctx.user`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.AuthContext)
|
||||
- In your application via `config["configuration"]["langgraph_auth_user"]`
|
||||
|
||||
??? tip "Supported Parameters"
|
||||
|
||||
The [`@auth.authenticate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.authenticate) handler can accept any of the following parameters by name:
|
||||
|
||||
* request (Request): The raw ASGI request object
|
||||
* body (dict): The parsed request body
|
||||
* path (str): The request path, e.g., "/threads/abcd-1234-abcd-1234/runs/abcd-1234-abcd-1234/stream"
|
||||
* method (str): The HTTP method, e.g., "GET"
|
||||
* path_params (dict[str, str]): URL path parameters, e.g., {"thread_id": "abcd-1234-abcd-1234", "run_id": "abcd-1234-abcd-1234"}
|
||||
* query_params (dict[str, str]): URL query parameters, e.g., {"stream": "true"}
|
||||
* headers (dict[bytes, bytes]): Request headers
|
||||
* authorization (str | None): The Authorization header value (e.g., "Bearer <token>")
|
||||
|
||||
In many of our tutorials, we will just show the "authorization" parameter to be concise, but you can opt to accept more information as needed
|
||||
to implement your custom authentication scheme.
|
||||
|
||||
### Agent authentication
|
||||
|
||||
Custom authentication permits delegated access. The values you return in `@auth.authenticate` are added to the run context, giving agents user-scoped credentials lets them access resources on the user’s behalf.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
%% Actors
|
||||
participant ClientApp as Client
|
||||
participant AuthProv as Auth Provider
|
||||
participant LangGraph as LangGraph Backend
|
||||
participant SecretStore as Secret Store
|
||||
participant ExternalService as External Service
|
||||
|
||||
%% Platform login / AuthN
|
||||
ClientApp ->> AuthProv: 1. Login (username / password)
|
||||
AuthProv -->> ClientApp: 2. Return token
|
||||
ClientApp ->> LangGraph: 3. Request with token
|
||||
|
||||
Note over LangGraph: 4. Validate token (@auth.authenticate)
|
||||
LangGraph -->> AuthProv: 5. Fetch user info
|
||||
AuthProv -->> LangGraph: 6. Confirm validity
|
||||
|
||||
%% Fetch user tokens from secret store
|
||||
LangGraph ->> SecretStore: 6a. Fetch user tokens
|
||||
SecretStore -->> LangGraph: 6b. Return tokens
|
||||
|
||||
Note over LangGraph: 7. Apply access control (@auth.on.*)
|
||||
|
||||
%% External Service round-trip
|
||||
LangGraph ->> ExternalService: 8. Call external service (with header)
|
||||
Note over ExternalService: 9. External service validates header and executes action
|
||||
ExternalService -->> LangGraph: 10. Service response
|
||||
|
||||
%% Return to caller
|
||||
LangGraph -->> ClientApp: 11. Return resources
|
||||
```
|
||||
|
||||
After authentication, the platform creates a special configuration object that is passed to your graph and all nodes via the configurable context.
|
||||
This object contains information about the current user, including any custom fields you return from your [`@auth.authenticate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.authenticate) handler.
|
||||
|
||||
To enable an agent to act on behalf of the user, use [custom authentication middleware](../how-tos/auth/custom_auth.md). This will allow the agent to interact with external systems like MCP servers, external databases, and even other agents on behalf of the user.
|
||||
|
||||
For more information, see the [Use custom auth](../how-tos/auth/custom_auth.md#enable-agent-authentication) guide.
|
||||
|
||||
### Agent authentication with MCP
|
||||
|
||||
For information on how to authenticate an agent to an MCP server, see the [MCP conceptual guide](../concepts/mcp.md).
|
||||
|
||||
## Authorization
|
||||
|
||||
After authentication, LangGraph calls your [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) handlers to control access to specific resources (e.g., threads, assistants, crons). These handlers can:
|
||||
|
||||
1. Add metadata to be saved during resource creation by mutating the `value["metadata"]` dictionary directly. See the [supported actions table](#supported-actions) for the list of types the value can take for each action.
|
||||
2. Filter resources by metadata during search/list or read operations by returning a [filter dictionary](#filter-operations).
|
||||
3. Raise an HTTP exception if access is denied.
|
||||
|
||||
If you want to just implement simple user-scoped access control, you can use a single [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) handler for all resources and actions. If you want to have different control depending on the resource and action, you can use [resource-specific handlers](#resource-specific-handlers). See the [Supported Resources](#supported-resources) section for a full list of the resources that support access control.
|
||||
|
||||
```python
|
||||
@auth.on
|
||||
async def add_owner(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: dict # The payload being sent to this access method
|
||||
) -> dict: # Returns a filter dict that restricts access to resources
|
||||
"""Authorize all access to threads, runs, crons, and assistants.
|
||||
|
||||
This handler does two things:
|
||||
- Adds a value to resource metadata (to persist with the resource so it can be filtered later)
|
||||
- Returns a filter (to restrict access to existing resources)
|
||||
|
||||
Args:
|
||||
ctx: Authentication context containing user info, permissions, the path, and
|
||||
value: The request payload sent to the endpoint. For creation
|
||||
operations, this contains the resource parameters. For read
|
||||
operations, this contains the resource being accessed.
|
||||
|
||||
Returns:
|
||||
A filter dictionary that LangGraph uses to restrict access to resources.
|
||||
See [Filter Operations](#filter-operations) for supported operators.
|
||||
"""
|
||||
# Create filter to restrict access to just this user's resources
|
||||
filters = {"owner": ctx.user.identity}
|
||||
|
||||
# Get or create the metadata dictionary in the payload
|
||||
# This is where we store persistent info about the resource
|
||||
metadata = value.setdefault("metadata", {})
|
||||
|
||||
# Add owner to metadata - if this is a create or update operation,
|
||||
# this information will be saved with the resource
|
||||
# So we can filter by it later in read operations
|
||||
metadata.update(filters)
|
||||
|
||||
# Return filters to restrict access
|
||||
# These filters are applied to ALL operations (create, read, update, search, etc.)
|
||||
# to ensure users can only access their own resources
|
||||
return filters
|
||||
```
|
||||
|
||||
### Resource-Specific Handlers {#resource-specific-handlers}
|
||||
|
||||
You can register handlers for specific resources and actions by chaining the resource and action names together with the [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) decorator.
|
||||
When a request is made, the most specific handler that matches that resource and action is called. Below is an example of how to register handlers for specific resources and actions. For the following setup:
|
||||
|
||||
1. Authenticated users are able to create threads, read threads, and create runs on threads
|
||||
2. Only users with the "assistants:create" permission are allowed to create new assistants
|
||||
3. All other endpoints (e.g., e.g., delete assistant, crons, store) are disabled for all users.
|
||||
|
||||
!!! tip "Supported Handlers"
|
||||
|
||||
For a full list of supported resources and actions, see the [Supported Resources](#supported-resources) section below.
|
||||
|
||||
```python
|
||||
# Generic / global handler catches calls that aren't handled by more specific handlers
|
||||
@auth.on
|
||||
async def reject_unhandled_requests(ctx: Auth.types.AuthContext, value: Any) -> False:
|
||||
print(f"Request to {ctx.path} by {ctx.user.identity}")
|
||||
raise Auth.exceptions.HTTPException(
|
||||
status_code=403,
|
||||
detail="Forbidden"
|
||||
)
|
||||
|
||||
# Matches the "thread" resource and all actions - create, read, update, delete, search
|
||||
# Since this is **more specific** than the generic @auth.on handler, it will take precedence
|
||||
# over the generic handler for all actions on the "threads" resource
|
||||
@auth.on.threads
|
||||
async def on_thread_create(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: Auth.types.threads.create.value
|
||||
):
|
||||
if "write" not in ctx.permissions:
|
||||
raise Auth.exceptions.HTTPException(
|
||||
status_code=403,
|
||||
detail="User lacks the required permissions."
|
||||
)
|
||||
# Setting metadata on the thread being created
|
||||
# will ensure that the resource contains an "owner" field
|
||||
# Then any time a user tries to access this thread or runs within the thread,
|
||||
# we can filter by owner
|
||||
metadata = value.setdefault("metadata", {})
|
||||
metadata["owner"] = ctx.user.identity
|
||||
return {"owner": ctx.user.identity}
|
||||
|
||||
# Thread creation. This will match only on thread create actions
|
||||
# Since this is **more specific** than both the generic @auth.on handler and the @auth.on.threads handler,
|
||||
# it will take precedence for any "create" actions on the "threads" resources
|
||||
@auth.on.threads.create
|
||||
async def on_thread_create(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: Auth.types.threads.create.value
|
||||
):
|
||||
# Setting metadata on the thread being created
|
||||
# will ensure that the resource contains an "owner" field
|
||||
# Then any time a user tries to access this thread or runs within the thread,
|
||||
# we can filter by owner
|
||||
metadata = value.setdefault("metadata", {})
|
||||
metadata["owner"] = ctx.user.identity
|
||||
return {"owner": ctx.user.identity}
|
||||
|
||||
# Reading a thread. Since this is also more specific than the generic @auth.on handler, and the @auth.on.threads handler,
|
||||
# it will take precedence for any "read" actions on the "threads" resource
|
||||
@auth.on.threads.read
|
||||
async def on_thread_read(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: Auth.types.threads.read.value
|
||||
):
|
||||
# Since we are reading (and not creating) a thread,
|
||||
# we don't need to set metadata. We just need to
|
||||
# return a filter to ensure users can only see their own threads
|
||||
return {"owner": ctx.user.identity}
|
||||
|
||||
# Run creation, streaming, updates, etc.
|
||||
# This takes precedenceover the generic @auth.on handler and the @auth.on.threads handler
|
||||
@auth.on.threads.create_run
|
||||
async def on_run_create(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: Auth.types.threads.create_run.value
|
||||
):
|
||||
metadata = value.setdefault("metadata", {})
|
||||
metadata["owner"] = ctx.user.identity
|
||||
# Inherit thread's access control
|
||||
return {"owner": ctx.user.identity}
|
||||
|
||||
# Assistant creation
|
||||
@auth.on.assistants.create
|
||||
async def on_assistant_create(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: Auth.types.assistants.create.value
|
||||
):
|
||||
if "assistants:create" not in ctx.permissions:
|
||||
raise Auth.exceptions.HTTPException(
|
||||
status_code=403,
|
||||
detail="User lacks the required permissions."
|
||||
)
|
||||
```
|
||||
|
||||
Notice that we are mixing global and resource-specific handlers in the above example. Since each request is handled by the most specific handler, a request to create a `thread` would match the `on_thread_create` handler but NOT the `reject_unhandled_requests` handler. A request to `update` a thread, however would be handled by the global handler, since we don't have a more specific handler for that resource and action.
|
||||
|
||||
### Filter Operations {#filter-operations}
|
||||
|
||||
Authorization handlers can return `None`, a boolean, or a filter dictionary.
|
||||
- `None` and `True` mean "authorize access to all underling resources"
|
||||
- `False` means "deny access to all underling resources (raises a 403 exception)"
|
||||
- A metadata filter dictionary will restrict access to resources
|
||||
|
||||
A filter dictionary is a dictionary with keys that match the resource metadata. It supports three operators:
|
||||
|
||||
- The default value is a shorthand for exact match, or "$eq", below. For example, `{"owner": user_id}` will include only resources with metadata containing `{"owner": user_id}`
|
||||
- `$eq`: Exact match (e.g., `{"owner": {"$eq": user_id}}`) - this is equivalent to the shorthand above, `{"owner": user_id}`
|
||||
- `$contains`: List membership (e.g., `{"allowed_users": {"$contains": user_id}}`) The value here must be an element of the list. The metadata in the stored resource must be a list/container type.
|
||||
|
||||
A dictionary with multiple keys is treated using a logical `AND` filter. For example, `{"owner": org_id, "allowed_users": {"$contains": user_id}}` will only match resources with metadata whose "owner" is `org_id` and whose "allowed_users" list contains `user_id`.
|
||||
See the reference [here](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.FilterType) for more information.
|
||||
|
||||
## Common Access Patterns
|
||||
|
||||
Here are some typical authorization patterns:
|
||||
|
||||
### Single-Owner Resources
|
||||
|
||||
This common pattern lets you scope all threads, assistants, crons, and runs to a single user. It's useful for common single-user use cases like regular chatbot-style apps.
|
||||
|
||||
```python
|
||||
@auth.on
|
||||
async def owner_only(ctx: Auth.types.AuthContext, value: dict):
|
||||
metadata = value.setdefault("metadata", {})
|
||||
metadata["owner"] = ctx.user.identity
|
||||
return {"owner": ctx.user.identity}
|
||||
```
|
||||
|
||||
### Permission-based Access
|
||||
|
||||
This pattern lets you control access based on **permissions**. It's useful if you want certain roles to have broader or more restricted access to resources.
|
||||
|
||||
```python
|
||||
# In your auth handler:
|
||||
@auth.authenticate
|
||||
async def authenticate(headers: dict) -> Auth.types.MinimalUserDict:
|
||||
...
|
||||
return {
|
||||
"identity": "user-123",
|
||||
"is_authenticated": True,
|
||||
"permissions": ["threads:write", "threads:read"] # Define permissions in auth
|
||||
}
|
||||
|
||||
def _default(ctx: Auth.types.AuthContext, value: dict):
|
||||
metadata = value.setdefault("metadata", {})
|
||||
metadata["owner"] = ctx.user.identity
|
||||
return {"owner": ctx.user.identity}
|
||||
|
||||
@auth.on.threads.create
|
||||
async def create_thread(ctx: Auth.types.AuthContext, value: dict):
|
||||
if "threads:write" not in ctx.permissions:
|
||||
raise Auth.exceptions.HTTPException(
|
||||
status_code=403,
|
||||
detail="Unauthorized"
|
||||
)
|
||||
return _default(ctx, value)
|
||||
|
||||
|
||||
@auth.on.threads.read
|
||||
async def rbac_create(ctx: Auth.types.AuthContext, value: dict):
|
||||
if "threads:read" not in ctx.permissions and "threads:write" not in ctx.permissions:
|
||||
raise Auth.exceptions.HTTPException(
|
||||
status_code=403,
|
||||
detail="Unauthorized"
|
||||
)
|
||||
return _default(ctx, value)
|
||||
```
|
||||
|
||||
## Supported Resources
|
||||
|
||||
LangGraph provides three levels of authorization handlers, from most general to most specific:
|
||||
|
||||
1. **Global Handler** (`@auth.on`): Matches all resources and actions
|
||||
2. **Resource Handler** (e.g., `@auth.on.threads`, `@auth.on.assistants`, `@auth.on.crons`): Matches all actions for a specific resource
|
||||
3. **Action Handler** (e.g., `@auth.on.threads.create`, `@auth.on.threads.read`): Matches a specific action on a specific resource
|
||||
|
||||
The most specific matching handler will be used. For example, `@auth.on.threads.create` takes precedence over `@auth.on.threads` for thread creation.
|
||||
If a more specific handler is registered, the more general handler will not be called for that resource and action.
|
||||
|
||||
???+ tip "Type Safety"
|
||||
Each handler has type hints available for its `value` parameter at `Auth.types.on.<resource>.<action>.value`. For example:
|
||||
```python
|
||||
@auth.on.threads.create
|
||||
async def on_thread_create(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: Auth.types.on.threads.create.value # Specific type for thread creation
|
||||
):
|
||||
...
|
||||
|
||||
@auth.on.threads
|
||||
async def on_threads(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: Auth.types.on.threads.value # Union type of all thread actions
|
||||
):
|
||||
...
|
||||
|
||||
@auth.on
|
||||
async def on_all(
|
||||
ctx: Auth.types.AuthContext,
|
||||
value: dict # Union type of all possible actions
|
||||
):
|
||||
...
|
||||
```
|
||||
More specific handlers provide better type hints since they handle fewer action types.
|
||||
|
||||
#### Supported actions and types {#supported-actions}
|
||||
Here are all the supported action handlers:
|
||||
|
||||
| Resource | Handler | Description | Value Type |
|
||||
|----------|---------|-------------|------------|
|
||||
| **Threads** | `@auth.on.threads.create` | Thread creation | [`ThreadsCreate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.ThreadsCreate) |
|
||||
| | `@auth.on.threads.read` | Thread retrieval | [`ThreadsRead`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.ThreadsRead) |
|
||||
| | `@auth.on.threads.update` | Thread updates | [`ThreadsUpdate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.ThreadsUpdate) |
|
||||
| | `@auth.on.threads.delete` | Thread deletion | [`ThreadsDelete`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.ThreadsDelete) |
|
||||
| | `@auth.on.threads.search` | Listing threads | [`ThreadsSearch`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.ThreadsSearch) |
|
||||
| | `@auth.on.threads.create_run` | Creating or updating a run | [`RunsCreate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.RunsCreate) |
|
||||
| **Assistants** | `@auth.on.assistants.create` | Assistant creation | [`AssistantsCreate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.AssistantsCreate) |
|
||||
| | `@auth.on.assistants.read` | Assistant retrieval | [`AssistantsRead`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.AssistantsRead) |
|
||||
| | `@auth.on.assistants.update` | Assistant updates | [`AssistantsUpdate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.AssistantsUpdate) |
|
||||
| | `@auth.on.assistants.delete` | Assistant deletion | [`AssistantsDelete`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.AssistantsDelete) |
|
||||
| | `@auth.on.assistants.search` | Listing assistants | [`AssistantsSearch`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.AssistantsSearch) |
|
||||
| **Crons** | `@auth.on.crons.create` | Cron job creation | [`CronsCreate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.CronsCreate) |
|
||||
| | `@auth.on.crons.read` | Cron job retrieval | [`CronsRead`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.CronsRead) |
|
||||
| | `@auth.on.crons.update` | Cron job updates | [`CronsUpdate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.CronsUpdate) |
|
||||
| | `@auth.on.crons.delete` | Cron job deletion | [`CronsDelete`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.CronsDelete) |
|
||||
| | `@auth.on.crons.search` | Listing cron jobs | [`CronsSearch`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.types.CronsSearch) |
|
||||
|
||||
???+ note "About Runs"
|
||||
|
||||
Runs are scoped to their parent thread for access control. This means permissions are typically inherited from the thread, reflecting the conversational nature of the data model. All run operations (reading, listing) except creation are controlled by the thread's handlers.
|
||||
There is a specific `create_run` handler for creating new runs because it had more arguments that you can view in the handler.
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
For implementation details:
|
||||
|
||||
- Check out the introductory tutorial on [setting up authentication](../tutorials/auth/getting_started.md)
|
||||
- See the how-to guide on implementing a [custom auth handlers](../how-tos/auth/custom_auth.md)
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Deployment Options
|
||||
|
||||
## Free deployment
|
||||
|
||||
There are two free options for deploying LangGraph applications via the LangGraph Server:
|
||||
|
||||
1. [Local](../tutorials/langgraph-platform/local-server.md): Deploy for local testing and development.
|
||||
1. [Standalone Container (Lite)](../concepts/langgraph_standalone_container.md): A limited version of Standalone Container for deployments unlikely to see more that 1 million node executions per year and that do not need crons and other enterprise features. Standalone Container (Lite) deployment option is free with a LangSmith API key.
|
||||
|
||||
## Production deployment
|
||||
|
||||
There are 4 main options for deploying with the [LangGraph Platform](langgraph_platform.md):
|
||||
|
||||
1. [Cloud SaaS](#cloud-saas)
|
||||
|
||||
1. [Self-Hosted Data Plane](#self-hosted-data-plane)
|
||||
|
||||
1. [Self-Hosted Control Plane](#self-hosted-control-plane)
|
||||
|
||||
1. [Standalone Container](#standalone-container)
|
||||
|
||||
|
||||
A quick comparison:
|
||||
|
||||
| | **Cloud SaaS** | **Self-Hosted Data Plane** | **Self-Hosted Control Plane** | **Standalone Container** |
|
||||
|----------------------|----------------|----------------------------|-------------------------------|--------------------------|
|
||||
| **[Control plane UI/API](../concepts/langgraph_control_plane.md)** | Yes | Yes | Yes | No |
|
||||
| **CI/CD** | Managed internally by platform | Managed externally by you | Managed externally by you | Managed externally by you |
|
||||
| **Data/compute residency** | LangChain's cloud | Your cloud | Your cloud | Your cloud |
|
||||
| **LangSmith compatibility** | Trace to LangSmith SaaS | Trace to LangSmith SaaS | Trace to Self-Hosted LangSmith | Optional tracing |
|
||||
| **[Server version compatibility](../concepts/langgraph_server.md#server-versions)** | Enterprise | Enterprise | Enterprise | Lite, Enterprise |
|
||||
| **[Pricing](https://www.langchain.com/pricing-langgraph-platform)** | Plus | Enterprise | Enterprise | Developer |
|
||||
|
||||
## Cloud SaaS
|
||||
|
||||
The [Cloud SaaS](./langgraph_cloud.md) deployment option is a fully managed model for deployment where we manage the [control plane](./langgraph_control_plane.md) and [data plane](./langgraph_data_plane.md) in our cloud. This option provides a simple way to deploy and manage your LangGraph Servers.
|
||||
|
||||
Connect your GitHub repositories to the platform and deploy your LangGraph Servers from the [control plane UI](./langgraph_control_plane.md#control-plane-ui). The build process (i.e. CI/CD) is managed internally by the platform.
|
||||
|
||||
For more information, please see:
|
||||
|
||||
* [Cloud SaaS Conceptual Guide](./langgraph_cloud.md)
|
||||
* [How to deploy to Cloud SaaS](../cloud/deployment/cloud.md)
|
||||
|
||||
## Self-Hosted Data Plane
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option requires an [Enterprise](../concepts/plans.md) plan.
|
||||
|
||||
The [Self-Hosted Data Plane](./langgraph_self_hosted_data_plane.md) deployment option is a "hybrid" model for deployment where we manage the [control plane](./langgraph_control_plane.md) in our cloud and you manage the [data plane](./langgraph_data_plane.md) in your cloud. This option provides a way to securely manage your data plane infrastructure, while offloading control plane management to us.
|
||||
|
||||
Build a Docker image using the [LangGraph CLI](./langgraph_cli.md) and deploy your LangGraph Server from the [control plane UI](./langgraph_control_plane.md#control-plane-ui).
|
||||
|
||||
Supported Compute Platforms: [Kubernetes](https://kubernetes.io/), [Amazon ECS](https://aws.amazon.com/ecs/) (coming soon!)
|
||||
|
||||
For more information, please see:
|
||||
|
||||
* [Self-Hosted Data Plane Conceptual Guide](./langgraph_self_hosted_data_plane.md)
|
||||
* [How to deploy the Self-Hosted Data Plane](../cloud/deployment/self_hosted_data_plane.md)
|
||||
|
||||
## Self-Hosted Control Plane
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option requires an [Enterprise](../concepts/plans.md) plan.
|
||||
|
||||
The [Self-Hosted Control Plane](./langgraph_self_hosted_control_plane.md) deployment option is a fully self-hosted model for deployment where you manage the [control plane](./langgraph_control_plane.md) and [data plane](./langgraph_data_plane.md) in your cloud. This option gives you full control and responsibility of the control plane and data plane infrastructure.
|
||||
|
||||
Build a Docker image using the [LangGraph CLI](./langgraph_cli.md) and deploy your LangGraph Server from the [control plane UI](./langgraph_control_plane.md#control-plane-ui).
|
||||
|
||||
Supported Compute Platforms: [Kubernetes](https://kubernetes.io/)
|
||||
|
||||
For more information, please see:
|
||||
|
||||
* [Self-Hosted Control Plane Conceptual Guide](./langgraph_self_hosted_control_plane.md)
|
||||
* [How to deploy the Self-Hosted Control Plane](../cloud/deployment/self_hosted_control_plane.md)
|
||||
|
||||
## Standalone Container
|
||||
|
||||
The [Standalone Container](./langgraph_standalone_container.md) deployment option is the least restrictive model for deployment. Deploy standalone instances of a LangGraph Server in your cloud, using any of the [available](./plans.md) license options.
|
||||
|
||||
Build a Docker image using the [LangGraph CLI](./langgraph_cli.md) and deploy your LangGraph Server using the container deployment tooling of your choice. Images can be deployed to any compute platform.
|
||||
|
||||
For more information, please see:
|
||||
|
||||
* [Standalone Container Conceptual Guide](./langgraph_standalone_container.md)
|
||||
* [How to deploy a Standalone Container](../cloud/deployment/standalone_container.md)
|
||||
|
||||
## Related
|
||||
|
||||
For more information, please see:
|
||||
|
||||
* [LangGraph Platform plans](./plans.md)
|
||||
* [LangGraph Platform pricing](https://www.langchain.com/langgraph-platform-pricing)
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Double Texting
|
||||
|
||||
!!! info "Prerequisites"
|
||||
- [LangGraph Server](./langgraph_server.md)
|
||||
|
||||
Many times users might interact with your graph in unintended ways.
|
||||
For instance, a user may send one message and before the graph has finished running send a second message.
|
||||
More generally, users may invoke the graph a second time before the first run has finished.
|
||||
We call this "double texting".
|
||||
|
||||
Currently, LangGraph only addresses this as part of [LangGraph Platform](langgraph_platform.md), not in the open source.
|
||||
The reason for this is that in order to handle this we need to know how the graph is deployed, and since LangGraph Platform deals with deployment the logic needs to live there.
|
||||
If you do not want to use LangGraph Platform, we describe the options we have implemented in detail below.
|
||||
|
||||

|
||||
|
||||
## Reject
|
||||
|
||||
This is the simplest option, this just rejects any follow-up runs and does not allow double texting.
|
||||
See the [how-to guide](../cloud/how-tos/reject_concurrent.md) for configuring the reject double text option.
|
||||
|
||||
## Enqueue
|
||||
|
||||
This is a relatively simple option which continues the first run until it completes the whole run, then sends the new input as a separate run.
|
||||
See the [how-to guide](../cloud/how-tos/enqueue_concurrent.md) for configuring the enqueue double text option.
|
||||
|
||||
## Interrupt
|
||||
|
||||
This option interrupts the current execution but saves all the work done up until that point.
|
||||
It then inserts the user input and continues from there.
|
||||
|
||||
If you enable this option, your graph should be able to handle weird edge cases that may arise.
|
||||
For example, you could have called a tool but not yet gotten back a result from running that tool.
|
||||
You may need to remove that tool call in order to not have a dangling tool call.
|
||||
|
||||
See the [how-to guide](../cloud/how-tos/interrupt_concurrent.md) for configuring the interrupt double text option.
|
||||
|
||||
## Rollback
|
||||
|
||||
This option interrupts the current execution AND rolls back all work done up until that point, including the original run input. It then sends the new user input in, basically as if it was the original input.
|
||||
|
||||
See the [how-to guide](../cloud/how-tos/rollback_concurrent.md) for configuring the rollback double text option.
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph CLI
|
||||
|
||||
**LangGraph CLI** is a multi-platform command-line tool for building and running the [LangGraph API server](./langgraph_server.md) locally. The resulting server includes all API endpoints for your graph's runs, threads, assistants, etc. as well as the other services required to run your agent, including a managed database for checkpointing and storage.
|
||||
|
||||
## Installation
|
||||
|
||||
The LangGraph CLI can be installed via pip or [Homebrew](https://brew.sh/):
|
||||
|
||||
=== "pip"
|
||||
```bash
|
||||
pip install langgraph-cli
|
||||
```
|
||||
|
||||
=== "Homebrew"
|
||||
```bash
|
||||
brew install langgraph-cli
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
LangGraph CLI provides the following core functionality:
|
||||
|
||||
| Command | Description |
|
||||
| -------- | -------|
|
||||
| [`langgraph build`](../cloud/reference/cli.md#build) | Builds a Docker image for the [LangGraph API server](./langgraph_server.md) that can be directly deployed. |
|
||||
| [`langgraph dev`](../cloud/reference/cli.md#dev) | Starts a lightweight development server that requires no Docker installation. This server is ideal for rapid development and testing. This is available in version 0.1.55 and up.
|
||||
| [`langgraph dockerfile`](../cloud/reference/cli.md#dockerfile) | Generates a [Dockerfile](https://docs.docker.com/reference/dockerfile/) that can be used to build images for and deploy instances of the [LangGraph API server](./langgraph_server.md). This is useful if you want to further customize the dockerfile or deploy in a more custom way. |
|
||||
| [`langgraph up`](../cloud/reference/cli.md#up) | Starts an instance of the [LangGraph API server](./langgraph_server.md) locally in a docker container. This requires the docker server to be running locally. It also requires a LangSmith API key for local development or a license key for production use. |
|
||||
|
||||
For more information, see the [LangGraph CLI Reference](../cloud/reference/cli.md).
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Cloud SaaS
|
||||
|
||||
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy to Cloud SaaS](../cloud/deployment/cloud.md).
|
||||
|
||||
## Overview
|
||||
|
||||
The Cloud SaaS deployment option is a fully managed model for deployment where we manage the [control plane](./langgraph_control_plane.md) and [data plane](./langgraph_data_plane.md) in our cloud.
|
||||
|
||||
| | [Control plane](../concepts/langgraph_control_plane.md) | [Data plane](../concepts/langgraph_data_plane.md) |
|
||||
|-------------------|-------------------|------------|
|
||||
| **What is it?** | <ul><li>Control plane UI for creating deployments and revisions</li><li>Control plane APIs for creating deployments and revisions</li></ul> | <ul><li>Data plane "listener" for reconciling deployments with control plane state</li><li>LangGraph Servers</li><li>Postgres, Redis, etc</li></ul> |
|
||||
| **Where is it hosted?** | LangChain's cloud | LangChain's cloud |
|
||||
| **Who provisions and manages it?** | LangChain | LangChain |
|
||||
|
||||
## Architecture
|
||||
|
||||

|
||||
@@ -1,13 +0,0 @@
|
||||
## Components
|
||||
|
||||
The LangGraph Platform consists of components that work together to support the development, deployment, debugging, and monitoring of LangGraph applications:
|
||||
|
||||
- [LangGraph Server](./langgraph_server.md): The server defines an opinionated API and architecture that incorporates best practices for deploying agentic applications, allowing you to focus on building your agent logic rather than developing server infrastructure.
|
||||
- [LangGraph CLI](./langgraph_cli.md): LangGraph CLI is a command-line interface that helps to interact with a local LangGraph
|
||||
- [LangGraph Studio](./langgraph_studio.md): LangGraph Studio is a specialized IDE that can connect to a LangGraph Server to enable visualization, interaction, and debugging of the application locally.
|
||||
- [Python/JS SDK](./sdk.md): The Python/JS SDK provides a programmatic way to interact with deployed LangGraph Applications.
|
||||
- [Remote Graph](../how-tos/use-remote-graph.md): A RemoteGraph allows you to interact with any deployed LangGraph application as though it were running locally.
|
||||
- [LangGraph control plane](./langgraph_control_plane.md): The LangGraph Control Plane refers to the Control Plane UI where users create and update LangGraph Servers and the Control Plane APIs that support the UI experience.
|
||||
- [LangGraph data plane](./langgraph_data_plane.md): The LangGraph Data Plane refers to LangGraph Servers, the corresponding infrastructure for each server, and the "listener" application that continuously polls for updates from the LangGraph Control Plane.
|
||||
|
||||

|
||||
@@ -1,124 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Control Plane
|
||||
|
||||
The term "control plane" is used broadly to refer to the control plane UI where users create and update [LangGraph Servers](./langgraph_server.md) (deployments) and the control plane APIs that support the UI experience.
|
||||
|
||||
When a user makes an update through the control plane UI, the update is stored in the control plane state. The [LangGraph Data Plane](./langgraph_data_plane.md) "listener" application polls for these updates by calling the control plane APIs.
|
||||
|
||||
## Control Plane UI
|
||||
|
||||
From the control plane UI, you can:
|
||||
|
||||
- View a list of outstanding deployments.
|
||||
- View details of an individual deployment.
|
||||
- Create a new deployment.
|
||||
- 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).
|
||||
|
||||
## Control Plane API
|
||||
|
||||
This section describes the data model of the control plane API. The API is used to create, update, and delete deployments. See the [control plane API reference](../cloud/reference/api/api_ref_control_plane.md) for more details.
|
||||
|
||||
### Deployment
|
||||
|
||||
A deployment is an instance of a LangGraph Server. A single deployment can have many revisions.
|
||||
|
||||
### Revision
|
||||
|
||||
A revision is an iteration of a deployment. When a new deployment is created, an initial revision is automatically created. To deploy code changes or update secrets for a deployment, a new revision must be created.
|
||||
|
||||
## Control Plane Features
|
||||
|
||||
This section describes various features of the control plane.
|
||||
|
||||
### Deployment Types
|
||||
|
||||
For simplicity, the control plane offers two deployment types with different resource allocations: `Development` and `Production`.
|
||||
|
||||
| **Deployment Type** | **CPU/Memory** | **Scaling** | **Database** |
|
||||
|---------------------|-----------------|---------------------|----------------------------------------------------------------------------------|
|
||||
| Development | 1 CPU, 1 GB RAM | Up to 1 replica | 10 GB disk, no backups |
|
||||
| Production | 2 CPU, 2 GB RAM | Up to 10 replicas | Autoscaling disk, automatic backups, highly available (multi-zone configuration) |
|
||||
|
||||
CPU and memory resources are per replica.
|
||||
|
||||
!!! warning "Immutable Deployment Type"
|
||||
|
||||
Once a deployment is created, the deployment type cannot be changed.
|
||||
|
||||
!!! info "Self-Hosted Deployment"
|
||||
Resources for [Self-Hosted Data Plane](../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../concepts/langgraph_self_hosted_control_plane.md) deployments can be fully customized. Deployment types are only applicable for [Cloud SaaS](../concepts/langgraph_cloud.md) deployments.
|
||||
|
||||
#### Production
|
||||
|
||||
`Production` type deployments are suitable for "production" workloads. For example, select `Production` for customer-facing applications in the critical path.
|
||||
|
||||
Resources for `Production` type deployments can be manually increased on a case-by-case basis depending on use case and capacity constraints. Contact support@langchain.dev to request an increase in resources.
|
||||
|
||||
#### Development
|
||||
|
||||
`Development` type deployments are suitable development and testing. For example, select `Development` for internal testing environments. `Development` type deployments are not suitable for "production" workloads.
|
||||
|
||||
!!! danger "Preemptible Compute Infrastructure"
|
||||
`Development` type deployments (API server, queue server, and database) are provisioned on preemptible compute infrastructure. This means the compute infrastructure **may be terminated at any time without notice**. This may result in intermittent...
|
||||
|
||||
- Redis connection timeouts/errors
|
||||
- Postgres connection timeouts/errors
|
||||
- Failed or retrying background runs
|
||||
|
||||
This behavior is expected. Preemptible compute infrastructure **significantly reduces the cost to provision a `Development` type deployment**. By design, LangGraph Server is fault-tolerant. The implementation will automatically attempt to recover from Redis/Postgres connection errors and retry failed background runs.
|
||||
|
||||
`Production` type deployments are provisioned on durable compute infrastructure, not preemptible compute infrastructure.
|
||||
|
||||
Database disk size for `Development` type deployments can be manually increased on a case-by-case basis depending on use case and capacity constraints. For most use cases, [TTLs](../how-tos/ttl/configure_ttl.md) should be configured to manage disk usage. Contact support@langchain.dev to request an increase in resources.
|
||||
|
||||
### Database Provisioning
|
||||
|
||||
The control plane and [LangGraph Data Plane](./langgraph_data_plane.md) "listener" application coordinate to automatically create a Postgres database for each deployment. The database serves as the [persistence layer](../concepts/persistence.md) for the deployment.
|
||||
|
||||
When implementing a LangGraph application, a [checkpointer](../concepts/persistence.md#checkpointer-libraries) does not need to be configured by the developer. Instead, a checkpointer is automatically configured for the graph. Any checkpointer configured for a graph will be replaced by the one that is automatically configured.
|
||||
|
||||
There is no direct access to the database. All access to the database occurs through the [LangGraph Server](../concepts/langgraph_server.md).
|
||||
|
||||
The database is never deleted until the deployment itself is deleted.
|
||||
|
||||
!!! info
|
||||
A custom Postgres instance can be configured for [Self-Hosted Data Plane](../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
### Asynchronous Deployment
|
||||
|
||||
Infrastructure for deployments and revisions are provisioned and deployed asynchronously. They are not deployed immediately after submission. Currently, deployment can take up to several minutes.
|
||||
|
||||
- When a new deployment is created, a new database is created for the deployment. Database creation is a one-time step. This step contributes to a longer deployment time for the initial revision of the deployment.
|
||||
- When a subsequent revision is created for a deployment, there is no database creation step. The deployment time for a subsequent revision is significantly faster compared to the deployment time of the initial revision.
|
||||
- The deployment process for each revision contains a build step, which can take up to a few minutes.
|
||||
|
||||
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.
|
||||
- [LangGraph Server queue](../concepts/langgraph_server.md#persistence-and-task-queue) pending/active run count.
|
||||
- [LangGraph Server API](../concepts/langgraph_server.md) success response count, error response count, and latency.
|
||||
|
||||
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.
|
||||
|
||||
When a deployment is deleted, the traces and the tracing project are not deleted.
|
||||
@@ -1,137 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Data Plane
|
||||
|
||||
The term "data plane" is used broadly to refer to [LangGraph Servers](./langgraph_server.md) (deployments), the corresponding infrastructure for each server, and the "listener" application that continuously polls for updates from the [LangGraph Control Plane](./langgraph_control_plane.md).
|
||||
|
||||
## Server Infrastructure
|
||||
|
||||
In addition to the [LangGraph Server](./langgraph_server.md) itself, the following infrastructure components for each server are also included in the broad definition of "data plane":
|
||||
|
||||
- Postgres
|
||||
- Redis
|
||||
- Secrets store
|
||||
- Autoscalers
|
||||
|
||||
## "Listener" Application
|
||||
|
||||
The data plane "listener" application periodically calls [control plane APIs](../concepts/langgraph_control_plane.md#control-plane-api) to:
|
||||
|
||||
- Determine if new deployments should be created.
|
||||
- Determine if existing deployments should be updated (i.e. new revisions).
|
||||
- Determine if existing deployments should be deleted.
|
||||
|
||||
In other words, the data plane "listener" reads the latest state of the control plane (desired state) and takes action to reconcile outstanding deployments (current state) to match the latest state.
|
||||
|
||||
## Postgres
|
||||
|
||||
Postgres is the persistence layer for all user, run, and long-term memory data in a LangGraph Server. This stores both checkpoints (see more info [here](./persistence.md)), server resources (threads, runs, assistants and crons), as well as items saved in the long-term memory store (see more info [here](./persistence.md#memory-store)).
|
||||
|
||||
## Redis
|
||||
|
||||
Redis is used in each LangGraph Server as a way for server and queue workers to communicate, and to store ephemeral metadata. No user or run data is stored in Redis.
|
||||
|
||||
### Communication
|
||||
|
||||
All runs in a LangGraph Server are executed by a pool of background workers that are part of each deployment. In order to enable some features for those runs (such as cancellation and output streaming) we need a channel for two-way communication between the server and the worker handling a particular run. We use Redis to organize that communication.
|
||||
|
||||
1. A Redis list is used as a mechanism to wake up a worker as soon as a new run is created. Only a sentinel value is stored in this list, no actual run information. The run information is then retrieved from Postgres by the worker.
|
||||
2. A combination of a Redis string and Redis PubSub channel is used for the server to communicate a run cancellation request to the appropriate worker.
|
||||
3. A Redis PubSub channel is used by the worker to broadcast streaming output from an agent while the run is being handled. Any open `/stream` request in the server will subscribe to that channel and forward any events to the response as they arrive. No events are stored in Redis at any time.
|
||||
|
||||
### Ephemeral metadata
|
||||
|
||||
Runs in a LangGraph Server may be retried for specific failures (currently only for transient Postgres errors encountered during the run). In order to limit the number of retries (currently limited to 3 attempts per run) we record the attempt number in a Redis string when it is picked up. This contains no run-specific info other than its ID, and expires after a short delay.
|
||||
|
||||
## Data Plane Features
|
||||
|
||||
This section describes various features of the data plane.
|
||||
|
||||
### Data Region
|
||||
|
||||
!!! info "Only for Cloud SaaS"
|
||||
Data regions are only applicable for [Cloud SaaS](../concepts/langgraph_cloud.md) deployments.
|
||||
|
||||
Deployments can be created in 2 data regions: US and EU
|
||||
|
||||
The data region for a deployment is implied by the data region of the LangSmith organization where the deployment is created. Deployments and the underlying database for the deployments cannot be migrated between data regions.
|
||||
|
||||
### Autoscaling
|
||||
|
||||
[`Production` type](../concepts/langgraph_control_plane.md#deployment-types) deployments automatically scale up to 10 containers. Scaling is based on 3 metrics:
|
||||
|
||||
1. CPU utilization
|
||||
1. Memory utilization
|
||||
1. Number of pending (in progress) [runs](./assistants.md#execution)
|
||||
|
||||
For CPU utilization, the autoscaler targets 75% utilization. This means the autoscaler will scale the number of containers up or down to ensure that CPU utilization is at or near 75%. For memory utilization, the autoscaler targets 75% utilization as well.
|
||||
|
||||
For number of pending runs, the autoscaler targets 10 pending runs. For example, if the current number of containers is 1, but the number of pending runs in 20, the autoscaler will scale up the deployment to 2 containers (20 pending runs / 2 containers = 10 pending runs per container).
|
||||
|
||||
Each metric is computed independently and the autoscaler will determine the scaling action based on the metric that results in the largest number of containers.
|
||||
|
||||
Scale down actions are delayed for 30 minutes before any action is taken. In other words, if the autoscaler decides to scale down a deployment, it will first wait for 30 minutes before scaling down. After 30 minutes, the metrics are recomputed and the deployment will scale down if the recomputed metrics result in a lower number of containers than the current number. Otherwise, the deployment remains scaled up. This "cool down" period ensures that deployments do not scale up and down too frequently.
|
||||
|
||||
### Static IP Addresses
|
||||
|
||||
!!! info "Only for Cloud SaaS"
|
||||
Static IP addresses are only available for [Cloud SaaS](../concepts/langgraph_cloud.md) deployments.
|
||||
|
||||
All traffic from deployments created after January 6th 2025 will come through a NAT gateway. This NAT gateway will have several static IP addresses depending on the data region. Refer to the table below for the list of static IP addresses:
|
||||
|
||||
| US | EU |
|
||||
|----------------|----------------|
|
||||
| 35.197.29.146 | 34.13.192.67 |
|
||||
| 34.145.102.123 | 34.147.105.64 |
|
||||
| 34.169.45.153 | 34.90.22.166 |
|
||||
| 34.82.222.17 | 34.147.36.213 |
|
||||
| 35.227.171.135 | 34.32.137.113 |
|
||||
| 34.169.88.30 | 34.91.238.184 |
|
||||
| 34.19.93.202 | 35.204.101.241 |
|
||||
| 34.19.34.50 | 35.204.48.32 |
|
||||
|
||||
### Custom Postgres
|
||||
|
||||
!!! info
|
||||
Custom Postgres instances are only available for [Self-Hosted Data Plane](../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
A custom Postgres instance can be used instead of the [one automatically created by the control plane](./langgraph_control_plane.md#database-provisioning). Specify the [`POSTGRES_URI_CUSTOM`](../cloud/reference/env_var.md#postgres_uri_custom) environment variable to use a custom Postgres instance.
|
||||
|
||||
Multiple deployments can share the same Postgres instance. For example, for `Deployment A`, `POSTGRES_URI_CUSTOM` can be set to `postgres://<user>:<password>@/<database_name_1>?host=<hostname_1>` and for `Deployment B`, `POSTGRES_URI_CUSTOM` can be set to `postgres://<user>:<password>@/<database_name_2>?host=<hostname_1>`. `<database_name_1>` and `database_name_2` are different databases within the same instance, but `<hostname_1>` is shared. **The same database cannot be used for separate deployments**.
|
||||
|
||||
### Custom Redis
|
||||
|
||||
!!! info
|
||||
Custom Redis instances are only available for [Self-Hosted Data Plane](../concepts/langgraph_self_hosted_control_plane.md) and [Self-Hosted Control Plane](../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
A custom Redis instance can be used instead of the one automatically created by the control plane. Specify the [REDIS_URI_CUSTOM](../cloud/reference/env_var.md#redis_uri_custom) environment variable to use a custom Redis instance.
|
||||
|
||||
|
||||
Multiple deployments can share the same Redis instance. For example, for `Deployment A`, `REDIS_URI_CUSTOM` can be set to `redis://<hostname_1>:<port>/1` and for `Deployment B`, `REDIS_URI_CUSTOM` can be set to `redis://<hostname_1>:<port>/2`. `1` and `2` are different database numbers within the same instance, but `<hostname_1>` is shared. **The same database number cannot be used for separate deployments**.
|
||||
|
||||
### LangSmith Tracing
|
||||
|
||||
LangGraph Server is automatically configured to send traces to LangSmith. See the table below for details with respect to each deployment option.
|
||||
|
||||
| Cloud SaaS | Self-Hosted Data Plane | Self-Hosted Control Plane | Standalone Container |
|
||||
|------------|------------------------|---------------------------|----------------------|
|
||||
| Required<br><br>Trace to LangSmith SaaS. | Optional<br><br>Disable tracing or trace to LangSmith SaaS. | Optional<br><br>Disable tracing or trace to Self-Hosted LangSmith. | Optional<br><br>Disable tracing, trace to LangSmith SaaS, or trace to Self-Hosted LangSmith. |
|
||||
|
||||
### Telemetry
|
||||
|
||||
LangGraph Server is automatically configured to report telemetry metadata for billing purposes. See the table below for details with respect to each deployment option.
|
||||
|
||||
| Cloud SaaS | Self-Hosted Data Plane | Self-Hosted Control Plane | Standalone Container |
|
||||
|------------|------------------------|---------------------------|----------------------|
|
||||
| Telemetry sent to LangSmith SaaS. | Telemetry sent to LangSmith SaaS. | Self-reported usage (audit) for air-gapped license key.<br><br>Telemetry sent to LangSmith SaaS for LangGraph Platform License Key. | Self-reported usage (audit) for air-gapped license key.<br><br>Telemetry sent to LangSmith SaaS for LangGraph Platform License Key. |
|
||||
|
||||
### Licensing
|
||||
|
||||
LangGraph Server is automatically configured to perform license key validation. See the table below for details with respect to each deployment option.
|
||||
|
||||
| Cloud SaaS | Self-Hosted Data Plane | Self-Hosted Control Plane | Standalone Container |
|
||||
|------------|------------------------|---------------------------|----------------------|
|
||||
| LangSmith API Key validated against LangSmith SaaS. | LangSmith API Key validated against LangSmith SaaS. | Air-gapped license key or LangGraph Platform License Key validated against LangSmith SaaS. | Air-gapped license key or LangGraph Platform License Key validated against LangSmith SaaS. |
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Platform
|
||||
|
||||
Develop, deploy, scale, and manage agents with **LangGraph Platform** — the purpose-built platform for long-running, agentic workflows.
|
||||
|
||||
!!! tip "Get started with LangGraph Platform"
|
||||
|
||||
Check out the [LangGraph Platform quickstart](../tutorials/langgraph-platform/local-server.md) for instructions on how to use LangGraph Platform to run a LangGraph application locally.
|
||||
|
||||
## Why use LangGraph Platform?
|
||||
|
||||
<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/pfAQxBS5z88?si=XGS6Chydn6lhSO1S" title="What is LangGraph Platform?" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>
|
||||
|
||||
LangGraph Platform makes it easy to get your agent running in production — whether it’s built with LangGraph or another framework — so you can focus on your app logic, not infrastructure. Deploy with one click to get a live endpoint, and use our robust APIs and built-in task queues to handle production scale.
|
||||
|
||||
- **[Streaming Support](../cloud/how-tos/streaming.md)**: As agents grow more sophisticated, they often benefit from streaming both token outputs and intermediate states back to the user. Without this, users are left waiting for potentially long operations with no feedback. LangGraph Server provides multiple streaming modes optimized for various application needs.
|
||||
|
||||
- **[Background Runs](../cloud/how-tos/background_run.md)**: For agents that take longer to process (e.g., hours), maintaining an open connection can be impractical. The LangGraph Server supports launching agent runs in the background and provides both polling endpoints and webhooks to monitor run status effectively.
|
||||
|
||||
- **Support for long runs**: Regular server setups often encounter timeouts or disruptions when handling requests that take a long time to complete. LangGraph Server’s API provides robust support for these tasks by sending regular heartbeat signals, preventing unexpected connection closures during prolonged processes.
|
||||
|
||||
- **Handling Burstiness**: Certain applications, especially those with real-time user interaction, may experience "bursty" request loads where numerous requests hit the server simultaneously. LangGraph Server includes a task queue, ensuring requests are handled consistently without loss, even under heavy loads.
|
||||
|
||||
- **[Double-texting](../cloud/how-tos/interrupt_concurrent.md)**: In user-driven applications, it’s common for users to send multiple messages rapidly. This “double texting” can disrupt agent flows if not handled properly. LangGraph Server offers built-in strategies to address and manage such interactions.
|
||||
|
||||
- **[Checkpointers and memory management](persistence.md#checkpoints)**: For agents needing persistence (e.g., conversation memory), deploying a robust storage solution can be complex. LangGraph Platform includes optimized [checkpointers](persistence.md#checkpoints) and a [memory store](persistence.md#memory-store), managing state across sessions without the need for custom solutions.
|
||||
|
||||
- **[Human-in-the-loop support](../cloud/how-tos/human_in_the_loop_breakpoint.md)**: In many applications, users require a way to intervene in agent processes. LangGraph Server provides specialized endpoints for human-in-the-loop scenarios, simplifying the integration of manual oversight into agent workflows.
|
||||
|
||||
- **[LangGraph Studio](./langgraph_studio.md)**: Enables visualization, interaction, and debugging of agentic systems that implement the LangGraph Server API protocol. Studio also integrates with LangSmith to enable tracing, evaluation, and prompt engineering.
|
||||
|
||||
- **[Deployment](./deployment_options.md)**: There are four ways to deploy on LangGraph Platform: [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), and [Standalone Container](../concepts/langgraph_standalone_container.md).
|
||||
@@ -1,34 +0,0 @@
|
||||
# Self-Hosted Control Plane
|
||||
|
||||
There are two versions of the self-hosted deployment: [Self-Hosted Data Plane](./deployment_options.md#self-hosted-data-plane) and [Self-Hosted Control Plane](./deployment_options.md#self-hosted-control-plane).
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Control Plane deployment option requires an [Enterprise](plans.md) plan.
|
||||
|
||||
## Requirements
|
||||
|
||||
- You use `langgraph-cli` and/or [LangGraph Studio](./langgraph_studio.md) app to test graph locally.
|
||||
- You use `langgraph build` command to build image.
|
||||
- You have a Self-Hosted LangSmith instance deployed.
|
||||
- You are using Ingress for your LangSmith instance. All agents will be deployed as Kubernetes services behind this ingress.
|
||||
|
||||
## Self-Hosted Control Plane
|
||||
|
||||
The [Self-Hosted Control Plane](./langgraph_self_hosted_control_plane.md) deployment option is a fully self-hosted model for deployment where you manage the [control plane](./langgraph_control_plane.md) and [data plane](./langgraph_data_plane.md) in your cloud. This option gives you full control and responsibility of the control plane and data plane infrastructure.
|
||||
|
||||
| | [Control plane](../concepts/langgraph_control_plane.md) | [Data plane](../concepts/langgraph_data_plane.md) |
|
||||
|-------------------|-------------------|------------|
|
||||
| **What is it?** | <ul><li>Control plane UI for creating deployments and revisions</li><li>Control plane APIs for creating deployments and revisions</li></ul> | <ul><li>Data plane "listener" for reconciling deployments with control plane state</li><li>LangGraph Servers</li><li>Postgres, Redis, etc</li></ul> |
|
||||
| **Where is it hosted?** | Your cloud | Your cloud |
|
||||
| **Who provisions and manages it?** | You | You |
|
||||
|
||||
### Architecture
|
||||
|
||||

|
||||
|
||||
### Compute Platforms
|
||||
|
||||
- **Kubernetes**: The Self-Hosted Control Plane deployment option supports deploying control plane and data plane infrastructure to any Kubernetes cluster.
|
||||
|
||||
!!! tip
|
||||
If you would like to enable this on your LangSmith instance, please follow the [Self-Hosted Control Plane deployment guide](../cloud/deployment/self_hosted_control_plane.md).
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Self-Hosted Data Plane
|
||||
|
||||
There are two versions of the self-hosted deployment: [Self-Hosted Data Plane](./deployment_options.md#self-hosted-data-plane) and [Self-Hosted Control Plane](./deployment_options.md#self-hosted-control-plane).
|
||||
|
||||
!!! info "Important"
|
||||
The Self-Hosted Data Plane deployment option requires an [Enterprise](plans.md) plan.
|
||||
|
||||
## Requirements
|
||||
|
||||
- You use `langgraph-cli` and/or [LangGraph Studio](./langgraph_studio.md) app to test graph locally.
|
||||
- You use `langgraph build` command to build image.
|
||||
|
||||
## Self-Hosted Data Plane
|
||||
|
||||
The [Self-Hosted Data Plane](../cloud/deployment/self_hosted_data_plane.md) deployment option is a "hybrid" model for deployment where we manage the [control plane](./langgraph_control_plane.md) in our cloud and you manage the [data plane](./langgraph_data_plane.md) in your cloud. This option provides a way to securely manage your data plane infrastructure, while offloading control plane management to us. When using the Self-Hosted Data Plane version, you authenticate with a [LangSmith](https://smith.langchain.com/) API key.
|
||||
|
||||
| | [Control plane](../concepts/langgraph_control_plane.md) | [Data plane](../concepts/langgraph_data_plane.md) |
|
||||
|-------------------|-------------------|------------|
|
||||
| **What is it?** | <ul><li>Control plane UI for creating deployments and revisions</li><li>Control plane APIs for creating deployments and revisions</li></ul> | <ul><li>Data plane "listener" for reconciling deployments with control plane state</li><li>LangGraph Servers</li><li>Postgres, Redis, etc</li></ul> |
|
||||
| **Where is it hosted?** | LangChain's cloud | Your cloud |
|
||||
| **Who provisions and manages it?** | LangChain | You |
|
||||
|
||||
For information on how to deploy a [LangGraph Server](../concepts/langgraph_server.md) to Self-Hosted Data Plane, see [Deploy to Self-Hosted Data Plane](../cloud/deployment/self_hosted_data_plane.md)
|
||||
|
||||
### Architecture
|
||||
|
||||

|
||||
|
||||
### Compute Platforms
|
||||
|
||||
- **Kubernetes**: The Self-Hosted Data Plane deployment option supports deploying data plane infrastructure to any Kubernetes cluster.
|
||||
- **Amazon ECS**: Coming soon!
|
||||
|
||||
!!! tip
|
||||
If you would like to deploy to Kubernetes, you can follow the [Self-Hosted Data Plane deployment guide](../cloud/deployment/self_hosted_data_plane.md).
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Server
|
||||
|
||||
**LangGraph Server** offers an API for creating and managing agent-based applications. It is built on the concept of [assistants](assistants.md), which are agents configured for specific tasks, and includes built-in [persistence](persistence.md#memory-store) and a **task queue**. This versatile API supports a wide range of agentic application use cases, from background processing to real-time interactions.
|
||||
|
||||
Use LangGraph Server to create and manage [assistants](assistants.md), [threads](./persistence.md#threads), [runs](./assistants.md#execution), [cron jobs](../cloud/concepts/cron_jobs.md), [webhooks](../cloud/concepts/webhooks.md), and more.
|
||||
|
||||
!!! tip "API reference"
|
||||
|
||||
For detailed information on the API endpoints and data models, see [LangGraph Platform API reference docs](../cloud/reference/api/api_ref.html).
|
||||
|
||||
## Server versions
|
||||
|
||||
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](../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:
|
||||
|
||||
| | Lite | Enterprise |
|
||||
|-------|------------|------------|
|
||||
| [Cron Jobs](../cloud/concepts/cron_jobs.md) |❌|✅|
|
||||
| [Custom Authentication](../concepts/auth.md) |❌|✅|
|
||||
| [Deployment options](../concepts/deployment_options.md) | Standalone container | Cloud SaaS, Self-Hosted Data Plane, Self-Hosted Control Plane, Standalone container
|
||||
|
||||
## Application structure
|
||||
|
||||
To deploy a LangGraph Server application, you need to specify the graph(s) you want to deploy, as well as any relevant configuration settings, such as dependencies and environment variables.
|
||||
|
||||
Read the [application structure](./application_structure.md) guide to learn how to structure your LangGraph application for deployment.
|
||||
|
||||
## Parts of a deployment
|
||||
|
||||
When you deploy LangGraph Server, you are deploying one or more [graphs](#graphs), a database for [persistence](persistence.md), and a task queue.
|
||||
|
||||
### Graphs
|
||||
|
||||
When you deploy a graph with LangGraph Server, you are deploying a "blueprint" for an [Assistant](assistants.md).
|
||||
|
||||
An [Assistant](assistants.md) is a graph paired with specific configuration settings. You can create multiple assistants per graph, each with unique settings to accommodate different use cases
|
||||
that can be served by the same graph.
|
||||
|
||||
Upon deployment, LangGraph Server will automatically create a default assistant for each graph using the graph's default configuration settings.
|
||||
|
||||
!!! note
|
||||
|
||||
We often think of a graph as implementing an [agent](agentic_concepts.md), but a graph does not necessarily need to implement an agent. For example, a graph could implement a simple
|
||||
chatbot that only supports back-and-forth conversation, without the ability to influence any application control flow. In reality, as applications get more complex, a graph will often implement a more complex flow that may use [multiple agents](./multi_agent.md) working in tandem.
|
||||
|
||||
### Persistence and task queue
|
||||
|
||||
LangGraph Server leverages a database for [persistence](persistence.md) and a task queue.
|
||||
|
||||
Currently, only [Postgres](https://www.postgresql.org/) is supported as a database for LangGraph Server and [Redis](https://redis.io/) as the task queue.
|
||||
|
||||
If you're deploying using [LangGraph Platform](./langgraph_cloud.md), these components are managed for you. If you're deploying LangGraph Server on your own infrastructure, you'll need to set up and manage these components yourself.
|
||||
|
||||
Please review the [deployment options](./deployment_options.md) guide for more information on how these components are set up and managed.
|
||||
|
||||
## Learn more
|
||||
|
||||
* LangGraph [Application Structure](./application_structure.md) guide explains how to structure your LangGraph application for deployment.
|
||||
* The [LangGraph Platform API Reference](../cloud/reference/api/api_ref.html) provides detailed information on the API endpoints and data models.
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Standalone Container
|
||||
|
||||
To deploy a [LangGraph Server](../concepts/langgraph_server.md), follow the how-to guide for [how to deploy a Standalone Container](../cloud/deployment/standalone_container.md).
|
||||
|
||||
## Overview
|
||||
|
||||
The Standalone Container deployment option is the least restrictive model for deployment. There is no [control plane](./langgraph_control_plane.md). [Data plane](./langgraph_data_plane.md) infrastructure is managed by you.
|
||||
|
||||
| | [Control plane](../concepts/langgraph_control_plane.md) | [Data plane](../concepts/langgraph_data_plane.md) |
|
||||
|-------------------|-------------------|------------|
|
||||
| **What is it?** | n/a | <ul><li>LangGraph Servers</li><li>Postgres, Redis, etc</li></ul> |
|
||||
| **Where is it hosted?** | n/a | Your cloud |
|
||||
| **Who provisions and manages it?** | n/a | You |
|
||||
|
||||
!!! warning
|
||||
|
||||
LangGraph Platform should not be deployed in serverless environments. Scale to zero may cause task loss and scaling up will not work reliably.
|
||||
|
||||
## Architecture
|
||||
|
||||

|
||||
|
||||
## Compute Platforms
|
||||
|
||||
### Kubernetes
|
||||
|
||||
The Standalone Container deployment option supports deploying data plane infrastructure to a Kubernetes cluster.
|
||||
|
||||
### Docker
|
||||
|
||||
The Standalone Container deployment option supports deploying data plane infrastructure to any Docker-supported compute platform.
|
||||
|
||||
## Lite vs. Enterprise
|
||||
|
||||
The Standalone Container deployment option supports both of the [server versions](../concepts/langgraph_server.md#langgraph-server):
|
||||
|
||||
- The `Lite` version is free, but has limited features.
|
||||
- The `Enterprise` version has custom pricing and is fully featured.
|
||||
|
||||
For more details on feature difference, see [LangGraph Server](../concepts/langgraph_server.md#server-versions).
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Studio
|
||||
|
||||
!!! info "Prerequisites"
|
||||
|
||||
- [LangGraph Platform](./langgraph_platform.md)
|
||||
- [LangGraph Server](./langgraph_server.md)
|
||||
- [LangGraph CLI](./langgraph_cli.md)
|
||||
|
||||
LangGraph Studio is a specialized agent IDE that enables visualization, interaction, and debugging of agentic systems that implement the LangGraph Server API protocol. Studio also integrates with LangSmith to enable tracing, evaluation, and prompt engineering.
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
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)
|
||||
- [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)
|
||||
|
||||
LangGraph Studio works for graphs that are deployed on [LangGraph Platform](../cloud/quick_start.md) or for graphs that are running locally via the [LangGraph Server](../tutorials/langgraph-platform/local-server.md).
|
||||
|
||||
Studio supports two modes:
|
||||
|
||||
### Graph mode
|
||||
|
||||
Graph mode exposes the full feature-set of Studio and is useful when you would like as many details about the execution of your agent, including the nodes traversed, intermediate states, and LangSmith integrations (such as adding to datasets and playground).
|
||||
|
||||
### Chat mode
|
||||
|
||||
Chat mode is a simpler UI for iterating on and testing chat-specific agents. It is useful for business users and those who want to test overall agent behavior. Chat mode is only supported for graph's whose state includes or extends [`MessagesState`](https://langchain-ai.github.io/langgraph/how-tos/graph-api/#messagesstate).
|
||||
|
||||
## Learn more
|
||||
|
||||
- See this guide on how to [get started](../cloud/how-tos/studio/quick_start.md) with LangGraph Studio.
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph Platform Plans
|
||||
|
||||
|
||||
## Overview
|
||||
LangGraph Platform is a solution for deploying agentic applications in production.
|
||||
There are three different plans for using it.
|
||||
|
||||
- **Developer**: All [LangSmith](https://smith.langchain.com/) users have access to this plan. You can sign up for this plan simply by creating a LangSmith account. This gives you access to the [Standalone Container (Lite)](./deployment_options.md) deployment option.
|
||||
- **Plus**: All [LangSmith](https://smith.langchain.com/) users with a [Plus account](https://docs.smith.langchain.com/administration/pricing) have access to this plan. You can sign up for this plan simply by upgrading your LangSmith account to the Plus plan type. This gives you access to the [Cloud](./deployment_options.md#cloud-saas) deployment option.
|
||||
- **Enterprise**: This is separate from LangSmith plans. You can sign up for this plan by contacting sales@langchain.dev. This gives you access to all [deployment options](./deployment_options.md).
|
||||
|
||||
|
||||
## Plan Details
|
||||
|
||||
| | 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](../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 | ✅ | ✅ | ✅ |
|
||||
| Real-time streaming of outputs and intermediate steps | ✅ | ✅ | ✅ |
|
||||
| Assistants API (configurable templates for LangGraph apps) | ✅ | ✅ | ✅ |
|
||||
| Cron scheduling | -- | ✅ | ✅ |
|
||||
| LangGraph Studio for prototyping | ✅ | ✅ | ✅ |
|
||||
| Authentication & authorization to call the LangGraph APIs | -- | Coming Soon! | Coming Soon! |
|
||||
| Smart caching to reduce traffic to LLM API | -- | Coming Soon! | Coming Soon! |
|
||||
| Publish/subscribe API for state | -- | Coming Soon! | Coming Soon! |
|
||||
| Scheduling prioritization | -- | Coming Soon! | Coming Soon! |
|
||||
|
||||
For pricing information, see [LangGraph Platform Pricing](https://www.langchain.com/langgraph-platform-pricing).
|
||||
|
||||
## Related
|
||||
|
||||
For more information, please see:
|
||||
|
||||
* [Deployment Options conceptual guide](./deployment_options.md)
|
||||
* [LangGraph Platform Pricing](https://www.langchain.com/langgraph-platform-pricing)
|
||||
* [LangSmith Plans](https://docs.smith.langchain.com/administration/pricing)
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# Scalability & Resilience
|
||||
|
||||
LangGraph Platform is designed to scale horizontally with your workload. Each instance of the service is stateless, and keeps no resources in memory. The service is designed to gracefully handle new instances being added or removed, including hard shutdown cases.
|
||||
|
||||
## Server scalability
|
||||
|
||||
As you add more instances to a service, they will share the HTTP load as long as an appropriate load balancer mechanism is placed in front of them. In most deployment modalities we configure a load balancer for the service automatically. In the “self-hosted without control plane” modality it’s your responsibility to add a load balancer. Since the instances are stateless any load balancing strategy will work, no session stickiness is needed, or recommended. Any instance of the server can communicate with any queue instance (through Redis PubSub), meaning that requests to cancel or stream an in-progress run can be handled by any arbitrary instance.
|
||||
|
||||
## Queue scalability
|
||||
|
||||
As you add more instances to a service, they will increase run throughput linearly, as each instance is configured to handle a set number of concurrent runs (by default 10). Each attempt for each run will be handled by a single instance, with exactly-once semantics enforced through Postgres’s MVCC model (refer to section below for crash resilience details). Attempts that fail due to transient database errors are retried up to 3 times. We do not make use of long-lived transactions or locks, this enables us to make more efficient use of Postgres resources.
|
||||
|
||||
## Resilience
|
||||
|
||||
While a run is being handled by a queue instance, a periodic heartbeat timestamp will be recorded in Redis by that queue worker.
|
||||
|
||||
When a graceful shutdown request is received (SIGINT) an instance enters shutdown mode, which
|
||||
|
||||
- stops accepting new HTTP requests
|
||||
- gives any in-progress runs a limited number of seconds to finish (if not finished it will be put back in the queue)
|
||||
- stops the instance from picking up more runs from the queue
|
||||
|
||||
If a hard shutdown occurs due to a server crash or an infrastructure failure, any runs that were in progress will be picked up by an internal sweeper task that looks for in-progress runs that have breached their heartbeat window. The sweeper runs every 2 minutes and will put the runs back in the queue for another instance to pick them up.
|
||||
|
||||
## Postgres resilience
|
||||
|
||||
For deployment modalities where we manage the Postgres database, we have periodic backups and continuously replicated standby replicas for automatic failover. This Postgres configuration is available in the [Cloud SaaS deployment option](../concepts/langgraph_cloud.md) for [`Production` deployment types](../concepts/langgraph_control_plane.md#deployment-types) only.
|
||||
|
||||
All communication with Postgres implements retries for retry-able errors. If Postgres is momentarily unavailable, such as during a database restart, most/all traffic should continue to succeed. Prolonged failure of Postgres will render the LangGraph Server unavailable.
|
||||
|
||||
## Redis resilience
|
||||
|
||||
All data that requires durable storage is stored in Postgres, not Redis. Redis is used only for ephemeral metadata, and communication between instances. Therefore we place no durability requirements on Redis.
|
||||
|
||||
All communication with Redis implements retries for retry-able errors. If Redis is momentarily unavailable, such as during a database restart, most/all traffic should continue to succeed. Prolonged failure of Redis will render the LangGraph Server unavailable.
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
# LangGraph SDK
|
||||
|
||||
LangGraph Platform provides both a Python SDK for interacting with [LangGraph Server](./langgraph_server.md).
|
||||
|
||||
!!! tip "Python SDK reference"
|
||||
|
||||
For detailed information about the Python SDK, see [Python SDK reference docs](../cloud/reference/sdk/python_sdk_ref.md).
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the packages using the appropriate package manager for your language:
|
||||
|
||||
=== "Python"
|
||||
```bash
|
||||
pip install langgraph-sdk
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
```bash
|
||||
yarn add @langchain/langgraph-sdk
|
||||
```
|
||||
|
||||
## Python sync vs. async
|
||||
|
||||
The Python SDK provides both synchronous (`get_sync_client`) and asynchronous (`get_client`) clients for interacting with LangGraph Server:
|
||||
|
||||
=== "Sync"
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_sync_client
|
||||
|
||||
client = get_sync_client(url=..., api_key=...)
|
||||
client.assistants.search()
|
||||
```
|
||||
|
||||
=== "Async"
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
client = get_client(url=..., api_key=...)
|
||||
await client.assistants.search()
|
||||
```
|
||||
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Python SDK Reference](../cloud/reference/sdk/python_sdk_ref.md)
|
||||
- [LangGraph CLI API Reference](../cloud/reference/cli.md)
|
||||
- [JS/TS SDK Reference](../cloud/reference/sdk/js_ts_sdk_ref.md)
|
||||
@@ -1,259 +0,0 @@
|
||||
---
|
||||
tags:
|
||||
- mcp
|
||||
- platform
|
||||
hide:
|
||||
- tags
|
||||
---
|
||||
|
||||
# MCP endpoint in LangGraph Server
|
||||
|
||||
The [Model Context Protocol (MCP)](./mcp.md) is an open protocol for describing tools and data sources in a model-agnostic format, enabling LLMs to discover and use them via a structured API.
|
||||
|
||||
[LangGraph Server](./langgraph_server.md) implements MCP using the [Streamable HTTP transport](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http). This allows LangGraph **agents** to be exposed as **MCP tools**, making them usable with any MCP-compliant client supporting Streamable HTTP.
|
||||
|
||||
The MCP endpoint is available at `/mcp` on [LangGraph Server](./langgraph_server.md).
|
||||
|
||||
## Requirements
|
||||
|
||||
To use MCP, ensure you have the following dependencies installed:
|
||||
|
||||
- `langgraph-api >= 0.2.3`
|
||||
- `langgraph-sdk >= 0.1.61`
|
||||
|
||||
Install them with:
|
||||
|
||||
```bash
|
||||
pip install "langgraph-api>=0.2.3" "langgraph-sdk>=0.1.61"
|
||||
```
|
||||
|
||||
## Usage overview
|
||||
|
||||
To enable MCP:
|
||||
|
||||
- Upgrade to use langgraph-api>=0.2.3. If you are deploying LangGraph Platform, this will be done for you automatically if you create a new revision.
|
||||
- MCP tools (agents) will be automatically exposed.
|
||||
- Connect with any MCP-compliant client that supports Streamable HTTP.
|
||||
|
||||
|
||||
### Client
|
||||
|
||||
Use an MCP-compliant client to connect to the LangGraph server. The following examples show how to connect using different programming languages.
|
||||
|
||||
=== "JavaScript/TypeScript"
|
||||
|
||||
```bash
|
||||
npm install @modelcontextprotocol/sdk
|
||||
```
|
||||
|
||||
> **Note**
|
||||
> Replace `serverUrl` with your LangGraph server URL and configure authentication headers as needed.
|
||||
|
||||
```js
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||
|
||||
// Connects to the LangGraph MCP endpoint
|
||||
async function connectClient(url) {
|
||||
const baseUrl = new URL(url);
|
||||
const client = new Client({
|
||||
name: 'streamable-http-client',
|
||||
version: '1.0.0'
|
||||
});
|
||||
|
||||
const transport = new StreamableHTTPClientTransport(baseUrl);
|
||||
await client.connect(transport);
|
||||
|
||||
console.log("Connected using Streamable HTTP transport");
|
||||
console.log(JSON.stringify(await client.listTools(), null, 2));
|
||||
return client;
|
||||
}
|
||||
|
||||
const serverUrl = "http://localhost:2024/mcp";
|
||||
|
||||
connectClient(serverUrl)
|
||||
.then(() => {
|
||||
console.log("Client connected successfully");
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Failed to connect client:", error);
|
||||
});
|
||||
```
|
||||
|
||||
=== "Python"
|
||||
|
||||
|
||||
Install the adapter with:
|
||||
|
||||
```bash
|
||||
pip install langchain-mcp-adapters
|
||||
```
|
||||
|
||||
Here is an example of how to connect to a remote MCP endpoint and use an agent as a tool:
|
||||
|
||||
```python
|
||||
# Create server parameters for stdio connection
|
||||
from mcp import ClientSession
|
||||
from mcp.client.streamable_http import streamablehttp_client
|
||||
import asyncio
|
||||
|
||||
from langchain_mcp_adapters.tools import load_mcp_tools
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
server_params = {
|
||||
"url": "https://mcp-finance-agent.xxx.us.langgraph.app/mcp",
|
||||
"headers": {
|
||||
"X-Api-Key":"lsv2_pt_your_api_key"
|
||||
}
|
||||
}
|
||||
|
||||
async def main():
|
||||
async with streamablehttp_client(**server_params) as (read, write, _):
|
||||
async with ClientSession(read, write) as session:
|
||||
# Initialize the connection
|
||||
await session.initialize()
|
||||
|
||||
# Load the remote graph as if it was a tool
|
||||
tools = await load_mcp_tools(session)
|
||||
|
||||
# Create and run a react agent with the tools
|
||||
agent = create_react_agent("openai:gpt-4.1", tools)
|
||||
|
||||
# Invoke the agent with a message
|
||||
agent_response = await agent.ainvoke({"messages": "What can the finance agent do for me?"})
|
||||
print(agent_response)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Expose an agent as MCP tool
|
||||
|
||||
When deployed, your agent will appear as a tool in the MCP endpoint
|
||||
with this configuration:
|
||||
|
||||
- **Tool name**: The agent's name.
|
||||
- **Tool description**: The agent's description.
|
||||
- **Tool input schema**: The agent's input schema.
|
||||
|
||||
### Setting name and description
|
||||
|
||||
You can set the name and description of your agent in `langgraph.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"graphs": {
|
||||
"my_agent": {
|
||||
"path": "./my_agent/agent.py:graph",
|
||||
"description": "A description of what the agent does"
|
||||
}
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
```
|
||||
|
||||
After deployment, you can update the name and description using the LangGraph SDK.
|
||||
|
||||
### Schema
|
||||
|
||||
Define clear, minimal input and output schemas to avoid exposing unnecessary internal complexity to the LLM.
|
||||
|
||||
The default [MessagesState](./low_level.md#messagesstate) uses `AnyMessage`, which supports many message types but is too general for direct LLM exposure.
|
||||
|
||||
Instead, define **custom agents or workflows** that use explicitly typed input and output structures.
|
||||
|
||||
For example, a workflow answering documentation questions might look like this:
|
||||
|
||||
```python
|
||||
from langgraph.graph import StateGraph, START, END
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
# Define input schema
|
||||
class InputState(TypedDict):
|
||||
question: str
|
||||
|
||||
# Define output schema
|
||||
class OutputState(TypedDict):
|
||||
answer: str
|
||||
|
||||
# Combine input and output
|
||||
class OverallState(InputState, OutputState):
|
||||
pass
|
||||
|
||||
# Define the processing node
|
||||
def answer_node(state: InputState):
|
||||
# Replace with actual logic and do something useful
|
||||
return {"answer": "bye", "question": state["question"]}
|
||||
|
||||
# Build the graph with explicit schemas
|
||||
builder = StateGraph(OverallState, input_schema=InputState, output_schema=OutputState)
|
||||
builder.add_node(answer_node)
|
||||
builder.add_edge(START, "answer_node")
|
||||
builder.add_edge("answer_node", END)
|
||||
graph = builder.compile()
|
||||
|
||||
# Run the graph
|
||||
print(graph.invoke({"question": "hi"}))
|
||||
```
|
||||
|
||||
For more details, see the [low-level concepts guide](https://langchain-ai.github.io/langgraph/concepts/low_level/#state).
|
||||
|
||||
## Use user-scoped MCP tools in your deployment
|
||||
|
||||
!!! tip "Prerequisites"
|
||||
|
||||
You have added your own [custom auth middleware](https://langchain-ai.github.io/langgraph/how-tos/auth/custom_auth/) that populates the `langgraph_auth_user` object, making it accessible through configurable context for every node in your graph.
|
||||
|
||||
To make user-scoped tools available to your LangGraph Platform deployment, start with implementing a snippet like the following:
|
||||
|
||||
```python
|
||||
from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
|
||||
def mcp_tools_node(state, config):
|
||||
user = config["configurable"].get("langgraph_auth_user")
|
||||
# e.g., user["github_token"], user["email"], etc.
|
||||
|
||||
client = MultiServerMCPClient({
|
||||
"github": {
|
||||
"transport": "streamable_http", # (1)
|
||||
"url": "https://my-github-mcp-server/mcp", # (2)
|
||||
"headers": {
|
||||
"Authorization": f"Bearer {user['github_token']}"
|
||||
}
|
||||
}
|
||||
})
|
||||
tools = await client.get_tools() # (3)
|
||||
|
||||
# Your tool-calling logic here
|
||||
|
||||
tool_messages = ...
|
||||
return {"messages": tool_messages}
|
||||
```
|
||||
|
||||
1. MCP only supports adding headers to requests made to `streamable_http` and `sse` `transport` servers.
|
||||
2. Your MCP server URL.
|
||||
3. Get available tools from your MCP server.
|
||||
|
||||
_This can also be done by [rebuilding your graph at runtime](https://langchain-ai.github.io/langgraph/cloud/deployment/graph_rebuild/) to have a different configuration for a new run_
|
||||
|
||||
## Session behavior
|
||||
|
||||
The current LangGraph MCP implementation does not support sessions. Each `/mcp` request is stateless and independent.
|
||||
|
||||
## Authentication
|
||||
|
||||
The `/mcp` endpoint uses the same authentication as the rest of the LangGraph API. Refer to the [authentication guide](./auth.md) for setup details.
|
||||
|
||||
## Disable MCP
|
||||
|
||||
To disable the MCP endpoint, set `disable_mcp` to `true` in your `langgraph.json` configuration file:
|
||||
|
||||
```json
|
||||
{
|
||||
"http": {
|
||||
"disable_mcp": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will prevent the server from exposing the `/mcp` endpoint.
|
||||
@@ -9,15 +9,4 @@ The pages in this section provide end-to-end examples for the following topics:
|
||||
- [Agent Supervisor](../tutorials/multi_agent/agent_supervisor.md): Build a supervisor agent that can manage a team of agents.
|
||||
- [SQL agent](../tutorials/sql/sql-agent.md): Build a SQL agent that can execute SQL queries and return the results.
|
||||
- [Prebuilt chat UI](../agents/ui.md): Use a prebuilt chat UI to interact with any LangGraph agent.
|
||||
- [Graph runs in LangSmith](../how-tos/run-id-langsmith.md): Use LangSmith to track and analyze graph runs.
|
||||
|
||||
## LangGraph Platform
|
||||
|
||||
- [Set up custom authentication](../tutorials/auth/getting_started.md): Set up custom authentication for your LangGraph application.
|
||||
- [Make conversations private](../tutorials/auth/resource_auth.md): Make conversations private by using resource-based authentication.
|
||||
- [Connect an authentication provider](../tutorials/auth/add_auth_server.md): Connect an authentication provider to your LangGraph application.
|
||||
- [Rebuild graph at runtime](../cloud/deployment/graph_rebuild.md): Rebuild a graph at runtime.
|
||||
- [Use RemoteGraph](../how-tos/use-remote-graph.md): Use RemoteGraph to deploy your LangGraph application to a remote server.
|
||||
- [Deploy CrewAI, AutoGen, and other frameworks](../how-tos/autogen-integration.md): Deploy CrewAI, AutoGen, and other frameworks with LangGraph.
|
||||
- [Integrate LangGraph into a React app](../cloud/how-tos/use_stream_react.md)
|
||||
- [Implement Generative User Interfaces with LangGraph](../cloud/how-tos/generative_ui_react.md)
|
||||
- [Graph runs in LangSmith](../how-tos/run-id-langsmith.md): Use LangSmith to track and analyze graph runs.
|
||||