diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml
index 6c6a169fa..f29f41604 100644
--- a/.github/workflows/deploy_docs.yml
+++ b/.github/workflows/deploy_docs.yml
@@ -89,6 +89,7 @@ jobs:
--check-links-ignore "https://x.com/.*" \
--check-links-ignore "https://github\.com/.*" \
--check-links-ignore "http://localhost:8123/.*" \
+ --check-links-ignore "http://localhost:2024/.*" \
--check-links-ignore "/.*\.(ipynb|html)$" \
--check-links-ignore "https://python\.langchain\.com/.*" \
--check-links-ignore "https://openai\.com/.*" \
@@ -106,6 +107,7 @@ jobs:
poetry run pytest -v \
--check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" \
--check-links-ignore "http://localhost:8123/.*" \
+ --check-links-ignore "http://localhost:2024/.*" \
--check-links-ignore "https://x.com/.*" \
--check-links-ignore "https://github\.com/.*" \
--check-links-ignore "/.*\.(ipynb|html)$" \
diff --git a/Makefile b/Makefile
index 0d039591e..9bd2847e4 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ serve-clean-docs: clean-docs
poetry run python -m mkdocs serve -c -f docs/mkdocs.yml --strict -w ./libs/langgraph
serve-docs: build-typedoc
- poetry run python -m mkdocs serve -f docs/mkdocs.yml -w ./libs/langgraph -w ./libs/checkpoint --dirty
+ poetry run python -m mkdocs serve -f docs/mkdocs.yml -w ./libs/langgraph -w ./libs/checkpoint -w ./libs/sdk-py --dirty
clean-docs:
find ./docs/docs -name "*.ipynb" -type f -delete
diff --git a/docs/docs/cloud/deployment/img/01_login.png b/docs/docs/cloud/deployment/img/01_login.png
new file mode 100644
index 000000000..e0487b935
Binary files /dev/null and b/docs/docs/cloud/deployment/img/01_login.png differ
diff --git a/docs/docs/cloud/deployment/img/02_langgraph_platform.png b/docs/docs/cloud/deployment/img/02_langgraph_platform.png
new file mode 100644
index 000000000..6b7a6d1f6
Binary files /dev/null and b/docs/docs/cloud/deployment/img/02_langgraph_platform.png differ
diff --git a/docs/docs/cloud/deployment/img/03_deployments_page.png b/docs/docs/cloud/deployment/img/03_deployments_page.png
new file mode 100644
index 000000000..cbb1b23f4
Binary files /dev/null and b/docs/docs/cloud/deployment/img/03_deployments_page.png differ
diff --git a/docs/docs/cloud/deployment/img/04_create_new_deployment.png b/docs/docs/cloud/deployment/img/04_create_new_deployment.png
new file mode 100644
index 000000000..369d54c7c
Binary files /dev/null and b/docs/docs/cloud/deployment/img/04_create_new_deployment.png differ
diff --git a/docs/docs/cloud/deployment/img/05_configure_deployment.png b/docs/docs/cloud/deployment/img/05_configure_deployment.png
new file mode 100644
index 000000000..897762f25
Binary files /dev/null and b/docs/docs/cloud/deployment/img/05_configure_deployment.png differ
diff --git a/docs/docs/cloud/deployment/img/07_deployments_page.png b/docs/docs/cloud/deployment/img/07_deployments_page.png
new file mode 100644
index 000000000..ce5970650
Binary files /dev/null and b/docs/docs/cloud/deployment/img/07_deployments_page.png differ
diff --git a/docs/docs/cloud/deployment/img/08_deployment_view.png b/docs/docs/cloud/deployment/img/08_deployment_view.png
new file mode 100644
index 000000000..bdc75f50f
Binary files /dev/null and b/docs/docs/cloud/deployment/img/08_deployment_view.png differ
diff --git a/docs/docs/cloud/deployment/img/09_langgraph_studio.png b/docs/docs/cloud/deployment/img/09_langgraph_studio.png
new file mode 100644
index 000000000..e89ad9ff3
Binary files /dev/null and b/docs/docs/cloud/deployment/img/09_langgraph_studio.png differ
diff --git a/docs/docs/cloud/how-tos/interrupt_concurrent.md b/docs/docs/cloud/how-tos/interrupt_concurrent.md
index 02098c912..b5b767d2b 100644
--- a/docs/docs/cloud/how-tos/interrupt_concurrent.md
+++ b/docs/docs/cloud/how-tos/interrupt_concurrent.md
@@ -83,7 +83,7 @@ Now, let's import our required packages and instantiate our client, assistant, a
## Create runs
-Now we can start our two runs and join the second on euntil it has completed:
+Now we can start our two runs and join the second one until it has completed:
=== "Python"
diff --git a/docs/docs/cloud/quick_start.md b/docs/docs/cloud/quick_start.md
index 9f142c0bf..12944a01d 100644
--- a/docs/docs/cloud/quick_start.md
+++ b/docs/docs/cloud/quick_start.md
@@ -1,462 +1,273 @@
-# LangGraph Cloud Quick Start
+# Quickstart: Deploy on LangGraph Cloud
-In this tutorial you will build and deploy a simple chatbot agent that can look things up on the internet. You will be using [LangGraph Cloud](../concepts/langgraph_cloud.md), [LangGraph Studio](../concepts/langgraph_studio.md) to visualize and test it out, and [LangGraph SDK](./reference/sdk/python_sdk_ref.md) to interact with the deployed agent.
+!!! note "Prerequisites"
-If you want to learn how to build an agent like this from scratch, take a look at the [LangGraph Quick Start tutorial](../tutorials/introduction.ipynb).
+ Before you begin, ensure you have the following:
-## Set up requirements
+ - [GitHub account](https://github.com/)
+ - [LangSmith account](https://smith.langchain.com/)
-This tutorial will use:
+## Create a repository on GitHub
-- Anthropic for the LLM - sign up and get an API key [here](https://console.anthropic.com/).
-- Tavily for the search engine - sign up and get an API key [here](https://app.tavily.com/).
-- LangSmith for hosting - sign up and get an API key [here](https://smith.langchain.com/).
+To deploy a LangGraph application to **LangGraph Cloud**, your application code must reside in a GitHub repository. Both public and private repositories are supported.
-## Create and configure your app
+You can deploy any [LangGraph Application](../concepts/application_structure.md) to LangGraph Cloud.
-First, let's set create all of the necessary files for our LangGraph application.
+For this guide, we'll use the pre-built Python [**ReAct Agent**](https://github.com/langchain-ai/react-agent) template.
-1. __Create application directory and files__
+??? note "Get Required API Keys for the ReAct Agent template"
- Create a new application `my-app` with the following file structure:
+ This **ReAct Agent** application requires an API key from [Anthropic](https://console.anthropic.com/) and [Tavily](https://app.tavily.com/). You can get these API keys by signing up on their respective websites.
- ```shell
- mkdir my-app
- ```
+ **Alternative**: If you'd prefer a scaffold application that doesn't require API keys, use the [**New LangGraph Project**](https://github.com/langchain-ai/new-langgraph-project) template instead of the **ReAct Agent** template.
- === "Python"
- my-app/
- |-- agent.py # code for your LangGraph agent
- |-- requirements.txt # Python packages required for your graph
- |-- langgraph.json # configuration file for LangGraph
- |-- .env # environment files with API keys
-
- === "Javascript"
-
- my-app/
- |-- agent.ts # code for your LangGraph agent
- |-- package.json # Javascript packages required for your graph
- |-- langgraph.json # configuration file for LangGraph
- |-- .env # environment files with API keys
-
-
-1. __Define your graph__
-
- === "Python"
- The `agent.py` file should contain code with your graph.
-
- === "Javascript"
- The `agent.ts` file should contain code with your graph.
-
- The following code example is a simple chatbot agent (similar to the one in the [previous tutorial](../tutorials/introduction.ipynb)). Specifically, it uses [create_react_agent][langgraph.prebuilt.chat_agent_executor.create_react_agent], a prebuilt [ReAct](../concepts/agentic_concepts.md#react-implementation)-style agent.
-
- The `agent` file needs to have a variable with a [CompiledGraph][langgraph.graph.graph.CompiledGraph] (in this case the `graph` variable).
-
- === "Python"
-
- ```python
- # agent.py
- from langchain_anthropic import ChatAnthropic
- from langchain_community.tools.tavily_search import TavilySearchResults
- from langgraph.prebuilt import create_react_agent
-
- model = ChatAnthropic(model="claude-3-5-sonnet-20240620")
-
- tools = [TavilySearchResults(max_results=2)]
-
- # compiled graph
- graph = create_react_agent(model, tools)
- ```
-
- === "Javascript"
-
- ```ts
- // agent.ts
- import { ChatAnthropic } from "@langchain/anthropic";
- import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
- import { createReactAgent } from "@langchain/langgraph/prebuilt";
-
- const model = new ChatAnthropic({
- model: "claude-3-5-sonnet-20240620",
- });
-
- const tools = [
- new TavilySearchResults({ maxResults: 3, }),
- ];
-
- // compiled graph
- export const graph = createReactAgent({ llm: model, tools });
- ```
-
-1. __Specify dependencies__
-
- === "Python"
- You should add dependencies for your graph(s) to `requirements.txt`.
-
- === "Javascript"
- You should add dependencies for your graph(s) to `package.json`.
-
- In this case we only require four packages for our graph to run:
-
- === "Python"
-
- ```python
- langgraph
- langchain_anthropic
- tavily-python
- langchain_community
- ```
-
- === "Javascript"
-
- ```js
- {
- "name": "my-app",
- "packageManager": "yarn@1.22.22",
- "dependencies": {
- "@langchain/community": "^0.3.11",
- "@langchain/core": "^0.3.16",
- "@langchain/langgraph": "0.2.18",
- "@langchain/anthropic": "^0.3.7"
- }
- }
- ```
-
-1. __Create LangGraph configuration file__
-
- The [`langgraph.json`][langgraph.json] file is a configuration file that describes what graph(s) you are going to deploy. In this case we only have one graph: the compiled `graph` object from `agent.py` / `agent.ts`.
-
- === "Python"
-
- ```json
- {
- "dependencies": ["."],
- "graphs": {
- "agent": "./agent.py:graph"
- },
- "env": ".env"
- }
- ```
-
- === "Javascript"
-
- ```json
- {
- "node_version": "20",
- "dockerfile_lines": [],
- "dependencies": ["."],
- "graphs": {
- "agent": "./src/agent.ts:graph"
- },
- "env": ".env"
- }
- ```
-
- Learn more about the LangGraph CLI configuration file [here](./reference/cli.md#configuration-file).
-
-1. __Specify environment variables__
-
- The `.env` file should have any environment variables needed to run your graph. This will only be used for local testing, so if you are not testing locally you can skip this step.
-
- !!! warning
- The `.env` file should NOT be included with the rest of source code in your Github repository. When creating a deployment using LangGraph Cloud, you will be able to specify the environment variables manually.
-
- For this graph, we need two environment variables:
-
- ```shell
- ANTHROPIC_API_KEY=...
- TAVILY_API_KEY=...
- ```
-
-!!! tip
- Learn more about different application structure options [here](../how-tos/index.md#application-structure).
-
-Now that we have set everything up on our local file system, we are ready to test our graph locally.
-
-## Test the app locally
-
-To test the LangGraph app before deploying it using LangGraph Cloud, you can start the [LangGraph server](../concepts/langgraph_server.md) locally or use [LangGraph Studio](../concepts/langgraph_studio.md).
-
-## Using local server
-
-You can test your app by running [LangGraph server](../concepts/langgraph_server.md) locally. This is useful to make sure you have configured our [CLI configuration file][langgraph.json] correctly and can interact with your graph.
-
-To run the server locally, you need to first install the LangGraph CLI:
-
-```shell
-pip install langgraph-cli
-```
-
-You can then test our API server locally. In order to run the server locally, you will need to add your `LANGSMITH_API_KEY` to the `.env` file.
-
-```shell
-langgraph up
-```
-
-This will start up the LangGraph API server locally. If this runs successfully, you should see something like:
-
-```shell
-Ready!
-- API: http://localhost:8123
-```
-
-First, let's verify that the server is running correctly by calling `/ok` endpoint:
-
-```shell
-curl --request GET --url http://localhost:8123/ok
-```
-
-Output:
-
-```
-{"ok": "true"}
-```
-
-Now we're ready to test the app with the real inputs!
-
-```shell
-curl --request POST \
- --url http://localhost:8123/runs/stream \
- --header 'Content-Type: application/json' \
- --data '{
- "assistant_id": "agent",
- "input": {
- "messages": [
- {
- "role": "user",
- "content": "What is the weather in NYC?"
- }
- ]
- },
- "stream_mode": "updates"
-}'
-```
-
-Output:
-
-```
-...
-
-data: {
- "agent": {
- "messages": [
- {
- "content": "The search results from Tavily provide the current weather conditions in New York City, including temperature, wind speed, precipitation, humidity, and cloud cover. According to the results, as of 3:00pm on October 30th, 2024, it is overcast in NYC with a temperature of around 66°F (19°C), light winds from the southwest around 8 mph (13 km/h), and 66% humidity.\n\nSo in summary, the current weather in NYC is overcast with mild temperatures in the mid 60sF and light winds, based on the search results. Let me know if you need any other details!",
- "type": "ai",
- ...
- }
- ]
- }
-}
-```
-
-You can see that our agent responds with the up-to-date search results!
-
-### Using LangGraph Studio Desktop
-
-You can also test your app locally with [LangGraph Studio](../concepts/langgraph_studio.md). LangGraph Studio offers a new way to develop LLM applications by providing a specialized agent IDE that enables visualization, interaction, and debugging of complex agentic applications.
-
-With visual graphs and the ability to edit state, you can better understand agent workflows and iterate faster. LangGraph Studio integrates with LangSmith allowing you to collaborate with teammates to debug failure modes.
-
-LangGraph Studio is available as a [desktop app](https://studio.langchain.com/) for MacOS users. Once you have installed the app, you can select `my-app` directory, which will automatically start the server locally and load the graph in the UI.
-
-To interact with your chatbot agent in LangGraph Studio, you can add a new message in the `Input` section and press `Submit`.
-
-
+1. Go to the [ReAct Agent](https://github.com/langchain-ai/react-agent) repository.
+2. Fork the repository to your GitHub account by clicking the `Fork` button in the top right corner.
## Deploy to LangGraph Cloud
-Once you've tested your graph locally and verified that it works as expected, you can deploy it to the LangGraph Cloud.
+??? note "1. Log in to [LangSmith](https://smith.langchain.com/)"
-First, you'll need to turn the `my-app` directory into a GitHub repo and [push it to GitHub](https://docs.github.com/en/migrations/importing-source-code/using-the-command-line-to-import-source-code/adding-locally-hosted-code-to-github).
+
+ [{: style="max-height:300px"}](deployment/img/01_login.png)
+
+ Go to [LangSmith](https://smith.langchain.com/) and log in. If you don't have an account, you can sign up for free.
+
+
-Once you have created your GitHub repository with a Python file containing your compiled graph as well as a `langgraph.json` with the configuration, you can head over to [LangSmith](https://smith.langchain.com/) and click on the graph icon (`LangGraph Cloud`) on the bottom of the left navbar. This will open the LangGraph deployments page. On this page, click the `+ New Deployment` button in the top right corner.
-
+??? note "2. Click on LangGraph Platform (the left sidebar)"
-**_If you have not deployed to LangGraph Cloud before:_** there will be a button that shows up saying `Import from GitHub`. You’ll need to follow that flow to connect LangGraph Cloud to GitHub.
+
+ [{: style="max-height:300px"}](deployment/img/02_langgraph_platform.png)
+
+ Select **LangGraph Platform** from the left sidebar.
+
+
-**_Once you have set up your GitHub connection:_** the new deployment page will look as follows:
+??? note "3. Click on + New Deployment (top right corner)"
-
+
+ [{: style="max-height:300px"}](deployment/img/03_deployments_page.png)
+
+ Click on **+ New Deployment** to create a new deployment. This button is located in the top right corner.
+ It'll open a new modal where you can fill out the required fields.
+
+
-To deploy your application, you should do the following:
+??? note "4. Click on Import from GitHub (first time users)"
-1. Select your GitHub username or organization from the selector
-1. Search for your repo to deploy in the search bar and select it
-1. Choose a name for your deployment
-1. In the `Git Branch` field, you can specify either the branch for the code you want to deploy, or the exact commit SHA.
-1. In the `LangGraph API config file` field, enter the path to your `langgraph.json` file (which in this case is just `langgraph.json`)
-1. If your application needs environment variables, add those in the `Environment Variables` section. They will be propagated to the underlying server so your code can access them. In this case, we will need `ANTHROPIC_API_KEY` and `TAVILY_API_KEY`.
+
+ [](deployment/img/04_create_new_deployment.png)
+
+ Click on **Import from GitHub** and follow the instructions to connect your GitHub account. This step is needed for **first-time users** or to add private repositories that haven't been connected before.
+
-Hit `Submit` and your application will start deploying!
+??? note "5. Select the repository, configure ENV vars etc"
-After your deployment is complete, your deployments page should look as follows:
+
+ [{: style="max-height:300px"}](deployment/img/05_configure_deployment.png)
+
+ Select the repository, add env variables and secrets, and set other configuration options.
+
+
-
+ - **Repository**: Select the repository you forked earlier (or any other repository you want to deploy).
+ - Set the secrets and environment variables required by your application. For the **ReAct Agent** template, you need to set the following secrets:
+ - **ANTHROPIC_API_KEY**: Get an API key from [Anthropic](https://console.anthropic.com/).
+ - **TAVILY_API_KEY**: Get an API key on the [Tavily website](https://app.tavily.com/).
-## Interact with your deployment
+??? note "6. Click Submit to Deploy!"
-### Using LangGraph Studio (Cloud)
-On the deployment page for your application,, you should see a button in the top right corner that says `LangGraph Studio`. Clicking on this button will take you to the web version of LangGraph Studio. This is the same UI that you interacted with when [testing the app locally](#using-langgraph-studio-recommended), but instead of using a local LangGraph server, it uses the one from your LangGraph Cloud deployment.
+
+ [{: style="max-height:300px"}](deployment/img/05_configure_deployment.png)
+
+ Please note that this step may ~15 minutes to complete. You can check the status of your deployment in the **Deployments** view.
+ Click the Submit button at the top right corner to deploy your application.
+
+
-
-### Using LangGraph SDK
+## Lagraph Studio Web UI
-You can also interact with your deployed LangGraph application programmatically, using [LangGraph SDK](./reference/sdk/python_sdk_ref.md).
+Once your application is deployed, you can test it in **LangGraph Studio**.
-First, make sure you have the SDK installed:
+??? note "1. Click on an existing deployment"
-=== "Python"
+
+ [{: style="max-height:300px"}](deployment/img/07_deployments_page.png)
+
+ Click on the deployment you just created to view more details.
+
+
- ```shell
- pip install langgraph_sdk
- ```
+??? note "2. Click on LangGraph Studio"
-=== "Javascript"
+
+ [{: style="max-height:300px"}](deployment/img/08_deployment_view.png)
+
+ Click on the LangGraph Studio button to open LangGraph Studio.
+
+
- ```shell
- yarn add @langchain/langgraph-sdk
- ```
+
+[{: style="max-height:400px"}](deployment/img/09_langgraph_studio.png)
+
+ Sample graph run in LangGraph Studio.
+
+
-Before using, you need to get the URL of your LangGraph deployment. You can find this in the `Deployment` view. Click the URL to copy it to the clipboard.
+## Test the API
-You also need to make sure you have set up your API key properly so you can authenticate with LangGraph Cloud.
+!!! note
+
+ The API calls below are for the **ReAct Agent** template. If you're deploying a different application, you may need to adjust the API calls accordingly.
+
+Before using, you need to get the `URL` of your LangGraph deployment. You can find this in the `Deployment` view. Click the `URL` to copy it to the clipboard.
+
+You also need to make sure you have set up your API key properly, so you can authenticate with LangGraph Cloud.
```shell
export LANGSMITH_API_KEY=...
```
-The first thing to do when using the SDK is to setup our client, access our assistant, and create a thread to execute a run on:
+=== "Python SDK (Async)"
-=== "Python"
+ **Install the LangGraph Python SDK**
- ```python
- from langgraph_sdk import get_client
+ ```shell
+ pip install langgraph-sdk
+ ```
- client = get_client(url=)
- # get default assistant
- assistants = await client.assistants.search(metadata={"created_by": "system"})
- assistant = assistants[0]
- # create thread
- thread = await client.threads.create()
- print(thread)
- ```
-
-=== "Javascript"
-
- ```js
- import { Client } from "@langchain/langgraph-sdk";
-
- const client = new Client({ apiUrl: });
- // get default assistant
- const assistants = await client.assistants.search({ metadata: {"created_by": "system"} })
- const assistant = assistants[0];
- // create thread
- const thread = await client.threads.create();
- console.log(thread)
- ```
-
-=== "CURL"
-
- ```bash
- curl --request POST \
- --url /assistants/search \
- --header 'Content-Type: application/json' \
- --data '{
- "limit": 10,
- "offset": 0,
- "metadata": {"created_by": "system"}
- }' &&
- curl --request POST \
- --url /threads \
- --header 'Content-Type: application/json' \
- --data '{}'
- ```
-
-We can then execute a run on the thread:
-
-=== "Python"
+ **Send a message to the assistant (threadless run)**
```python
- input = {
- "messages": [{"role": "user", "content": "What is the weather in NYC?"}]
- }
+ 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(
- thread["thread_id"],
- assistant["assistant_id"],
- input=input,
+ None, # Threadless run
+ "agent", # Name of assistant. Defined in langgraph.json.
+ input={
+ "messages": [{
+ "role": "human",
+ "content": "What is LangGraph?",
+ }],
+ },
stream_mode="updates",
):
- if chunk.data:
- print(chunk.data)
+ print(f"Receiving new event of type: {chunk.event}...")
+ print(chunk.data)
+ print("\n\n")
```
-=== "Javascript"
+=== "Python SDK (Sync)"
+
+ **Install the LangGraph Python SDK**
+
+ ```shell
+ pip install langgraph-sdk
+ ```
+
+ **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"
+
+ **Install the LangGraph JS SDK**
+
+ ```shell
+ npm install @langchain/langgraph-sdk
+ ```
+
+ **Send a message to the assistant (threadless run)**
```js
- const input = { "messages": [{ "role": "user", "content": "What is the weather in NYC?" }] };
+ const { Client } = await import("@langchain/langgraph-sdk");
+
+ // only set the apiUrl if you changed the default port when calling langgraph up
+ const client = new Client({ apiUrl: "your-deployment-url", apiKey: "your-langsmith-api-key" });
const streamResponse = client.runs.stream(
- thread["thread_id"],
- assistant["assistant_id"],
- {
- input,
- streamMode: "updates"
- }
+ null, // Threadless run
+ "agent", // Assistant ID
+ {
+ input: {
+ "messages": [
+ { "role": "user", "content": "What is LangGraph?"}
+ ]
+ },
+ streamMode: "messages",
+ }
);
+
for await (const chunk of streamResponse) {
- if (chunk.data) {
- console.log(chunk.data);
- }
+ console.log(`Receiving new event of type: ${chunk.event}...`);
+ console.log(JSON.stringify(chunk.data));
+ console.log("\n\n");
}
```
-=== "CURL"
+=== "Rest API"
```bash
- curl --request POST \
- --url /threads//runs/stream \
- --header 'Content-Type: application/json' \
- --data '{
- "assistant_id": ,
- "input": {
- "messages": [
- {
- "role": "user",
- "content": "What is the weather in NYC?"
- }
- ]
- },
- "stream_mode": "updates"
- }'
+ curl -s --request POST \
+ --url \
+ --header 'Content-Type: application/json' \
+ --data "{
+ \"assistant_id\": \"agent\",
+ \"input\": {
+ \"messages\": [
+ {
+ \"role\": \"human\",
+ \"content\": \"What is LangGraph?\"
+ }
+ ]
+ },
+ \"stream_mode\": \"updates\"
+ }"
```
-Output:
-```
-...
-
-data: {
- "agent": {
- "messages": [
- {
- "content": "The search results from Tavily provide the current weather conditions in New York City, including temperature, wind speed, precipitation, humidity, and cloud cover. According to the results, as of 3:00pm on October 30th, 2024, it is overcast in NYC with a temperature of around 66°F (19°C), light winds from the southwest around 8 mph (13 km/h), and 66% humidity.\n\nSo in summary, the current weather in NYC is overcast with mild temperatures in the mid 60sF and light winds, based on the search results. Let me know if you need any other details!",
- "type": "ai",
- ...
- }
- ]
- }
-}
-```
-
-## Next steps
+## Next Steps
Congratulations! If you've worked your way through this tutorial you are well on your way to becoming a LangGraph Cloud expert. Here are some other resources to check out to help you out on the path to expertise:
-* [LangGraph How-to guides](../how-tos/index.md)
-* [LangGraph Tutorials](../tutorials/index.md)
\ No newline at end of file
+### LangGraph Framework
+
+- **[LangGraph Tutorial](../tutorials/introduction.ipynb)**: Get started with LangGraph framework.
+- **[LangGraph Concepts](../concepts/index.md)**: Learn the foundational concepts of LangGraph.
+- **[LangGraph How-to Guides](../how-tos/index.md)**: Guides for common tasks with LangGraph.
+
+### 📚 Learn More about LangGraph Platform
+
+Expand your knowledge with these resources:
+
+- **[LangGraph Platform Concepts](../concepts/index.md#langgraph-platform)**: Understand the foundational concepts of the LangGraph Platform.
+- **[LangGraph Platform How-to Guides](../how-tos/index.md#langgraph-platform)**: Discover step-by-step guides to build and deploy applications.
+- **[Launch Local LangGraph Server](../tutorials/langgraph-platform/local-server.md)**: This quick start guide shows how to start a LangGraph Server locally for the **ReAct Agent** template. The steps are similar for other templates.
+
+
diff --git a/docs/docs/cloud/reference/sdk/python_sdk_ref.md b/docs/docs/cloud/reference/sdk/python_sdk_ref.md
index 0492a08cd..4126ca306 100644
--- a/docs/docs/cloud/reference/sdk/python_sdk_ref.md
+++ b/docs/docs/cloud/reference/sdk/python_sdk_ref.md
@@ -6,3 +6,25 @@
::: langgraph_sdk.schema
handler: python
+
+
+::: langgraph_sdk.auth
+ handler: python
+
+::: langgraph_sdk.auth.types.Authenticator
+ handler: python
+
+::: langgraph_sdk.auth.types.Handler
+ handler: python
+
+::: langgraph_sdk.auth.types.HandlerResult
+ handler: python
+
+::: langgraph_sdk.auth.types.FilterType
+ handler: python
+
+::: langgraph_sdk.auth.types.AuthContext
+ handler: python
+
+::: langgraph_sdk.auth.exceptions
+ handler: python
\ No newline at end of file
diff --git a/docs/docs/concepts/double_texting.md b/docs/docs/concepts/double_texting.md
index 6906a9e4d..76c5a564b 100644
--- a/docs/docs/concepts/double_texting.md
+++ b/docs/docs/concepts/double_texting.md
@@ -16,7 +16,7 @@ If you do not want to use LangGraph Platform, we describe the options we have im
## Reject
-This is the simplest option, this just rejects any follow up runs and does not allow double texting.
+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
diff --git a/docs/docs/concepts/faq.md b/docs/docs/concepts/faq.md
index 33457bdf8..a626e8133 100644
--- a/docs/docs/concepts/faq.md
+++ b/docs/docs/concepts/faq.md
@@ -22,21 +22,21 @@ Yes. LangGraph is an MIT-licensed open-source library and is free to use.
LangGraph is a stateful, orchestration framework that brings added control to agent workflows. LangGraph Platform is a service for deploying and scaling LangGraph applications, with an opinionated API for building agent UXs, plus an integrated developer studio.
-| Features | LangGraph (open source) | LangGraph Platform |
-|----------|------------------------|-------------------|
-| Description | Stateful orchestration framework for agentic applications | Scalable infrastructure for deploying LangGraph applications |
-| SDKs | Python and JavaScript | Python and JavaScript |
-| HTTP APIs | None | Yes - useful for retrieving & updating state or long-term memory, or creating a configurable assistant |
-| Streaming | Basic | Dedicated mode for token-by-token messages |
-| Checkpointer | Community contributed | Supported out-of-the-box |
-| Persistence Layer | Self-managed | Managed Postgres with efficient storage |
-| Deployment | Self-managed | • Cloud SaaS • Free self-hosted • Enterprise (BYOC or paid self-hosted) |
-| Scalability | Self-managed | Auto-scaling of task queues and servers |
-| Fault-tolerance | Self-managed | Automated retries |
-| Concurrency Control | Simple threading | Supports double-texting |
-| Scheduling | None | Cron scheduling |
-| Monitoring | None | Integrated with LangSmith for observability |
-| IDE integration | LangGraph Studio for Desktop | LangGraph Studio for Desktop & Cloud |
+| Features | LangGraph (open source) | LangGraph Platform |
+|---------------------|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
+| Description | Stateful orchestration framework for agentic applications | Scalable infrastructure for deploying LangGraph applications |
+| SDKs | Python and JavaScript | Python and JavaScript |
+| HTTP APIs | None | Yes - useful for retrieving & updating state or long-term memory, or creating a configurable assistant |
+| Streaming | Basic | Dedicated mode for token-by-token messages |
+| Checkpointer | Community contributed | Supported out-of-the-box |
+| Persistence Layer | Self-managed | Managed Postgres with efficient storage |
+| Deployment | Self-managed | • Cloud SaaS • Free self-hosted • Enterprise (BYOC or paid self-hosted) |
+| Scalability | Self-managed | Auto-scaling of task queues and servers |
+| Fault-tolerance | Self-managed | Automated retries |
+| Concurrency Control | Simple threading | Supports double-texting |
+| Scheduling | None | Cron scheduling |
+| Monitoring | None | Integrated with LangSmith for observability |
+| IDE integration | LangGraph Studio for Desktop | LangGraph Studio for Desktop & Cloud |
## What are my deployment options for LangGraph Platform?
diff --git a/docs/docs/concepts/langgraph_platform.md b/docs/docs/concepts/langgraph_platform.md
index 8b2e6b61c..d9908aa8e 100644
--- a/docs/docs/concepts/langgraph_platform.md
+++ b/docs/docs/concepts/langgraph_platform.md
@@ -18,32 +18,7 @@ The LangGraph Platform offers a few different deployment options described in th
## Why Use LangGraph Platform?
-LangGraph Platform is designed to make deploying agentic applications seamless and production-ready.
-
-For simpler applications, deploying a LangGraph agent can be as straightforward as using your own server logic—for example, setting up a FastAPI endpoint and invoking LangGraph directly.
-
-### Option 1: Deploying with Custom Server Logic
-
-For basic LangGraph applications, you may choose to handle deployment using your custom server infrastructure. Setting up endpoints with frameworks like [FastAPI](https://fastapi.tiangolo.com/) allows you to quickly deploy and run LangGraph as you would any other Python application:
-
-```python
-from fastapi import FastAPI
-from your_agent_package import graph
-
-app = FastAPI()
-
-@app.get("/foo")
-async def foo(...):
- return await graph.ainvoke({...})
-```
-
-This approach works well for simple applications with straightforward needs and provides you with full control over the deployment setup. For example, you might use this for a single-assistant application that doesn’t require long-running sessions or persistent memory.
-
-### Option 2: Leveraging LangGraph Platform for Complex Deployments
-
-As your applications scale or add complex features, the deployment requirements often evolve. Running an application with more nodes, longer processing times, or a need for persistent memory can introduce challenges that quickly become time-consuming and difficult to manage manually. [LangGraph Platform](./langgraph_platform.md) is built to handle these challenges seamlessly, allowing you to focus on agent logic rather than server infrastructure.
-
-Here are some common issues that arise in complex deployments, which LangGraph Platform addresses:
+**LangGraph Platform** handles common issues that arise when deploying LLM applications to production, allowing you to focus on agent logic instead of managing server infrastructure.
- **[Streaming Support](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](streaming.md) optimized for various application needs.
diff --git a/docs/docs/concepts/multi_agent.md b/docs/docs/concepts/multi_agent.md
index 4ca186fcb..d4d549385 100644
--- a/docs/docs/concepts/multi_agent.md
+++ b/docs/docs/concepts/multi_agent.md
@@ -94,13 +94,13 @@ This is a special case of updating the graph state from tools where in addition
!!! important
If you want to use tools that return `Command`, you can either use prebuilt [`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent] / [`ToolNode`][langgraph.prebuilt.tool_node.ToolNode] components, or implement your own tool-executing node that collects `Command` objects returned by the tools and returns a list of them, e.g.:
-
-```python
-def call_tools(state):
- ...
- commands = [tools_by_name[tool_call["name"]].invoke(tool_call) for tool_call in tool_calls]
- return commands
-```
+
+ ```python
+ def call_tools(state):
+ ...
+ commands = [tools_by_name[tool_call["name"]].invoke(tool_call) for tool_call in tool_calls]
+ return commands
+ ```
Let's now take a closer look at the different multi-agent architectures.
diff --git a/docs/docs/concepts/template_applications.md b/docs/docs/concepts/template_applications.md
index 32209ad47..dff269da6 100644
--- a/docs/docs/concepts/template_applications.md
+++ b/docs/docs/concepts/template_applications.md
@@ -12,7 +12,7 @@ You can create an application from a template using the LangGraph CLI.
## Install the LangGraph CLI
```bash
-pip install "langgraph-cli[inmem]==0.1.58" python-dotenv
+pip install "langgraph-cli[inmem]" --upgrade
```
## Available Templates
diff --git a/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb b/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb
index 6ec8059bd..7ffc7a759 100644
--- a/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb
+++ b/docs/docs/how-tos/multi-agent-multi-turn-convo.ipynb
@@ -40,7 +40,7 @@
" \"content\": user_input,\n",
" }]\n",
" },\n",
- " goto=active_agent,\n",
+ " goto=active_agent,)\n",
"\n",
"def agent(state) -> Command[Literal[\"agent\", \"another_agent\", \"human\"]]:\n",
" # The condition for routing/halting can be anything, e.g. LLM tool call / structured output, etc.\n",
@@ -140,7 +140,6 @@
"\n",
"from langchain_openai import ChatOpenAI\n",
"from langchain_core.messages import AnyMessage\n",
- "from langchain_openai import ChatOpenAI\n",
"from langgraph.graph import MessagesState, StateGraph, START, END\n",
"from langgraph.types import Command, interrupt\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
diff --git a/docs/docs/how-tos/update-state-from-tools.ipynb b/docs/docs/how-tos/update-state-from-tools.ipynb
index 690a36a31..29a513904 100644
--- a/docs/docs/how-tos/update-state-from-tools.ipynb
+++ b/docs/docs/how-tos/update-state-from-tools.ipynb
@@ -166,8 +166,6 @@
"\n",
"\n",
"class State(AgentState):\n",
- " # user provided\n",
- " last_name: str\n",
" # updated by the tool\n",
" user_info: dict[str, Any]\n",
"\n",
diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md
index 65cafb115..02a7983d2 100644
--- a/docs/docs/tutorials/index.md
+++ b/docs/docs/tutorials/index.md
@@ -11,9 +11,9 @@ New to LangGraph or LLM app development? Read this material to get up and runnin
## Get Started
- [LangGraph Quickstart](introduction.ipynb): Build a chatbot that can use tools and keep track of conversation history. Add human-in-the-loop capabilities and explore how time-travel works.
-- [LangGraph Server Quickstart](langgraph-platform/local-server.md): Launch a LangGraph server locally and interact with it using the REST API and LangGraph Studio Web UI.
-- [LangGraph Cloud QuickStart](../cloud/quick_start.md): Deploy a LangGraph app using LangGraph Cloud.
-- [LangGraph Template Quickstart](../concepts/template_applications.md): Quickly start building with LangGraph Platform using a template application.
+- [LangGraph Server Quickstart](langgraph-platform/local-server.md): Launch a LangGraph server locally and interact with it using REST API and LangGraph Studio Web UI.
+- [LangGraph Template Quickstart](../concepts/template_applications.md): Start building with LangGraph Platform using a template application.
+- [Deploy with LangGraph Cloud Quickstart](../cloud/quick_start.md): Deploy a LangGraph app using LangGraph Cloud.
## Use cases
diff --git a/docs/docs/tutorials/langgraph-platform/local-server.md b/docs/docs/tutorials/langgraph-platform/local-server.md
index 359c164c1..c5a564cca 100644
--- a/docs/docs/tutorials/langgraph-platform/local-server.md
+++ b/docs/docs/tutorials/langgraph-platform/local-server.md
@@ -1,4 +1,4 @@
-# Quick Start: Launch Local LangGraph Server
+# QuickStart: Launch Local LangGraph Server
This is a quick start guide to help you get a LangGraph app up and running locally.
@@ -10,7 +10,7 @@ This is a quick start guide to help you get a LangGraph app up and running local
## Install the LangGraph CLI
```bash
-pip install -U "langgraph-cli[inmem]" python-dotenv
+pip install --upgrade "langgraph-cli[inmem]"
```
## 🌱 Create a LangGraph App
@@ -53,21 +53,12 @@ ANTHROPIC_API_KEY=sk-
OPENAI_API_KEY=sk-...
```
-Get API Keys
-
- TAVILY_API_KEY: Get an API key on the Tavily website.
-
-
-
+??? note "Get API Keys"
+
+ - **LANGSMITH_API_KEY**: Go to the [LangSmith Settings page](https://smith.langchain.com/settings). Then clck **Create API Key**.
+ - **ANTHROPIC_API_KEY**: Get an API key from [Anthropic](https://console.anthropic.com/).
+ - **OPENAI_API_KEY**: Get an API key from [OpenAI](https://openai.com/).
+ - **TAVILY_API_KEY**: Get an API key on the [Tavily website](https://app.tavily.com/).
## 🚀 Launch LangGraph Server
@@ -79,11 +70,11 @@ This will start up the LangGraph API server locally. If this runs successfully,
> Ready!
>
-> - API: [http://localhost:8123](http://localhost:8123/)
+> - API: [http://localhost:2024](http://localhost:2024/)
>
-> - Docs: http://localhost:8123/docs
+> - Docs: http://localhost:2024/docs
>
-> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:8123
+> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
!!! note "In-Memory Mode"
@@ -95,9 +86,9 @@ This will start up the LangGraph API server locally. If this runs successfully,
## LangGraph Studio Web UI
-Test your graph in the LangGraph Studio Web UI by visiting the URL provided in the output of the `langgraph up` command.
+Test your graph in the LangGraph Studio Web UI by visiting the URL provided in the output of the `langgraph dev` command.
-> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:8123
+> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
!!! warning "Safari Compatibility"
@@ -118,7 +109,7 @@ Test your graph in the LangGraph Studio Web UI by visiting the URL provided in t
```python
from langgraph_sdk import get_client
- client = get_client(url="http://localhost:8123")
+ client = get_client(url="http://localhost:2024")
async for chunk in client.runs.stream(
None, # Threadless run
@@ -149,7 +140,7 @@ Test your graph in the LangGraph Studio Web UI by visiting the URL provided in t
```python
from langgraph_sdk import get_sync_client
- client = get_sync_client(url="http://localhost:8123")
+ client = get_sync_client(url="http://localhost:2024")
for chunk in client.runs.stream(
None, # Threadless run
@@ -181,7 +172,7 @@ Test your graph in the LangGraph Studio Web UI by visiting the URL provided in t
const { Client } = await import("@langchain/langgraph-sdk");
// only set the apiUrl if you changed the default port when calling langgraph up
- const client = new Client({ apiUrl: "http://localhost:8123"});
+ const client = new Client({ apiUrl: "http://localhost:2024"});
const streamResponse = client.runs.stream(
null, // Threadless run
@@ -207,7 +198,7 @@ Test your graph in the LangGraph Studio Web UI by visiting the URL provided in t
```bash
curl -s --request POST \
- --url "http://localhost:8123/runs/stream" \
+ --url "http://localhost:2024/runs/stream" \
--header 'Content-Type: application/json' \
--data "{
\"assistant_id\": \"agent\",
@@ -235,7 +226,7 @@ Now that you have a LangGraph app running locally, take your journey further by
### 🌐 Deploy to LangGraph Cloud
-- **[LangGraph Cloud QuickStart](../../cloud/quick_start.md)**: Deploy your LangGraph app using LangGraph Cloud.
+- **[LangGraph Cloud Quickstart](../../cloud/quick_start.md)**: Deploy your LangGraph app using LangGraph Cloud.
### 📚 Learn More about LangGraph Platform
diff --git a/libs/cli/langgraph_cli/config.py b/libs/cli/langgraph_cli/config.py
index e7ba0e208..57d2ccadd 100644
--- a/libs/cli/langgraph_cli/config.py
+++ b/libs/cli/langgraph_cli/config.py
@@ -124,6 +124,7 @@ def validate_config(config: Config) -> Config:
{
"node_version": config.get("node_version"),
"dockerfile_lines": config.get("dockerfile_lines", []),
+ "dependencies": config.get("dependencies", []),
"graphs": config.get("graphs", {}),
"env": config.get("env", {}),
"store": config.get("store"),
@@ -528,8 +529,9 @@ def config_to_compose(
f"env_file: {config['env']}" if isinstance(config["env"], str) else ""
)
if watch:
+ dependencies = config.get("dependencies") or ["."]
watch_paths = [config_path.name] + [
- dep for dep in config["dependencies"] if dep.startswith(".")
+ dep for dep in dependencies if dep.startswith(".")
]
watch_actions = "\n".join(
f"""- path: {path}
diff --git a/libs/cli/pyproject.toml b/libs/cli/pyproject.toml
index ff5fc1a88..ad17f350a 100644
--- a/libs/cli/pyproject.toml
+++ b/libs/cli/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "langgraph-cli"
-version = "0.1.63"
+version = "0.1.64"
description = "CLI for interacting with LangGraph API"
authors = []
license = "MIT"
diff --git a/libs/sdk-py/langgraph_sdk/auth/__init__.py b/libs/sdk-py/langgraph_sdk/auth/__init__.py
index 5c34a8976..317c89e9f 100644
--- a/libs/sdk-py/langgraph_sdk/auth/__init__.py
+++ b/libs/sdk-py/langgraph_sdk/auth/__init__.py
@@ -11,26 +11,54 @@ AH = typing.TypeVar("AH", bound=types.Authenticator)
class Auth:
- """Authentication and authorization management for LangGraph.
+ """Add custom authentication and authorization management to your LangGraph application.
The Auth class provides a unified system for handling authentication and
- authorization in LangGraph applications. It supports:
+ authorization in LangGraph applications. It supports custom user authentication
+ protocols and fine-grained authorization rules for different resources and
+ actions.
- 1. Authentication via a decorator-based handler system
- 2. Fine-grained authorization rules for different resources and actions
- 3. Global and resource-specific authorization handlers
+ To use, create a separate python file and add the path to the file to your
+ LangGraph API configuration file (`langgraph.json`). Within that file, create
+ an instance of the Auth class and register authentication and authorization
+ handlers as needed.
+
+ Example `langgraph.json` file:
+
+ ```json
+ {
+ "dependencies": ["."],
+ "graphs": {
+ "agent": "./my_agent/agent.py:graph"
+ },
+ "env": ".env",
+ "auth": {
+ "path": "./auth.py:my_auth"
+ }
+ ```
+
+ Then the LangGraph server will load your auth file and run it server-side whenever a request comes in.
???+ example "Basic Usage"
```python
from langgraph_sdk import Auth
- auth = Auth()
+ my_auth = Auth()
+
+ async def verify_token(token: str) -> str:
+ # Verify token and return user_id
+ # This would typically be a call to your auth server
+ return "user_id"
@auth.authenticate
- async def authenticate(authorization: str) -> tuple[list[str], str]:
- # Verify token and return (scopes, user_id)
- user_id = verify_token(authorization)
- return ["read", "write"], user_id
+ async def authenticate(authorization: str) -> str:
+ # Verify token and return user_id
+ result = await verify_token(authorization)
+ if result != "user_id":
+ raise Auth.exceptions.HTTPException(
+ status_code=401, detail="Unauthorized"
+ )
+ return result
# Global fallback handler
@auth.on
@@ -44,11 +72,12 @@ class Auth:
```
???+ note "Request Processing Flow"
- 1. Authentication is performed first on every request
+ 1. Authentication (your `@auth.authenticate` handler) is performed first on **every request**
2. For authorization, the most specific matching handler is called:
- - If a handler exists for the exact resource and action, it is used
- - Otherwise, if a handler exists for the resource with any action, it is used
- - Finally, if no specific handlers match, the global handler is used (if any)
+ * If a handler exists for the exact resource and action, it is used (e.g., `@auth.on.threads.create`)
+ * Otherwise, if a handler exists for the resource with any action, it is used (e.g., `@auth.on.threads`)
+ * Finally, if no specific handlers match, the global handler is used (e.g., `@auth.on`)
+ * If no global handler is set, the request is accepted
This allows you to set default behavior with a global handler while
overriding specific routes as needed.
@@ -71,10 +100,64 @@ class Auth:
"""Reference to auth exception definitions.
Provides access to all exception definitions used in the auth system,
- like HTTPException, etc."""
+ like HTTPException, etc.
+ """
def __init__(self) -> None:
self.on = _On(self)
+ """Entry point for authorization handlers that control access to specific resources.
+
+ The on class provides a flexible way to define authorization rules for different
+ resources and actions in your application. It supports three main usage patterns:
+
+ 1. Global handlers that run for all resources and actions
+ 2. Resource-specific handlers that run for all actions on a resource
+ 3. Resource and action specific handlers for fine-grained control
+
+ Each handler must be an async function that accepts two parameters:
+ - ctx (AuthContext): Contains request context and authenticated user info
+ - value: The data being authorized (type varies by endpoint)
+
+ The handler should return one of:
+
+ - None or True: Accept the request
+ - False: Reject with 403 error
+ - FilterType: Apply filtering rules to the response
+
+ ???+ example "Examples"
+ Global handler for all requests:
+ ```python
+ @auth.on
+ async def reject_unhandled_requests(ctx: AuthContext, value: Any) -> None:
+ print(f"Request to {ctx.path} by {ctx.user.identity}")
+ return False
+ ```
+
+ Resource-specific handler. This would take precedence over the global handler
+ for all actions on the `threads` resource:
+ ```python
+ @auth.on.threads
+ async def check_thread_access(ctx: AuthContext, value: Any) -> bool:
+ # Allow access only to threads created by the user
+ return value.get("created_by") == ctx.user.identity
+ ```
+
+ Resource and action specific handler:
+ ```python
+ @auth.on.threads.delete
+ async def prevent_thread_deletion(ctx: AuthContext, value: Any) -> bool:
+ # Only admins can delete threads
+ return "admin" in ctx.user.permissions
+ ```
+
+ Multiple resources or actions:
+ ```python
+ @auth.on(resources=["threads", "runs"], actions=["create", "update"])
+ async def rate_limit_writes(ctx: AuthContext, value: Any) -> bool:
+ # Implement rate limiting for write operations
+ return await check_rate_limit(ctx.user.identity)
+ ```
+ """
# These are accessed by the API. Changes to their names or types is
# will be considered a breaking change.
self._handlers: dict[tuple[str, str], list[types.Handler]] = {}
@@ -88,21 +171,23 @@ class Auth:
The authentication handler is responsible for verifying credentials
and returning user scopes. It 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
- - method (str): The HTTP method
- - scopes (list[str]): Required scopes
- - path_params (dict[str, str]): URL path parameters
- - query_params (dict[str, str]): URL query parameters
- - headers (dict[str, bytes]): Request headers
- - authorization (str): The Authorization header value
+ - 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 ")
Args:
fn (Callable): The authentication handler function to register.
- Must return tuple[scopes, user]
- where scopes is a list of string claims (like "runs:read", etc.)
- and user is either a user object (or similar dict) or a user id string.
+ Must return a representation of the user. This could be a:
+ - string (the user id)
+ - dict containing {"identity": str, "permissions": list[str]}
+ - or an object with identity and permissions properties
+ Permissions can be optionally used by your handlers downstream.
Returns:
The registered handler function.
@@ -114,21 +199,38 @@ class Auth:
Basic token authentication:
```python
@auth.authenticate
- async def authenticate(authorization: str) -> tuple[list[str], str]:
+ async def authenticate(authorization: str) -> str:
user_id = verify_token(authorization)
- return ["read"], user_id
+ return user_id
```
- Complex authentication with request context:
+ Accept the full request context:
```python
@auth.authenticate
async def authenticate(
method: str,
path: str,
headers: dict[str, bytes]
- ) -> tuple[list[str], MinimalUser]:
+ ) -> str:
user = await verify_request(method, path, headers)
- return user.scopes, user
+ return user
+ ```
+
+ Return user name and permissions:
+ ```python
+ @auth.authenticate
+ async def authenticate(
+ method: str,
+ path: str,
+ headers: dict[str, bytes]
+ ) -> Auth.types.MinimalUserDict:
+ permissions, user = await verify_request(method, path, headers)
+ # Permissions could be things like ["runs:read", "runs:write", "threads:read", "threads:write"]
+ return {
+ "identity": user["id"],
+ "permissions": permissions,
+ "display_name": user["name"],
+ }
```
"""
if self._authenticate_handler is not None:
@@ -363,9 +465,58 @@ AHO = typing.TypeVar("AHO", bound=_ActionHandler[dict[str, typing.Any]])
class _On:
+ """Entry point for authorization handlers that control access to specific resources.
+
+ The _On class provides a flexible way to define authorization rules for different resources
+ and actions in your application. It supports three main usage patterns:
+
+ 1. Global handlers that run for all resources and actions
+ 2. Resource-specific handlers that run for all actions on a resource
+ 3. Resource and action specific handlers for fine-grained control
+
+ Each handler must be an async function that accepts two parameters:
+ - ctx (AuthContext): Contains request context and authenticated user info
+ - value: The data being authorized (type varies by endpoint)
+
+ The handler should return one of:
+ - None or True: Accept the request
+ - False: Reject with 403 error
+ - FilterType: Apply filtering rules to the response
+
+ ???+ example "Examples"
+
+ Global handler for all requests:
+ ```python
+ @auth.on
+ async def log_all_requests(ctx: AuthContext, value: Any) -> None:
+ print(f"Request to {ctx.path} by {ctx.user.identity}")
+ return True
+ ```
+
+ Resource-specific handler:
+ ```python
+ @auth.on.threads
+ async def check_thread_access(ctx: AuthContext, value: Any) -> bool:
+ # Allow access only to threads created by the user
+ return value.get("created_by") == ctx.user.identity
+ ```
+
+ Resource and action specific handler:
+ ```python
+ @auth.on.threads.delete
+ async def prevent_thread_deletion(ctx: AuthContext, value: Any) -> bool:
+ # Only admins can delete threads
+ return "admin" in ctx.user.permissions
+ ```
+
+ Multiple resources or actions:
+ ```python
+ @auth.on(resources=["threads", "runs"], actions=["create", "update"])
+ async def rate_limit_writes(ctx: AuthContext, value: Any) -> bool:
+ # Implement rate limiting for write operations
+ return await check_rate_limit(ctx.user.identity)
+ ```
"""
- Entry point for @auth.on decorators.
- Provides access to specific resources."""
__slots__ = (
"_auth",
@@ -420,7 +571,9 @@ class _On:
return fn
# Used with parameters, return a decorator
- def decorator(handler: AHO) -> AHO:
+ def decorator(
+ handler: AHO,
+ ) -> AHO:
if isinstance(resources, str):
resource_list = [resources]
else:
diff --git a/libs/sdk-py/langgraph_sdk/auth/types.py b/libs/sdk-py/langgraph_sdk/auth/types.py
index 21503f8b9..cc47651d0 100644
--- a/libs/sdk-py/langgraph_sdk/auth/types.py
+++ b/libs/sdk-py/langgraph_sdk/auth/types.py
@@ -16,6 +16,8 @@ from dataclasses import dataclass
from datetime import datetime
from uuid import UUID
+import typing_extensions
+
RunStatus = typing.Literal["pending", "error", "success", "timeout", "interrupted"]
"""Status of a run execution.
@@ -143,9 +145,10 @@ class MinimalUser(typing.Protocol):
class MinimalUserDict(typing.TypedDict, total=False):
"""The minimal user dictionary."""
- identity: str
+ identity: typing_extensions.Required[str]
display_name: str
is_authenticated: bool
+ permissions: Sequence[str]
@typing.runtime_checkable
@@ -167,17 +170,28 @@ class BaseUser(typing.Protocol):
"""The unique identifier for the user."""
...
+ @property
+ def permissions(self) -> Sequence[str]:
+ """The permissions associated with the user."""
+ ...
+
Authenticator = Callable[
- ..., Awaitable[tuple[list[str], typing.Union[MinimalUser, str, MinimalUserDict]]]
+ ...,
+ Awaitable[
+ typing.Union[
+ MinimalUser, str, BaseUser, MinimalUserDict, typing.Mapping[str, typing.Any]
+ ],
+ ],
]
"""Type for authentication functions.
An authenticator can return either:
-1. A tuple of (scopes, MinimalUser/BaseUser)
-2. A tuple of (scopes, str) where str is the user identity
+1. A string (user_id)
+2. A dict containing {"identity": str, "permissions": list[str]}
+3. An object with identity and permissions properties
-Scopes can be used downstream by your authorization logic to determine
+Permissions can be used downstream by your authorization logic to determine
access permissions to different resources.
The authenticate decorator will automatically inject any of the following parameters
@@ -188,11 +202,10 @@ Parameters:
body (dict): The parsed request body
path (str): The request path
method (str): The HTTP method (GET, POST, etc.)
- scopes (list[str]): The required scopes for this endpoint
path_params (dict[str, str] | None): URL path parameters
query_params (dict[str, str] | None): URL query parameters
headers (dict[str, bytes] | None): Request headers
- authorization (str | None): The Authorization header value
+ authorization (str | None): The Authorization header value (e.g. "Bearer ")
???+ example "Examples"
Basic authentication with token:
@@ -202,9 +215,8 @@ Parameters:
auth = Auth()
@auth.authenticate
- async def authenticate1(authorization: str) -> tuple[list[str], MinimalUser]:
- user = await get_user(authorization)
- return ["read", "write"], user
+ async def authenticate1(authorization: str) -> Auth.types.MinimalUserDict:
+ return await get_user(authorization)
```
Authentication with multiple parameters:
@@ -214,17 +226,17 @@ Parameters:
method: str,
path: str,
headers: dict[str, bytes]
- ) -> tuple[list[str], str]:
+ ) -> Auth.types.MinimalUserDict:
# Custom auth logic using method, path and headers
- user_id = verify_request(method, path, headers)
- return ["read"], user_id
+ user = verify_request(method, path, headers)
+ return user
```
Accepting the raw ASGI request:
```python
MY_SECRET = "my-secret-key"
@auth.authenticate
- async def get_current_user(request: Request) -> tuple[list[str], dict]:
+ async def get_current_user(request: Request) -> Auth.types.MinimalUserDict:
try:
token = (request.headers.get("authorization") or "").split(" ", 1)[1]
payload = jwt.decode(token, MY_SECRET, algorithms=["HS256"])
@@ -244,10 +256,11 @@ Parameters:
raise HTTPException(status_code=401, detail="User not found")
user_data = response.json()
- return payload.get("role", []), {
- "username": user_data["id"],
- "email": user_data["email"],
- "full_name": user_data.get("user_metadata", {}).get("full_name")
+ return {
+ "identity": user_data["id"],
+ "display_name": user_data.get("name"),
+ "permissions": user_data.get("permissions", []),
+ "is_authenticated": True,
}
```
"""
@@ -261,8 +274,8 @@ class BaseAuthContext:
authorization decisions.
"""
- scopes: Sequence[str]
- """The scopes granted to the authenticated user."""
+ permissions: Sequence[str]
+ """The permissions granted to the authenticated user."""
user: BaseUser
"""The authenticated user."""
@@ -688,16 +701,18 @@ class on:
```python
from langgraph_sdk import Auth
- @Auth.on
+ auth = Auth()
+
+ @auth.on
def handle_all(params: Auth.on.value):
raise Exception("Not authorized")
- @Auth.on.threads.create
+ @auth.on.threads.create
def handle_thread_create(params: Auth.on.threads.create.value):
# Handle thread creation
pass
- @Auth.on.assistants.search
+ @auth.on.assistants.search
def handle_assistant_search(params: Auth.on.assistants.search.value):
# Handle assistant search
pass
diff --git a/libs/sdk-py/pyproject.toml b/libs/sdk-py/pyproject.toml
index 7839c816e..c73c8afa1 100644
--- a/libs/sdk-py/pyproject.toml
+++ b/libs/sdk-py/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "langgraph-sdk"
-version = "0.1.46"
+version = "0.1.47"
description = "SDK for interacting with LangGraph API"
authors = []
license = "MIT"