"""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..() 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()