Compare commits

..
24 changed files with 1037 additions and 765 deletions
+20
View File
@@ -0,0 +1,20 @@
name: Setup LangGraph CLI
description: Set up Python and install the CLI
inputs:
python-version:
description: Python version (e.g. 3.11)
required: true
runs:
using: composite
steps:
- uses: astral-sh/setup-uv@v7
with:
python-version: ${{ inputs.python-version }}
enable-cache: true
cache-suffix: cli-integration-test
ignore-nothing-to-cache: true
- name: Install CLI
shell: bash
working-directory: libs/cli
run: pip install -e .
+37 -13
View File
@@ -1,7 +1,8 @@
import logging
import pathlib
import sys
import time
from urllib import request, error
from urllib import error, request
import langgraph_cli
import langgraph_cli.config
@@ -11,9 +12,13 @@ from langgraph_cli.constants import DEFAULT_PORT
from langgraph_cli.exec import Runner, subp_exec
from langgraph_cli.progress import Progress
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
"""Spin up API with Postgres/Redis via docker compose and wait until ready."""
logger.info("Starting test...")
with Runner() as runner, Progress(message="Pulling...") as set:
# Detect docker/compose capabilities
capabilities = langgraph_cli.docker.check_capabilities(runner)
@@ -57,7 +62,9 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
sys.stderr.write(f"docker compose up failed: {e}\n")
try:
sys.stderr.write("\n== docker compose ps ==\n")
runner.run(subp_exec(*compose_cmd, *args, "ps", input=stdin, verbose=False))
runner.run(
subp_exec(*compose_cmd, *args, "ps", input=stdin, verbose=False)
)
except Exception:
pass
try:
@@ -93,7 +100,7 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
set("")
base_url = f"http://localhost:{port}"
ok_url = f"{base_url}/ok"
print(f"Waiting for {ok_url} to respond with 200...")
logger.info(f"Waiting for {ok_url} to respond with 200...")
deadline = time.time() + 30
last_err: Exception | None = None
while time.time() < deadline:
@@ -107,13 +114,16 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
break
else:
last_err = RuntimeError(f"Unexpected status: {resp.status}")
print(f"Unexpected status: {resp.status}")
logger.error(f"Unexpected status: {resp.status}")
except error.URLError as e:
logger.error(f"URLError: {e}")
last_err = e
except Exception as e: # noqa: BLE001
logger.error(f"Exception: {e}")
last_err = e
time.sleep(0.5)
else:
logger.error("Timeout waiting for /ok to return 200")
# Bring stack down before raising
args_down = [*args, "down", "-v", "--remove-orphans"]
try:
@@ -131,15 +141,23 @@ def test(config: pathlib.Path, port: int, tag: str, verbose: bool):
)
# Clean up: bring compose stack down to free ports for next test
args_down = [*args, "down", "-v", "--remove-orphans"]
runner.run(
subp_exec(
*compose_cmd,
*args_down,
input=stdin,
verbose=verbose,
logger.info("Test succeeded. Bringing down compose stack...")
try:
args_down = [*args, "down", "-v", "--remove-orphans"]
runner.run(
subp_exec(
*compose_cmd,
*args_down,
input=stdin,
verbose=verbose,
)
)
)
logger.info("Compose stack down. Finishing...")
except Exception:
logger.exception("Failed to bring down compose stack")
pass
logger.info("Test finished")
if __name__ == "__main__":
@@ -150,4 +168,10 @@ if __name__ == "__main__":
parser.add_argument("-c", "--config", type=str, default="./langgraph.json")
parser.add_argument("-p", "--port", type=int, default=DEFAULT_PORT)
args = parser.parse_args()
test(pathlib.Path(args.config), args.port, args.tag, verbose=True)
try:
test(pathlib.Path(args.config), args.port, args.tag, verbose=True)
except BaseException:
logger.exception("Test failed")
raise
logger.info("Test execution finished")
-111
View File
@@ -1,111 +0,0 @@
name: CLI integration test
on:
workflow_call:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.11"
- "3.14"
example:
- name: A
workdir: libs/cli/examples
tag: langgraph-test-a
- name: B
workdir: libs/cli/examples/graphs
tag: langgraph-test-b
- name: C
workdir: libs/cli/examples/graphs_reqs_a
tag: langgraph-test-c
- name: D
workdir: libs/cli/examples/graphs_reqs_b
tag: langgraph-test-d
name: "CLI integration test"
defaults:
run:
working-directory: libs/cli
steps:
- uses: actions/checkout@v5
- name: Get changed files
id: changed-files
uses: Ana06/get-changed-files@v2.3.0
with:
filter: "libs/cli/**"
- name: Set up Python ${{ matrix.python-version }}
if: steps.changed-files.outputs.all
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
cache-suffix: "cli-integration-test"
ignore-nothing-to-cache: true
- name: Install cli globally
if: steps.changed-files.outputs.all
run: pip install -e .
- name: Build and test service ${{ matrix.example.name }}
if: steps.changed-files.outputs.all
working-directory: ${{ matrix.example.workdir }}
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
# Build the image for this example
langgraph build -t ${{ matrix.example.tag }}
# Prepare environment file from local or parent example directory
if [ -f .env.example ]; then cp .env.example .env; elif [ -f ../.env.example ]; then cp ../.env.example .env && cp ../.env.example ../.env; fi
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env; if [ -f ../.env ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> ../.env; fi; fi
# Run the integration test using the built tag
# Compute repo root to reference the shared script robustly
REPO_ROOT=$(git rev-parse --show-toplevel)
timeout 60 python "$REPO_ROOT/.github/scripts/run_langgraph_cli_test.py" -t ${{ matrix.example.tag }}
- name: Build JS service
if: steps.changed-files.outputs.all
working-directory: libs/cli/js-examples
run: |
langgraph build -t langgraph-test-e
- name: Build JS monorepo service
if: steps.changed-files.outputs.all
working-directory: libs/cli/js-monorepo-example
run: |
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json --build-command "yarn run turbo build" --install-command "yarn install"
- name: Build Python monorepo service
if: steps.changed-files.outputs.all
working-directory: libs/cli/python-monorepo-example
run: |
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
cp apps/agent/.env.example apps/agent/.env
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env; fi
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
- name: Build and test prerelease reqs service
if: steps.changed-files.outputs.all
working-directory: libs/cli/examples/graph_prerelease_reqs
run: |
langgraph build -t langgraph-test-h
cp ../.env.example .env
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env; fi
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-h
LANGGRAPH_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langgraph'); print(v);")
if [ "$LANGGRAPH_VERSION" != "1.0.0a2" ]; then
exit 1
fi
LANGCHAIN_OPENAI_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "import sys; from importlib.metadata import version; v = version('langchain-openai'); print(v);")
if [ "$LANGCHAIN_OPENAI_VERSION" != "0.3.0" ]; then
exit 1
fi
- name: Build and test prerelease reqs fail service
if: steps.changed-files.outputs.all
working-directory: libs/cli/examples/graph_prerelease_reqs_fail
run: |
langgraph build -t langgraph-test-i || [ $? -eq 1 ]
+1 -1
View File
@@ -148,7 +148,7 @@ jobs:
needs: changes
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.deps == 'true'
name: CLI integration test
uses: ./.github/workflows/_integration_test.yml
uses: ./.github/workflows/ci_integration_tests.yml
secrets: inherit
ci_success:
+159
View File
@@ -0,0 +1,159 @@
name: CLI integration test
on:
workflow_call:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
detect_changes:
runs-on: ubuntu-latest
outputs:
cli_changed: ${{ steps.filter.outputs.cli_changed }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: filter
uses: dorny/paths-filter@v3
with:
filters: |
cli_changed:
- 'libs/cli/**'
base: 'main'
build_matrix:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.cli_changed == 'true' }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.14"
example:
- name: A
workdir: libs/cli/examples
tag: langgraph-test-a
- name: B
workdir: libs/cli/examples/graphs
tag: langgraph-test-b
- name: C
workdir: libs/cli/examples/graphs_reqs_a
tag: langgraph-test-c
- name: D
workdir: libs/cli/examples/graphs_reqs_b
tag: langgraph-test-d
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-cli
with:
python-version: ${{ matrix.python-version }}
- name: Build and test service ${{ matrix.example.name }}
working-directory: ${{ matrix.example.workdir }}
env:
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
run: |
langgraph build -t ${{ matrix.example.tag }}
if [ -f .env.example ]; then
cp .env.example .env
elif [ -f ../.env.example ]; then
cp ../.env.example .env && cp ../.env.example ../.env
fi
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env
if [ -f ../.env ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> ../.env; fi
fi
REPO_ROOT=$(git rev-parse --show-toplevel)
timeout 60 python "$REPO_ROOT/.github/scripts/run_langgraph_cli_test.py" -t ${{ matrix.example.tag }}
build_js_service:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.cli_changed == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-cli
with:
python-version: "3.11"
- name: Build JS service
working-directory: libs/cli/js-examples
run: langgraph build -t langgraph-test-e
build_js_monorepo_service:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.cli_changed == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-cli
with:
python-version: "3.11"
- name: Build JS monorepo service
working-directory: libs/cli/js-monorepo-example
run: |
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json \
--build-command "yarn run turbo build" \
--install-command "yarn install"
build_python_monorepo_service:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.cli_changed == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-cli
with:
python-version: "3.11"
- name: Build Python monorepo service
working-directory: libs/cli/python-monorepo-example
run: |
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
cp apps/agent/.env.example apps/agent/.env
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then
echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env
fi
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
build_and_test_prerelease_reqs_service:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.cli_changed == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-cli
with:
python-version: "3.11"
- name: Build and test prerelease reqs service
working-directory: libs/cli/examples/graph_prerelease_reqs
run: |
langgraph build -t langgraph-test-h
cp ../.env.example .env
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> .env; fi
timeout 60 python ../../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-h
echo "Finished initial test. Checking versions..."
LANGGRAPH_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "from importlib.metadata import version; print(version('langgraph'))")
test "$LANGGRAPH_VERSION" = "1.0.2"
LANGCHAIN_OPENAI_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "from importlib.metadata import version; print(version('langchain-openai'))")
test "$LANGCHAIN_OPENAI_VERSION" = "1.0.1"
LANGCHAIN_ANTHROPIC_VERSION=$(docker run --rm --entrypoint "" langgraph-test-h python -c "from importlib.metadata import version; print(version('langchain-anthropic'))")
test "$LANGCHAIN_ANTHROPIC_VERSION" = "1.0.0a5"
build_and_test_prerelease_reqs_fail_service:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.cli_changed == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-cli
with:
python-version: "3.11"
- name: Build and test prerelease reqs fail service
working-directory: libs/cli/examples/graph_prerelease_reqs_fail
run: |
langgraph build -t langgraph-test-i || [ $? -eq 1 ]
+1 -1
View File
@@ -180,7 +180,7 @@ REDIRECT_MAP = {
"cloud/concepts/data_storage_and_privacy.md": "https://docs.langchain.com/langsmith/data-storage-and-privacy",
"cloud/deployment/semantic_search.md": "https://docs.langchain.com/langsmith/semantic-search",
"how-tos/ttl/configure_ttl.md": "https://docs.langchain.com/langsmith/configure-ttl",
"concepts/deployment_options.md": "https://docs.langchain.com/langsmith/hosting",
"concepts/deployment_options.md": "https://docs.langchain.com/langsmith/platform-setup",
"cloud/quick_start.md": "https://docs.langchain.com/langsmith/deployment-quickstart",
"cloud/deployment/setup.md": "https://docs.langchain.com/langsmith/setup-app-requirements-txt",
"cloud/deployment/setup_pyproject.md": "https://docs.langchain.com/langsmith/setup-pyproject",
@@ -1,13 +1,12 @@
from collections.abc import Sequence
from typing import Annotated, Literal, TypedDict
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import END, StateGraph, add_messages
from langgraph.prebuilt import ToolNode
tools = [TavilySearchResults(max_results=1)]
tools = []
model_oai = ChatOpenAI(temperature=0)
@@ -5,5 +5,5 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langgraph==0.6.0"
"langgraph==1.0.2"
]
@@ -5,5 +5,5 @@ description = "Test for prerelease stuff"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langchain-openai==0.3.0"
"langchain-openai==1.0.0"
]
@@ -6,9 +6,9 @@ readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"langchain-openai==1.0.0a2",
"langgraph==1.0.0a2",
"langchain_community>=0.3.0",
"langchain-anthropic==1.0.0a5",
"langgraph==1.0.2"
]
[tool.uv]
prerelease = "allow"
prerelease = "allow"
+3 -1
View File
@@ -13,7 +13,7 @@ from pathlib import Path
import msgspec
from langgraph_cli.config import (
from langgraph_cli.schemas import (
AuthConfig,
CheckpointerConfig,
Config,
@@ -22,6 +22,7 @@ from langgraph_cli.config import (
HttpConfig,
IndexConfig,
SecurityConfig,
SerdeConfig,
StoreConfig,
ThreadTTLConfig,
TTLConfig,
@@ -112,6 +113,7 @@ def add_descriptions_to_schema(schema, cls):
CorsConfig,
ThreadTTLConfig,
CheckpointerConfig,
SerdeConfig,
TTLConfig,
ConfigurableHeaderConfig,
]:
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.4.4"
__version__ = "0.4.5"
+3 -499
View File
@@ -4,10 +4,12 @@ import pathlib
import re
import textwrap
from collections import Counter
from typing import Any, Literal, NamedTuple, TypedDict
from typing import Literal, NamedTuple
import click
from langgraph_cli.schemas import Config, Distros
MIN_NODE_VERSION = "20"
DEFAULT_NODE_VERSION = "20"
@@ -17,504 +19,6 @@ DEFAULT_PYTHON_VERSION = "3.11"
DEFAULT_IMAGE_DISTRO = "debian"
Distros = Literal["debian", "wolfi", "bullseye", "bookworm"]
MiddlewareOrders = Literal["auth_first", "middleware_first"]
class TTLConfig(TypedDict, total=False):
"""Configuration for TTL (time-to-live) behavior in the store."""
refresh_on_read: bool
"""Default behavior for refreshing TTLs on read operations (`GET` and `SEARCH`).
If `True`, TTLs will be refreshed on read operations (get/search) by default.
This can be overridden per-operation by explicitly setting `refresh_ttl`.
Defaults to `True` if not configured.
"""
default_ttl: float | None
"""Optional. Default TTL (time-to-live) in minutes for new items.
If provided, all new items will have this TTL unless explicitly overridden.
If omitted, items will have no TTL by default.
"""
sweep_interval_minutes: int | None
"""Optional. Interval in minutes between TTL sweep iterations.
If provided, the store will periodically delete expired items based on the TTL.
If omitted, no automatic sweeping will occur.
"""
class IndexConfig(TypedDict, total=False):
"""Configuration for indexing documents for semantic search in the store.
This governs how text is converted into embeddings and stored for vector-based lookups.
"""
dims: int
"""Required. Dimensionality of the embedding vectors you will store.
Must match the output dimension of your selected embedding model or custom embed function.
If mismatched, you will likely encounter shape/size errors when inserting or querying vectors.
Common embedding model output 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
"""
embed: str
"""Required. Identifier or reference to the embedding model or a custom embedding function.
The format can vary:
- "<provider>:<model_name>" for recognized providers (e.g., "openai:text-embedding-3-large")
- "path/to/module.py:function_name" for your own local embedding function
- "my_custom_embed" if it's a known alias in your system
Examples:
- "openai:text-embedding-3-large"
- "cohere:embed-multilingual-v3.0"
- "src/app.py:embeddings"
Note: Must return embeddings of dimension `dims`.
"""
fields: list[str] | None
"""Optional. List of JSON fields to extract before generating embeddings.
Defaults to ["$"], which means the entire JSON object is embedded as one piece of text.
If you provide multiple fields (e.g. ["title", "content"]), each is extracted and embedded separately,
often saving token usage if you only care about certain parts of the data.
Example:
fields=["title", "abstract", "author.biography"]
"""
class StoreConfig(TypedDict, total=False):
"""Configuration for the built-in long-term memory store.
This store can optionally perform semantic search. If you omit `index`,
the store will just handle traditional (non-embedded) data without vector lookups.
"""
index: IndexConfig | None
"""Optional. Defines the vector-based semantic search configuration.
If provided, the store will:
- Generate embeddings according to `index.embed`
- Enforce the embedding dimension given by `index.dims`
- Embed only specified JSON fields (if any) from `index.fields`
If omitted, no vector index is initialized.
"""
ttl: TTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the store will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
class ThreadTTLConfig(TypedDict, total=False):
"""Configure a default TTL for checkpointed data within threads."""
strategy: Literal["delete"]
"""Strategy to use for deleting checkpointed data.
Choices:
- "delete": Delete all checkpoints for a thread after TTL expires.
"""
default_ttl: float | None
"""Default TTL (time-to-live) in minutes for checkpointed data."""
sweep_interval_minutes: int | None
"""Interval in minutes between sweep iterations.
If omitted, a default interval will be used (typically ~ 5 minutes)."""
class CheckpointerConfig(TypedDict, total=False):
"""Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
ttl: ThreadTTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the checkpointer will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
class SecurityConfig(TypedDict, total=False):
"""Configuration for OpenAPI security definitions and requirements.
Useful for specifying global or path-level authentication and authorization flows
(e.g., OAuth2, API key headers, etc.).
"""
securitySchemes: dict[str, dict[str, Any]]
"""Describe each security scheme recognized by your OpenAPI spec.
Keys are scheme names (e.g. "OAuth2", "ApiKeyAuth") and values are their definitions.
Example:
{
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"read": "Read data", "write": "Write data"}
}
}
}
}
"""
security: list[dict[str, list[str]]]
"""Global security requirements across all endpoints.
Each element in the list maps a security scheme (e.g. "OAuth2") to a list of scopes (e.g. ["read", "write"]).
Example:
[
{"OAuth2": ["read", "write"]},
{"ApiKeyAuth": []}
]
"""
# path => {method => security}
paths: dict[str, dict[str, list[dict[str, list[str]]]]]
"""Path-specific security overrides.
Keys are path templates (e.g., "/items/{item_id}"), mapping to:
- Keys that are HTTP methods (e.g., "GET", "POST"),
- Values are lists of security definitions (just like `security`) for that method.
Example:
{
"/private_data": {
"GET": [{"OAuth2": ["read"]}],
"POST": [{"OAuth2": ["write"]}]
}
}
"""
class AuthConfig(TypedDict, total=False):
"""Configuration for custom authentication logic and how it integrates into the OpenAPI spec."""
path: str
"""Required. Path to an instance of the Auth() class that implements custom authentication.
Format: "path/to/file.py:my_auth"
"""
disable_studio_auth: bool
"""Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.
Defaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header
value is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom
authentication logic, regardless of origin of the request.
"""
openapi: SecurityConfig
"""The security configuration to include in your server's OpenAPI spec.
Example (OAuth2):
{
"securitySchemes": {
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"me": "Read user info", "items": "Manage items"}
}
}
}
},
"security": [
{"OAuth2": ["me"]}
]
}
"""
class CorsConfig(TypedDict, total=False):
"""Specifies Cross-Origin Resource Sharing (CORS) rules for your server.
If omitted, defaults are typically very restrictive (often no cross-origin requests).
Configure carefully if you want to allow usage from browsers hosted on other domains.
"""
allow_origins: list[str]
"""Optional. List of allowed origins (e.g., "https://example.com").
Default is often an empty list (no external origins).
Use "*" only if you trust all origins, as that bypasses most restrictions.
"""
allow_methods: list[str]
"""Optional. HTTP methods permitted for cross-origin requests (e.g. ["GET", "POST"]).
Default might be ["GET", "POST", "OPTIONS"] depending on your server framework.
"""
allow_headers: list[str]
"""Optional. HTTP headers that can be used in cross-origin requests (e.g. ["Content-Type", "Authorization"])."""
allow_credentials: bool
"""Optional. If `True`, cross-origin requests can include credentials (cookies, auth headers).
Default False to avoid accidentally exposing secured endpoints to untrusted sites.
"""
allow_origin_regex: str
"""Optional. A regex pattern for matching allowed origins, used if you have dynamic subdomains.
Example: "^https://.*\\.mycompany\\.com$"
"""
expose_headers: list[str]
"""Optional. List of headers that browsers are allowed to read from the response in cross-origin contexts."""
max_age: int
"""Optional. How many seconds the browser may cache preflight responses.
Default might be 600 (10 minutes). Larger values reduce preflight requests but can cause stale configurations.
"""
class ConfigurableHeaderConfig(TypedDict):
"""Customize which headers to include as configurable values in your runs.
By default, omits x-api-key, x-tenant-id, and x-service-key.
Exclusions (if provided) take precedence.
Each value can be a raw string with an optional wildcard.
"""
includes: list[str] | None
"""Headers to include (if not also matches against an 'exludes' pattern.
Examples:
- 'user-agent'
- 'x-configurable-*'
"""
excludes: list[str] | None
"""Headers to exclude. Applied before the 'includes' checks.
Examples:
- 'x-api-key'
- '*key*'
- '*token*'
"""
class HttpConfig(TypedDict, total=False):
"""Configuration for the built-in HTTP server that powers your deployment's routes and endpoints."""
app: str
"""Optional. Import path to a custom Starlette/FastAPI application to mount.
Format: "path/to/module.py:app_var"
If provided, it can override or extend the default routes.
"""
disable_assistants: bool
"""Optional. If `True`, /assistants routes are removed from the server.
Default is False (meaning /assistants is enabled).
"""
disable_threads: bool
"""Optional. If `True`, /threads routes are removed.
Default is False.
"""
disable_runs: bool
"""Optional. If `True`, /runs routes are removed.
Default is False.
"""
disable_store: bool
"""Optional. If `True`, /store routes are removed, disabling direct store interactions via HTTP.
Default is False.
"""
disable_mcp: bool
"""Optional. If `True`, /mcp routes are removed, disabling the MCP server.
Default is False.
"""
disable_meta: bool
"""Optional. Remove meta endpoints.
Set to True to disable the following endpoints: /openapi.json, /info, /metrics, /docs.
This will also make the /ok endpoint skip any DB or other checks, always returning {"ok": True}.
Default is False.
"""
cors: CorsConfig | None
"""Optional. Defines CORS restrictions. If omitted, no special rules are set and
cross-origin behavior depends on default server settings.
"""
configurable_headers: ConfigurableHeaderConfig | None
"""Optional. Defines how headers are treated for a run's configuration.
You can include or exclude headers as configurable values to condition your
agent's behavior or permissions on a request's headers."""
logging_headers: ConfigurableHeaderConfig | None
"""Optional. Defines which headers are excluded from logging."""
middleware_order: MiddlewareOrders | None
"""Optional. Defines the order in which to apply server customizations.
Choices:
- "auth_first": Authentication hooks (custom or default) are evaluated
before custom middleware.
- "middleware_first": Custom middleware is evaluated
before authentication hooks (custom or default).
Default is `middleware_first`.
"""
enable_custom_route_auth: bool
"""Optional. If `True`, authentication is enabled for custom routes,
not just the routes that are protected by default.
(Routes protected by default include /assistants, /threads, and /runs).
Default is False. This flag only affects authentication behavior
if `app` is provided and contains custom routes.
"""
class Config(TypedDict, total=False):
"""Top-level config for langgraph-cli or similar deployment tooling."""
python_version: str
"""Optional. Python version in 'major.minor' format (e.g. '3.11').
Must be at least 3.11 or greater for this deployment to function properly.
"""
node_version: str | None
"""Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.
Must be >= 20 if provided.
"""
api_version: str | None
"""Optional. Which semantic version of the LangGraph API server to use.
Defaults to latest. Check the
[changelog](https://docs.langchain.com/langgraph-platform/langgraph-server-changelog)
for more information."""
_INTERNAL_docker_tag: str | None
"""Optional. Internal use only.
"""
base_image: str | None
"""Optional. Base image to use for the LangGraph API server.
Defaults to langchain/langgraph-api or langchain/langgraphjs-api."""
image_distro: Distros | None
"""Optional. Linux distribution for the base image.
Must be one of 'wolfi', 'debian', 'bullseye', or 'bookworm'.
If omitted, defaults to 'debian' ('latest').
"""
pip_config_file: str | None
"""Optional. Path to a pip config file (e.g., "/etc/pip.conf" or "pip.ini") for controlling
package installation (custom indices, credentials, etc.).
Only relevant if Python dependencies are installed via pip. If omitted, default pip settings are used.
"""
pip_installer: str | None
"""Optional. Python package installer to use ('auto', 'pip', 'uv').
- 'auto' (default): Use uv for supported base images, otherwise pip
- 'pip': Force use of pip regardless of base image support
- 'uv': Force use of uv (will fail if base image doesn't support it)
"""
dockerfile_lines: list[str]
"""Optional. Additional Docker instructions that will be appended to your base Dockerfile.
Useful for installing OS packages, setting environment variables, etc.
Example:
dockerfile_lines=[
"RUN apt-get update && apt-get install -y libmagic-dev",
"ENV MY_CUSTOM_VAR=hello_world"
]
"""
dependencies: list[str]
"""List of Python dependencies to install, either from PyPI or local paths.
Examples:
- "." or "./src" if you have a local Python package
- str (aka "anthropic") for a PyPI package
- "git+https://github.com/org/repo.git@main" for a Git-based package
Defaults to an empty list, meaning no additional packages installed beyond your base environment.
"""
graphs: dict[str, str]
"""Optional. Named definitions of graphs, each pointing to a Python object.
Graphs can be StateGraph, @entrypoint, or any other Pregel object OR they can point to (async) context
managers that accept a single configuration argument (of type RunnableConfig) and return a pregel object
(instance of Stategraph, etc.).
Keys are graph names, values are "path/to/file.py:object_name".
Example:
{
"mygraph": "graphs/my_graph.py:graph_definition",
"anothergraph": "graphs/another.py:get_graph"
}
"""
env: dict[str, str] | str
"""Optional. Environment variables to set for your deployment.
- If given as a dict, keys are variable names and values are their values.
- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.
Example as a dict:
env={"API_TOKEN": "abc123", "DEBUG": "true"}
Example as a file path:
env=".env"
"""
store: StoreConfig | None
"""Optional. Configuration for the built-in long-term memory store, including semantic search indexing.
If omitted, no vector index is set up (the object store will still be present, however).
"""
checkpointer: CheckpointerConfig | None
"""Optional. Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
auth: AuthConfig | None
"""Optional. Custom authentication config, including the path to your Python auth logic and
the OpenAPI security definitions it uses.
"""
http: HttpConfig | None
"""Optional. Configuration for the built-in HTTP server, controlling which custom routes are exposed
and how cross-origin requests are handled.
"""
ui: dict[str, str] | None
"""Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file.
"""
keep_pkg_tools: bool | list[str] | None
"""Optional. Control whether to retain Python packaging tools in the final image.
Allowed tools are: "pip", "setuptools", "wheel".
You can also set to true to include all packaging tools.
"""
_BUILD_TOOLS = ("pip", "setuptools", "wheel")
+558
View File
@@ -0,0 +1,558 @@
from typing import Any, Literal, TypedDict
Distros = Literal["debian", "wolfi", "bullseye", "bookworm"]
MiddlewareOrders = Literal["auth_first", "middleware_first"]
class TTLConfig(TypedDict, total=False):
"""Configuration for TTL (time-to-live) behavior in the store."""
refresh_on_read: bool
"""Default behavior for refreshing TTLs on read operations (`GET` and `SEARCH`).
If `True`, TTLs will be refreshed on read operations (get/search) by default.
This can be overridden per-operation by explicitly setting `refresh_ttl`.
Defaults to `True` if not configured.
"""
default_ttl: float | None
"""Optional. Default TTL (time-to-live) in minutes for new items.
If provided, all new items will have this TTL unless explicitly overridden.
If omitted, items will have no TTL by default.
"""
sweep_interval_minutes: int | None
"""Optional. Interval in minutes between TTL sweep iterations.
If provided, the store will periodically delete expired items based on the TTL.
If omitted, no automatic sweeping will occur.
"""
class IndexConfig(TypedDict, total=False):
"""Configuration for indexing documents for semantic search in the store.
This governs how text is converted into embeddings and stored for vector-based lookups.
"""
dims: int
"""Required. Dimensionality of the embedding vectors you will store.
Must match the output dimension of your selected embedding model or custom embed function.
If mismatched, you will likely encounter shape/size errors when inserting or querying vectors.
Common embedding model output 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
"""
embed: str
"""Required. Identifier or reference to the embedding model or a custom embedding function.
The format can vary:
- "<provider>:<model_name>" for recognized providers (e.g., "openai:text-embedding-3-large")
- "path/to/module.py:function_name" for your own local embedding function
- "my_custom_embed" if it's a known alias in your system
Examples:
- "openai:text-embedding-3-large"
- "cohere:embed-multilingual-v3.0"
- "src/app.py:embeddings"
Note: Must return embeddings of dimension `dims`.
"""
fields: list[str] | None
"""Optional. List of JSON fields to extract before generating embeddings.
Defaults to ["$"], which means the entire JSON object is embedded as one piece of text.
If you provide multiple fields (e.g. ["title", "content"]), each is extracted and embedded separately,
often saving token usage if you only care about certain parts of the data.
Example:
fields=["title", "abstract", "author.biography"]
"""
class StoreConfig(TypedDict, total=False):
"""Configuration for the built-in long-term memory store.
This store can optionally perform semantic search. If you omit `index`,
the store will just handle traditional (non-embedded) data without vector lookups.
"""
index: IndexConfig | None
"""Optional. Defines the vector-based semantic search configuration.
If provided, the store will:
- Generate embeddings according to `index.embed`
- Enforce the embedding dimension given by `index.dims`
- Embed only specified JSON fields (if any) from `index.fields`
If omitted, no vector index is initialized.
"""
ttl: TTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the store will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
class ThreadTTLConfig(TypedDict, total=False):
"""Configure a default TTL for checkpointed data within threads."""
strategy: Literal["delete"]
"""Strategy to use for deleting checkpointed data.
Choices:
- "delete": Delete all checkpoints for a thread after TTL expires.
"""
default_ttl: float | None
"""Default TTL (time-to-live) in minutes for checkpointed data."""
sweep_interval_minutes: int | None
"""Interval in minutes between sweep iterations.
If omitted, a default interval will be used (typically ~ 5 minutes)."""
class SerdeConfig(TypedDict, total=False):
"""Configuration for the built-in serde, which handles checkpointing of state.
If omitted, no serde is set up (the object store will still be present, however)."""
allowed_json_modules: list[list[str]] | bool | None
"""Optional. List of allowed python modules to de-serialize custom objects from.
If provided, only the specified modules will be allowed to be deserialized.
If omitted, no modules are allowed, and the object returned will simply be a json object OR
a deserialized langchain object.
Example:
{...
"serde": {
"allowed_json_modules": [
["my_agent", "my_file", "SomeType"],
]
}
}
If you set this to True, any module will be allowed to be deserialized.
Example:
{...
"serde": {
"allowed_json_modules": true
}
}
"""
pickle_fallback: bool
"""Optional. Whether to allow pickling as a fallback for deserialization.
If True, pickling will be allowed as a fallback for deserialization.
If False, pickling will not be allowed as a fallback for deserialization.
Defaults to True if not configured."""
class CheckpointerConfig(TypedDict, total=False):
"""Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
ttl: ThreadTTLConfig | None
"""Optional. Defines the TTL (time-to-live) behavior configuration.
If provided, the checkpointer will apply TTL settings according to the configuration.
If omitted, no TTL behavior is configured.
"""
serde: SerdeConfig | None
"""Optional. Defines the serde configuration.
If provided, the checkpointer will apply serde settings according to the configuration.
If omitted, no serde behavior is configured.
This configuration requires server version 0.5 or later to take effect.
"""
class SecurityConfig(TypedDict, total=False):
"""Configuration for OpenAPI security definitions and requirements.
Useful for specifying global or path-level authentication and authorization flows
(e.g., OAuth2, API key headers, etc.).
"""
securitySchemes: dict[str, dict[str, Any]]
"""Describe each security scheme recognized by your OpenAPI spec.
Keys are scheme names (e.g. "OAuth2", "ApiKeyAuth") and values are their definitions.
Example:
{
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"read": "Read data", "write": "Write data"}
}
}
}
}
"""
security: list[dict[str, list[str]]]
"""Global security requirements across all endpoints.
Each element in the list maps a security scheme (e.g. "OAuth2") to a list of scopes (e.g. ["read", "write"]).
Example:
[
{"OAuth2": ["read", "write"]},
{"ApiKeyAuth": []}
]
"""
# path => {method => security}
paths: dict[str, dict[str, list[dict[str, list[str]]]]]
"""Path-specific security overrides.
Keys are path templates (e.g., "/items/{item_id}"), mapping to:
- Keys that are HTTP methods (e.g., "GET", "POST"),
- Values are lists of security definitions (just like `security`) for that method.
Example:
{
"/private_data": {
"GET": [{"OAuth2": ["read"]}],
"POST": [{"OAuth2": ["write"]}]
}
}
"""
class AuthConfig(TypedDict, total=False):
"""Configuration for custom authentication logic and how it integrates into the OpenAPI spec."""
path: str
"""Required. Path to an instance of the Auth() class that implements custom authentication.
Format: "path/to/file.py:my_auth"
"""
disable_studio_auth: bool
"""Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.
Defaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header
value is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom
authentication logic, regardless of origin of the request.
"""
openapi: SecurityConfig
"""The security configuration to include in your server's OpenAPI spec.
Example (OAuth2):
{
"securitySchemes": {
"OAuth2": {
"type": "oauth2",
"flows": {
"password": {
"tokenUrl": "/token",
"scopes": {"me": "Read user info", "items": "Manage items"}
}
}
}
},
"security": [
{"OAuth2": ["me"]}
]
}
"""
class CorsConfig(TypedDict, total=False):
"""Specifies Cross-Origin Resource Sharing (CORS) rules for your server.
If omitted, defaults are typically very restrictive (often no cross-origin requests).
Configure carefully if you want to allow usage from browsers hosted on other domains.
"""
allow_origins: list[str]
"""Optional. List of allowed origins (e.g., "https://example.com").
Default is often an empty list (no external origins).
Use "*" only if you trust all origins, as that bypasses most restrictions.
"""
allow_methods: list[str]
"""Optional. HTTP methods permitted for cross-origin requests (e.g. ["GET", "POST"]).
Default might be ["GET", "POST", "OPTIONS"] depending on your server framework.
"""
allow_headers: list[str]
"""Optional. HTTP headers that can be used in cross-origin requests (e.g. ["Content-Type", "Authorization"])."""
allow_credentials: bool
"""Optional. If `True`, cross-origin requests can include credentials (cookies, auth headers).
Default False to avoid accidentally exposing secured endpoints to untrusted sites.
"""
allow_origin_regex: str
"""Optional. A regex pattern for matching allowed origins, used if you have dynamic subdomains.
Example: "^https://.*\\.mycompany\\.com$"
"""
expose_headers: list[str]
"""Optional. List of headers that browsers are allowed to read from the response in cross-origin contexts."""
max_age: int
"""Optional. How many seconds the browser may cache preflight responses.
Default might be 600 (10 minutes). Larger values reduce preflight requests but can cause stale configurations.
"""
class ConfigurableHeaderConfig(TypedDict):
"""Customize which headers to include as configurable values in your runs.
By default, omits x-api-key, x-tenant-id, and x-service-key.
Exclusions (if provided) take precedence.
Each value can be a raw string with an optional wildcard.
"""
includes: list[str] | None
"""Headers to include (if not also matches against an 'exludes' pattern.
Examples:
- 'user-agent'
- 'x-configurable-*'
"""
excludes: list[str] | None
"""Headers to exclude. Applied before the 'includes' checks.
Examples:
- 'x-api-key'
- '*key*'
- '*token*'
"""
class HttpConfig(TypedDict, total=False):
"""Configuration for the built-in HTTP server that powers your deployment's routes and endpoints."""
app: str
"""Optional. Import path to a custom Starlette/FastAPI application to mount.
Format: "path/to/module.py:app_var"
If provided, it can override or extend the default routes.
"""
disable_assistants: bool
"""Optional. If `True`, /assistants routes are removed from the server.
Default is False (meaning /assistants is enabled).
"""
disable_threads: bool
"""Optional. If `True`, /threads routes are removed.
Default is False.
"""
disable_runs: bool
"""Optional. If `True`, /runs routes are removed.
Default is False.
"""
disable_store: bool
"""Optional. If `True`, /store routes are removed, disabling direct store interactions via HTTP.
Default is False.
"""
disable_mcp: bool
"""Optional. If `True`, /mcp routes are removed, disabling the MCP server.
Default is False.
"""
disable_meta: bool
"""Optional. Remove meta endpoints.
Set to True to disable the following endpoints: /openapi.json, /info, /metrics, /docs.
This will also make the /ok endpoint skip any DB or other checks, always returning {"ok": True}.
Default is False.
"""
cors: CorsConfig | None
"""Optional. Defines CORS restrictions. If omitted, no special rules are set and
cross-origin behavior depends on default server settings.
"""
configurable_headers: ConfigurableHeaderConfig | None
"""Optional. Defines how headers are treated for a run's configuration.
You can include or exclude headers as configurable values to condition your
agent's behavior or permissions on a request's headers."""
logging_headers: ConfigurableHeaderConfig | None
"""Optional. Defines which headers are excluded from logging."""
middleware_order: MiddlewareOrders | None
"""Optional. Defines the order in which to apply server customizations.
Choices:
- "auth_first": Authentication hooks (custom or default) are evaluated
before custom middleware.
- "middleware_first": Custom middleware is evaluated
before authentication hooks (custom or default).
Default is `middleware_first`.
"""
enable_custom_route_auth: bool
"""Optional. If `True`, authentication is enabled for custom routes,
not just the routes that are protected by default.
(Routes protected by default include /assistants, /threads, and /runs).
Default is False. This flag only affects authentication behavior
if `app` is provided and contains custom routes.
"""
class Config(TypedDict, total=False):
"""Top-level config for langgraph-cli or similar deployment tooling."""
python_version: str
"""Optional. Python version in 'major.minor' format (e.g. '3.11').
Must be at least 3.11 or greater for this deployment to function properly.
"""
node_version: str | None
"""Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.
Must be >= 20 if provided.
"""
api_version: str | None
"""Optional. Which semantic version of the LangGraph API server to use.
Defaults to latest. Check the
[changelog](https://docs.langchain.com/langgraph-platform/langgraph-server-changelog)
for more information."""
_INTERNAL_docker_tag: str | None
"""Optional. Internal use only.
"""
base_image: str | None
"""Optional. Base image to use for the LangGraph API server.
Defaults to langchain/langgraph-api or langchain/langgraphjs-api."""
image_distro: Distros | None
"""Optional. Linux distribution for the base image.
Must be one of 'wolfi', 'debian', 'bullseye', or 'bookworm'.
If omitted, defaults to 'debian' ('latest').
"""
pip_config_file: str | None
"""Optional. Path to a pip config file (e.g., "/etc/pip.conf" or "pip.ini") for controlling
package installation (custom indices, credentials, etc.).
Only relevant if Python dependencies are installed via pip. If omitted, default pip settings are used.
"""
pip_installer: str | None
"""Optional. Python package installer to use ('auto', 'pip', 'uv').
- 'auto' (default): Use uv for supported base images, otherwise pip
- 'pip': Force use of pip regardless of base image support
- 'uv': Force use of uv (will fail if base image doesn't support it)
"""
dockerfile_lines: list[str]
"""Optional. Additional Docker instructions that will be appended to your base Dockerfile.
Useful for installing OS packages, setting environment variables, etc.
Example:
dockerfile_lines=[
"RUN apt-get update && apt-get install -y libmagic-dev",
"ENV MY_CUSTOM_VAR=hello_world"
]
"""
dependencies: list[str]
"""List of Python dependencies to install, either from PyPI or local paths.
Examples:
- "." or "./src" if you have a local Python package
- str (aka "anthropic") for a PyPI package
- "git+https://github.com/org/repo.git@main" for a Git-based package
Defaults to an empty list, meaning no additional packages installed beyond your base environment.
"""
graphs: dict[str, str]
"""Optional. Named definitions of graphs, each pointing to a Python object.
Graphs can be StateGraph, @entrypoint, or any other Pregel object OR they can point to (async) context
managers that accept a single configuration argument (of type RunnableConfig) and return a pregel object
(instance of Stategraph, etc.).
Keys are graph names, values are "path/to/file.py:object_name".
Example:
{
"mygraph": "graphs/my_graph.py:graph_definition",
"anothergraph": "graphs/another.py:get_graph"
}
"""
env: dict[str, str] | str
"""Optional. Environment variables to set for your deployment.
- If given as a dict, keys are variable names and values are their values.
- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.
Example as a dict:
env={"API_TOKEN": "abc123", "DEBUG": "true"}
Example as a file path:
env=".env"
"""
store: StoreConfig | None
"""Optional. Configuration for the built-in long-term memory store, including semantic search indexing.
If omitted, no vector index is set up (the object store will still be present, however).
"""
checkpointer: CheckpointerConfig | None
"""Optional. Configuration for the built-in checkpointer, which handles checkpointing of state.
If omitted, no checkpointer is set up (the object store will still be present, however).
"""
auth: AuthConfig | None
"""Optional. Custom authentication config, including the path to your Python auth logic and
the OpenAPI security definitions it uses.
"""
http: HttpConfig | None
"""Optional. Configuration for the built-in HTTP server, controlling which custom routes are exposed
and how cross-origin requests are handled.
"""
ui: dict[str, str] | None
"""Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file.
"""
keep_pkg_tools: bool | list[str] | None
"""Optional. Control whether to retain Python packaging tools in the final image.
Allowed tools are: "pip", "setuptools", "wheel".
You can also set to true to include all packaging tools.
"""
__all__ = [
"Config",
"StoreConfig",
"CheckpointerConfig",
"AuthConfig",
"HttpConfig",
"MiddlewareOrders",
"Distros",
"TTLConfig",
"IndexConfig",
]
@@ -8,7 +8,7 @@ authors = [
license = { text = "MIT" }
requires-python = ">=3.11,<4.0"
dependencies = [
"langgraph>=0.6.0,<0.7.0",
"langgraph>=0.6.0,<2",
"langchain-core>=0.2.14",
]
+43
View File
@@ -472,6 +472,17 @@
"description": "Configuration for the built-in checkpointer, which handles checkpointing of state.\n\nIf omitted, no checkpointer is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"serde": {
"anyOf": [
{
"$ref": "#/$defs/SerdeConfig"
},
{
"type": "null"
}
],
"description": "Optional. Defines the serde configuration.\n\nIf provided, the checkpointer will apply serde settings according to the configuration.\nIf omitted, no serde behavior is configured.\n\nThis configuration requires server version 0.5 or later to take effect.\n"
},
"ttl": {
"anyOf": [
{
@@ -486,6 +497,38 @@
},
"required": []
},
"SerdeConfig": {
"title": "SerdeConfig",
"description": "Configuration for the built-in serde, which handles checkpointing of state.\n\nIf omitted, no serde is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"allowed_json_modules": {
"anyOf": [
{
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Optional. List of allowed python modules to de-serialize custom objects from.\n\nIf provided, only the specified modules will be allowed to be deserialized.\nIf omitted, no modules are allowed, and the object returned will simply be a json object OR\na deserialized langchain object.\n"
},
"pickle_fallback": {
"type": "boolean",
"description": "Optional. Whether to allow pickling as a fallback for deserialization.\n\nIf True, pickling will be allowed as a fallback for deserialization.\nIf False, pickling will not be allowed as a fallback for deserialization.\nDefaults to True if not configured."
}
},
"required": []
},
"ThreadTTLConfig": {
"title": "ThreadTTLConfig",
"description": "Configure a default TTL for checkpointed data within threads.",
+43
View File
@@ -472,6 +472,17 @@
"description": "Configuration for the built-in checkpointer, which handles checkpointing of state.\n\nIf omitted, no checkpointer is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"serde": {
"anyOf": [
{
"$ref": "#/$defs/SerdeConfig"
},
{
"type": "null"
}
],
"description": "Optional. Defines the serde configuration.\n\nIf provided, the checkpointer will apply serde settings according to the configuration.\nIf omitted, no serde behavior is configured.\n\nThis configuration requires server version 0.5 or later to take effect.\n"
},
"ttl": {
"anyOf": [
{
@@ -486,6 +497,38 @@
},
"required": []
},
"SerdeConfig": {
"title": "SerdeConfig",
"description": "Configuration for the built-in serde, which handles checkpointing of state.\n\nIf omitted, no serde is set up (the object store will still be present, however).",
"type": "object",
"properties": {
"allowed_json_modules": {
"anyOf": [
{
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Optional. List of allowed python modules to de-serialize custom objects from.\n\nIf provided, only the specified modules will be allowed to be deserialized.\nIf omitted, no modules are allowed, and the object returned will simply be a json object OR\na deserialized langchain object.\n"
},
"pickle_fallback": {
"type": "boolean",
"description": "Optional. Whether to allow pickling as a fallback for deserialization.\n\nIf True, pickling will be allowed as a fallback for deserialization.\nIf False, pickling will not be allowed as a fallback for deserialization.\nDefaults to True if not configured."
}
},
"required": []
},
"ThreadTTLConfig": {
"title": "ThreadTTLConfig",
"description": "Configure a default TTL for checkpointed data within threads.",
+1 -2
View File
@@ -81,9 +81,8 @@ def push_ui_message(
metadata: Optional additional metadata about the UI message.
message: Optional message object to associate with the UI message.
state_key: Key in the graph state where the UI messages are stored.
Defaults to "ui".
merge: Whether to merge props with existing UI message (True) or replace
them (False). Defaults to False.
them (False).
Returns:
The created UI message.
+64 -54
View File
@@ -55,6 +55,7 @@ __all__ = (
"Command",
"Durability",
"interrupt",
"Overwrite",
)
Durability = Literal["sync", "async", "exit"]
@@ -283,26 +284,32 @@ class Send:
node (str): The name of the target node to send the message to.
arg (Any): The state or message to send to the target node.
Examples:
>>> from typing import Annotated
>>> import operator
>>> class OverallState(TypedDict):
... subjects: list[str]
... jokes: Annotated[list[str], operator.add]
>>> from langgraph.types import Send
>>> from langgraph.graph import END, START
>>> def continue_to_jokes(state: OverallState):
... return [Send("generate_joke", {"subject": s}) for s in state["subjects"]]
>>> from langgraph.graph import StateGraph
>>> builder = StateGraph(OverallState)
>>> builder.add_node("generate_joke", lambda state: {"jokes": [f"Joke about {state['subject']}"]})
>>> builder.add_conditional_edges(START, continue_to_jokes)
>>> builder.add_edge("generate_joke", END)
>>> graph = builder.compile()
>>>
>>> # Invoking with two subjects results in a generated joke for each
>>> graph.invoke({"subjects": ["cats", "dogs"]})
{'subjects': ['cats', 'dogs'], 'jokes': ['Joke about cats', 'Joke about dogs']}
!!! example
```python
from typing import Annotated
from langgraph.types import Send
from langgraph.graph import END, START
from langgraph.graph import StateGraph
import operator
class OverallState(TypedDict):
subjects: list[str]
jokes: Annotated[list[str], operator.add]
def continue_to_jokes(state: OverallState):
return [Send("generate_joke", {"subject": s}) for s in state["subjects"]]
builder = StateGraph(OverallState)
builder.add_node("generate_joke", lambda state: {"jokes": [f"Joke about {state['subject']}"]})
builder.add_conditional_edges(START, continue_to_jokes)
builder.add_edge("generate_joke", END)
graph = builder.compile()
# Invoking with two subjects results in a generated joke for each
graph.invoke({"subjects": ["cats", "dogs"]})
# {'subjects': ['cats', 'dogs'], 'jokes': ['Joke about cats', 'Joke about dogs']}
```
"""
__slots__ = ("node", "arg")
@@ -342,10 +349,8 @@ N = TypeVar("N", bound=Hashable)
class Command(Generic[N], ToolOutputMixin):
"""One or more commands to update the graph's state and send messages to nodes.
!!! version-added "Added in version 0.2.24"
Args:
graph: graph to send the command to. Supported values are:
graph: Graph to send the command to. Supported values are:
- `None`: the current graph
- `Command.PARENT`: closest parent graph
@@ -415,7 +420,8 @@ def interrupt(value: Any) -> Any:
To use an `interrupt`, you must enable a checkpointer, as the feature relies
on persisting the graph state.
Example:
!!! example
```python
import uuid
from typing import Optional
@@ -520,38 +526,42 @@ def interrupt(value: Any) -> Any:
@dataclass(slots=True)
class Overwrite:
"""Bypass a reducer and write the wrapped value directly to a BinaryOperatorAggregate channel.
"""Bypass a reducer and write the wrapped value directly to a `BinaryOperatorAggregate` channel.
Receiving multiple Overwrite values for the same channel in a single super-step will raise an InvalidUpdateError.
Receiving multiple `Overwrite` values for the same channel in a single super-step
will raise an `InvalidUpdateError`.
Example:
>>> from typing import Annotated
>>> import operator
>>> from langgraph.graph import StateGraph
>>> from langgraph.types import Overwrite
>>>
>>> class State(TypedDict):
... messages: Annotated[list, operator.add]
>>>
>>> def node_a(state: TypedDict):
... # Normal update: uses the reducer (operator.add)
... return {"messages": ["a"]}
>>>
>>> def node_b(state: State):
... # Overwrite: bypasses the reducer and replaces the entire value
... return {"messages": Overwrite(value=["b"])}
>>>
>>> builder = StateGraph(State)
>>> builder.add_node("node_a", node_a)
>>> builder.add_node("node_b", node_b)
>>> builder.set_entry_point("node_a")
>>> builder.add_edge("node_a", "node_b")
>>> graph = builder.compile()
>>>
>>> # Without Overwrite in node_b, messages would be ["START", "a", "b"]
>>> # With Overwrite, messages is just ["b"]
>>> result = graph.invoke({"messages": ["START"]})
>>> assert result == {"messages": ["b"]}
!!! example
```python
from typing import Annotated
import operator
from langgraph.graph import StateGraph
from langgraph.types import Overwrite
class State(TypedDict):
messages: Annotated[list, operator.add]
def node_a(state: TypedDict):
# Normal update: uses the reducer (operator.add)
return {"messages": ["a"]}
def node_b(state: State):
# Overwrite: bypasses the reducer and replaces the entire value
return {"messages": Overwrite(value=["b"])}
builder = StateGraph(State)
builder.add_node("node_a", node_a)
builder.add_node("node_b", node_b)
builder.set_entry_point("node_a")
builder.add_edge("node_a", "node_b")
graph = builder.compile()
# Without Overwrite in node_b, messages would be ["START", "a", "b"]
# With Overwrite, messages is just ["b"]
result = graph.invoke({"messages": ["START"]})
assert result == {"messages": ["b"]}
```
"""
value: Any
+1 -1
View File
@@ -27,7 +27,7 @@ dependencies = [
"langchain-core>=0.1",
"langgraph-checkpoint>=2.1.0,<4.0.0",
"langgraph-sdk>=0.2.2,<0.3.0",
"langgraph-prebuilt>=1.0.1,<1.1.0",
"langgraph-prebuilt>=1.0.2,<1.1.0",
"xxhash>=3.5.0",
"pydantic>=2.7.4",
]
@@ -309,37 +309,40 @@ def create_react_agent(
model: The language model for the agent. Supports static and dynamic
model selection.
- **Static model**: A chat model instance (e.g., `ChatOpenAI()`) or
string identifier (e.g., `"openai:gpt-4"`)
- **Static model**: A chat model instance (e.g.,
[`ChatOpenAI`][langchain_openai.ChatOpenAI]) or string identifier (e.g.,
`"openai:gpt-4"`)
- **Dynamic model**: A callable with signature
`(state, runtime) -> BaseChatModel` that returns different models
based on runtime context
If the model has tools bound via `.bind_tools()` or other configurations,
the return type should be a Runnable[LanguageModelInput, BaseMessage]
Coroutines are also supported, allowing for asynchronous model selection.
`(state, runtime) -> BaseChatModel` that returns different models
based on runtime context
If the model has tools bound via `bind_tools` or other configurations,
the return type should be a `Runnable[LanguageModelInput, BaseMessage]`
Coroutines are also supported, allowing for asynchronous model selection.
Dynamic functions receive graph state and runtime, enabling
context-dependent model selection. Must return a `BaseChatModel`
instance. For tool calling, bind tools using `.bind_tools()`.
Bound tools must be a subset of the `tools` parameter.
Dynamic model example:
```python
from dataclasses import dataclass
!!! example "Dynamic model"
@dataclass
class ModelContext:
model_name: str = "gpt-3.5-turbo"
```python
from dataclasses import dataclass
# Instantiate models globally
gpt4_model = ChatOpenAI(model="gpt-4")
gpt35_model = ChatOpenAI(model="gpt-3.5-turbo")
@dataclass
class ModelContext:
model_name: str = "gpt-3.5-turbo"
def select_model(state: AgentState, runtime: Runtime[ModelContext]) -> ChatOpenAI:
model_name = runtime.context.model_name
model = gpt4_model if model_name == "gpt-4" else gpt35_model
return model.bind_tools(tools)
```
# Instantiate models globally
gpt4_model = ChatOpenAI(model="gpt-4")
gpt35_model = ChatOpenAI(model="gpt-3.5-turbo")
def select_model(state: AgentState, runtime: Runtime[ModelContext]) -> ChatOpenAI:
model_name = runtime.context.model_name
model = gpt4_model if model_name == "gpt-4" else gpt35_model
return model.bind_tools(tools)
```
!!! note "Dynamic Model Requirements"
@@ -351,23 +354,26 @@ def create_react_agent(
If an empty list is provided, the agent will consist of a single LLM node without tool calling.
prompt: An optional prompt for the LLM. Can take a few different forms:
- str: This is converted to a SystemMessage and added to the beginning of the list of messages in state["messages"].
- SystemMessage: this is added to the beginning of the list of messages in state["messages"].
- Callable: This function should take in full graph state and the output is then passed to the language model.
- Runnable: This runnable should take in full graph state and the output is then passed to the language model.
- `str`: This is converted to a `SystemMessage` and added to the beginning of the list of messages in `state["messages"]`.
- `SystemMessage`: this is added to the beginning of the list of messages in `state["messages"]`.
- `Callable`: This function should take in full graph state and the output is then passed to the language model.
- `Runnable`: This runnable should take in full graph state and the output is then passed to the language model.
response_format: An optional schema for the final agent output.
If provided, output will be formatted to match the given schema and returned in the 'structured_response' state key.
If not provided, `structured_response` will not be present in the output state.
Can be passed in as:
- an OpenAI function/tool schema,
- a JSON Schema,
- a TypedDict class,
- or a Pydantic class.
- a tuple (prompt, schema), where schema is one of the above.
The prompt will be used together with the model that is being used to generate the structured response.
- An OpenAI function/tool schema,
- A JSON Schema,
- A TypedDict class,
- A Pydantic class.
- A tuple `(prompt, schema)`, where schema is one of the above.
The prompt will be used together with the model that is being used to
generate the structured response.
!!! Important
`response_format` requires the model to support `.with_structured_output`
@@ -428,13 +434,16 @@ def create_react_agent(
store: An optional store object. This is used for persisting data
across multiple threads (e.g., multiple conversations / users).
interrupt_before: An optional list of node names to interrupt before.
Should be one of the following: "agent", "tools".
Should be one of the following: `"agent"`, `"tools"`.
This is useful if you want to add a user confirmation or other interrupt before taking an action.
interrupt_after: An optional list of node names to interrupt after.
Should be one of the following: "agent", "tools".
Should be one of the following: `"agent"`, `"tools"`.
This is useful if you want to return directly or run additional processing on an output.
debug: A flag indicating whether to enable debug mode.
version: Determines the version of the graph to create.
Can be one of:
- `"v1"`: The tool node processes a single message. All tool
@@ -443,7 +452,7 @@ def create_react_agent(
Tool calls are distributed across multiple instances of the tool
node using the [Send](https://langchain-ai.github.io/langgraph/concepts/low_level/#send)
API.
name: An optional name for the CompiledStateGraph.
name: An optional name for the `CompiledStateGraph`.
This name will be automatically used when adding ReAct agent graph to another graph as a subgraph node -
particularly useful for building multi-agent systems.
@@ -453,14 +462,14 @@ def create_react_agent(
Returns:
A compiled LangChain runnable that can be used for chat interactions.
A compiled LangChain `Runnable` that can be used for chat interactions.
The "agent" node calls the language model with the messages list (after applying the prompt).
If the resulting AIMessage contains `tool_calls`, the graph will then call the ["tools"][langgraph.prebuilt.tool_node.ToolNode].
The "tools" node executes the tools (1 tool per `tool_call`) and adds the responses to the messages list
as `ToolMessage` objects. The agent node then calls the language model again.
The process repeats until no more `tool_calls` are present in the response.
The agent then returns the full list of messages as a dictionary containing the key "messages".
The agent then returns the full list of messages as a dictionary containing the key `'messages'`.
``` mermaid
sequenceDiagram
+10 -8
View File
@@ -36,7 +36,7 @@ class ActionRequest(TypedDict):
Contains the action type and any associated arguments needed for the action.
Attributes:
action: The type or name of action being requested (e.g., "Approve XYZ action")
action: The type or name of action being requested (e.g., `"Approve XYZ action"`)
args: Key-value pairs of arguments needed for the action
"""
@@ -89,14 +89,16 @@ class HumanResponse(TypedDict):
Attributes:
type: The type of response:
- "accept": Approves the current state without changes
- "ignore": Skips/ignores the current step
- "response": Provides text feedback or instructions
- "edit": Modifies the current state/content
- `'accept'`: Approves the current state without changes
- `'ignore'`: Skips/ignores the current step
- `'response'`: Provides text feedback or instructions
- `'edit'`: Modifies the current state/content
args: The response payload:
- None: For ignore/accept actions
- str: For text responses
- ActionRequest: For edit actions with updated content
- `None`: For ignore/accept actions
- `str`: For text responses
- `ActionRequest`: For edit actions with updated content
"""
type: Literal["accept", "ignore", "response", "edit"]
+36 -26
View File
@@ -6,6 +6,7 @@ Tools are functions that models can call to interact with external systems,
APIs, databases, or perform computations.
The module implements design patterns for:
- Parallel execution of multiple tool calls for efficiency
- Robust error handling with customizable error messages
- State injection for tools that need access to graph state
@@ -13,11 +14,13 @@ The module implements design patterns for:
- Command-based state updates for advanced control flow
Key Components:
`ToolNode`: Main class for executing tools in LangGraph workflows
`InjectedState`: Annotation for injecting graph state into tools
`InjectedStore`: Annotation for injecting persistent store into tools
`ToolRuntime`: Runtime information for tools, bundling together state, context, config, stream_writer, tool_call_id, and store
`tools_condition`: Utility function for conditional routing based on tool calls
- `ToolNode`: Main class for executing tools in LangGraph workflows
- `InjectedState`: Annotation for injecting graph state into tools
- `InjectedStore`: Annotation for injecting persistent store into tools
- `ToolRuntime`: Runtime information for tools, bundling together `state`, `context`,
`config`, `stream_writer`, `tool_call_id`, and `store`
- `tools_condition`: Utility function for conditional routing based on tool calls
Typical Usage:
```python
@@ -552,44 +555,52 @@ class ToolNode(RunnableCallable):
Output format depends on input type and tool behavior:
**For Regular tools**:
- Dict input → `{"messages": [ToolMessage(...)]}`
- List input → `[ToolMessage(...)]`
**For Command tools**:
- Returns `[Command(...)]` or mixed list with regular tool outputs
- Commands can update state, trigger navigation, or send messages
- `Command` can update state, trigger navigation, or send messages
Args:
tools: A sequence of tools that can be invoked by this node. Supports:
tools: A sequence of tools that can be invoked by this node.
Supports:
- **BaseTool instances**: Tools with schemas and metadata
- **Plain functions**: Automatically converted to tools with inferred schemas
name: The name identifier for this node in the graph. Used for debugging
and visualization. Defaults to "tools".
and visualization.
tags: Optional metadata tags to associate with the node for filtering
and organization. Defaults to `None`.
and organization.
handle_tool_errors: Configuration for error handling during tool execution.
Supports multiple strategies:
- **True**: Catch all errors and return a ToolMessage with the default
- `True`: Catch all errors and return a `ToolMessage` with the default
error template containing the exception details.
- **str**: Catch all errors and return a ToolMessage with this custom
- `str`: Catch all errors and return a `ToolMessage` with this custom
error message string.
- **type[Exception]**: Only catch exceptions with the specified type and
- `type[Exception]`: Only catch exceptions with the specified type and
return the default error message for it.
- **tuple[type[Exception], ...]**: Only catch exceptions with the specified
- `tuple[type[Exception], ...]`: Only catch exceptions with the specified
types and return default error messages for them.
- **Callable[..., str]**: Catch exceptions matching the callable's signature
- `Callable[..., str]`: Catch exceptions matching the callable's signature
and return the string result of calling it with the exception.
- **False**: Disable error handling entirely, allowing exceptions to
- `False`: Disable error handling entirely, allowing exceptions to
propagate.
Defaults to a callable that:
- catches tool invocation errors (due to invalid arguments provided by the model) and returns a descriptive error message
- ignores tool execution errors (they will be re-raised)
- Catches tool invocation errors (due to invalid arguments provided by the
model) and returns a descriptive error message
- Ignores tool execution errors (they will be re-raised)
messages_key: The key in the state dictionary that contains the message list.
This same key will be used for the output `ToolMessage` objects.
Defaults to "messages".
Allows custom state schemas with different message field names.
Examples:
@@ -1393,7 +1404,7 @@ def tools_condition(
"""Conditional routing function for tool-calling workflows.
This utility function implements the standard conditional logic for ReAct-style
agents: if the last AI message contains tool calls, route to the tool execution
agents: if the last `AIMessage` contains tool calls, route to the tool execution
node; otherwise, end the workflow. This pattern is fundamental to most tool-calling
agent architectures.
@@ -1402,16 +1413,15 @@ def tools_condition(
Args:
state: The current graph state to examine for tool calls. Supported formats:
- Dictionary containing a messages key (for StateGraph)
- BaseModel instance with a messages attribute
- Dictionary containing a messages key (for `StateGraph`)
- `BaseModel` instance with a messages attribute
messages_key: The key or attribute name containing the message list in the state.
This allows customization for graphs using different state schemas.
Defaults to "messages".
Returns:
Either "tools" if tool calls are present in the last AI message, or "__end__"
to terminate the workflow. These are the standard routing destinations for
tool-calling conditional edges.
Either `'tools'` if tool calls are present in the last `AIMessage`, or `'__end__'`
to terminate the workflow. These are the standard routing destinations for
tool-calling conditional edges.
Raises:
ValueError: If no messages can be found in the provided state format.
@@ -1608,7 +1618,7 @@ class InjectedStore(InjectedToolArg):
This annotation enables tools to access LangGraph's persistent storage system
without exposing storage details to the language model. Tools annotated with
InjectedStore receive the store instance automatically during execution while
`InjectedStore` receive the store instance automatically during execution while
remaining invisible to the model's tool-calling interface.
The store provides persistent, cross-session data storage that tools can use
@@ -45,9 +45,9 @@ def _default_format_error(
category=LangGraphDeprecatedSinceV10,
)
class ValidationNode(RunnableCallable):
"""A node that validates all tools requests from the last AIMessage.
"""A node that validates all tools requests from the last `AIMessage`.
It can be used either in StateGraph with a "messages" key.
It can be used either in `StateGraph` with a `'messages'` key.
!!! note
@@ -57,7 +57,8 @@ class ValidationNode(RunnableCallable):
messages and tool IDs (for use in multi-turn conversations).
Returns:
(Union[Dict[str, List[ToolMessage]], Sequence[ToolMessage]]): A list of ToolMessages with the validated content or error messages.
(Union[Dict[str, List[ToolMessage]], Sequence[ToolMessage]]): A list of
`ToolMessage` objects with the validated content or error messages.
Example:
```python title="Example usage for re-prompting the model to generate a valid response:"