a7d6d88f6f
CI / changes (push) Has been cancelled
CI / cd libs/checkpoint (push) Has been cancelled
CI / cd libs/checkpoint-conformance (push) Has been cancelled
CI / cd libs/checkpoint-postgres (push) Has been cancelled
CI / cd libs/checkpoint-sqlite (push) Has been cancelled
CI / cd libs/cli (push) Has been cancelled
CI / cd libs/prebuilt (push) Has been cancelled
CI / cd libs/sdk-py (push) Has been cancelled
CI / cd libs/langgraph (push) Has been cancelled
CI / Check SDK methods matching (push) Has been cancelled
CI / Check CLI schema hasn't changed #3.13 (push) Has been cancelled
CI / CLI integration test (push) Has been cancelled
CI / sdk-py integration test (push) Has been cancelled
CI / CI Success (push) Has been cancelled
baseline / benchmark (push) Has been cancelled
Deploy Redirects to GitHub Pages / deploy (push) Has been cancelled
179 lines
6.0 KiB
Python
179 lines
6.0 KiB
Python
"""Async LangGraph client."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
from collections.abc import Mapping
|
|
from types import TracebackType
|
|
|
|
import httpx
|
|
|
|
from langgraph_sdk._async.assistants import AssistantsClient
|
|
from langgraph_sdk._async.cron import CronClient
|
|
from langgraph_sdk._async.http import HttpClient
|
|
from langgraph_sdk._async.runs import RunsClient
|
|
from langgraph_sdk._async.store import StoreClient
|
|
from langgraph_sdk._async.threads import ThreadsClient
|
|
from langgraph_sdk._shared.types import TimeoutTypes
|
|
from langgraph_sdk._shared.utilities import (
|
|
NOT_PROVIDED,
|
|
_get_headers,
|
|
_registered_transports,
|
|
get_asgi_transport,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_client(
|
|
*,
|
|
url: str | None = None,
|
|
api_key: str | None = NOT_PROVIDED,
|
|
headers: Mapping[str, str] | None = None,
|
|
timeout: TimeoutTypes | None = None,
|
|
) -> LangGraphClient:
|
|
"""Create and configure a LangGraphClient.
|
|
|
|
The client provides programmatic access to LangSmith Deployment. It supports
|
|
both remote servers and local in-process connections (when running inside a LangGraph server).
|
|
|
|
Args:
|
|
url:
|
|
Base URL of the LangGraph API.
|
|
- If `None`, the client first attempts an in-process connection via ASGI transport.
|
|
If that fails, it defers registration until after app initialization. This
|
|
only works if the client is used from within the Agent server.
|
|
api_key:
|
|
API key for authentication. Can be:
|
|
- A string: use this exact API key
|
|
- `None`: explicitly skip loading from environment variables
|
|
- Not provided (default): auto-load from environment in this order:
|
|
1. `LANGGRAPH_API_KEY`
|
|
2. `LANGSMITH_API_KEY`
|
|
3. `LANGCHAIN_API_KEY`
|
|
headers:
|
|
Additional HTTP headers to include in requests. Merged with authentication headers.
|
|
timeout:
|
|
HTTP timeout configuration. May be:
|
|
- `httpx.Timeout` instance
|
|
- float (total seconds)
|
|
- tuple `(connect, read, write, pool)` in seconds
|
|
Defaults: connect=5, read=300, write=300, pool=5.
|
|
|
|
Returns:
|
|
LangGraphClient:
|
|
A top-level client exposing sub-clients for assistants, threads,
|
|
runs, and cron operations.
|
|
|
|
???+ example "Connect to a remote server:"
|
|
|
|
```python
|
|
from langgraph_sdk import get_client
|
|
|
|
# get top-level LangGraphClient
|
|
client = get_client(url="http://localhost:8123")
|
|
|
|
# example usage: client.<model>.<method_name>()
|
|
assistants = await client.assistants.get(assistant_id="some_uuid")
|
|
```
|
|
|
|
???+ example "Connect in-process to a running LangGraph server:"
|
|
|
|
```python
|
|
from langgraph_sdk import get_client
|
|
|
|
client = get_client(url=None)
|
|
|
|
async def my_node(...):
|
|
subagent_result = await client.runs.wait(
|
|
thread_id=None,
|
|
assistant_id="agent",
|
|
input={"messages": [{"role": "user", "content": "Foo"}]},
|
|
)
|
|
```
|
|
|
|
???+ example "Skip auto-loading API key from environment:"
|
|
|
|
```python
|
|
from langgraph_sdk import get_client
|
|
|
|
# Don't load API key from environment variables
|
|
client = get_client(
|
|
url="http://localhost:8123",
|
|
api_key=None
|
|
)
|
|
```
|
|
"""
|
|
|
|
transport: httpx.AsyncBaseTransport | None = None
|
|
if url is None:
|
|
url = "http://api"
|
|
if os.environ.get("__LANGGRAPH_DEFER_LOOPBACK_TRANSPORT") == "true":
|
|
transport = get_asgi_transport()(app=None, root_path="/noauth") # ty: ignore[invalid-argument-type]
|
|
_registered_transports.append(transport)
|
|
else:
|
|
try:
|
|
from langgraph_api.server import app # type: ignore
|
|
|
|
transport = get_asgi_transport()(app, root_path="/noauth")
|
|
except Exception:
|
|
logger.debug(
|
|
"Failed to connect to in-process LangGraph server. Deferring configuration.",
|
|
exc_info=True,
|
|
)
|
|
transport = get_asgi_transport()(app=None, root_path="/noauth") # ty: ignore[invalid-argument-type]
|
|
_registered_transports.append(transport)
|
|
|
|
if transport is None:
|
|
transport = httpx.AsyncHTTPTransport(retries=5)
|
|
client = httpx.AsyncClient(
|
|
base_url=url,
|
|
transport=transport,
|
|
timeout=(
|
|
httpx.Timeout(timeout) # ty: ignore[invalid-argument-type]
|
|
if timeout is not None
|
|
else httpx.Timeout(connect=5, read=300, write=300, pool=5)
|
|
),
|
|
headers=_get_headers(api_key, headers),
|
|
)
|
|
return LangGraphClient(client)
|
|
|
|
|
|
class LangGraphClient:
|
|
"""Top-level client for LangGraph API.
|
|
|
|
Attributes:
|
|
assistants: Manages versioned configuration for your graphs.
|
|
threads: Handles (potentially) multi-turn interactions, such as conversational threads.
|
|
runs: Controls individual invocations of the graph.
|
|
crons: Manages scheduled operations.
|
|
store: Interfaces with persistent, shared data storage.
|
|
"""
|
|
|
|
def __init__(self, client: httpx.AsyncClient) -> None:
|
|
self.http = HttpClient(client)
|
|
self.assistants = AssistantsClient(self.http)
|
|
self.threads = ThreadsClient(self.http)
|
|
self.runs = RunsClient(self.http)
|
|
self.crons = CronClient(self.http)
|
|
self.store = StoreClient(self.http)
|
|
|
|
async def __aenter__(self) -> LangGraphClient:
|
|
"""Enter the async context manager."""
|
|
return self
|
|
|
|
async def __aexit__(
|
|
self,
|
|
exc_type: type[BaseException] | None,
|
|
exc_val: BaseException | None,
|
|
exc_tb: TracebackType | None,
|
|
) -> None:
|
|
"""Exit the async context manager."""
|
|
await self.aclose()
|
|
|
|
async def aclose(self) -> None:
|
|
"""Close the underlying HTTP client."""
|
|
if hasattr(self, "http"):
|
|
await self.http.client.aclose()
|