# Create Connection Source: https://docs.langchain.com/api-reference/agent-connections-v2/create-connection https://api.host.langchain.com/openapi.json post /v2/auth/agents/{agent_id}/connections # List Connections Source: https://docs.langchain.com/api-reference/agent-connections-v2/list-connections https://api.host.langchain.com/openapi.json get /v2/auth/agents/{agent_id}/connections # Remove Connection Source: https://docs.langchain.com/api-reference/agent-connections-v2/remove-connection https://api.host.langchain.com/openapi.json delete /v2/auth/agents/{agent_id}/connections/{connection_id} # Upsert Agent Provider Token Source: https://docs.langchain.com/api-reference/agent-connections-v2/upsert-agent-provider-token https://api.host.langchain.com/openapi.json post /v2/auth/agents/{agent_id}/providers/{provider_id}/tokens Import an OAuth token and bind it to the agent for the given provider. Replaces any prior connection for the same agent+provider. The previously linked token is deleted only when it is agent-owned (no user owner) and no other agent connections still reference it — user vault credentials and shared tokens are left intact. Tokens are stored without a LangSmith user owner so synthetic MDA actor agent_ids resolve via the agent-connection path. # Authenticate Source: https://docs.langchain.com/api-reference/auth-service-v2/authenticate https://api.host.langchain.com/openapi.json post /v2/auth/authenticate Get OAuth token or start authentication flow if needed. # Check Oauth Token Exists Source: https://docs.langchain.com/api-reference/auth-service-v2/check-oauth-token-exists https://api.host.langchain.com/openapi.json get /v2/auth/tokens/exists Return whether the current user has any tokens for a given provider (across agents). # Check Oauth Tokens Exist Batch Source: https://docs.langchain.com/api-reference/auth-service-v2/check-oauth-tokens-exist-batch https://api.host.langchain.com/openapi.json post /v2/auth/tokens/exists/batch Batch token-presence check: per requested provider, whether the current user has any token. # Check Workspace Slack Tokens Exist Source: https://docs.langchain.com/api-reference/auth-service-v2/check-workspace-slack-tokens-exist https://api.host.langchain.com/openapi.json get /v2/auth/tokens/workspace/slack/exists Check if the workspace has any Slack tokens. # Create Mcp Oauth Provider Source: https://docs.langchain.com/api-reference/auth-service-v2/create-mcp-oauth-provider https://api.host.langchain.com/openapi.json post /v2/auth/providers/mcp-discover Create an OAuth provider via MCP auto-discovery. # Create Oauth Provider Source: https://docs.langchain.com/api-reference/auth-service-v2/create-oauth-provider https://api.host.langchain.com/openapi.json post /v2/auth/providers Create a new OAuth provider manually. # Delete Oauth Provider Source: https://docs.langchain.com/api-reference/auth-service-v2/delete-oauth-provider https://api.host.langchain.com/openapi.json delete /v2/auth/providers/{provider_id} Delete an OAuth provider. # Delete Oauth Tokens For User Source: https://docs.langchain.com/api-reference/auth-service-v2/delete-oauth-tokens-for-user https://api.host.langchain.com/openapi.json delete /v2/auth/tokens Delete all tokens for the current user for the given provider (across agents). # Delete Single Oauth Token Source: https://docs.langchain.com/api-reference/auth-service-v2/delete-single-oauth-token https://api.host.langchain.com/openapi.json delete /v2/auth/tokens/{token_id} Delete a specific OAuth token, revoking it at the provider first. Only the token owner can delete it. # Get Oauth Provider Source: https://docs.langchain.com/api-reference/auth-service-v2/get-oauth-provider https://api.host.langchain.com/openapi.json get /v2/auth/providers/{provider_id} Get a specific OAuth provider. # Get Platform Oauth Provider Source: https://docs.langchain.com/api-reference/auth-service-v2/get-platform-oauth-provider https://api.host.langchain.com/openapi.json get /v2/auth/platform-providers/{provider_id} Get a platform-level OAuth provider available to all workspaces. # Import Oauth Token Source: https://docs.langchain.com/api-reference/auth-service-v2/import-oauth-token https://api.host.langchain.com/openapi.json post /v2/auth/tokens/import Persist a directly-obtained OAuth token (no authorization-code exchange). The Slack managed-install flow receives a bot token inline from ``apps.managedInstall`` instead of through the browser OAuth redirect. This stores it (Fernet-encrypted at rest, via ``create_oauth_token``) for the caller's org/user against an existing provider. Requiring the provider to already exist in the caller's org scopes the write and blocks cross-org token creation. The token value is never logged or returned. # List Oauth Providers Source: https://docs.langchain.com/api-reference/auth-service-v2/list-oauth-providers https://api.host.langchain.com/openapi.json get /v2/auth/providers List OAuth providers. # List Oauth Tokens For User Source: https://docs.langchain.com/api-reference/auth-service-v2/list-oauth-tokens-for-user https://api.host.langchain.com/openapi.json get /v2/auth/tokens List the calling user's tokens for a provider. # List Platform Oauth Providers Source: https://docs.langchain.com/api-reference/auth-service-v2/list-platform-oauth-providers https://api.host.langchain.com/openapi.json get /v2/auth/platform-providers List platform-level OAuth providers available to all workspaces. # List Token Events For User Source: https://docs.langchain.com/api-reference/auth-service-v2/list-token-events-for-user https://api.host.langchain.com/openapi.json get /v2/auth/token-events List the calling user's OAuth connection audit events, newest first. Backs the frontend "your connection dropped, reconnect" surface. Scoped to the authenticated user + org; both come from the auth context, never from request input. # Oauth Callback Source: https://docs.langchain.com/api-reference/auth-service-v2/oauth-callback https://api.host.langchain.com/openapi.json post /v2/auth/callback/{provider_id} Finalize an OAuth flow. Claims the auth request, verifies the caller, exchanges the code, and saves the token. Used by both the frontend bridge and the headless flow (where a customer-owned service forwards the code/state, optionally proxied through smith-go). User-subject sessions require the authenticated caller to match the initiator. Agent-subject sessions (MDA Connect) skip that match — start is gated by ``deployments:create`` / service key, and the FE bridge may present a different same-org workspace session to complete consent — then bind via ``host_oauth_agent_connections``. # Oauth Callback Get Source: https://docs.langchain.com/api-reference/auth-service-v2/oauth-callback-get https://api.host.langchain.com/openapi.json get /v2/auth/callback/{provider_id} Handle OAuth callback redirect from OAuth providers. Always delegates to the frontend host-oauth-callback when LANGSMITH_URL is set — including agent-subject sessions — so finalize goes through the authenticated POST callback (org-scoped). Unauthenticated GET never mints or binds tokens for agent subjects. # Oauth Setup Callback Source: https://docs.langchain.com/api-reference/auth-service-v2/oauth-setup-callback https://api.host.langchain.com/openapi.json get /v2/auth/setup/{provider_id} Handle OAuth setup callback redirect from GitHub Apps. This endpoint handles the "Setup URL" callback from GitHub Apps, which is triggered when a user installs or updates their GitHub App installation. For "update" actions (user modified repo access via GitHub), we just show a success page since no token exchange is needed. For new installations with code/state, we process similar to the regular OAuth callback. # Revoke All Slack Tokens For Workspace Source: https://docs.langchain.com/api-reference/auth-service-v2/revoke-all-slack-tokens-for-workspace https://api.host.langchain.com/openapi.json delete /v2/auth/tokens/workspace/slack Revoke ALL Slack tokens for the workspace. Admin-only action that disconnects Slack entirely. This is a destructive operation that: - Revokes all Slack tokens on Slack's side for all users in the workspace - Deletes all Slack tokens from the database # Update Oauth Provider Source: https://docs.langchain.com/api-reference/auth-service-v2/update-oauth-provider https://api.host.langchain.com/openapi.json patch /v2/auth/providers/{provider_id} Update an OAuth provider. # Update Token Label Source: https://docs.langchain.com/api-reference/auth-service-v2/update-token-label https://api.host.langchain.com/openapi.json patch /v2/auth/tokens/{token_id}/metadata Update a token's provider_account_label. Only the token owner can update. # Wait For Auth Completion Source: https://docs.langchain.com/api-reference/auth-service-v2/wait-for-auth-completion https://api.host.langchain.com/openapi.json get /v2/auth/wait/{auth_id} Wait for OAuth authentication completion. # Create Deployment Source: https://docs.langchain.com/api-reference/deployments-v2/create-deployment https://api.host.langchain.com/openapi.json post /v2/deployments Create a new deployment. # Create Deployment Revision Source: https://docs.langchain.com/api-reference/deployments-v2/create-deployment-revision https://api.host.langchain.com/openapi.json post /v2/deployments/{deployment_id}/revisions Create a new revision for a deployment. The dedicated create-revision entry point: unlike PATCH, this always creates a revision and returns the created ``Revision`` directly. # Delete Deployment Source: https://docs.langchain.com/api-reference/deployments-v2/delete-deployment https://api.host.langchain.com/openapi.json delete /v2/deployments/{deployment_id} Delete a deployment by ID. # Delete Deployments Source: https://docs.langchain.com/api-reference/deployments-v2/delete-deployments https://api.host.langchain.com/openapi.json delete /v2/deployments Delete multiple deployments with partial success support. Returns: - 200: All deployments deleted successfully - 207: Some deployments deleted successfully, some failed # Get Deployment Source: https://docs.langchain.com/api-reference/deployments-v2/get-deployment https://api.host.langchain.com/openapi.json get /v2/deployments/{deployment_id} Get a deployment by ID. # Get Free Deployment Count Source: https://docs.langchain.com/api-reference/deployments-v2/get-free-deployment-count https://api.host.langchain.com/openapi.json get /v2/deployments/free-count Return the number of free deployments used by the caller's organization. # Get Revision Source: https://docs.langchain.com/api-reference/deployments-v2/get-revision https://api.host.langchain.com/openapi.json get /v2/deployments/{deployment_id}/revisions/{revision_id} Get a revision by ID for a deployment. # Interrupt Deployment Revision Source: https://docs.langchain.com/api-reference/deployments-v2/interrupt-deployment-revision https://api.host.langchain.com/openapi.json post /v2/deployments/{deployment_id}/revisions/{revision_id}/interrupt Interrupt an in-progress revision build/deploy. # List Deployment Logs Source: https://docs.langchain.com/api-reference/deployments-v2/list-deployment-logs https://api.host.langchain.com/openapi.json get /v2/deployments/{deployment_id}/logs List deploy logs for a deployment (across all revisions). # List Deployments Source: https://docs.langchain.com/api-reference/deployments-v2/list-deployments https://api.host.langchain.com/openapi.json get /v2/deployments List all deployments. # List Revision Logs Source: https://docs.langchain.com/api-reference/deployments-v2/list-revision-logs https://api.host.langchain.com/openapi.json get /v2/deployments/{deployment_id}/revisions/{revision_id}/logs List build or deploy logs for a specific revision of a deployment. # List Revisions Source: https://docs.langchain.com/api-reference/deployments-v2/list-revisions https://api.host.langchain.com/openapi.json get /v2/deployments/{deployment_id}/revisions List all revisions for a deployment. # Patch Deployment Source: https://docs.langchain.com/api-reference/deployments-v2/patch-deployment https://api.host.langchain.com/openapi.json patch /v2/deployments/{deployment_id} Patch a deployment by ID. # Patch Deployment Resource Tiers Source: https://docs.langchain.com/api-reference/deployments-v2/patch-deployment-resource-tiers https://api.host.langchain.com/openapi.json patch /v2/deployments/{deployment_id}/resource-tiers Patch a deployment's compute and database resource tiers independently. # Patch Deployment Tier Source: https://docs.langchain.com/api-reference/deployments-v2/patch-deployment-tier https://api.host.langchain.com/openapi.json patch /v2/deployments/{deployment_id}/deployment-tier Patch a deployment's fixed resource tier. # Redeploy Revision Source: https://docs.langchain.com/api-reference/deployments-v2/redeploy-revision https://api.host.langchain.com/openapi.json post /v2/deployments/{deployment_id}/revisions/{revision_id}/redeploy Redeploy a specific revision ID. # List Forge GitHub Integrations Source: https://docs.langchain.com/api-reference/integrations-v1/list-forge-github-integrations https://api.host.langchain.com/openapi.json get /v1/integrations/forge/github/install List available Forge GitHub integrations. # List Forge GitHub Repositories Source: https://docs.langchain.com/api-reference/integrations-v1/list-forge-github-repositories https://api.host.langchain.com/openapi.json get /v1/integrations/forge/github/{integration_id}/repos List available GitHub repositories for a Forge integration. # List GitHub Integrations Source: https://docs.langchain.com/api-reference/integrations-v1/list-github-integrations https://api.host.langchain.com/openapi.json get /v1/integrations/github/install List available GitHub integrations for LangGraph Platfom Cloud SaaS. # List GitHub Repositories Source: https://docs.langchain.com/api-reference/integrations-v1/list-github-repositories https://api.host.langchain.com/openapi.json get /v1/integrations/github/{integration_id}/repos List available GitHub repositories for an integration that are available to deploy to LangSmith Deployment. # Create Listener Source: https://docs.langchain.com/api-reference/listeners-v2/create-listener https://api.host.langchain.com/openapi.json post /v2/listeners Create a listener.

Creating a listener is only allowed for LangSmith organizations with self-hosted enterprise plans. # Delete Listener Source: https://docs.langchain.com/api-reference/listeners-v2/delete-listener https://api.host.langchain.com/openapi.json delete /v2/listeners/{listener_id} Delete a listener by ID. # Get Listener Source: https://docs.langchain.com/api-reference/listeners-v2/get-listener https://api.host.langchain.com/openapi.json get /v2/listeners/{listener_id} Get a listener by ID. # List Listeners Source: https://docs.langchain.com/api-reference/listeners-v2/list-listeners https://api.host.langchain.com/openapi.json get /v2/listeners List all listeners. # Patch Listener Source: https://docs.langchain.com/api-reference/listeners-v2/patch-listener https://api.host.langchain.com/openapi.json patch /v2/listeners/{listener_id} Patch a listener by ID. # Build Source: https://docs.langchain.com/build-overview Build agents with LangChain, LangGraph, and Deep Agents.

Build

The LangChain open source stack provides the building blocks you need to design, test, and ship agents.

Choose your starting point

Deep Agents, LangChain, and LangGraph share the same stack, so choose based on how much control you need: Build agents for complex, long-running tasks. A complete agent harness with planning, subagents, a virtual filesystem, and long-term memory built in. The fastest way to start. A minimal, configurable agent framework. Compose exactly what you need from models, tools, prompts, and middleware. Low-level orchestration for stateful, long-running agents: durable execution, streaming, memory, and human-in-the-loop. Build agents for complex, long-running tasks. A complete agent harness with planning, subagents, a virtual filesystem, and long-term memory built in. The fastest way to start. A minimal, configurable agent framework. Compose exactly what you need from models, tools, prompts, and middleware. Low-level orchestration for stateful, long-running agents: durable execution, streaming, memory, and human-in-the-loop.

Use a ready-made agent

Open source terminal coding agent (`dcode`) built on the Deep Agents SDK. Switch models mid-session, customize skills and memory, and approve shell execution from the CLI.

Explore

Connect to model providers, vector stores, retrievers, and other components. Follow tutorials and conceptual guides for common agent patterns and use cases. API references, error codes, release notes, and migration guides. Contribute documentation, code, and integrations to the LangChain ecosystem. Connect to model providers, vector stores, retrievers, and other components. Follow tutorials and conceptual guides for common agent patterns and use cases. API references, error codes, release notes, and migration guides. Contribute documentation, code, and integrations to the LangChain ecosystem.
***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/build-overview.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Source: https://docs.langchain.com/index

The platform for agent engineering

One platform to improve every step of the agent development lifecycle, so you can ship reliable agents faster.

Agent development lifecycle

Build agents with code using LangChain, LangGraph, and Deep Agents. Evaluate agents with datasets, evaluations, and prompt engineering. Deploy and serve agents at scale. Trace, debug, and observe agents in production.

Platform

Set up Cloud or Self-hosted LangSmith, and govern users and compliance. Route, control, and observe LLM traffic across providers. Build and run agents without code using LangSmith Fleet. Find and fix recurring agent issues automatically with LangSmith Engine. Code with an AI agent in your terminal using the open source `dcode` CLI.

Resources

Take free courses on building and improving agents with LangSmith and our open source frameworks. Ask questions, share solutions, and discuss best practices. Submit tickets and track support requests. Start with LangSmith for free. Real-time status of LangSmith services and APIs. HIPAA, SOC 2 Type 2, and GDPR compliance details.
# Attribute-based access control Source: https://docs.langchain.com/langsmith/abac This reference explains LangSmith's Attribute-Based Access Control (ABAC) system, which enables fine-grained access control based on resource attributes, complementing [RBAC](/langsmith/rbac). For automated user provisioning into roles, see [SCIM](/langsmith/user-management#set-up-scim-for-your-organization). ABAC (Attribute-Based Access Control) is an Enterprise feature for managing fine-grained access control. If you are interested in this feature, [contact our sales team](https://www.langchain.com/contact-sales). Other plans default to using the Admin role for all users. ABAC complements [Role-Based Access Control (RBAC)](/langsmith/rbac) by adding tag-based conditions to access decisions. While RBAC grants blanket permissions based on a user's role (e.g., "can read all projects"), ABAC lets you restrict or grant access based on resource tags (e.g., "can only read projects tagged with Environment=Development"). Roles and resource tags can be managed via the UI or API. ABAC policies are configurable via the [API](https://api.smith.langchain.com/docs#/access_policies). Once configured, policies are automatically enforced in both the API and the UI. ## Before you begin * [Set up resource tags](/langsmith/set-up-resource-tags) in your workspace. * ABAC currently only supports `resource_tag_key` as an `attribute_name` in policies, for evaluating against resource tags. No other attributes are supported yet. ## Enable ABAC for self-hosted deployments 1. ABAC requires a [self-hosted](/langsmith/self-hosted) LangSmith deployment running Helm chart 0.11.28 or later (application version 0.12.1). Once you've upgraded, use one of the following options to enable ABAC: * **Enable for a specific organization:** Run the following against your LangSmith PostgreSQL database, replacing `` with the ID copied from the organization settings page in the UI: ```sql theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} UPDATE organizations SET config = config || '{"can_use_abac": true}' WHERE id = '' AND NOT is_personal; ``` * **Enable for all organizations:** Add the following environment variable to `commonEnv` in your `values.yaml`: ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} DEFAULT_ORG_FEATURE_CAN_USE_ABAC: "true" ``` This environment variable has no effect on personal organizations, because [RBAC](/langsmith/rbac) is not enabled for personal organizations. 2. Set up authentication. To manage access policies via the API, you need a Personal Access Token (PAT) from an [Organization Admin](/langsmith/rbac#organization-admin) user, or an organization-scoped service key with Organization Admin permissions. Set the following environment variables before running any scripts: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} export LANGSMITH_API_KEY="your_admin_api_key" # Required for self-hosted or regional SaaS deployments: # export LANGCHAIN_ENDPOINT="https://eu.api.smith.langchain.com" # export LANGCHAIN_ENDPOINT="https://aws.api.smith.langchain.com" # export LANGCHAIN_ENDPOINT="https://apac.api.smith.langchain.com" # export LANGCHAIN_ENDPOINT="https://langsmith.yourdomain.com/api" ``` ## Access policy structure An access policy defines conditions under which access is granted or denied. Here's the structure: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "name": "Policy Name", "description": "Optional description", "effect": "allow | deny", "condition_groups": [ { "permission": "projects:read", "resource_type": "project", "conditions": [ { "attribute_name": "resource_tag_key", "attribute_key": "Environment", "operator": "equals", "attribute_value": "Production" } ] } ], "role_ids": [""] } ``` ### Effect The `effect` determines what happens when conditions match: * **`allow`** - Grant access when conditions match * **`deny`** - Block access when conditions match Deny policies always take precedence. If both an allow and deny policy match, access is denied. ### Condition groups The `condition_groups` array contains one or more condition groups. Multiple condition groups are evaluated with **OR logic** - if any group matches, the policy applies. Each condition group specifies: * **`permission`** - The permission this group applies to * **`resource_type`** - The resource type to match * **`conditions`** - Array of conditions (evaluated with **AND logic** within the group) #### Resource types and permissions | Resource type | Supported permissions | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `project` | `projects:read`, `projects:update`, `projects:delete`, `runs:read`, `runs:share`, `runs:delete`, `projects:increase-trace-tier`, `projects:decrease-trace-tier` | | `prompt` | `prompts:read`, `prompts:update`, `prompts:delete`, `prompts:share`, `prompts:tag` | | `dataset` | `datasets:read`, `datasets:update`, `datasets:delete`, `datasets:share`, `datasets:download` | | `deployment` | `deployments:read`, `deployments:update`, `deployments:delete` | | `queues` | `annotation-queues:create`, `annotation-queues:delete`, `annotation-queues:read`, `annotation-queues:update` | | `mcp_server` | `mcp-servers:read`, `mcp-servers:invoke`, `mcp-servers:update`, `mcp-servers:delete`. See [Fleet tool access control](/langsmith/fleet/access-and-oversight#tool-access-control). | | `fleet_integration` | `mcp-servers:read`, `mcp-servers:invoke`. See [Fleet tool access control](/langsmith/fleet/access-and-oversight#tool-access-control). | Runs don't have their own tags. Run permissions (`runs:read`, `runs:create`, `runs:share`, `runs:delete`) are evaluated against the parent project's tags. #### Conditions Each condition in the `conditions` array specifies: * **`attribute_name`** - Currently only `resource_tag_key` is supported * **`attribute_key`** - The tag key to match (e.g., `Environment`, `Team`) * **`operator`** - The comparison operator * **`attribute_value`** - The value to compare against ##### Operators | Operator | Description | | ------------------------ | ------------------------------------------------ | | `equals` | Exact match (case sensitive) | | `not_equals` | Values differ (case sensitive) | | `equals_ignore_case` | Exact match (case insensitive) | | `not_equals_ignore_case` | Values differ (case insensitive) | | `matches` | Glob pattern matching with `*` and `?` wildcards | | `not_matches` | Match when value doesn't match glob pattern | ##### `_if_exists` variants Each operator has an `_if_exists` variant that matches by default when the tag key is absent, or evaluates the condition normally when the tag exists: | Operator | Description | | ---------------------------------- | ----------------------------------------------------------------- | | `equals_if_exists` | Exact match (case sensitive), or if tag key absent | | `not_equals_if_exists` | Values differ (case sensitive), or if tag key absent | | `equals_ignore_case_if_exists` | Exact match (case insensitive), or if tag key absent | | `not_equals_ignore_case_if_exists` | Values differ (case insensitive), or if tag key absent | | `matches_if_exists` | Glob pattern match, or if tag key absent | | `not_matches_if_exists` | Match when value doesn't match glob pattern, or if tag key absent | In an **allow** policy, `_if_exists` variants grant access to resources that either match the condition or don't have the specified tag key. In a **deny** policy, they block resources that either match the condition or don't have the tag key. ### Roles The `role_ids` array specifies which workspace roles the policy applies to. When a user with that role accesses a resource, the policy conditions are evaluated. Policies can be attached to roles when creating the policy, or attached later via the API. ## Managing access policies Access policies are managed via the LangSmith API by [Organization Admins](/langsmith/rbac#organization-admin). Before creating policies, [set up resource tags](/langsmith/set-up-resource-tags) in your workspace. ## How ABAC works with RBAC [RBAC](/langsmith/rbac) permissions and ABAC policies are both considered when determining access to resources: * ABAC **deny** policies override RBAC permissions * ABAC **allow** policies can grant access even without RBAC permissions * If no ABAC policies match, the system falls back to RBAC ### Policy evaluation outcomes **Feature combinations:** | RBAC enabled | ABAC enabled | Behavior | | ------------ | ------------ | --------------------------------------------------- | | ✗ | ✗ | All workspace members have Admin-level access | | ✓ | ✗ | Standard RBAC - access based on role permissions | | ✓ | ✓ | RBAC + ABAC - fine-grained tag-based access control | **When both RBAC and ABAC are enabled:** | RBAC permits | Allow policy matches | Deny policy matches | Result | | ------------ | -------------------- | ------------------- | -------------------------------- | | ✓ | ✓ | ✗ | **Allowed** | | ✓ | ✗ | ✗ | **Allowed** (RBAC fallback) | | ✓ | ✓ | ✓ | **Denied** (deny wins) | | ✓ | ✗ | ✓ | **Denied** (deny wins) | | ✗ | ✓ | ✗ | **Allowed** (ABAC grants access) | | ✗ | ✗ | ✗ | **Denied** | | ✗ | ✓ | ✓ | **Denied** (deny wins) | ## Example scenarios ### 1. Annotator team assignment Allow annotators to only access datasets tagged for their team: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "name": "Annotator Team A Access", "effect": "allow", "condition_groups": [{ "permission": "datasets:read", "resource_type": "dataset", "conditions": [{ "attribute_name": "resource_tag_key", "attribute_key": "Annotation-Team", "operator": "equals", "attribute_value": "Team-A" }] }] } ``` ### 2. Block sensitive data Deny access to datasets containing PII. Since deny policies override allow policies, this blocks access even for users with RBAC permissions: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "name": "Block PII Datasets", "effect": "deny", "condition_groups": [{ "permission": "datasets:read", "resource_type": "dataset", "conditions": [{ "attribute_name": "resource_tag_key", "attribute_key": "Contains-PII", "operator": "equals", "attribute_value": "true" }] }] } ``` ### 3. Application-based access with wildcards Allow engineers to access projects for any application in the "chatbot" family using glob patterns: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "name": "Chatbot Apps Access", "effect": "allow", "condition_groups": [{ "permission": "projects:read", "resource_type": "project", "conditions": [{ "attribute_name": "resource_tag_key", "attribute_key": "Application", "operator": "matches", "attribute_value": "chatbot-*" }] }] } ``` ### 4. Client and purpose isolation (AND logic) Grant access only if both conditions are met - dataset is for training AND belongs to a specific client: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "name": "Client Training Data Access", "effect": "allow", "condition_groups": [{ "permission": "datasets:read", "resource_type": "dataset", "conditions": [ { "attribute_name": "resource_tag_key", "attribute_key": "Purpose", "operator": "equals", "attribute_value": "Training" }, { "attribute_name": "resource_tag_key", "attribute_key": "Client", "operator": "equals", "attribute_value": "Acme-Corp" } ] }] } ``` ### 5. Client data plus resources without a `Client` tag using `_if_exists` Consultants don't have RBAC `datasets:read` permission, but this policy grants them access to datasets tagged `Client=Acme-Corp`, as well as datasets that don't have a `Client` tag at all. Datasets tagged with a different client (e.g., `Client=Other-Corp`) remain blocked: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "name": "Acme Consultant Access", "effect": "allow", "condition_groups": [{ "permission": "datasets:read", "resource_type": "dataset", "conditions": [{ "attribute_name": "resource_tag_key", "attribute_key": "Client", "operator": "equals_if_exists", "attribute_value": "Acme-Corp" }] }] } ``` ## Tag resources at creation time When ABAC policies are active, resources are access-controlled based on their tags. To make sure a resource is protected as soon as it is created, you can supply tags directly in the creation request using the `tag_value_ids` parameter. This is supported on project, dataset, and prompt creation endpoints (including fork and clone operations). Tags are applied atomically in the same database transaction as resource creation. For full details and examples, see [Tag a resource at creation time](/langsmith/set-up-resource-tags#tag-a-resource-at-creation-time) in the resource tags guide. If you rely on the LangSmith SDK to auto-create tracing projects during trace ingestion, the `tag_value_ids` parameter is not available on that auto-create path. To ensure ABAC policies apply from the start, pre-create the project via `POST /api/v1/sessions` with the desired `tag_value_ids` before starting your trace session. ## Troubleshooting **Access unexpectedly denied?** * Check if a deny policy is matching (deny always takes precedence) * Check if the user has RBAC permissions or a matching allow policy * Verify the resource has the expected tag and value * Deny policies with `_if_exists` operators block resources missing that tag key * For case-sensitive operators (`equals`, `not_equals`), check for case mismatches * With multiple conditions in a group, all must match (AND logic) **Access unexpectedly granted?** * Review RBAC permissions (users may have access via their role) * Check if an allow policy is too broad (e.g., using wildcards) * `_if_exists` operators match resources missing that tag key **Policy not taking effect?** * Confirm the policy is attached to the correct role * Verify the user has that role in the workspace * Check that `resource_type` and `permission` match the resource being accessed ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/abac.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Access the current run (span) within a traced function Source: https://docs.langchain.com/langsmith/access-current-span In some cases you will want to access the current run (span) within a traced function. This can be useful for extracting UUIDs, tags, or other information from the current run. You can access the current run by calling the `get_current_run_tree`/`getCurrentRunTree` function in the Python or TypeScript SDK, respectively. For a full list of available properties on the `RunTree` object, see [this reference](/langsmith/run-data-format). ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langsmith import traceable from langsmith.run_helpers import get_current_run_tree from openai import Client openai = Client() @traceable def format_prompt(subject): run = get_current_run_tree() print(f"format_prompt Run Id: {run.id}") print(f"format_prompt Trace Id: {run.trace_id}") print(f"format_prompt Parent Run Id: {run.parent_run.id}") return [ { "role": "system", "content": "You are a helpful assistant.", }, { "role": "user", "content": f"What's a good name for a store that sells {subject}?" } ] @traceable(run_type="llm") def invoke_llm(messages): run = get_current_run_tree() print(f"invoke_llm Run Id: {run.id}") print(f"invoke_llm Trace Id: {run.trace_id}") print(f"invoke_llm Parent Run Id: {run.parent_run.id}") return openai.chat.completions.create( messages=messages, model="gpt-5.4-mini", temperature=0 ) @traceable def parse_output(response): run = get_current_run_tree() print(f"parse_output Run Id: {run.id}") print(f"parse_output Trace Id: {run.trace_id}") print(f"parse_output Parent Run Id: {run.parent_run.id}") return response.choices[0].message.content @traceable def run_pipeline(): run = get_current_run_tree() print(f"run_pipeline Run Id: {run.id}") print(f"run_pipeline Trace Id: {run.trace_id}") messages = format_prompt("colorful socks") response = invoke_llm(messages) return parse_output(response) run_pipeline() ``` ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { traceable, getCurrentRunTree } from "langsmith/traceable"; import OpenAI from "openai"; const openai = new OpenAI(); const formatPrompt = traceable((subject: string) => { const run = getCurrentRunTree(); console.log("formatPrompt Run ID", run.id) console.log("formatPrompt Trace ID", run.trace_id) console.log("formatPrompt Parent Run ID", run.parent_run.id) return [ { role: "system" as const, content: "You are a helpful assistant.", }, { role: "user" as const, content: `What's a good name for a store that sells ${subject}?`, }, ]; }, { name: "formatPrompt" }); const invokeLLM = traceable( async (messages: { role: string; content: string }[]) => { const run = getCurrentRunTree(); console.log("invokeLLM Run ID", run.id) console.log("invokeLLM Trace ID", run.trace_id) console.log("invokeLLM Parent Run ID", run.parent_run.id) return openai.chat.completions.create({ model: "gpt-5.4-mini", messages: messages, temperature: 0, }); }, { run_type: "llm", name: "invokeLLM" } ); const parseOutput = traceable( (response: any) => { const run = getCurrentRunTree(); console.log("parseOutput Run ID", run.id) console.log("parseOutput Trace ID", run.trace_id) console.log("parseOutput Parent Run ID", run.parent_run.id) return response.choices[0].message.content; }, { name: "parseOutput" } ); const runPipeline = traceable( async () => { const run = getCurrentRunTree(); console.log("runPipline Run ID", run.id) console.log("runPipline Trace ID", run.trace_id) console.log("runPipline Parent Run ID", run.parent_run?.id) const messages = await formatPrompt("colorful socks"); const response = await invokeLLM(messages); return parseOutput(response); }, { name: "runPipeline" } ); await runPipeline(); ``` ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/access-current-span.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Connect an authentication provider Source: https://docs.langchain.com/langsmith/add-auth-server In [the last tutorial](/langsmith/resource-auth), you added resource authorization to give users private conversations. However, you are still using hard-coded tokens for authentication, which is not secure. Now you'll replace those tokens with real user accounts using [OAuth2](/langsmith/deployment-quickstart). You'll keep the same [`Auth`](https://reference.langchain.com/python/langgraph-sdk/auth/Auth) object and [resource-level access control](/langsmith/auth#single-owner-resources), but upgrade authentication to use Supabase as your identity provider. While Supabase is used in this tutorial, the concepts apply to any OAuth2 provider. You'll learn how to: 1. Replace test tokens with real JWT tokens 2. Integrate with OAuth2 providers for secure user authentication 3. Handle user sessions and metadata while maintaining our existing authorization logic ## Background OAuth2 involves three main roles: 1. **Authorization server**: The identity provider (e.g., Supabase, Auth0, Google) that handles user authentication and issues tokens 2. **Application backend**: Your LangGraph application. This validates tokens and serves protected resources (conversation data) 3. **Client application**: The web or mobile app where users interact with your service A standard OAuth2 flow works something like this: ```mermaid theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} sequenceDiagram participant User participant Client participant AuthServer participant Agent Server User->>Client: Initiate login User->>AuthServer: Enter credentials AuthServer->>Client: Send tokens Client->>Agent Server: Request with token Agent Server->>AuthServer: Validate token AuthServer->>Agent Server: Token valid Agent Server->>Client: Serve request (e.g., run agent or graph) ``` ## Prerequisites Before you start this tutorial, ensure you have: * The [bot from the second tutorial](/langsmith/resource-auth) running without errors. * A [Supabase project](https://supabase.com/dashboard) to use its authentication server. ## 1. Install dependencies Install the required dependencies. Start in your `custom-auth` directory and ensure you have the `langgraph-cli` installed: ```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} cd custom-auth pip install -U "langgraph-cli[inmem]" ``` ```bash uv theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} cd custom-auth uv add "langgraph-cli[inmem]" ``` ## 2. Set up the authentication provider Next, fetch the URL of your auth server and the private key for authentication. Since you're using Supabase for this, you can do this in the Supabase dashboard: 1. In the left sidebar, click on t️⚙ Project Settings" and then click "API" 2. Copy your project URL and add it to your `.env` file ```shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} echo "SUPABASE_URL=your-project-url" >> .env ``` 3. Copy your service role secret key and add it to your `.env` file: ```shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} echo "SUPABASE_SERVICE_KEY=your-service-role-key" >> .env ``` 4. Copy your "anon public" key and note it down. This will be used later when you set up our client code. ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} SUPABASE_URL=your-project-url SUPABASE_SERVICE_KEY=your-service-role-key ``` ## 3. Implement token validation In the previous tutorials, you used the [`Auth`](https://reference.langchain.com/python/langgraph-sdk/auth/Auth) object to [validate hard-coded tokens](/langsmith/set-up-custom-auth) and [add resource ownership](/langsmith/resource-auth). Now you'll upgrade your authentication to validate real JWT tokens from Supabase. The main changes will all be in the [`@auth.authenticate`](https://reference.langchain.com/python/langgraph-sdk/auth/Auth/authenticate) decorated function: * Instead of checking against a hard-coded list of tokens, you'll make an HTTP request to Supabase to validate the token. * You'll extract real user information (ID, email) from the validated token. * The existing resource authorization logic remains unchanged. Update `src/security/auth.py` to implement this: ```python {highlight={8-9,20-30}} title="src/security/auth.py" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import os import httpx from langgraph_sdk import Auth auth = Auth() # This is loaded from the `.env` file you created above SUPABASE_URL = os.environ["SUPABASE_URL"] SUPABASE_SERVICE_KEY = os.environ["SUPABASE_SERVICE_KEY"] @auth.authenticate async def get_current_user(authorization: str | None): """Validate JWT tokens and extract user information.""" assert authorization scheme, token = authorization.split() assert scheme.lower() == "bearer" try: # Verify token with auth provider async with httpx.AsyncClient() as client: response = await client.get( f"{SUPABASE_URL}/auth/v1/user", headers={ "Authorization": authorization, "apiKey": SUPABASE_SERVICE_KEY, }, ) assert response.status_code == 200 user = response.json() return { "identity": user["id"], # Unique user identifier "email": user["email"], "is_authenticated": True, } except Exception as e: raise Auth.exceptions.HTTPException(status_code=401, detail=str(e)) # ... the rest is the same as before # Keep our resource authorization from the previous tutorial @auth.on async def add_owner(ctx, value): """Make resources private to their creator using resource metadata.""" filters = {"owner": ctx.user.identity} metadata = value.setdefault("metadata", {}) metadata.update(filters) return filters ``` The most important change is that we're now validating tokens with a real authentication server. Our authentication handler has the private key for our Supabase project, which we can use to validate the user's token and extract their information. ## 4. Test authentication flow Let's test out the new authentication flow. You can run the following code in a file or notebook. You will need to provide: * A valid email address * A Supabase project URL (from [above](#setup-auth-provider)) * A Supabase anon **public key** (also from [above](#setup-auth-provider)) ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import os import httpx from getpass import getpass from langgraph_sdk import get_client # Get email from command line email = getpass("Enter your email: ") base_email = email.split("@") password = "secure-password" # CHANGEME email1 = f"{base_email[0]}+1@{base_email[1]}" email2 = f"{base_email[0]}+2@{base_email[1]}" SUPABASE_URL = os.environ.get("SUPABASE_URL") if not SUPABASE_URL: SUPABASE_URL = getpass("Enter your Supabase project URL: ") # This is your PUBLIC anon key (which is safe to use client-side) # Do NOT mistake this for the secret service role key SUPABASE_ANON_KEY = os.environ.get("SUPABASE_ANON_KEY") if not SUPABASE_ANON_KEY: SUPABASE_ANON_KEY = getpass("Enter your public Supabase anon key: ") async def sign_up(email: str, password: str): """Create a new user account.""" async with httpx.AsyncClient() as client: response = await client.post( f"{SUPABASE_URL}/auth/v1/signup", json={"email": email, "password": password}, headers={"apiKey": SUPABASE_ANON_KEY}, ) assert response.status_code == 200 return response.json() # Create two test users print(f"Creating test users: {email1} and {email2}") await sign_up(email1, password) await sign_up(email2, password) ``` ⚠️ Before continuing: Check your email and click both confirmation links. Supabase will reject `/login` requests until after you have confirmed your users' email. Now test that users can only see their own data. Make sure the server is running (run `langgraph dev`) before proceeding. The following snippet requires the "anon public" key that you copied from the Supabase dashboard while [setting up the auth provider](#setup-auth-provider) previously. ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} async def login(email: str, password: str): """Get an access token for an existing user.""" async with httpx.AsyncClient() as client: response = await client.post( f"{SUPABASE_URL}/auth/v1/token?grant_type=password", json={ "email": email, "password": password }, headers={ "apikey": SUPABASE_ANON_KEY, "Content-Type": "application/json" }, ) assert response.status_code == 200 return response.json()["access_token"] # Log in as user 1 user1_token = await login(email1, password) user1_client = get_client( url="http://localhost:2024", headers={"Authorization": f"Bearer {user1_token}"} ) # Create a thread as user 1 thread = await user1_client.threads.create() print(f"✅ User 1 created thread: {thread['thread_id']}") # Try to access without a token unauthenticated_client = get_client(url="http://localhost:2024") try: await unauthenticated_client.threads.create() print("❌ Unauthenticated access should fail!") except Exception as e: print("✅ Unauthenticated access blocked:", e) # Try to access user 1's thread as user 2 user2_token = await login(email2, password) user2_client = get_client( url="http://localhost:2024", headers={"Authorization": f"Bearer {user2_token}"} ) try: await user2_client.threads.get(thread["thread_id"]) print("❌ User 2 shouldn't see User 1's thread!") except Exception as e: print("✅ User 2 blocked from User 1's thread:", e) ``` The output should look like this: ```shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} ✅ User 1 created thread: d6af3754-95df-4176-aa10-dbd8dca40f1a ✅ Unauthenticated access blocked: Client error '403 Forbidden' for url 'http://localhost:2024/threads' ✅ User 2 blocked from User 1's thread: Client error '404 Not Found' for url 'http://localhost:2024/threads/d6af3754-95df-4176-aa10-dbd8dca40f1a' ``` Your authentication and authorization are working together: 1. Users must log in to access the bot 2. Each user can only see their own threads All users are managed by the Supabase auth provider, so you don't need to implement any additional user management logic. ## Next steps You've successfully built a production-ready authentication system for your LangGraph application! Let's review what you've accomplished: 1. Set up an authentication provider (Supabase in this case) 2. Added real user accounts with email/password authentication 3. Integrated JWT token validation into your Agent Server 4. Implemented proper authorization to ensure users can only access their own data 5. Created a foundation that's ready to handle your next authentication challenge Now that you have production authentication, consider: 1. Building a web UI with your preferred framework (see the [Custom Auth](https://github.com/langchain-ai/custom-auth) template for an example) 2. Learn more about the other aspects of authentication and authorization in the [conceptual guide on authentication](/langsmith/auth). 3. Customize your handlers and setup further after reading the [reference docs](https://reference.langchain.com/python/langgraph-sdk/auth/Auth). ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/add-auth-server.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Human-in-the-loop using server API Source: https://docs.langchain.com/langsmith/add-human-in-the-loop To review, edit, and approve tool calls in an agent or workflow, use LangGraph's [human-in-the-loop](/oss/python/langgraph/interrupts) features. ## Dynamic interrupts ```python {highlight={2,34}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langgraph_sdk import get_client from langgraph_sdk.schema import Command client = get_client(url=) # Using the graph deployed with the name "agent" assistant_id = "agent" # create a thread thread = await client.threads.create() thread_id = thread["thread_id"] # Run the graph until the interrupt is hit. result = await client.runs.wait( thread_id, assistant_id, input={"some_text": "original text"} # (1)! ) print(result['__interrupt__']) # (2)! # > [ # > { # > 'value': {'text_to_revise': 'original text'}, # > 'resumable': True, # > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'], # > 'when': 'during' # > } # > ] # Resume the graph print(await client.runs.wait( thread_id, assistant_id, command=Command(resume="Edited text") # (3)! )) # > {'some_text': 'Edited text'} ``` 1. The graph is invoked with some initial state. 2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata. 3\. The graph is resumed with a `Command(resume=...)`, injecting the human's input and continuing execution. ```javascript {highlight={32}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { Client } from "@langchain/langgraph-sdk"; const client = new Client({ apiUrl: }); // Using the graph deployed with the name "agent" const assistantID = "agent"; // create a thread const thread = await client.threads.create(); const threadID = thread["thread_id"]; // Run the graph until the interrupt is hit. const result = await client.runs.wait( threadID, assistantID, { input: { "some_text": "original text" } } # (1)! ); console.log(result['__interrupt__']); # (2)! // > [ # > { # > 'value': {'text_to_revise': 'original text'}, # > 'resumable': True, # > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'], # > 'when': 'during' # > } # > ] // Resume the graph console.log(await client.runs.wait( threadID, assistantID, { command: { resume: "Edited text" }} # (3)! )); # > {'some_text': 'Edited text'} ``` 1. The graph is invoked with some initial state. 2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata. 3. The graph is resumed with a `{ resume: ... }` command object, injecting the human's input and continuing execution. Create a thread: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads \ --header 'Content-Type: application/json' \ --data '{}' ``` Run the graph until the interrupt is hit.: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads//runs/wait \ --header 'Content-Type: application/json' \ --data "{ \"assistant_id\": \"agent\", \"input\": {\"some_text\": \"original text\"} }" ``` Resume the graph: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads//runs/wait \ --header 'Content-Type: application/json' \ --data "{ \"assistant_id\": \"agent\", \"command\": { \"resume\": \"Edited text\" } }" ``` This is an example graph you can run in the Agent Server. See [LangSmith quickstart](/langsmith/deployment-quickstart) for more details. ```python {highlight={7,13}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from typing import TypedDict import uuid from langgraph.checkpoint.memory import InMemorySaver from langgraph.constants import START from langgraph.graph import StateGraph from langgraph.types import interrupt, Command class State(TypedDict): some_text: str def human_node(state: State): value = interrupt( # (1)! { "text_to_revise": state["some_text"] # (2)! } ) return { "some_text": value # (3)! } # Build the graph graph_builder = StateGraph(State) graph_builder.add_node("human_node", human_node) graph_builder.add_edge(START, "human_node") graph = graph_builder.compile() ``` 1. `interrupt(...)` pauses execution at `human_node`, surfacing the given payload to a human. 2. Any JSON serializable value can be passed to the [`interrupt`](https://reference.langchain.com/python/langgraph/types/interrupt) function. Here, a dict containing the text to revise. 3. Once resumed, the return value of `interrupt(...)` is the human-provided input, which is used to update the state. Once you have a running Agent Server, you can interact with it using [LangGraph SDK](/langsmith/langgraph-python-sdk) ```python {highlight={2,34}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langgraph_sdk import get_client from langgraph_sdk.schema import Command client = get_client(url=) # Using the graph deployed with the name "agent" assistant_id = "agent" # create a thread thread = await client.threads.create() thread_id = thread["thread_id"] # Run the graph until the interrupt is hit. result = await client.runs.wait( thread_id, assistant_id, input={"some_text": "original text"} # (1)! ) print(result['__interrupt__']) # (2)! # > [ # > { # > 'value': {'text_to_revise': 'original text'}, # > 'resumable': True, # > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'], # > 'when': 'during' # > } # > ] # Resume the graph print(await client.runs.wait( thread_id, assistant_id, command=Command(resume="Edited text") # (3)! )) # > {'some_text': 'Edited text'} ``` 1. The graph is invoked with some initial state. 2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata. 3\. The graph is resumed with a `Command(resume=...)`, injecting the human's input and continuing execution. ```javascript {highlight={32}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { Client } from "@langchain/langgraph-sdk"; const client = new Client({ apiUrl: }); // Using the graph deployed with the name "agent" const assistantID = "agent"; // create a thread const thread = await client.threads.create(); const threadID = thread["thread_id"]; // Run the graph until the interrupt is hit. const result = await client.runs.wait( threadID, assistantID, { input: { "some_text": "original text" } } # (1)! ); console.log(result['__interrupt__']); # (2)! # > [ # > { # > 'value': {'text_to_revise': 'original text'}, # > 'resumable': True, # > 'ns': ['human_node:fc722478-2f21-0578-c572-d9fc4dd07c3b'], # > 'when': 'during' # > } # > ] // Resume the graph console.log(await client.runs.wait( threadID, assistantID, { command: { resume: "Edited text" }} # (3)! )); # > {'some_text': 'Edited text'} ``` 1. The graph is invoked with some initial state. 2. When the graph hits the interrupt, it returns an interrupt object with the payload and metadata. 3. The graph is resumed with a `{ resume: ... }` command object, injecting the human's input and continuing execution. Create a thread: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads \ --header 'Content-Type: application/json' \ --data '{}' ``` Run the graph until the interrupt is hit: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads//runs/wait \ --header 'Content-Type: application/json' \ --data "{ \"assistant_id\": \"agent\", \"input\": {\"some_text\": \"original text\"} }" ``` Resume the graph: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads//runs/wait \ --header 'Content-Type: application/json' \ --data "{ \"assistant_id\": \"agent\", \"command\": { \"resume\": \"Edited text\" } }" ``` ## Static interrupts Static interrupts (also known as static breakpoints) are triggered either before or after a node executes. Static interrupts are **not** recommended for human-in-the-loop workflows. They are best used for debugging and testing. You can set static interrupts by specifying `interrupt_before` and `interrupt_after` at compile time: ```python {highlight={1,2,3}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} graph = graph_builder.compile( # (1)! interrupt_before=["node_a"], # (2)! interrupt_after=["node_b", "node_c"], # (3)! ) ``` 1. The breakpoints are set during `compile` time. 2. `interrupt_before` specifies the nodes where execution should pause before the node is executed. 3. `interrupt_after` specifies the nodes where execution should pause after the node is executed. Alternatively, you can set static interrupts at run time: ```python {highlight={1,5,6}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} await client.runs.wait( # (1)! thread_id, assistant_id, inputs=inputs, interrupt_before=["node_a"], # (2)! interrupt_after=["node_b", "node_c"] # (3)! ) ``` 1. `client.runs.wait` is called with the `interrupt_before` and `interrupt_after` parameters. This is a run-time configuration and can be changed for every invocation. 2. `interrupt_before` specifies the nodes where execution should pause before the node is executed. 3. `interrupt_after` specifies the nodes where execution should pause after the node is executed. ```javascript {highlight={1,6,7}} theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} await client.runs.wait( // (1)! threadID, assistantID, { input: input, interruptBefore: ["node_a"], // (2)! interruptAfter: ["node_b", "node_c"] // (3)! } ) ``` 1. `client.runs.wait` is called with the `interruptBefore` and `interruptAfter` parameters. This is a run-time configuration and can be changed for every invocation. 2. `interruptBefore` specifies the nodes where execution should pause before the node is executed. 3. `interruptAfter` specifies the nodes where execution should pause after the node is executed. ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads//runs/wait \ --header 'Content-Type: application/json' \ --data "{ \"assistant_id\": \"agent\", \"interrupt_before\": [\"node_a\"], \"interrupt_after\": [\"node_b\", \"node_c\"], \"input\": }" ``` The following example shows how to add static interrupts: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langgraph_sdk import get_client client = get_client(url=) # Using the graph deployed with the name "agent" assistant_id = "agent" # create a thread thread = await client.threads.create() thread_id = thread["thread_id"] # Run the graph until the breakpoint result = await client.runs.wait( thread_id, assistant_id, input=inputs # (1)! ) # Resume the graph await client.runs.wait( thread_id, assistant_id, input=None # (2)! ) ``` 1. The graph is run until the first breakpoint is hit. 2. The graph is resumed by passing in `None` for the input. This will run the graph until the next breakpoint is hit. ```js theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { Client } from "@langchain/langgraph-sdk"; const client = new Client({ apiUrl: }); // Using the graph deployed with the name "agent" const assistantID = "agent"; // create a thread const thread = await client.threads.create(); const threadID = thread["thread_id"]; // Run the graph until the breakpoint const result = await client.runs.wait( threadID, assistantID, { input: input } # (1)! ); // Resume the graph await client.runs.wait( threadID, assistantID, { input: null } # (2)! ); ``` 1. The graph is run until the first breakpoint is hit. 2. The graph is resumed by passing in `null` for the input. This will run the graph until the next breakpoint is hit. Create a thread: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads \ --header 'Content-Type: application/json' \ --data '{}' ``` Run the graph until the breakpoint: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads//runs/wait \ --header 'Content-Type: application/json' \ --data "{ \"assistant_id\": \"agent\", \"input\": }" ``` Resume the graph: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url /threads//runs/wait \ --header 'Content-Type: application/json' \ --data "{ \"assistant_id\": \"agent\" }" ``` ## Learn more * [Human-in-the-loop conceptual guide](/oss/python/langgraph/interrupts): learn more about LangGraph human-in-the-loop features. * [Common patterns](/oss/python/langgraph/interrupts#common-patterns): learn how to implement patterns like approving/rejecting actions, requesting user input, tool call review, and validating human input. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/add-human-in-the-loop.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Add metadata and tags to traces Source: https://docs.langchain.com/langsmith/add-metadata-tags LangSmith supports sending arbitrary metadata and tags along with traces. Tags are strings that can be used to categorize or label a trace. Metadata is a dictionary of key-value pairs that can be used to store additional information about a trace. Both are useful for associating additional information with a trace, such as the environment in which it was executed, the user who initiated it, or an internal correlation ID. For more information on tags and metadata, see the [Concepts](/langsmith/observability-concepts#tags) page. For information on how to query traces and runs by metadata and tags, see the [Filter traces in the application](/langsmith/filter-traces-in-application) page. ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import openai import langsmith as ls from langsmith.wrappers import wrap_openai client = openai.Client() messages = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"} ] # You can set metadata & tags **statically** when decorating a function # Use the @traceable decorator with tags and metadata # Ensure that the LANGSMITH_TRACING environment variables are set for @traceable to work @ls.traceable( run_type="llm", name="OpenAI Call Decorator", tags=["my-tag"], metadata={"my-key": "my-value"} ) def call_openai( messages: list[dict], model: str = "gpt-5.4-mini" ) -> str: # You can also dynamically set metadata on the parent run: rt = ls.get_current_run_tree() rt.metadata["some-conditional-key"] = "some-val" rt.tags.extend(["another-tag"]) return client.chat.completions.create( model=model, messages=messages, ).choices[0].message.content call_openai( messages, # To add at **invocation time**, when calling the function. # via the langsmith_extra parameter langsmith_extra={"tags": ["my-other-tag"], "metadata": {"my-other-key": "my-value"}} ) # or you can dynamically set default metadata for runs in the given scope # tracing_context doesn't create a span itself, but it does initialize the # context for child spans that are created. with ls.tracing_context(metadata={"default-key": "default-value"}): call_openai(messages) # Alternatively, you can use the trace context manager # This creates a new span with the given metadata and tags with ls.trace( name="OpenAI Call Trace", run_type="llm", inputs={"messages": messages}, tags=["my-tag"], metadata={"my-key": "my-value"}, ) as rt: chat_completion = client.chat.completions.create( model="gpt-5.4-mini", messages=messages, ) rt.metadata["some-conditional-key"] = "some-val" rt.end(outputs={"output": chat_completion}) # You can use the same techniques with the wrapped client patched_client = wrap_openai( client, tracing_extra={"metadata": {"my-key": "my-value"}, "tags": ["a-tag"]} ) chat_completion = patched_client.chat.completions.create( model="gpt-5.4-mini", messages=messages, langsmith_extra={ "tags": ["my-other-tag"], "metadata": {"my-other-key": "my-value"}, }, ) ``` ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import OpenAI from "openai"; import { traceable, getCurrentRunTree } from "langsmith/traceable"; import { wrapOpenAI } from "langsmith/wrappers"; const client = wrapOpenAI(new OpenAI()); const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Hello!" }, ]; const traceableCallOpenAI = traceable( async (messages: OpenAI.Chat.ChatCompletionMessageParam[]) => { const completion = await client.chat.completions.create({ model: "gpt-5.4-mini", messages, }); const runTree = getCurrentRunTree(); runTree.extra.metadata = { ...runTree.extra.metadata, someKey: "someValue", }; runTree.tags = [...(runTree.tags ?? []), "runtime-tag"]; return completion.choices[0].message.content; }, { run_type: "llm", name: "OpenAI Call Traceable", tags: ["my-tag"], metadata: { "my-key": "my-value" }, } ); // Call the traceable function await traceableCallOpenAI(messages); ``` **LangSmith Deployments**: To add metadata dynamically per invocation in Agent Server deployments, we recommend using `tracing_context` in a [factory function](/langsmith/graph-rebuild). See [Customize tracing in deployed agents](/langsmith/conditional-tracing#customize-tracing-in-deployed-agents) for examples. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/add-metadata-tags.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Account Source: https://docs.langchain.com/langsmith/admin Set up your LangSmith account, including API keys, profile configuration, integrations, and pricing tiers. Set up your LangSmith account: create API keys, configure your profile, connect integrations, and choose the right pricing tier. ## Get started Sign up at [smith.langchain.com](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=snippets-langsmith-account-api-key-quickstart) (no credit card required). You can log in with **Google**, **GitHub**, or **email**. Go to your [Settings page](https://smith.langchain.com/settings) → **API Keys** → **Create API Key**. Copy the key and save it securely. ## Explore Configure your personal account settings and preferences. Connect LangSmith to your existing tools and services. Compare plans and choose the right tier for your team. Enterprise capabilities, support, and onboarding. ## Related * [Govern](/langsmith/govern-overview): manage organizations, users, access control, and compliance policies for your LangSmith deployment. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/admin.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Overview Source: https://docs.langchain.com/langsmith/administration-overview This overview covers topics related to managing users, organizations, workspaces, and applications within LangSmith. ## Resource hierarchy ### Organizations An organization is a logical grouping of users within LangSmith that defines shared settings applying across all of its workspaces. These settings govern organization-wide concerns rather than individual projects within a workspace. Common organization-level configurations include user management, single sign-on (SSO), OAuth provider configuration, custom role creation, billing, and usage tracking. Typically, there is one organization per company. An organization can have multiple workspaces. For more details, see the [setup guide](/langsmith/set-up-hierarchy#set-up-an-organization). When you log in for the first time, a personal organization will be created for you automatically. If you'd like to collaborate with others, you can create a separate organization and invite your team members to join. There are a few important differences between your personal organization and shared organizations: | Feature | Personal | Shared | | ------------------- | ------------------- | ------------------------------------------------------------------------------------------------- | | Maximum workspaces | 1 | Variable, depending on plan (see the [pricing page](https://www.langchain.com/pricing-langsmith)) | | Collaboration | Cannot invite users | Can invite users | | Billing: paid plans | Developer plan only | All other plans available | ### Workspaces Workspaces were formerly called Tenants. Some code and APIs may still reference the old name for a period of time during the transition. A workspace is a logical grouping of users and resources within an organization. Workspaces are commonly used to isolate teams or business units, providing separation between projects and their associated resources. A workspace separates trust boundaries for resources and access control. Users are granted permissions at the workspace level, which determine their access to resources in that workspace, including tracing projects, datasets, annotation queues, and prompts. For details on setup, see the [setup guide](/langsmith/set-up-hierarchy#set-up-a-workspace) and for details on permissions see [Workspaces (RBAC)](/langsmith/administration-overview#workspace-roles-rbac). We recommend creating a separate workspace for each team within your organization. To organize resources even further, you can use [Applications](#applications) to group resources within a workspace. For guidance on different workspace organization models based on your team's isolation requirements, refer to [Workload isolation](/langsmith/workload-isolation). ### Applications An application is a logical grouping of resources within a workspace. Applications are often agents, but you can use them for any project within a team. Applications keep the UI organized by only surfacing the resources associated with the application currently in context. Applications are built on top of [resource tags](/langsmith/administration-overview#resource-tags) and can be used to control resource access using [ABAC](/langsmith/organization-workspace-operations#access-policies). Switch applications from the main navigation sidebar in the [LangSmith UI](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=langsmith-administration-overview). Use the **Application** dropdown at the top of the sidebar to select an application. Any resource can be created without being tagged to an application. These resources will be visible when the **All applications** option is selected. ### Resources Resources are the concrete entities used to build, run, and observe applications and agents, such as tracing projects, prompts, datasets, and deployments. Resources are scoped to a specific application. ### Additional info The following diagram explains the relationship between organizations, workspaces, applications, and resources: Resource Hierarchy See the table below for details on which features are available in which scope(s): | Resource/Setting | Scope | | --------------------------------------------------------------------------- | ------------------------ | | Trace Projects | Workspace or Application | | Annotation Queues | Workspace or Application | | Deployments | Workspace or Application | | Datasets & Experiments | Workspace or Application | | Prompts | Workspace or Application | | Resource Tags | Workspace | | API Keys | Workspace | | Settings including Secrets, Feedback config, Models, Rules, and Shared URLs | Workspace | | User management: Invite User to Workspace | Workspace | | RBAC: Assigning Workspace Roles | Workspace | | Data Retention, Usage Limits | Workspace\* | | Plans and Billing, Credits, Invoices | Organization | | User management: Invite User to Organization | Organization\*\* | | Adding Workspaces | Organization | | Assigning Organization Roles | Organization | | RBAC: Creating/Editing/Deleting Custom Roles | Organization | \* Data retention settings and usage limits will be available soon for the organization level as well \*\* Self-hosted installations may enable workspace-level invites of users to the organization via a feature flag. For details, refer to the [self-hosted user management docs](/langsmith/self-host-user-management). ### Resource tags Resource tags allow you to further segregate resources within a workspace for use with [ABAC](/langsmith/organization-workspace-operations#access-policies). Each tag is a key-value pair that you can assign to a resource. LangSmith resource tags are very similar to tags in cloud services like [AWS](https://docs.aws.amazon.com/tag-editor/latest/userguide/tagging.html). Navigate to **Settings** in the [LangSmith UI](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=langsmith-administration-overview) to select the **Resource tags** page in the sidebar. ## User management and RBAC ### Users A user is a person who has access to LangSmith. Users can be members of one or more organizations and workspaces within those organizations. Organization members are managed on the **Settings** page under **Members and roles**. And workspace members are managed on the **Workspaces** page under **Settings**. ### API keys We ended support for legacy API keys prefixed with `ls__` on October 22, 2024 in favor of personal access tokens (PATs) and service keys. We require using PATs and service keys for all new integrations. API keys prefixed with `ls__` will no longer work as of October 22, 2024. #### Expiration dates When you create an API key, you have the option to set an expiration date. Adding an expiration date to keys enhances security and minimizes the risk of unauthorized access. For example, you may set expiration dates on keys for temporary tasks that require elevated access. By default, keys never expire. Once expired, an API key is no longer valid and cannot be reactivated or have its expiration modified. #### Personal access tokens (PATs) Personal Access Tokens (PATs) are used to authenticate requests to the LangSmith API. They are created by users and scoped to a user. The PAT will have the same permissions as the user that created it. We recommend not using these to authenticate requests from your application, but rather using them for personal scripts or tools that interact with the LangSmith API. If the user associated with the PAT is removed from the organization, the PAT will no longer work. PATs are prefixed with `lsv2_pt_` #### Service keys Service keys are similar to PATs, but are used to authenticate requests to the LangSmith API on behalf of a service account. Only admins can create service keys. We recommend using these for applications / services that need to interact with the LangSmith API, such as LangGraph agents or other integrations. Service keys may be scoped to a single workspace, multiple workspaces, or the entire organization, and can be used to authenticate requests to the LangSmith API for whichever workspace(s) it has access to. Service keys are prefixed with `lsv2_sk_` Use the `X-Tenant-Id` header to specify the target workspace. * **When using PATs**: If this header is omitted, requests will run against the default workspace associated with the key. * **When using organization-scoped service keys**: You must include the `X-Tenant-Id` header when accessing workspace-scoped resources. Without it, the request will fail with a `403 Forbidden` error. To see how to create a service key or Personal Access Token, see the [setup guide](/langsmith/create-account-api-key) ### Organization roles Organization roles are distinct from the [Enterprise feature workspace RBAC](#workspace-roles-rbac) and are used in the context of multiple [workspaces](#workspaces). Your organization role determines your workspace membership characteristics and your [organization-level permissions](/langsmith/organization-workspace-operations). The organization role selected also impacts workspace membership as described here: * [Organization Admin](/langsmith/rbac#organization-admin) grants full access to manage all organization configuration, users, billing, and workspaces. * An Organization Admin has `Admin` access to all workspaces in an organization. * [Organization User](/langsmith/rbac#organization-user) may read organization information but cannot execute any write actions at the organization level. An Organization User may create [Personal Access Tokens](#personal-access-tokens-pats). * An Organization User can be added to a subset of workspaces and assigned workspace roles as usual (if RBAC is enabled), which specify permissions at the workspace level. * [Organization Viewer](/langsmith/rbac#organization-viewer) is equivalent to Organization User, but **cannot** create Personal Access Tokens. (for self-hosted, available in Helm chart version 0.11.25+). The Organization User and Organization Viewer roles are only available in organizations on [Plus and Enterprise plans](https://langchain.com/pricing). In Developer organizations (single workspace), all users are assigned the Organization Admin role by default. See [security settings](/langsmith/manage-organization-by-api#security-settings) for instructions on how to disable PAT creation for the entire organization. For more information on setting up organizations and workspaces, refer to the [organization setup guide](/langsmith/set-up-hierarchy#organization-roles) for more information. The following table provides an overview of organization level permissions: | | Organization Viewer | Organization User | Organization Admin | | ------------------------------------------- | ------------------- | ----------------- | ------------------ | | View organization configuration | ✅ | ✅ | ✅ | | View organization roles | ✅ | ✅ | ✅ | | View organization members | ✅ | ✅ | ✅ | | View data retention settings | ✅ | ✅ | ✅ | | View usage limits | ✅ | ✅ | ✅ | | Create personal access tokens (PATs) | ❌ | ✅ | ✅ | | Admin access to all workspaces | ❌ | ❌ | ✅ | | Manage billing settings | ❌ | ❌ | ✅ | | Create workspaces | ❌ | ❌ | ✅ | | Create, edit, and delete organization roles | ❌ | ❌ | ✅ | | Invite new users to organization | ❌ | ❌ | ✅ | | Delete user invites | ❌ | ❌ | ✅ | | Remove users from an organization | ❌ | ❌ | ✅ | | Update data retention settings | ❌ | ❌ | ✅ | | Update usage limits | ❌ | ❌ | ✅ | For a comprehensive list of required permissions along with the operations and roles that can perform them, refer to the [Organization and workspace reference](/langsmith/organization-workspace-operations). ### Workspace roles (RBAC) RBAC (Role-Based Access Control) is a feature that is only available to Enterprise customers. If you are interested in this feature, [contact our sales team](https://www.langchain.com/contact-sales). Other plans default to using the Admin role for all users. Roles are used to define the set of permissions that a user has within a workspace. There are three built-in system roles that cannot be edited: * [Workspace Admin](/langsmith/rbac#workspace-admin) has full access to all resources within the workspace. * [Workspace Editor](/langsmith/rbac#workspace-editor) has full permissions except for workspace management (adding/removing users, changing roles, configuring service keys). * [Workspace Viewer](/langsmith/rbac#workspace-viewer) has read-only access to all resources within the workspace. [Organization admins](/langsmith/rbac#organization-admin) can also create/edit custom roles with specific permissions for different resources. You can manage roles under **Organization Settings** > **Members and roles** and select the **Roles** tab. * For comprehensive documentation on roles and permissions, refer to the [Role-based access control](/langsmith/rbac) guide. * For more details on assigning and creating roles, refer to the [User Management](/langsmith/user-management) guide. * For a comprehensive list of required permissions along with the operations and roles that can perform them, refer to the [Organization and workspace reference](/langsmith/organization-workspace-operations). ## Best practices ### Environment separation Use [resource tags](#resource-tags) to organize resources by environment using the default tag key `Environment` and different values for the environment (e.g., `dev`, `staging`, `prod`). We do not recommend using separate workspaces for environment separation because resources cannot be shared across workspaces, which would prevent you from promoting resources (like prompts) between environments. **Resource tags vs. commit tags for prompt management** While both types of tags can use environment terminology like `dev`, `staging`, and `prod`, they serve different purposes: * **Resource tags** (`Environment: prod`): Use these to *organize and filter* resources across your workspace. Apply resource tags to tracing projects, datasets, and other resources (including prompts) to group them by environment, which enables filtering in the UI. * [Commit tags](/langsmith/manage-prompts#commit-tags) (`prod` tag): Use these to manage which [prompt version](/langsmith/prompt-context-hub#prompts) your code references. Commit tags are labels that point to specific commits in a prompt's history. When your code pulls a prompt by tag name (e.g., `client.pull_prompt("prompt-name:prod")`), it retrieves whichever commit that tag currently points to. To promote a prompt from `staging` to `prod`, move the commit tag to point to the desired version. Resource tags organize **which resources** belong to an environment. Commit tags let you control **which version** of a prompt your code references without changing the code itself. ## Usage and billing ### Data retention This section covers how data retention works and how it's priced in LangSmith. #### Why retention matters * **Privacy**: Many data privacy regulations, such as GDPR in Europe or CCPA in California, require organizations to delete personal data once it's no longer necessary for the purposes for which it was collected. Setting retention periods aids in compliance with such regulations. * **Cost**: LangSmith charges less for traces that have low data retention. For more information, learn how to [enforce spend limits](/langsmith/billing#enforce-spend-limits). Plan your retention tiers before you start sending traces. Changes apply to new traces only—existing traces keep their original tier. See [Change project-level default retention](/langsmith/billing#change-project-level-default-retention). #### How it works LangSmith has two tiers of traces based on Data Retention with the following characteristics: | | Base | Extended | | -------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | | **Price** | [See pricing page](https://www.langchain.com/pricing-langsmith) | [See pricing page](https://www.langchain.com/pricing-langsmith) | | **Retention Period** | 14 days | 400 days | Enterprise customers can customize the extended retention period per workspace. Changes apply to new traces only—existing traces are unaffected. See [Customize extended retention policy](/langsmith/data-purging-compliance#customize-extended-retention-policy). **Data deletion after retention ends** After the specified retention period, traces are no longer accessible in the tracing project UI or via the API. All user data associated with the trace (e.g. inputs and outputs) is deleted from our internal systems within a day thereafter. Some metadata associated with each trace may be retained indefinitely for analytics and billing purposes. #### Data retention auto-upgrades Auto upgrades can have an impact on your bill. Please read this section carefully to fully understand your estimated LangSmith tracing costs. Most traces use base retention. Some actions, such as online evaluators and automation rules, can extend a trace to a longer retention period at a higher cost. You control which actions extend retention. When you use certain features with `base` tier traces, their data retention may be automatically upgraded to `extended` tier. This increases both the retention period and the cost of the trace. Retention behavior by action: * **Feedback via API or SDK**: Feedback is added to any run on the trace (or any trace in the thread) through an API or SDK call that explicitly passes `extend_trace_retention=true` (`extendTraceRetention: true` in TypeScript). For more information, see [Attach user feedback](/langsmith/attach-user-feedback). The LangSmith UI sends feedback and notes without extending retention. * **Online evaluators**: An online evaluator scores the trace and its retention setting is enabled. Both trace-level and thread-level evaluators can opt out of this upgrade. * **Automation rules**: An [automation rule](/langsmith/rules#create-a-rule) with retention extension enabled matches any run within a trace. * **Manual annotation queue adds** (no upgrade): Manually adding runs or threads to an [annotation queue](/langsmith/annotation-queues#assign-runs-and-threads-to-a-single-run-queue) does not upgrade retention by default. This change applies to new actions only. Traces that were already upgraded by a previous action keep their extended retention. When you create or edit an online evaluator on a tracing project, you can opt out of upgrading the traces that evaluator scores, keeping them at base retention. This option is available only when the project's default retention is the base tier. For step-by-step instructions, see [Manage evaluator trace retention](/langsmith/evaluators#manage-evaluator-trace-retention). Retention extension is enabled by default for new online evaluators and automation rules. You can opt out when configuring each evaluator or rule. **Why auto-upgrade traces?** We have two reasons behind the auto-upgrade model for tracing: 1. We think that traces that match any of these conditions are fundamentally more interesting than other traces, and therefore it is good for users to be able to keep them around longer. 2. We philosophically want to charge customers an order of magnitude lower for traces that may not be interacted with meaningfully. We think auto-upgrades align our pricing model with the value that LangSmith brings, where only traces with meaningful interaction are charged at a higher rate. If you have questions or concerns about our pricing model, please feel free to contact support via [support.langchain.com](https://support.langchain.com) and let us know your thoughts! **How does data retention affect downstream features?** The following features interact with retention differently: * **Experiments**: Runs are created at extended retention by default. * **Automation rules and evaluators**: Upgrade matching traces to extended retention when their retention setting is enabled. * **UI feedback, notes, and annotation queues**: Leave a trace's retention tier unchanged. Other features behave independently of a trace's retention tier: * **Monitoring**: The monitoring tab will continue to work even after a base tier trace's data retention period ends. It is powered by trace metadata that exists for >30 days, meaning that your monitoring graphs will continue to stay accurate even on `base` tier traces. * **Datasets**: Datasets have an indefinite data retention period. Restated differently, if you add a trace's inputs and outputs to a dataset, they will never be deleted. We suggest that if you are using LangSmith for data collection, you take advantage of the datasets feature. #### Billing model **Billable metrics** On your LangSmith invoice, you will see two metrics that we charge for: * LangSmith Traces (Base Charge) * LangSmith Traces (Extended Data Retention Upgrades). The first metric includes all traces, regardless of tier. The second metric just counts the number of extended retention traces. **Why measure all traces + upgrades instead of base and extended traces?** A natural question to ask when considering our pricing is why not just show the number of `base` tier and `extended` tier traces directly on the invoice? While we understand this would be more straightforward, it doesn't fit trace upgrades properly. Consider a `base` tier trace that was recorded on June 30, and upgraded to `extended` tier on July 3. The `base` tier trace occurred in the June billing period, but the upgrade occurred in the July billing period. Therefore, we need to be able to measure these two events independently to properly bill our customers. If your trace was recorded as an extended retention trace, then the `base` and `extended` metrics will both be recorded with the same timestamp. ### Rate limits LangSmith has rate limits which are designed to ensure the stability of the service for all users. To ensure access and stability, LangSmith will respond with HTTP Status Code 429 indicating that rate or usage limits have been exceeded under the following circumstances: #### Temporary throughput limit over a 1 minute period at our application load balancer This 429 is the result of exceeding a fixed number of API calls over a 1 minute window on a per service key or PAT basis. The start of the window will vary slightly—it is not guaranteed to start at the start of a clock minute—and may change depending on application deployment events. After the max events are received we will respond with a 429 until 60 seconds from the start of the evaluation window has been reached and then the process repeats. This 429 is thrown by our application load balancer and is a mechanism in place for all LangSmith users independent of plan tier to ensure continuity of service for all users. | Method | Endpoints | Limit | Window | | ----------------- | ------------- | ----- | -------- | | `DELETE` | `/sessions*` | 30 | 1 minute | | `POST` OR `PATCH` | `/runs*` | 5000 | 1 minute | | `GET` | `/runs/:id` | 30 | 1 minute | | `POST` | `/feedbacks*` | 5000 | 1 minute | | `*` | `*` | 2000 | 1 minute | The LangSmith SDK takes steps to minimize the likelihood of reaching these limits on run-related endpoints by batching up to 100 runs from a single session ID into a single API call. #### Plan-level hourly trace event limit This 429 is the result of reaching your maximum hourly events ingested and is evaluated in a fixed window starting at the beginning of each clock hour in UTC and resets at the top of each new hour. An event in this context is the creation or update of a run. If a run is created and then subsequently updated in the same hourly window, that counts as 2 events against this limit. This is thrown by our application and varies by plan tier, with organizations on our Startup/Plus and Enterprise plan tiers having higher hourly limits than our Free and Developer Plan Tiers which are designed for personal use. | Plan | Limit | Window | | -------------------------------- | -------------- | ------ | | Developer (no payment on file) | 50,000 events | 1 hour | | Developer (with payment on file) | 250,000 events | 1 hour | | Startup/Plus | 500,000 events | 1 hour | | Enterprise | Custom | Custom | #### Plan-level hourly trace data ingest limit This 429 is the result of reaching the maximum amount of data ingested across your trace inputs, outputs, and metadata and is evaluated in a fixed window starting at the beginning of each clock hour in UTC and resets at the top of each new hour. Typically, inputs, outputs, and metadata are sent on both run creation and update events. If a run is created at 2.0MB and updated to 3.0MB in the same hourly window, that counts as 5.0MB of storage against this limit. This is thrown by our application and varies by plan tier, with organizations on our Startup/Plus and Enterprise plan tiers having higher hourly limits than our Free and Developer Plan Tiers which are designed for personal use. | Plan | Limit | Window | | -------------------------------- | ------ | ------ | | Developer (no payment on file) | 500MB | 1 hour | | Developer (with payment on file) | 2.5GB | 1 hour | | Startup/Plus | 5.0GB | 1 hour | | Enterprise | Custom | Custom | #### Plan-level monthly unique traces limit This 429 is the result of reaching your maximum monthly traces ingested and is evaluated in a fixed window starting at the beginning of each calendar month in UTC and resets at the beginning of each new month. This is thrown by our application and applies only to the Developer Plan Tier when there is no payment method on file. | Plan | Limit | Window | | ------------------------------ | ------------ | ------- | | Developer (no payment on file) | 5,000 traces | 1 month | #### Self-configured monthly usage limits This 429 is the result of reaching your usage limit as configured by your organization admin and is evaluated in a fixed window starting at the beginning of each calendar month in UTC and resets at the beginning of each new month. This is thrown by our application and varies by organization based on their configured settings. #### Maximum runs per trace #### Run query endpoint The [`POST /runs/query`](/langsmith/smith-api/run/query-runs) endpoint has additional per-tenant rate limits based on query parameters. See [Query traces using the SDK](/langsmith/export-traces#rate-limits) for details. #### Handling 429s responses in your application Since some 429 responses are temporary and may succeed on a successive call, if you are directly calling the LangSmith API in your application we recommend implementing retry logic with exponential backoff and jitter. For convenience, LangChain applications built with the LangSmith SDK has this capability built-in. It is important to note that if you are saturating the endpoints for extended periods of time, retries may not be effective as your application will eventually run large enough backlogs to exhaust all retries. If that is the case, we would like to discuss your needs more specifically. Please contact support via [LangSmith Support](https://support.langchain.com) with details about your applications throughput needs and sample code and we can work with you to better understand whether the best approach is fixing a bug, changes to your application code, or a different LangSmith plan. ### Usage limits LangSmith lets you configure usage limits on tracing. Note that these are *usage* limits, not *spend* limits, which mean they let you limit the quantity of occurrences of some event rather than the total amount you will spend. LangSmith lets you set two different monthly limits, mirroring our Billable Metrics discussed in the aforementioned data retention guide: * All traces limit * Extended data retention traces limit These let you limit the number of total traces, and extended data retention traces respectively. For *spend* limits on evaluator runs specifically, refer to [Track and limit evaluator spend](/langsmith/evaluator-spend). #### Properties of usage limiting Usage limiting is approximate, meaning that we do not guarantee the exactness of the limit. In rare cases, there may be a small period of time where additional traces are processed above the limit threshold before usage limiting begins to apply. #### Side effects of extended data retention traces limit The extended data retention traces limit has side effects. If the limit is already reached, any feature that could cause an auto-upgrade of tracing tiers becomes inaccessible. This is because an auto-upgrade of a trace would cause another extended retention trace to be created, which in turn should not be allowed by the limit. Therefore, you can no longer: 1. match run rules 2. add feedback to traces 3. add runs to annotation queues Each of these features may cause an auto upgrade, so we shut them off when the limit is reached. #### Updating usage limits Usage limits can be updated from the `Settings` page under `Usage and Billing`. Limit values are cached, so it may take a minute or two before the new limits apply. #### Per-project and per-user trace limits In addition to the [workspace-wide limits](#usage-limits), you can cap monthly traces for a single tracing project or an individual workspace member. This prevents one project or user from consuming a disproportionate share of a workspace's tracing budget. To configure these limits, open **Settings**, go to **Usage configuration**, and select the **Project & user limits** tab. Choose **Add limit**, then set: * **Scope**: **Project** to cap a single tracing project, or **User** to cap a single workspace member. * **Workspace**: the workspace that contains the project or member. * **Project** or **User**: the target to cap. * **Monthly trace limit**: the maximum number of traces allowed per calendar month. Updating these limits requires the same permission as workspace usage limits (`Update usage limits`). Like workspace limits, per-project and per-user limits are evaluated per calendar month in UTC and reset at the start of each new month. Once a project or user reaches its limit, its new traces are dropped and are not ingested again until the limit resets. Enforcement is approximate, so a small number of traces may be processed above the threshold before the limit takes effect. These limits apply to both [Cloud](/langsmith/cloud) and [Self-hosted](/langsmith/self-hosted). Per-project and per-user limits are **additional** to your workspace-wide and plan limits. A trace must be within every applicable limit to be ingested. Per-user limits count only traces attributed to a specific workspace member. Traces sent with an API key or service key that isn't tied to a member are not counted toward a per-user limit. Limit values are cached, so it may take a minute or two before a new or changed limit applies. ### Related content * Tutorial on how to [enforce spend limits](/langsmith/billing#enforce-spend-limits) ## Additional resources * **[Release policy](/langsmith/release-versions)**: Learn about the self-hosted release channels, cadence, and version numbering. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/administration-overview.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Set up Agent Auth Source: https://docs.langchain.com/langsmith/agent-auth Enable secure access from agents to any system using OAuth 2.0 credentials with Agent Auth. Agent Auth is in **[beta](/langsmith/release-stages)** and under active development. To provide feedback or use this feature, reach out to the [LangChain team](https://forum.langchain.com/c/help/langsmith/). ## Installation ```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} pip install langchain-auth ``` ```bash uv theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} uv add langchain-auth ``` ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} npm install @langchain/auth ``` ## Quickstart ### 1. Initialize the client ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langchain_auth import Client client = Client(api_key="your-langsmith-api-key") ``` ```javascript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { Client } from '@langchain/auth'; const client = new Client({ apiKey: 'your-langsmith-api-key' }); ``` #### Self-hosted configuration For self-hosted LangSmith instances, specify the API URL using the `/api-host` path on your instance. ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} export LANGSMITH_API_URL="https://your-langsmith-instance.com/api-host" ``` Then initialize the client normally: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} client = Client(api_key="your-langsmith-api-key") ``` ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} client = Client( api_key="your-langsmith-api-key", api_url="https://your-langsmith-instance.com/api-host" ) ``` ```javascript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} const client = new Client({ apiKey: 'your-langsmith-api-key', apiUrl: 'https://your-langsmith-instance.com/api-host' }); ``` ### 2. Set up OAuth providers Before agents can authenticate, you need to configure an OAuth provider using the following process: 1. Select a unique identifier for your OAuth provider to use in LangChain's platform (e.g., "github-local-dev", "google-workspace-prod"). 2. Go to your OAuth provider's developer console and create a new OAuth application. 3. Set the callback URL in your OAuth provider: ``` https://smith.langchain.com/host-oauth-callback/{provider_id} ``` For example, if your provider\_id is "github-local-dev", use: ``` https://smith.langchain.com/host-oauth-callback/github-local-dev ``` ``` https://{your-langsmith-instance}/host-oauth-callback/{provider_id} ``` For example, if your instance is `langsmith.example.com` and provider\_id is "github", use: ``` https://langsmith.example.com/host-oauth-callback/github ``` 4. Use `client.create_oauth_provider()` with the credentials from your OAuth app: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} new_provider = await client.create_oauth_provider( provider_id="{provider_id}", # Provide any unique ID name="{provider_display_name}", # Provide any display name client_id="{your_client_id}", client_secret="{your_client_secret}", auth_url="{auth_url_of_your_provider}", token_url="{token_url_of_your_provider}", ) ``` ```javascript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} const newProvider = await client.createOAuthProvider({ providerId: '{provider_id}', // Provide any unique ID name: '{provider_display_name}', // Provide any display name clientId: '{your_client_id}', clientSecret: '{your_client_secret}', authUrl: '{auth_url_of_your_provider}', tokenUrl: '{token_url_of_your_provider}', }); ``` ### 3. Authenticate from an agent The client `authenticate()` API is used to get OAuth tokens from pre-configured providers. On the first call, it takes the caller through an OAuth 2.0 auth flow. #### In LangGraph context By default, tokens are scoped to the calling agent using the Assistant ID parameter. ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} auth_result = await client.authenticate( provider="{provider_id}", scopes=["scopeA"], user_id="your_user_id" # Any unique identifier to scope this token to the human caller ) # Or explicitly specify an agent_id for agent-scoped tokens auth_result = await client.authenticate( provider="{provider_id}", scopes=["scopeA"], user_id="your_user_id", agent_id="specific-agent-id" # Optional: explicitly set agent scope ) ``` During execution, if authentication is required, the SDK will throw an [interrupt](/langsmith/add-human-in-the-loop). The agent execution pauses and presents the OAuth URL to the user: After the user completes OAuth authentication and we receive the callback from the provider, they will see the auth success page. The agent then resumes execution from the point it left off at, and the token can be used for any API calls. We store and refresh OAuth tokens so that future uses of the service by either the user or agent do not require an OAuth flow. ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} token = auth_result.token ``` #### Outside LangGraph context Provide the `auth_url` to the user for out-of-band OAuth flows. ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} auth_result = await client.authenticate( provider="{provider_id}", scopes=["scopeA"], user_id="your_user_id" ) if auth_result.status == "pending": print(f"Complete OAuth at: {auth_result.url}") # Wait for user to complete OAuth completed_auth = await client.wait_for_completion(auth_result.auth_id) print("Authentication completed!") else: token = auth_result.token print(f"Already authenticated, token: {token}") ``` ```javascript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} const authResult = await client.authenticate({ provider: '{provider_id}', scopes: ['scopeA'], userId: 'your_user_id' }); if (authResult.status === 'pending') { console.log(`Complete OAuth at: ${authResult.authUrl}`); // Wait for user to complete OAuth const completedAuth = await client.waitForCompletion(authResult.authId); console.log('Authentication completed!'); } else { const token = authResult.token; console.log(`Already authenticated, token: ${token}`); } ``` ## Troubleshooting ### Self-hosted: 405 Method Not Allowed If you receive a `405 Method Not Allowed` error, ensure `LANGSMITH_API_URL` points to the `/api-host` path: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} export LANGSMITH_API_URL="https://your-instance.com/api-host" ``` ### Self-hosted: Malformed OAuth callback URL Ensure your OAuth provider's redirect URI matches your LangSmith instance URL: ``` https://your-instance.com/host-oauth-callback/{provider_id} ``` ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-auth.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Agent Server Source: https://docs.langchain.com/langsmith/agent-server LangSmith Deployment's **Agent Server** offers an API for creating and managing agent-based applications. It is built on the concept of [assistants](/langsmith/assistants), which are agents configured for specific tasks, and includes built-in [persistence](/oss/python/langgraph/persistence#memory-store) and a [**task queue**](#task-queue). This versatile API supports a wide range of agentic application use cases, from background processing to real-time interactions. Use Agent Server to create and manage: **API reference**
For detailed information on the API endpoints and data models, refer to the [Agent Server API reference](/langsmith/server-api-ref).
## Application structure To deploy an Agent Server application, you need to specify the graph(s) you want to deploy, as well as any relevant configuration settings, such as dependencies and environment variables. Read the [application structure](/langsmith/application-structure) guide to learn how to structure your LangGraph application for deployment. [LangSmith cloud](/langsmith/cloud) manages the database for you. If you're deploying on your [own infrastructure](/langsmith/self-hosted), you'll need to set it up yourself. ## Parts of a deployment When you deploy Agent Server, you are deploying one or more [graphs](#graphs), a database for [persistence](/oss/python/langgraph/persistence), and a [task queue](#task-queue). ### Graphs When you deploy a graph with Agent Server, you are deploying a "blueprint" for an [Assistant](/langsmith/assistants). A graph most commonly implements an [agent](/oss/python/langgraph/workflows-agents), but it does not have to. For example, a graph could implement a simple chatbot that only supports back-and-forth conversation, without the ability to influence any application control flow. In reality, as applications get more complex, a graph will often implement a more complex flow that may use [multiple agents](/oss/python/langchain/multi-agent) working in tandem. Graphs don't have to be written with LangGraph. You can also deploy agents built with other frameworks—such as [Strands, Claude Agent SDK, and more](/langsmith/deploy-other-frameworks) or [Google ADK](/langsmith/deploy-google-adk)—using the LangGraph Functional API or the `deployments-wrap-sdk` package. #### Graph loading and compilation How and when your graph is compiled depends on how you register it in your [application structure](/langsmith/application-structure): 1. **Compiled graph** (recommended): Export an already-compiled `CompiledGraph` instance. The server loads it once at container startup and reuses it for every run—no compilation overhead per request. 2. **Factory function**: Export an agent factory function that the server invokes each time it needs the graph. Use this only when you need per-run graph customization (for example, choosing different models or tools based on the assistant config). Keep factory functions lightweight, since they run on every invocation. Use a compiled graph unless you specifically need per-run customization. Factory functions add overhead on every invocation; compiled graphs do not. In both cases, the server automatically injects the checkpointer and memory store configured for that deployment at runtime. **Do not configure these in your graph code** because the server needs to manage them for other operations. ### Persistence Agent Server persists three types of data, all backed by [PostgreSQL](https://www.postgresql.org/) by default: * **Core resource data**: assistants, threads, runs, and cron jobs. Always stored in PostgreSQL. * **Checkpoints (short-term memory)**: snapshots of graph execution state written at each step. They make runs durable: if a worker is interrupted, the run can resume from the last checkpoint rather than from the beginning. Durability mode controls checkpoint frequency—`async` (default) writes after each step; `exit` stores only the final state. LangSmith stores this in PostgreSQL by default; but you can switch to [MongoDB](https://www.mongodb.com/) or a custom implementation. For details, refer to [Configure checkpointer backend](/langsmith/configure-checkpointer). * **Store (long-term memory)**: memory that persists across threads, enabling agents to retain information between separate conversations. Stored in PostgreSQL by default but can be replaced with a custom implementation. For details, refer to [Add custom store](/langsmith/custom-store). ### Task queue When a client creates a run, the API server enqueues it and a queue worker picks it up for execution. Workers can also be signaled to cancel a run in progress, and publish output events that open `/stream` connections forward to the client in real time. [Redis](https://redis.io/) handles the signaling, cancellation, and streaming pub/sub between API servers and queue workers. It stores only ephemeral data—no user or run data persists in Redis. Run data itself is always read from and written to PostgreSQL. For more information on how to set up and manage these components, review the [hosting options](/langsmith/platform-setup) guide. ## Runtime architecture ### Deployment modes Agent Server supports three runtime configurations: * **Single host**: The API server manages the task queue directly with no separate queue workers. This is the default for self-hosted deployments and is suitable for development and low-traffic use cases. * **Split API and queue**: Dedicated queue workers handle run execution on separate hosts from the API server. For self-hosted deployments, enable this by setting `queue.enabled: true` in your configuration. Each tier scales independently—API servers scale on request volume, queue workers scale on pending run count. * **Distributed runtime**: The API and queue processes are again run separately, but instead of a single queue process handling both the orchestration and execution of your graph, the distributed runtime uses one process for orchestration and one process for execution. Use this for large-scale deployments with high concurrency requirements. The container architecture and run lifecycle described below apply to single host and split API and queue configurations. ### Container architecture A typical deployment consists of two kinds of long-running containers, both built from the same Docker image (a base image with your project code installed on top): * **API servers** handle client requests (creating runs, reading thread state, streaming results) but do not execute agent code themselves. * **Queue workers** are the execution engine. They listen to the durable task queue, execute your graph code, and write checkpoints. Containers are **stateless** but persistent. At least 1 queue worker must listen to the task queue at any time to ensure no runs are orphaned. The containers can serve many runs over their lifetime. API servers and queue workers are separate container pools and [scale independently](/langsmith/data-plane#autoscaling). ```mermaid theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} flowchart TB User["User"] API["API Servers"] subgraph WorkerContainer["Worker Containers"] QueueLoop["Queue Loop"] W1["Worker"] W2["Worker"] Wn["..."] QueueLoop -->|dispatch| W1 QueueLoop -->|dispatch| W2 end DB[(Postgres)] Redis[(Redis)] User -->|request| API API -->|create run| DB API -->|notify| Redis Redis -->|wake| QueueLoop QueueLoop -->|claim next run| DB WorkerContainer -->|save checkpoints / update status| DB WorkerContainer -->|publish events| Redis Redis -->|stream events| API API -->|SSE response| User style User fill:#F2FAFF,stroke:#40668D,stroke-width:2px,color:#2F4B68 style API fill:#EBD0F0,stroke:#885270,stroke-width:2px,color:#441E33 style DB fill:#E5F4FF,stroke:#006DDD,stroke-width:2px,color:#030710 style Redis fill:#F8E8E6,stroke:#B27D75,stroke-width:2px,color:#634643 style WorkerContainer fill:#F6FFDB,stroke:#6E8900,stroke-width:2px,color:#2E3900 style QueueLoop fill:#FDF3FF,stroke:#7E65AE,stroke-width:2px,color:#504B5F style W1 fill:#F2FAFF,stroke:#40668D,stroke-width:2px,color:#2F4B68 style W2 fill:#F2FAFF,stroke:#40668D,stroke-width:2px,color:#2F4B68 style Wn fill:#F2FAFF,stroke:#40668D,stroke-width:2px,color:#2F4B68 ``` ### Run execution lifecycle When you invoke a run, the request flows through several components: 1. A client sends a request to an API server, which creates a pending run in the durable task queue. 2. A queue worker picks up the run, acquires a lease on it, loads the appropriate graph, and begins execution. The queue enforces that at most 1 run can be executed for a given thread at one time. 3. As the graph executes, the worker writes checkpoints to the persistence layer (the frequency depends on the [durability mode](/oss/python/langgraph/persistence#durability-modes)) and broadcasts streaming events over the configured pubsub provider. 4. If the client opened a `/stream` connection, the API server subscribes to the pubsub channel and forwards events to the client via server-sent events in real time. 5. When execution completes, the worker updates the run status and releases its slot for the next run. Each worker executes up to [`N_JOBS_PER_WORKER`](/langsmith/env-var-self-hosted) runs concurrently (default: 10), so a single worker container serves many runs in parallel. This bounds concurrent run execution, not the number of API requests the deployment can serve. API servers handle requests independently and scale separately, so request-serving capacity is not capped by `N_JOBS_PER_WORKER`. See [Configure Agent Server for scale](/langsmith/agent-server-scale) for tuning guidance. ## Learn more * [Application Structure](/langsmith/application-structure) guide explains how to structure your application for deployment. * The [API Reference](https://docs.langchain.com/langsmith/server-api-ref) provides detailed information on the API endpoints and data models. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-server.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# A2A JSON-RPC Source: https://docs.langchain.com/langsmith/agent-server-api/a2a/a2a-json-rpc /langsmith/agent-server-openapi.json post /a2a/{assistant_id} Communicate with an assistant using the Agent-to-Agent (A2A) Protocol over JSON-RPC 2.0. This endpoint accepts a JSON-RPC envelope and dispatches based on `method`. **Supported Methods:** - `message/send`: Send a message and wait for the final Task result. - `message/stream`: Send a message and receive Server-Sent Events (SSE) JSON-RPC responses. - `tasks/get`: Fetch the current state of a Task by ID. - `tasks/cancel`: Request cancellation (currently not supported; returns an error). **LangGraph Mapping:** - `message.contextId` maps to LangGraph `thread_id`. **Notes:** - Only `text` and `data` parts are supported; `file` parts are not. - If `message.contextId` is omitted, a new context is created. - Text parts require the assistant input schema to include a `messages` field. # Count Assistants Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/count-assistants /langsmith/agent-server-openapi.json post /assistants/count Get the count of assistants matching the specified criteria. # Create Assistant Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/create-assistant /langsmith/agent-server-openapi.json post /assistants Create an assistant. An initial version of the assistant will be created and the assistant is set to that version. To change versions, use the `POST /assistants/{assistant_id}/latest` endpoint. # Delete Assistant Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/delete-assistant /langsmith/agent-server-openapi.json delete /assistants/{assistant_id} Delete an assistant by ID. All versions of the assistant will be deleted as well. # Get Assistant Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/get-assistant /langsmith/agent-server-openapi.json get /assistants/{assistant_id} Get an assistant by ID. # Get Assistant Graph Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/get-assistant-graph /langsmith/agent-server-openapi.json get /assistants/{assistant_id}/graph Get an assistant by ID. # Get Assistant Schemas Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/get-assistant-schemas /langsmith/agent-server-openapi.json get /assistants/{assistant_id}/schemas Get an assistant by ID. # Get Assistant Subgraphs Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/get-assistant-subgraphs /langsmith/agent-server-openapi.json get /assistants/{assistant_id}/subgraphs Get an assistant's subgraphs. # Get Assistant Subgraphs by Namespace Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/get-assistant-subgraphs-by-namespace /langsmith/agent-server-openapi.json get /assistants/{assistant_id}/subgraphs/{namespace} Get an assistant's subgraphs filtered by namespace. # Get Assistant Versions Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/get-assistant-versions /langsmith/agent-server-openapi.json post /assistants/{assistant_id}/versions Get all versions of an assistant. # Patch Assistant Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/patch-assistant /langsmith/agent-server-openapi.json patch /assistants/{assistant_id} Update an assistant. # Search Assistants Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/search-assistants /langsmith/agent-server-openapi.json post /assistants/search Search for assistants. This endpoint also functions as the endpoint to list all assistants. # Set Latest Assistant Version Source: https://docs.langchain.com/langsmith/agent-server-api/assistants/set-latest-assistant-version /langsmith/agent-server-openapi.json post /assistants/{assistant_id}/latest Set the latest version for an assistant. # Count Crons Source: https://docs.langchain.com/langsmith/agent-server-api/crons/count-crons /langsmith/agent-server-openapi.json post /runs/crons/count Get the count of crons matching the specified criteria. # Create Cron Source: https://docs.langchain.com/langsmith/agent-server-api/crons/create-cron /langsmith/agent-server-openapi.json post /runs/crons Create a cron to schedule runs on new threads. # Create Thread Cron Source: https://docs.langchain.com/langsmith/agent-server-api/crons/create-thread-cron /langsmith/agent-server-openapi.json post /threads/{thread_id}/runs/crons Create a cron to schedule runs on a thread. # Delete Cron Source: https://docs.langchain.com/langsmith/agent-server-api/crons/delete-cron /langsmith/agent-server-openapi.json delete /runs/crons/{cron_id} Delete a cron by ID. # Get Cron Source: https://docs.langchain.com/langsmith/agent-server-api/crons/get-cron /langsmith/agent-server-openapi.json get /runs/crons/{cron_id} Get a cron by ID. # Search Crons Source: https://docs.langchain.com/langsmith/agent-server-api/crons/search-crons /langsmith/agent-server-openapi.json post /runs/crons/search Search all active crons # Update Cron Source: https://docs.langchain.com/langsmith/agent-server-api/crons/update-cron /langsmith/agent-server-openapi.json patch /runs/crons/{cron_id} Update a cron job by ID. # MCP Get Source: https://docs.langchain.com/langsmith/agent-server-api/mcp/mcp-get /langsmith/agent-server-openapi.json get /mcp/ Implemented according to the Streamable HTTP Transport specification. # MCP Post Source: https://docs.langchain.com/langsmith/agent-server-api/mcp/mcp-post /langsmith/agent-server-openapi.json post /mcp/ Implemented according to the Streamable HTTP Transport specification. Sends a JSON-RPC 2.0 message to the server. - **Request**: Provide an object with `jsonrpc`, `id`, `method`, and optional `params`. - **Response**: Returns a JSON-RPC response or acknowledgment. **Notes:** - Stateless: Sessions are not persisted across requests. # Terminate Session Source: https://docs.langchain.com/langsmith/agent-server-api/mcp/terminate-session /langsmith/agent-server-openapi.json delete /mcp/ Implemented according to the Streamable HTTP Transport specification. Terminate an MCP session. The server implementation is stateless, so this is a no-op. # Create Background Run Source: https://docs.langchain.com/langsmith/agent-server-api/stateless-runs/create-background-run /langsmith/agent-server-openapi.json post /runs Create a run and return the run ID immediately. Don't wait for the final run output. # Create Run Batch Source: https://docs.langchain.com/langsmith/agent-server-api/stateless-runs/create-run-batch /langsmith/agent-server-openapi.json post /runs/batch Create a batch of runs and return immediately. # Create Run, Stream Output Source: https://docs.langchain.com/langsmith/agent-server-api/stateless-runs/create-run-stream-output /langsmith/agent-server-openapi.json post /runs/stream Create a run and stream the output. # Create Run, Wait for Output Source: https://docs.langchain.com/langsmith/agent-server-api/stateless-runs/create-run-wait-for-output /langsmith/agent-server-openapi.json post /runs/wait Create a run, wait for the final output and then return it. # Delete an item. Source: https://docs.langchain.com/langsmith/agent-server-api/store/delete-an-item /langsmith/agent-server-openapi.json delete /store/items # List namespaces with optional match conditions. Source: https://docs.langchain.com/langsmith/agent-server-api/store/list-namespaces-with-optional-match-conditions /langsmith/agent-server-openapi.json post /store/namespaces # Retrieve a single item. Source: https://docs.langchain.com/langsmith/agent-server-api/store/retrieve-a-single-item /langsmith/agent-server-openapi.json get /store/items # Search or list items within a namespace prefix. Source: https://docs.langchain.com/langsmith/agent-server-api/store/search-or-list-items-within-a-namespace-prefix /langsmith/agent-server-openapi.json post /store/items/search Lists items ordered by last updated time. If a `query` is provided, performs a natural language search instead. Supports pagination via `limit` and `offset`, and filtering via `filter`. # Store or update an item. Source: https://docs.langchain.com/langsmith/agent-server-api/store/store-or-update-an-item /langsmith/agent-server-openapi.json put /store/items # Protocol v2 Command Source: https://docs.langchain.com/langsmith/agent-server-api/streaming/protocol-v2-command /langsmith/agent-server-openapi.json post /threads/{thread_id}/commands Send a single protocol command scoped to a thread. The request body is a `ProtocolCommand` envelope with a `method` (e.g. `run.start`, `input.respond`, `agent.getTree`) and method-specific `params`. The response is either a `ProtocolSuccess` (with method-specific `result`) or a `ProtocolError`. Commands that create runs (`run.start`, `input.respond`) leave the run executing in the background on the worker queue. Event streaming for that run is observed via a concurrent `POST /threads/{thread_id}/stream/events` connection. WebSocket clients use the same command envelope in-band on `/threads/{thread_id}/stream/events` and additionally have access to `subscription.subscribe` / `subscription.unsubscribe` over the same connection. # Protocol v2 Event Stream (SSE) Source: https://docs.langchain.com/langsmith/agent-server-api/streaming/protocol-v2-event-stream-sse /langsmith/agent-server-openapi.json post /threads/{thread_id}/stream/events Open a connection-scoped SSE event stream for a thread. The request body is a `ProtocolEventStreamRequest` carrying channel and namespace filters; the server replies with `Content-Type: text/event-stream` and pushes matching `ProtocolEvent` frames for the lifetime of the connection. Closing the connection unsubscribes — no state is persisted server-side. Reconnect: clients pass the last `seq` they received as `since` in the body. Buffered events with `seq > since` are replayed before the stream goes live. The endpoint is POST-only, so browser-native `EventSource` auto-resume (`Last-Event-ID`) does not apply — clients drive resume explicitly via the body. # API Documentation Source: https://docs.langchain.com/langsmith/agent-server-api/system/api-documentation /langsmith/agent-server-openapi.json get /docs A local reference to the Agent Server API documentation. # Health Check Source: https://docs.langchain.com/langsmith/agent-server-api/system/health-check /langsmith/agent-server-openapi.json get /ok Check the health status of the server. Optionally check database connectivity. # Server Information Source: https://docs.langchain.com/langsmith/agent-server-api/system/server-information /langsmith/agent-server-openapi.json get /info Get server version information, feature flags, and metadata. # System Metrics Source: https://docs.langchain.com/langsmith/agent-server-api/system/system-metrics /langsmith/agent-server-openapi.json get /metrics Get system metrics in Prometheus or JSON format for monitoring and observability. # Cancel Run Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/cancel-run /langsmith/agent-server-openapi.json post /threads/{thread_id}/runs/{run_id}/cancel # Cancel Runs Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/cancel-runs /langsmith/agent-server-openapi.json post /runs/cancel Cancel one or more runs. Can cancel runs by thread ID and run IDs, or by status filter. # Create Background Run Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/create-background-run /langsmith/agent-server-openapi.json post /threads/{thread_id}/runs Create a run in existing thread, return the run ID immediately. Don't wait for the final run output. # Create Run, Stream Output Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/create-run-stream-output /langsmith/agent-server-openapi.json post /threads/{thread_id}/runs/stream Create a run in existing thread. Stream the output. # Create Run, Wait for Output Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/create-run-wait-for-output /langsmith/agent-server-openapi.json post /threads/{thread_id}/runs/wait Create a run in existing thread. Wait for the final output and then return it. # Delete Run Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/delete-run /langsmith/agent-server-openapi.json delete /threads/{thread_id}/runs/{run_id} Delete a run by ID. # Get Run Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/get-run /langsmith/agent-server-openapi.json get /threads/{thread_id}/runs/{run_id} Get a run by ID. # Join Run Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/join-run /langsmith/agent-server-openapi.json get /threads/{thread_id}/runs/{run_id}/join Wait for a run to finish. # Join Run Stream Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/join-run-stream /langsmith/agent-server-openapi.json get /threads/{thread_id}/runs/{run_id}/stream Join a run stream. This endpoint streams output in real-time from a run similar to the /threads/__THREAD_ID__/runs/stream endpoint. If the run has been created with `stream_resumable=true`, the stream can be resumed from the last seen event ID. # List Runs Source: https://docs.langchain.com/langsmith/agent-server-api/thread-runs/list-runs /langsmith/agent-server-openapi.json get /threads/{thread_id}/runs List runs for a thread. # Copy Thread Source: https://docs.langchain.com/langsmith/agent-server-api/threads/copy-thread /langsmith/agent-server-openapi.json post /threads/{thread_id}/copy Create a new thread with a copy of the state and checkpoints from an existing thread. # Count Threads Source: https://docs.langchain.com/langsmith/agent-server-api/threads/count-threads /langsmith/agent-server-openapi.json post /threads/count Get the count of threads matching the specified criteria. # Create Thread Source: https://docs.langchain.com/langsmith/agent-server-api/threads/create-thread /langsmith/agent-server-openapi.json post /threads Create a thread. # Delete Thread Source: https://docs.langchain.com/langsmith/agent-server-api/threads/delete-thread /langsmith/agent-server-openapi.json delete /threads/{thread_id} Delete a thread by ID. # Get Thread Source: https://docs.langchain.com/langsmith/agent-server-api/threads/get-thread /langsmith/agent-server-openapi.json get /threads/{thread_id} Get a thread by ID. # Get Thread History Source: https://docs.langchain.com/langsmith/agent-server-api/threads/get-thread-history /langsmith/agent-server-openapi.json get /threads/{thread_id}/history Get all past states for a thread. # Get Thread History Post Source: https://docs.langchain.com/langsmith/agent-server-api/threads/get-thread-history-post /langsmith/agent-server-openapi.json post /threads/{thread_id}/history Get all past states for a thread. # Get Thread State Source: https://docs.langchain.com/langsmith/agent-server-api/threads/get-thread-state /langsmith/agent-server-openapi.json get /threads/{thread_id}/state Get state for a thread. The latest state of the thread (i.e. latest checkpoint) is returned. # Get Thread State At Checkpoint Source: https://docs.langchain.com/langsmith/agent-server-api/threads/get-thread-state-at-checkpoint /langsmith/agent-server-openapi.json get /threads/{thread_id}/state/{checkpoint_id} Get state for a thread at a specific checkpoint. # Get Thread State At Checkpoint Source: https://docs.langchain.com/langsmith/agent-server-api/threads/get-thread-state-at-checkpoint-1 /langsmith/agent-server-openapi.json post /threads/{thread_id}/state/checkpoint Get state for a thread at a specific checkpoint. # Join Thread Stream Source: https://docs.langchain.com/langsmith/agent-server-api/threads/join-thread-stream /langsmith/agent-server-openapi.json get /threads/{thread_id}/stream This endpoint streams output in real-time from a thread. The stream will include the output of each run executed sequentially on the thread and will remain open indefinitely. It is the responsibility of the calling client to close the connection. # Patch Thread Source: https://docs.langchain.com/langsmith/agent-server-api/threads/patch-thread /langsmith/agent-server-openapi.json patch /threads/{thread_id} Update a thread. # Prune Threads Source: https://docs.langchain.com/langsmith/agent-server-api/threads/prune-threads /langsmith/agent-server-openapi.json post /threads/prune Prune threads by ID. The 'delete' strategy removes threads entirely. The 'keep_latest' strategy prunes old checkpoints but keeps threads and their latest state. # Search Threads Source: https://docs.langchain.com/langsmith/agent-server-api/threads/search-threads /langsmith/agent-server-openapi.json post /threads/search Search for threads. This endpoint also functions as the endpoint to list all threads. # Update Thread State Source: https://docs.langchain.com/langsmith/agent-server-api/threads/update-thread-state /langsmith/agent-server-openapi.json post /threads/{thread_id}/state Add state to a thread. # Agent Server changelog Source: https://docs.langchain.com/langsmith/agent-server-changelog **Subscribe**: Our changelog includes an [RSS feed](https://docs.langchain.com/langsmith/agent-server-changelog/rss.xml) that can integrate with [Slack](https://slack.com/help/articles/218688467-Add-RSS-feeds-to-Slack), [email](https://zapier.com/apps/email/integrations/rss/1441/send-new-rss-feed-entries-via-email), Discord bots like [Readybot](https://readybot.io/) or [RSS Feeds to Discord Bot](https://rss.app/en/bots/rssfeeds-discord-bot), and other subscription tools. [Agent Server](/langsmith/agent-server) is an API platform for creating and managing agent-based applications. It provides built-in persistence, a task queue, and supports deploying, configuring, and running assistants (agentic workflows) at scale. This changelog documents all notable updates, features, and fixes to Agent Server releases. ## Release cadence `langgraph-api` maintains three release streams: * `latest`: Published every morning with the latest bug fixes and test features. Semantic versioning uses a dev tag off the minor version, for example `0.9.0.dev1`. * `rc` (release candidate): Published every three weeks and patched as needed during the bake window for critical bug backfills. Recommended for users who want to test a feature in the next stable release. Semantic versioning uses an rc tag off the minor version, for example `0.9.0rc1`. * `stable`: Published every three weeks from rc. Patched for security-related dependency bumps or critical bug fixes. This is the default version used by new deployments and is recommended for production use. Semantic versioning uses minor bumps for regular promotions (for example `0.9.0`) and patch bumps for backfills (for example `0.9.1`). Deployments use the newest `stable` version by default and are automatically updated to the newest `stable` version on each new revision. To pin to a specific version, set [`api_version`](/langsmith/cli#pinning-api-version) to the desired version in langgraph.json. ## v0.11 Latest version: `0.11.0` ### Changes #### New Features * Added DeltaChannel-aware pruning that preserves only the minimum ancestor checkpoints needed for state reconstruction, replacing the previous approach that refused to prune threads with active delta channels. * Added opt-in Prometheus metrics scrape support. Set `LSD_PROM_METRICS_ENABLED=true` to expose OTel metrics (run lifecycle, latency, stream, worker gauges) on a dedicated Prometheus scrape endpoint at port `LSD_PROM_METRICS_PORT` (default 9464). Datadog OTLP push continues to work alongside Prometheus when both are configured. * Added `coreApi.runQueueTraceLog` config flag (`LSD_RUN_QUEUE_TRACE_LOG` env var, default `false`) to enable verbose Redis run-queue trace logs. * Added the `langsmith_session_name` field to each run and exposed support via `/info` so Studio can detect API versions that support this field. * Added `-fips` variants of Wolfi Python and JS server images (for example `3.13-wolfi-fips`, `22-wolfi-fips`), built with the Go FIPS 140 cryptographic module and FIPS-hardened OpenSSL for Node. #### Fixes * Fixed protocol v2 runs on JS graphs failing silently. The sidecar rejected `streamEvents` with a 400 due to strict stream-mode validation, the error was swallowed, and runs falsely reported success with 0 nodes executed. Relaxed stream-mode validation at the HTTP boundary and now raise a clear error on non-2xx sidecar responses instead of masking the failure. * Fixed protocol v2 event streaming against JS sidecar (remote) graphs, which were incorrectly served through the legacy reconstruction path. Remote graphs now use LangGraphJS's native v3 stream for v2 event-streaming runs, resolving tool calls not rendering, headless interrupts never executing or resuming, and `400: tool_use ids must be unique` errors on the final message after a resume. * Delete run now skips checkpoint deletion for threads using DeltaChannel and removes only the run record. Checkpoints that store delta writes later checkpoints depend on are preserved. Use thread prune APIs to reclaim checkpoint storage on delta-channel threads. * Fixed HTTP `input.respond` validation for Event Streaming v2 to read pending interrupts from the durable thread row instead of rebuilding thread state, preventing valid HITL resumes from incorrectly returning `no_such_interrupt` after reconnects, redeploys, or thread-state lookup failures. * Fixed `input.respond` so optional `update` and `goto` parameters are forwarded into the same `Command` as the resume value. * Fixed DeltaChannel replay for channels that migrated from a non-delta channel to DeltaChannel. The checkpointer did not correctly recognize the head seed checkpoint, which could produce incorrect reconstructed state for non-additive reducers. * Fixed custom stream events emitted from subgraphs not being forwarded to the client when using `stream_mode=["custom"]` with `stream_subgraphs=True` on JS deployments. * Fixed a bug where calling `join_stream` with a `stream_mode` filter could cause non-message events from subgraphs to be incorrectly filtered from the results. * Fixed Redis Cluster pub/sub failing to connect on TLS-only clusters when `REDIS_CLUSTER=true`, which previously attempted to dial port `0`. * Made queue runs query field selection explicit for backwards compatibility, so new run schema fields can be added without breaking older server versions during rollbacks. ## v0.11.0rc14 ### Fixes * Fixed OTLP latency histogram bucket configuration after the metrics migration so latency metrics use legacy second-scale buckets converted to milliseconds, restoring accurate p95/p99 for long HTTP polls, queue waits, and run execution. * Made queue runs query field selection explicit for backwards compatibility, so new run schema fields can be added without breaking older server versions during rollbacks. ## v0.11.0rc13 ### Fixes * Fixed JS worker port collisions when the API server and queue worker run as separate containers in the same Kubernetes pod. The queue entrypoint now offsets loopback ports. * Fixed Redis Cluster pub/sub failing to connect on TLS-only clusters when `REDIS_CLUSTER=true`, which previously attempted to dial port `0`. ## v0.11.0rc12 ### New Features * Added `-fips` variants of Wolfi Python and JS server images (for example `3.13-wolfi-fips`, `22-wolfi-fips`), built with the Go FIPS 140 cryptographic module and FIPS-hardened OpenSSL for Node. ## v0.11.0rc11 ### New Features * Added the `langsmith_session_name` field to each run. This field is the LangSmith tracing project name when tracing is enabled. Exposed support via `/info` so Studio can detect API versions that support this field. ### General Notes * Applied stranded Postgres migration `061` for `thread_ls_user_id_idx` and `thread_assistant_id_idx` btree indexes. ## v0.11.0rc10 ### New Features * Added core search cost rate limits for assistants, runs, crons, and threads search, wired through existing rate-limit config and metrics. ### General Notes * Wolfi (`chainguard-base-fips`) server images now ship a FIPS-compliant Go core-server and FIPS-mode Node, and no longer include the unused bun runtime. ## v0.11.0rc9 ### Fixes * Fixed a bug where calling `join_stream` with a `stream_mode` filter could cause non-message events from subgraphs to be incorrectly filtered from the results. ## v0.11.0rc8 ### General Notes * Agent Server metrics are now emitted through the OpenTelemetry/Prometheus client on the dedicated Prometheus scrape endpoint (`LSD_PROM_METRICS_PORT`, default 9464). Set `LSD_PROM_METRICS_ENABLED=true` to enable the endpoint and `EXPOSE_INTERNAL_METRICS_PROMETHEUS=true` to expose the internal metrics migrated from the main API `/metrics` path. By default, the Prometheus endpoint serves only LSD Deployment UI metrics. * **Potentially breaking** for Prometheus scrapers and dashboards: point collectors at the OTLP Prometheus port instead of the main API `/metrics` path. `lg_api_http_requests_latency_seconds` is now `lg_api_http_requests_latency` and reports milliseconds instead of seconds. Pool request counters now use a `_total` suffix (`lg_api_pg_pool_requests_queued_total`, `lg_api_pg_pool_requests_errors_total`). The `lg_api_pending_runs_wait_time_*` gauges are removed in favor of the `lg_api_run_queue_wait_time_1st_attempt` latency histogram. ## v0.11.0rc7 ### Fixes * Fixed custom stream events emitted from subgraphs not being forwarded to the client when using `stream_mode=["custom"]` with `stream_subgraphs=True` on JS deployments. ### General Notes * Includes security dependency updates for PyJWT, LangSmith, cryptography, Hono, undici, `golang.org/x/net`, `golang.org/x/crypto`, and Starlette. ## v0.11.0rc6 ### New Features * Added rate-limit observability metrics, including configured-limit gauges (`lg_api_rate_limit_configured_rate`, `lg_api_rate_limit_configured_burst`) and a per-bucket `rate_limit_key` tag on decision, error, and cost metrics. ## v0.11.0rc5 ### Fixes * Fixed DeltaChannel replay for channels that migrated from a non-delta channel to DeltaChannel. The checkpointer did not correctly recognize the head seed checkpoint, which could produce incorrect reconstructed state for non-additive reducers. ## v0.11.0rc4 ### Fixes * Fixed HTTP `input.respond` validation for Event Streaming v2 to read pending interrupts from the durable thread row instead of rebuilding thread state, preventing valid HITL resumes from incorrectly returning `no_such_interrupt` after reconnects, redeploys, or thread-state lookup failures. * Fixed `input.respond` so optional `update` and `goto` parameters are forwarded into the same `Command` as the resume value. ## v0.11.0rc3 ### New Features * Added `coreApi.runQueueTraceLog` config flag (`LSD_RUN_QUEUE_TRACE_LOG` env var, default `false`) to enable verbose Redis run-queue trace logs. ## v0.11.0rc2 ### Fixes * Loosened the `starlette` lower bound introduced in 0.11.0rc1 back to `>=0.38.6` so `langgraph-api` can be installed alongside environments that pin older Starlette versions. Builds still resolve Starlette to 1.0.1 through the lockfile. ## v0.11.0rc1 ### General Notes * Includes dependency and security maintenance updates. ### New Features * Added DeltaChannel-aware pruning that preserves only the minimum ancestor checkpoints needed for state reconstruction, replacing the previous approach that refused to prune threads with active delta channels. Supported across the Postgres, SQLite, DeferredDelete, and in-memory runtimes. * Added DeltaChannel-aware pruning for the MongoDB checkpointer, preserving only the minimum ancestor checkpoints and delta-channel blobs needed for state reconstruction. * Added opt-in Prometheus metrics scrape support. Set `LSD_PROM_METRICS_ENABLED=true` to expose OTel metrics (run lifecycle, latency, stream, worker gauges) on a dedicated Prometheus scrape endpoint at port `LSD_PROM_METRICS_PORT` (default 9464). Datadog OTLP push continues to work alongside Prometheus when both are configured. * Added opt-in Go core rate limits for unary core-api RPCs and Redis stream publish bytes, with Redis-backed GCRA enforcement, shadow/enforce modes, and `LS_RATE_LIMITS` bootstrap config with YAML override. * Allow passing custom certificate and key files (`ssl_certfile`, `ssl_keyfile`) to run the dev server over HTTPS. ### Fixes * Fixed protocol v2 runs on JS graphs failing silently. The sidecar rejected `streamEvents` with a 400 due to strict stream-mode validation, the error was swallowed, and runs falsely reported success with 0 nodes executed. Relaxed stream-mode validation at the HTTP boundary and now raise a clear error on non-2xx sidecar responses instead of masking the failure. * Fixed protocol v2 event streaming against JS sidecar (remote) graphs, which were incorrectly served through the legacy reconstruction path. Remote graphs now use LangGraphJS's native v3 stream for v2 event-streaming runs, resolving tool calls not rendering, headless interrupts never executing or resuming, and `400: tool_use ids must be unique` errors on the final message after a resume. * Delete run now skips checkpoint deletion for threads using DeltaChannel and removes only the run record. Checkpoints that store delta writes later checkpoints depend on are preserved. Use thread prune APIs to reclaim checkpoint storage on delta-channel threads. * Fixed Prometheus metrics export and aligned OpenTelemetry exporter configuration. ## v0.10.0 ### General Notes * v0.10.0 is the stable promotion of the v0.10.0rc line. Note in particular the potentially breaking security changes in 0.10.0rc1. * Includes dependency and security maintenance updates. ### New Features * Added DeltaChannel-aware pruning that preserves only the minimum ancestor checkpoints needed for state reconstruction, replacing the previous behavior that refused to prune threads with active delta channels. Supported across the Postgres, SQLite, DeferredDelete, and in-memory runtimes. ## v0.10.0rc3 ### Fixes * Fixed protocol v2 event streaming against JS sidecar (remote) graphs, which were incorrectly served through the legacy reconstruction path. Remote graphs now use LangGraphJS's native v3 stream for v2 event-streaming runs, resolving tool calls not rendering, headless interrupts never executing or resuming, and `400: tool_use ids must be unique` errors on the final message after a resume. ## v0.10.0rc2 ### Fixes * Fixed protocol v2 runs on JS graphs failing silently. The sidecar rejected `streamEvents` with a 400 due to strict stream-mode validation, the error was swallowed, and runs falsely reported success with 0 nodes executed. Relaxed stream-mode validation at the HTTP boundary and now raise a clear error on non-2xx sidecar responses instead of masking the failure. ## v0.10.0rc1 ### General Notes * v0.10.0rc1 includes breaking changes for security and correctness. Refer to the [Security section](#security) for more details. * Includes dependency and security maintenance updates. ### New Features * Added cron retrieval by ID endpoint (`GET /runs/crons/{cron_id}`). ### Fixes * Fixed Event Streaming v2 run start handling so checkpoint replay targets supplied via `config.configurable.checkpoint_id` are honored. * Fixed Event Streaming v2 `input.respond` returning `no_such_interrupt` for legitimate interrupts on the postgres backend over HTTP `POST /commands`. * Fixed a bug where a thread's `checkpoint_map` from a prior time-travel run would persist and contaminate a subsequent `Command(resume=...)`, causing nested subgraphs to incorrectly replay from the start. ### Security * **Potentially breaking** Loopback webhook targets are now denied by default to fix an authentication-bypass primitive ([GHSA-2c9q-c2q9-qgqv](https://github.com/langchain-ai/helm/security/advisories/GHSA-2c9q-c2q9-qgqv)). The `webhooks.url.disable_loopback` policy now defaults to `true`, blocking relative-URL webhooks (which dispatch through the in-process ASGI transport and bypass auth), as well as localhost / 127.x / ::1 / host.docker.internal absolute URLs and any hostname that DNS-resolves into the loopback range (mitigating DNS rebinding). Deployments that legitimately need loopback webhooks (e.g. `langgraph dev` with a localhost webhook receiver, or production setups that dispatch to a custom FastAPI route mounted on the same server) can opt back in by setting `webhooks.url.disable_loopback: false` in `langgraph.json` (or the equivalent `LANGGRAPH_WEBHOOKS` JSON env var). Only do this when you control the routes that loopback webhooks reach, as those routes are dispatched without authentication. * **Potentially breaking** `POST /runs` and `POST /threads/{thread_id}/runs` now authorize the attached assistant via the`assistants.read` auth event (matching cron creation and direct GET) instead of the previously-used `assistants.search` event with an incomplete payload ([GHSA-jfj5-wrj9-63x4](https://github.com/langchain-ai/helm/security/advisories/GHSA-jfj5-wrj9-63x4)). Deployments that registered only `@auth.on.assistants.read` (and no `.search` handler) were vulnerable to a cross-user authorization bypass; their existing read handler will now be invoked on the run-creation path. As a defense-in-depth follow-up, client-supplied run/cron metadata is no longer forwarded into the `assistants.read` auth event payload from `Runs.put` or `Crons.put`, and inmem/postgres runtimes now agree on the value shape. Breaking change for deployments with custom auth handlers: (1) any `@auth.on.assistants.search` handler that was previously invoked during run creation is no longer called there — ensure you have an equivalent`@auth.on.assistants.read` handler returning the same owner-style filter; (2) `value["metadata"]` on the `assistants.read` event invoked from run/cron creation is no longer populated, so handlers that inspected or mutated it must move that logic into `@auth.on.runs.create_run` / `@auth.on.crons.create` and rely on returning a filter for ownership enforcement. * Deployments now see a structured warning at server start listing every uncovered dispatch path along with a default-deny snippet to copy. The warning is silent for deployments that register a global `@auth.on` handler or that only use `@auth.authenticate` without any resource-level handlers. ## v0.9.0 ### General Notes * v0.9.0 is the stable promotion of the v0.9.0rc line. * Includes dependency and security maintenance updates. ## v0.9.0rc1 ### General Notes * Added cron metadata filtering in /runs/crons/search and /runs/crons/count, matching metadata filter behavior already available for assistants/threads. * Added Postgres checkpointer pool tuning knobs for cases when loading lots of large checkpoints at once. LANGGRAPH\_CHECKPOINTER\_POSTGRES\_POOL\_MIN\_SIZE and LANGGRAPH\_CHECKPOINTER\_POSTGRES\_POOL\_TIMEOUT\_SECONDS can now be set. * Fixed a crash in update\_state in the mongo checkpointer when a thread has no prior checkpoint. * Includes dependency updates for security vulnerabilities. ### New Features #### Delta channel support Delta channels are now supported so checkpoints can store incremental state updates instead of repeatedly storing full channel payloads, which helps with large, append-heavy state like message histories. To use, define state channels with LangGraph's DeltaChannel reducer pattern in your graph state. This behavior is enabled when the installed langgraph is >= 1.2. Docs: [DeltaChannel reference](/oss/python/langgraph/pregel#deltachannel) #### Event streaming APIs Event streaming APIs are being introduced, with a unified event-streaming surface intended for richer real-time run events and command/event workflows. The feature flag `FF_V2_EVENT_STREAMING` can be set to true to enable the new event streaming APIs. The new endpoints include: * `POST /threads/{thread_id}/stream/events` * `POST /threads/{thread_id}/commands` * `WS /threads/{thread_id}/stream/events` Docs: * [Agent Server API reference](/langsmith/server-api-ref) * [LangGraph event streaming reference](/oss/python/langgraph/event-streaming) ## v0.8.7 * Reverted changes from #3296 temporarily to address issues with the upcoming 0.8.6 release. ## v0.8.6 * Integrated DeltaChannel into the Postgres checkpointer for efficient snapshot and delta processing. * Introduced new v2 streaming primitives to the API for enhanced data handling. * Enabled dynamic port discovery for in-memory operations. * Linked A2A tool result messages with `toolCallId` correlation metadata to maintain alignment with initiating tool calls. * Fixed an issue where JS studio experiments didn't update the experiment screen, ensuring correct run routing to the experiment's tracing project with `reference_example_id` set. ## v0.8.5 * Addressed security vulnerabilities in langgraph JavaScript dependencies reported by Datadog and npm. ## v0.8.4 * Included trace/span IDs in access logs to improve trace correlation in Datadog and OTel. ## v0.8.3 * Added support for IAM-based authentication with Google Cloud Memorystore in cluster mode for secure access. ## v0.8.2 * Fixed the `langgraph-api` queue entrypoint to start correctly on IPv6-only clusters by ensuring the health/metrics server appropriately binds with an IPv6 literal. ## v0.8.1 * Improved performance by skipping the large `values` column in thread state and run endpoints when the full thread body isn't required. * Capped checkpoint ingestion batch size and delay window to minimize long-running transactions and row lock contention, with new configuration flags for batch size and delay control. ## v0.8.0 This minor version moves run queue polling from Postgres to Redis, saving database load and improving performance. Under the hood, Agent Server uses a durable run queue to manage run execution. Workers poll the queue for new runs and execute them. Previously, the queue polling logic went through Postgres. This could result in long running queries especially under high load. With this update, the queue polling logic now goes through Redis and then fetches run details from Postgres. This makes the hot path for queue polling substantially faster and reduces the load on the database. This is not a breaking change and does not require code changes to upgrade, but there are a couple of things to be aware of: * In the deployment immediately after upgrading, the queue will shift over. There may be a brief window where threads are scheduled non-chronologically. Run execution order is still guaranteed within each thread. * **Self-hosted only:** Redis traffic may increase slightly. In internal testing, the increase was modest. ## v0.7.103 * Resolved migration version conflict for checkpoint\_delete\_queue, ensuring proper execution and added duplicate version detection for future migrations. ## v0.7.102 * Improved handling of parallel interrupts by merging multiple interrupt chunks and ensuring consistent interrupt return behavior. * Updated Vite dependency to patch security vulnerabilities CVE-2026-39363 and CVE-2026-39364. * Pinned the Datadog image version to `1.9.9` due to missing `arm64` support in `1.9.10` manifest. ## v0.7.101 * Bumped Go stdlib to 1.25.9 to address high severity vulnerabilities CVE-2026-32280 and CVE-2026-32282. * Improved error propagation in DD and OTEL tracers to handle UserInterrupt exceptions without causing generator errors. ## v0.7.100 * Implemented background deletion of checkpoints to improve thread deletion and pruning performance, reducing I/O pressure and enhancing efficiency. * Bumped `@hono/node-server` from 1.19.12 to 1.19.13 to fix a security issue with the Serve Static Middleware. * Updated hono from version 4.12.9 to 4.12.12, including critical security patches for middleware and utilities. * Upgraded the hono library to version 4.12.12, addressing several security vulnerabilities. * Implemented strict version locking for build dependencies to ensure consistency across builds. ## v0.7.99 * Updated OpenAPI configuration to prevent 405 errors in `/docs` "try it" requests when using Istio with a path prefix. * Replaced `signal.raise_signal(SIGINT)` with `sys.exit` in `queue_with_signal` to improve shutdown reliability and handle stuck threads. * Added opt-in TLS configuration for executor clients, preserving backward-compatible cleartext behavior for existing non-loopback deployments. * Adjusted the precedence order for Datadog API key configuration to ensure proper key usage. ## v0.7.98 * Fixed an import issue in `langgraph dev` to ensure the dev server works without environment variables and added a regression test. ## v0.7.97 * Improved error propagation for JS graphs, ensuring clearer error messages from the `/assistants//schemas` endpoint. * Ensured stable startup when environment variables like `LANGGRAPH_SERVER_HOST` are set to an IPv6 address. * Enhanced query performance by using `->>` for string value filters in `EqAuthFilter`, enabling the use of B-tree indexes. ## v0.7.96 * Enhanced database performance by disabling nested loops and respecting lower `statement_timeout` settings when specified. ## v0.7.95 * Resolved a `BlockingError` by ensuring `ddtrace` is imported at module load time, preventing async context conflicts during initialization. * Propagated `ddtrace` context to worker ensuring `langgraph.graph_load` has a parent span instead of emitting as a root. * Added support for `Prefer: return=minimal` on `PATCH /threads/{id}` to improve efficiency by returning a 204 status with no body. * Enhanced `run_server` with dynamic port discovery to automatically select an available port when the default port (`2024`) is in use. ## v0.7.94 * Resolved an issue where JavaScript installs would incorrectly succeed after retry timeouts, ensuring proper failure handling. * Added a `langgraph.graph_load` ddtrace span around the graph factory load to improve APM visibility. ## v0.7.93 * Enabled `FF_OPTIMIZED_STREAMING` flag support from environment variables in `core-api` mode. ## v0.7.92 * Fixed an issue where `keep_latest` threads could accumulate checkpoints indefinitely by recreating the `thread_ttl` entry upon run completion. * Improved import performance by caching `importlib.metadata.packages_distributions()`, significantly reducing startup time when using `ddtrace` with Google API packages. ## v0.7.91 * Upgraded cryptography dependency from 46.0.5 to 46.0.6 to address a security issue related to name constraints in peer name verification. * Introduced an optimized streaming implementation using Redis Streams with a new protocol version (v2) for better performance and resumability, featuring payload compression and support for Redis Cluster read replicas. ## v0.7.90 * Improved error handling in the DR flow and set a 30-second default timeout for tests to ensure timely CI failure tracking. * Upgraded picomatch from 4.0.3 to 4.0.4 to address critical security vulnerabilities. ## v0.7.89 * Enhanced queue server metrics and established a requirement for the OpenTelemetry SDK. * Added a missing tag for the `COUNTER_RUN_FAILED_AFTER_RETRY` metric to improve monitoring accuracy. ## v0.7.87 * Implemented retries for run failures due to Redis-related streaming errors, with warning logs for visibility. ## v0.7.86 * Set default `DD_TRACE_ENABLED=false` in all images to reduce Orchestrion log noise for non-Datadog deployments. ## v0.7.84 * Downgraded noisy warning-level logs to info level to reduce log clutter, focusing on informational status messages like license lite mode and tracing disabled. * Enhanced Go `core-api-grpc` with Orchestrion DD APM tracing for automatic instrumentation and improved trace context propagation. ## v0.7.82 * Ensured A2A protocol compliance by preserving `kind` discriminators and using lowercase states/roles in responses for all client method name formats. ## v0.7.79 * Introduced beta release of the `swr` function for improved data fetching capabilities. * Upgraded the Go runtime to version 1.25.8 across all Dockerfiles and `go.mod` to address multiple CVEs. ## v0.7.77 * Introduced `HTTP_MAX_REQUEST_BODY_BYTES` config to limit HTTP request body size to 300MB, returning a 413 error for oversized requests to prevent memory exhaustion. * Added support for accessing store and checkpointer via config in JS graph factories to facilitate deep agent initialization. * Updated `pyasn1` dependency from version 0.6.2 to 0.6.3 to enhance security and fix parsing issues. * Added instrumentation to log time to first byte (TTFB) and response size for streaming endpoints, improving access log details. ## v0.7.76 * Relaxed `starlette-sse` version bounds to improve dependency compatibility. ## v0.7.75 * Correctly closed streams in `Runs.Enter` to prevent buffer issues and added a configurable environment variable for window size. ## v0.7.74 * Cleaned up some false error logs during queue shutdown operations. ## v0.7.73 * Improved thread search performance with `extract` by avoiding unnecessary detoasting of large JSONB values. ## v0.7.72 * Updated undici package from version 7.22.0 to 7.24.0 to address multiple security vulnerabilities. ## v0.7.71 * Cleaned up the API by removing unused parameters from Threads State Checkpoint and Runs create methods. * Fixed the `POST /threads/prune` with `strategy=delete` to ensure thread records are fully removed, not just checkpoint data. * Added A2A 1.0 `kind` discriminators to response objects, removed `{"task": ...}` wrapper, and fixed Anthropic streaming metadata issues. * Added support for custom encryption in the Redis queue to enhance data security. ## v0.7.69 * Added optional `timezone` field to crons, allowing `next_run_date` computation in user's specified timezone, defaulting to UTC. * Corrected the handling of 401 status codes in authentication exceptions to prevent incorrect defaulting to 403. ## v0.7.68 * Fixed issues with non-DR checkpoint AES JSON to improve functionality and extend test coverage. * Fixed A2A streaming to correctly emit interrupt artifacts as separate `artifact-update` events according to the specification. * Ensured secure tarfile extraction by only extracting validated and safe members to prevent arbitrary file write vulnerabilities. * Enhanced security by requiring an exact match for the `noauth` path in authentication middleware. * Fixed stale checkpoint values being written to thread state during rollback in multitasking strategy. ## v0.7.66 * Added a fallback to `LS_CHECKPOINTER_BACKEND` for default checkpointer configuration when `LANGGRAPH_CHECKPOINTER` is unset. ## v0.7.65 * Fixed a bug in `messages-tuple` streaming mode where `tool_call_chunks` contained `args_json` instead of `args`, preventing message reconstruction and causing errors. ## v0.7.64 * Enabled the MongoDB checkpointer URI to be set via `LS_MONGODB_URI` or `MONGODB_URI` environment variables, with precedence rules. ## v0.7.63 * Fixed a bug that could potentially deadlock queue instances by exhausting workers with invalid runs. ## v0.7.61 * Fixed a race condition to ensure graceful shutdown of the health and metric server. ## v0.7.59 * Updated the Redis queue to use zset with threads, reducing CPU usage by 25% and improving performance by eliminating unnecessary locking and optimizing indexes. ## v0.7.58 * Upgraded `langgraph-checkpoint` to 4.0.0 in `storage_postgres/uv.lock` to address CVE-2026-27794, with adjustments for dependency pinning issues. ## v0.7.57 * Fixed a regression preventing all users from creating crons associated with system graphs. ## v0.7.56 * Added support for `ttl`, `index`, and `refresh_ttl` parameters in store HTTP API endpoints to align with the SDK and in-process store interface. * Added support for the `?include=ttl` query parameter in the `GET /threads/{thread_id}` endpoint to return TTL information. * Updated metrics reporting to accurately account for PostgreSQL and Redis connections, ensuring consistent statistics between GRPC and Python metrics. ## v0.7.55 * Fixed a bug that caused duplicate run scheduling in the new cron scheduler backend. * Refactored the `GET /docs` endpoint to read from a static OpenAPI spec for improved compatibility with custom ingress configurations. ## v0.7.54 * Resolved an issue where the custom encryption context for gRPC services was not loading correctly due to hardcoded values. ## v0.7.52 * Added feedback URLs to the `/wait` and `/join` endpoint responses under a `__feedback__` key when `feedback_keys` are supplied. * Added feedback\_keys support to the distributed runtime, including presigned feedback token generation using langsmith-go SDK. * Upgraded Werkzeug from version 3.1.5 to 3.1.6 to address a Windows security issue with special device names in multi-segment paths. * Upgraded Go runtime to 1.25.7 to address critical and high severity CVEs identified in vulnerability scans. ## v0.7.51 * Improved license check resilience during upstream outages, including a cached fallback, a 24-hour grace period, and automatic cleanup of Redis entries. * Ensured assistant descriptions and names are synced on startup when `LANGSERVE_GRAPHS` config changes. * Enhanced the Checkpointer API by introducing a two-level protocol hierarchy and fixing capability detection to support extended methods directly. ## v0.7.49 * Reserved metadata keys in request payloads are now silently stripped rather than causing a 422 error, enhancing user experience. * Fixed an issue where store default TTL was not applied to items written without an explicit TTL parameter. ## v0.7.46 * Structured error payloads in webhooks now include `error` and `message` fields, replacing the previous flat string format, which may affect systems parsing the `error` field. * Expanded store auth tests with namespace-rewriting to enhance namespace handling and cross-user isolation. ## v0.7.45 * Replaced null bytes with U+FFFD in all handling paths to prevent key collisions. ## v0.7.44 * Increase flexibility of database URI parser * Add additional validation on some payloads ## v0.7.40 * Fixed a regression in assistant creation by ensuring `metadata` and `config` are populated as empty objects `{}` instead of `null` when not provided. ## v0.7.39 * Ensured auth configuration is passed correctly in distributed runtime operations to improve executor functionality. * Added support for Red Hat UBI-9 based Docker images for enterprise customers using RHEL-based containers. * Added graceful shutdown handoff for distributed runtime, allowing in-flight runs to transfer to the next pod without using a retry attempt. ## v0.7.38 * Added `state_updated_at` field to threads for tracking meaningful state changes, allowing filtering and sorting based on these changes. * Added support for scheduling crons within the core system. * Ensured accurate display of the `https` protocol in agent cards using the x-forwarded-proto header for proper A2A client functionality. ## v0.7.37 * Added generic fallbacks for `acopy_thread`, `aprune`, and `adelete_for_runs` in the BYOC checkpointer adapter, simplifying implementation for custom checkpointers. ## v0.7.36 * Updated A2A protocol support to v1.0 RC, renamed JSON-RPC methods, added a ListTasks handler, and enhanced role, state, and part formats for improved integration and compliance. * Improved authentication filtering for `Crons.search()` and `Crons.count()` to prevent unauthorized thread information access. * Fixed gaps in BYOC checkpointer for copy, rollback, and namespace filtering operations, ensuring proper handling across different storage backends. ## v0.7.35 * Added an optional `context` parameter to MCP `tools/call` and A2A `message/send` and `message/stream` endpoints, enabling middleware to inject runtime context from headers. ## v0.7.33 * Enhanced the Redis fixture by removing custom checkpointer test skips, improving typed serialization, and adding missing Redis methods. * Resolved a stored XSS vulnerability in the handle\_ui endpoint by sanitizing message names in single-quoted HTML onload attributes. * Fixed an authorization bypass issue in `put_item` to ensure correct namespace rewrite by the auth handler. * Enforced assistant ownership checks during run creation, preventing execution on unowned assistants while ensuring system assistants remain accessible to all authenticated users. * Implemented AES encryption for checkpoint blobs and writes in the Go checkpointer when using LANGGRAPH\_AES\_KEY. * Implemented initial checkpointer gRPC servicer with all necessary methods and conversion helpers. ## v0.7.32 * Sanitized error messages in streams and A2A responses to protect sensitive information like database connection strings and internal hostnames. * Fixed a bug in `join_run_stream` to correctly handle multiple `stream_mode` parameters, ensuring proper parsing of stringified JSON lists. * Added build, test, and publish processes for Node.js 24 images, supporting the latest LTS version. * Enhanced custom checkpointer adapter with new capabilities and improved metadata enrichment for consistent API responses. * Added stricter version constraints for langgraph libraries in executor Docker images to prevent unintended upgrades. * Enhanced security by sanitizing SSE event and id fields to prevent CR/LF injection. * Fixed an issue causing cron-created runs to use default encryption contexts instead of properly propagating the specified ones. ## v0.7.31 * Corrected metadata reading functionality to ensure accurate data processing. ## v0.7.30 * Propagated cron metadata for more comprehensive scheduling information. * Merges cron metadata on `PATCH` requests to align with other endpoints by preserving existing data. ## v0.7.29 * Refined authentication semantics for cron creation to prevent privilege escalation and ensure independent filtering for crons, threads, and assistants. * Validated tar file entries to prevent directory traversal vulnerabilities in the cloudflared download process. * Added an IDs filter to the `SearchThreadsRequest` to streamline thread endpoint operations. * Updated the fallback mechanism to use a Python Postgres connection for thread state, fixing issues with worker completion checkpoints. * Introduced a feature-flagged initial version of the Redis queue implementation with ongoing updates. ## v0.7.28 * Internal maintenance and stability improvements for MCP and gRPC. ## v0.7.27 * Improved MCP tool input schemas by removing common message types for cleaner tool definitions. * Added name sanitization for MCP tools to ensure valid tool names. ## v0.7.26 * Added validation for system keys on ingress. ## v0.7.25 * Switched the Python queue worker to use core go `Runs.next()`. * Fixed a monitoring issue in the long query monitor ## v0.7.24 * Optimized Postgres connection handling to prevent hitting connection limits under high load and removed unnecessary error logs. * Switched to a new backend for runs management and streaming using gRPC. ## v0.7.23 * Corrected the unmarshaling process for the `input` field in `RunCommand` to ensure accurate data mapping and enable a previously gated JS test. * Ensured race condition handling for run streaming by fully subscribing before execution starts, with added support for the `FF_LOG_DROPPED_EVENTS` environment variable. ## v0.7.22 * Ensured `get_store()` works in custom routes, enabling Store access from user-defined Starlette endpoints. ## v0.7.21 * Support for PATCH /crons/ ## v0.7.19 * Custom encryption improvements for core API. ## v0.7.18 * Update thread streaming for core API usage. ## v0.7.17 * Instrumentation for OTEL now requires explicit opt-in with `LS_APM_OTEL_ENABLED=true` for improved control. ## v0.7.16 * Switched threads streaming to the new gRPC backend for improved performance. * Introduced replica tracing in DR for enhanced evaluation capabilities in Studio. ## v0.7.15 * Added support for pausing crons with a new `is_enabled` field, allowing only enabled crons to be executed. * Introduced gRPC server support for JSON encryption and decryption operations. ## v0.7.14 * Ensured selected system fields are excluded from custom encryption to prevent unnecessary encryption of non-sensitive data. * Introduced a custom checkpointer adapter with unit tests to validate implementation checks. ## v0.7.13 * Fixed a bug where the app state was not properly preserved through requests when a mount prefix was set. ## v0.7.11 * Added configuration to control which payload fields can be exposed to webhooks. * Updated all dependencies in the `/api/langgraph_api/js` group, including `@langchain/core`, `hono`, `@types/react`, and `prettier`, to the latest versions for improved performance and security. * Upgraded `hono` from version 4.11.4 to 4.11.7 to address multiple security vulnerabilities in the middleware. ## v0.7.10 * Increased the gRPC server startup timeout to 1 minute to prevent occasional connection timeouts with the core server. * Updated @langchain/langgraph from version 1.1.0 to 1.1.2, introducing mixed schema support for StateGraph and type bag patterns for GraphNode and ConditionalEdgeRouter utilities. ## v0.7.9 * A2A `messageId` is now mapped to LangChain message IDs for proper message tracking across protocols. ## v0.7.7 * Ensured preservation of custom configurable fields in checkpoint metadata during gRPC serialization. ## v0.7.5 * Enforced custom encryption for values, interrupts, and errors when setting a thread's status, resolving previous inconsistencies. * Added A2A validation checks in `message/stream` and `message/send` routes for `parts`, `role`, and `messageId` fields. * Added native A2A interrupt support: `input-required` state is now returned when graphs are interrupted. Use the new `command` parameter in `message/stream` and `message/send` requests to resume with a `Command` payload. * Mounted `.well-known/agent-card.json` under `/a2a/{assistant_id}/` for A2A agent discovery. * Added proper A2A error codes for task existence checks in `tasks/cancel`. ## v0.7.4 * Fixed a bug with Redis URL parsing for `ssl_cert_reqs` field, ensuring compatibility with redis-go. * Added a gRPC client for streaming runs, controlled by the `FF_USE_CORE_API` feature flag. ## v0.7.2 * Updated `@langchain/langgraph` to version 1.1.0, introducing type utilities for graph nodes and conditional edges for enhanced TypeScript ergonomics. ## v0.7.0 * Switched to using the Go assistants implementation by default for improved performance. * Added `LANGGRAPH_AES_JSON_KEYS` configuration to enable AES encryption for specified JSON fields using a key name allowlist. ## v0.6.39 * Added gRPC client support for `Threads.State()` to the Python `core-api`, improving thread ID and run counting operations. ## v0.6.36 * Validated the length of `$and` and `$or` in auth filters and optimized unnecessary root-level filters. ## v0.6.35 * Unified the error format by removing the `code` field and standardizing all errors to return JSON with a `detail` field. ## v0.6.34 * Small fixes for feature-flagged internal environments (unreleased). ## v0.6.33 * Small fixes for feature-flagged internal environments (unreleased). ## v0.6.32 * Small fixes for feature-flagged internal environments (unreleased). ## v0.6.31 * Properly respected the disable\_a2a setting to ensure accurate configuration handling. ## v0.6.29 * Fix minor bugs. ## v0.6.28 * Added support for `ParentCommand` to correctly propagate control to parent graphs, enhancing command handling and navigation. * Added a Python gRPC client for managing run operations, enhancing consistency between Go and Python implementations. ## v0.6.27 * Fixed a regression issue in handling empty thread metadata. ## v0.6.26 * Fixed the port configuration issue for the persistence gRPC server. ## v0.6.25 * Ran the core-api gRPC server in the executor tier to support loopback API calls in graphs and removed unnecessary configuration for disabling the server. ## v0.6.24 * Fixed the behavior of the liveness probe in the executor tier, addressing issues from version 0.6.23. ## v0.6.23 * Integrated gRPC server health check with `/ok` endpoint in liveness probe to ensure proper startup coordination. * Reverted the previous change to disable the checkpointer and added a condition to enable RemoteCheckpointer only during testing. * Suppressed `langgraph_auth_*` and `langgraph_request_id` fields in checkpoint metadata to prevent inclusion of transient user data. ## v0.6.22 * Resolved an error caused by missing encryption contexts when using blob-only custom encryption, ensuring proper function without errors. ## v0.6.21 * Introduced a Python gRPC client for run operations, including `Search`, `Get`, `Delete`, `Cancel`, `Stats`, and `Sweep`, with updated API implementation and a new unit test suite for enum mappings. ## v0.6.19 * Reproduced OSS implementations of `get_state` and `update_state` in the engine server and re-enabled `test_weather_subgraph`. ## v0.6.18 * Added functionality to enforce specific license claims for self-hosted Enterprise users, enabling remote disabling of the Agent Builder product. * Added a new Prune endpoint for better resource management. * Merged graph configuration with invoke configuration in Pregel, giving precedence to invoke settings. * Introduced the `include=ttl` query parameter to the GET /threads/ endpoint for optional TTL information retrieval without affecting standard read performance. * Introduced a `keep_latest` TTL strategy to preserve the latest state while pruning older checkpoints via the core API. ## v0.6.17 * Ensured ongoing runs are stopped when an agent is deleted to prevent lingering processes. ## v0.6.16 * Streamlined and consolidated run operations in the Go persistence layer, improving efficiency and consistency across packages. ## v0.6.15 * Improved the utility converting custom route docstrings to OpenAPI schema content by adding error handling when parsing docstrings, applicable for users with custom Starlette apps. ## v0.6.12 * Improved resolve\_embeddings to be more robust, enabling multiple calls without errors. * Updated `@langchain/langgraph` from version 1.0.4 to 1.0.7, adding support for resumableStreams on remote graphs and undeprecating toolsCondition. * Implemented `RemoteCheckpointer` to enable subgraph checkpointing, enhancing task execution reliability. ## v0.6.11 * Made the maximum number of retries configurable for enhanced customization. ## v0.6.10 * Ensured run cancellation only processes 'message' type Redis events, improving pubsub client reliability. * Added custom encryption for the Store API `value` field, allowing users to choose which keys to encrypt for enhanced security. * Enabled streaming for subgraph custom events by updating TeeStream to handle event types separately. ## v0.6.9 * Enforced stable JSON keys for custom encryption, removed model-type-specific custom JSON functions, and improved error handling for double-encryption scenarios. ## v0.6.8 * Added profiling feature to enhance performance analysis and monitoring. ## v0.6.7 * Logged server startup time for improved monitoring and diagnostics. ## v0.6.5 * Added a warning log that triggers during import time for improved visibility. ## v0.6.4 * Enhanced custom encryption by parallelizing metadata and config processes, added encryption for thread.config and some checkpoints, improved tests and schema consistency. * Ensured the Go server starts as `core-api` in the queue entrypoint for consistent runtime behavior. ## v0.6.2 * Resolved an issue that caused duplicate calls to middleware when `mount_prefix` was specified. ## v0.6.0 This minor version updates the streaming APIs `/join-stream` and `/stream` behavior with respect to the `last-event-id` parameter to align with the SSE spec. Previously, passing a last-event-id would return that message in addition to any following messages. Going forward, these APIs will only return new messages following the provided last-event-id. For example, with the following stream, previously passing a last-event-id of `2` would return the messages with ids `2` and `3`, but will now only return the message with id `3`: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "id": 1, "event": "message", "data": { "content": "Excluded" } }, { "id": 2, "event": "message", "data": { "content": "Passed last-event-id" } }, { "id": 3, "event": "message", "data": { "content": "Included" } } ``` This bump also includes some fixes, including a bug exposing unintended internal events in run streams. ## v0.5.42 * Modified the Go server to rely solely on the CLI `-service` flag for determining service mode, ignoring the globally set `FF_USE_CORE_API` for better deployment specificity. ## v0.5.41 Fixed an issue with cron jobs in hybrid mode by ensuring proper initialization of the ENTERPRISE\_SAAS global flag. ## v0.5.39 * Completed the implementation of custom encryptions for runs and crons, along with simplifying encryption processes. * Introduced support for streaming subgraph events in both `values` and `updates` stream modes. ## v0.5.38 * Implemented complete custom encryption for threads, ensuring all thread data is properly secured and encrypted. * Ensured Redis attempt flags are consistently expired to prevent stale data. * Added core authentication and support for OR/AND filters, enhancing security and flexibility. ## v0.5.37 Added a `name` parameter to the assistants count API for improved search flexibility. ## v0.5.36 * Introduced configurable webhook support, allowing users to customize submitted webhooks and headers. * Added an `/ok` endpoint at the root for easier health checks and simplified configuration. ## v0.5.34 Introduced custom encryption middleware, allowing users to define their own encryption methods for enhanced data protection. ## v0.5.33 Set Uvicorn's keep-alive timeout to 75 seconds to prevent occasional 502 errors and improve connection handling. ## v0.5.32 Introduced OpenTelemetry telemetry agent with support for New Relic integration. ## v0.5.31 Added Py-Spy profiling for improved analysis of deployment performance, with some limitations on coverage. ## v0.5.30 * Always configure loopback transport clients to enhance reliability. * Ensured authentication headers are passed for remote non-stream methods in JS. ## v0.5.28 * Introduced a faster, Rust-based implementation of uuid7 to improve performance, now used in langsmith and langchain-core. * Added support for `$or` and `$and` in PostgreSQL auth filters to enable complex logic in authentication checks. * Capped psycopg and psycopg-pool versions to prevent infinite waiting on startup. ## v0.5.27 * Ensured `runs.list` with filters returns only run fields, preventing incorrect status data from being included. * (JS) Updated `uuid` from version 10.0.0 to 13.0.0. and `exit-hook` from version 4.0.0 to 5.0.1. ## v0.5.26 Resolved issues with `store.put` when used without AsyncBatchedStore in the JavaScript environment. ## v0.5.25 * Introduced the ability to search assistants by their `name` using a new endpoint. * Casted store\_get return types to tuple in JavaScript to ensure type consistency. ## v0.5.24 * Added executor metrics for Datadog and enhanced core stream API metrics for better performance tracking. * Disabled Redis Go maintenance notifications to prevent startup errors with unsupported commands in Redis versions below 8. ## v0.5.20 Resolved an error in the executor service that occurred when handling large messages. ## v0.5.19 Upgraded built-in langchain-core to version 1.0.7 to address a prompt formatting vulnerability. ## v0.5.18 Introduced persistent cron threads with `on_run_completed: {keep,delete}` for enhanced cron management and retrieval options. ## v0.5.17 Enhanced task handling to support multiple interrupts, aligning with open-source functionality. ## v0.5.15 Added custom JSON unmarshalling for `Resume` and `Goto` commands to fix map-style null resume interpretation issues. ## v0.5.14 Ensured `pg make start` command functions correctly with core-api enabled. ## v0.5.13 Support `include` and `exclude` (plural form key for `includes` and `excludes`) since a doc incorrectly claimed support for that. Now the server accepts either. ## v0.5.11 * Ensured auth handlers are applied consistently when streaming threads, aligning with recent security practices. * Bumped `undici` dependency from version 6.21.3 to 7.16.0, introducing various performance improvements and bug fixes. * Updated `p-queue` from version 8.0.1 to 9.0.0, introducing new features and breaking changes, including the removal of the `throwOnTimeout` option. ## v0.5.10 Implemented healthcheck calls in the queue /ok handler to improve Kubernetes liveness and readiness probe compatibility. ## v0.5.9 * Resolved an issue causing an "unbound local error" for the `elapsed` variable during a SIGINT interruption. * Mapped the "interrupted" status to A2A's "input-required" status for better task status alignment. ## v0.5.8 * Ensured environment variables are passed as a dictionary when starting langgraph-ui for compatibility with `uvloop`. * Implemented CRUD operations for runs in Go, simplifying JSON merges and improving transaction readability, with PostgreSQL as a reference. ## v0.5.7 Replaced no-retry Redis client with a retry client to handle connection errors more effectively and reduced corresponding logging severity. ## v0.5.6 * Added pending time metrics to provide better insights into task waiting times. * Replaced `pb.Value` with `ChannelValue` to streamline code structure. ## v0.5.5 Made the Redis `health_check_interval` more frequent and configurable for better handling of idle connections. ## v0.5.4 Implemented `ormsgpack` with `OPT_REPLACE_SURROGATES` and updated for compatibility with the latest FastAPI release affecting custom authentication dependencies. ## v0.5.2 Added retry logic for PostgreSQL connections during startup to enhance deployment reliability and improved error logging for easier debugging. ## v0.5.1 * Resolved an issue where persistence was not functioning correctly with LangChain.js's createAgent feature. * Optimized assistants CRUD performance by improving database connection pooling and gRPC client reuse, reducing latency for large payloads. ## v0.5.0 This minor version now requires langgraph-checkpoint versions later than 3.0 to prevent a deserialization vulnerability in earlier versions of the langgraph-checkpoint library. The `langgraph-checkpoint` library is compatible with `langgraph` minor versions 0.4, 0.5, 0.6, and 1.0. This version removes default support for deserialization of payloads saved using the "json" type, which has never been the default. By default, objects are serialized using msgpack. Under certain uncommon situations, payloads were serialized using an older "json" mode. If those payloads contained custom python objects, those will no longer be deserializable unless you provide a `serde` config: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "checkpointer": { "serde": { "allowed_json_modules": [ ["my_agent", "my_file", "SomeType"], ] } } } ``` ## v0.4.47 * Validated and auto-corrected environment configuration types using TypeAdapter. * Added support for LangChain.js and LangGraph.js version 1.x, ensuring compatibility. * Updated hono library from version 4.9.7 to 4.10.3, addressing a CORS middleware security issue and enhancing JWT audience validation. * Introduced a modular benchmark framework, adding support for assistants and streams, with improvements to the existing ramp benchmark methodology. * Introduced a gRPC API for core threads CRUD operations, with updated Python and TypeScript clients. * Updated `hono` package from version 4.9.7 to 4.10.2, including security improvements for JWT audience validation. * Updated `hono` dependency from version 4.9.7 to 4.10.3 to fix a security issue and improve CORS middleware handling. * Introduced basic CRUD operations for threads, including create, get, patch, delete, search, count, and copy, with support for Go, gRPC server, and Python and TypeScript clients. ## v0.4.46 Added an option to enable message streaming from subgraph events, giving users more control over event notifications. ## v0.4.45 * Implemented support for authorization on custom routes, controlled by the `enable_custom_route_auth` flag. * Set default tracing to off for improved performance and simplified debugging. ## v0.4.44 Used Redis key prefix for license-related keys to prevent conflicts with existing setups. ## v0.4.43 Implemented a health check for Redis connections to prevent them from idling out. ## v0.4.40 * Prevented duplicate messages in resumable run and thread streams by addressing a race condition and adding tests to ensure consistent behavior. * Ensured that runs don't start until the pubsub subscription is confirmed to prevent message drops on startup. * Renamed platform from langgraph to improve clarity and branding. * Reset PostgreSQL connections after use to prevent lock holding and improved error reporting for transaction issues. ## v0.4.39 * Upgraded `hono` from version 4.7.6 to 4.9.7, addressing a security issue related to the `bodyLimit` middleware. * Allowed customization of the base authentication URL to enhance flexibility. * Pinned the 'ty' dependency to a stable version using 'uv' to prevent unexpected linting failures. ## v0.4.38 * Replaced `LANGSMITH_API_KEY` with `LANGSMITH_CONTROL_PLANE_API_KEY` to support hybrid deployments requiring license verification. * Introduced self-hosted log ingestion support, configurable via `SELF_HOSTED_LOGS_ENABLED` and `SELF_HOSTED_LOGS_ENDPOINT` environment variables. ## v0.4.37 Required create permissions for copying threads to ensure proper authorization. ## v0.4.36 * Improved error handling and added a delay to the sweep loop for smoother operation during Redis downtime or cancellation errors. * Updated the queue entrypoint to start the core-api gRPC server when `FF_USE_CORE_API` is enabled. * Introduced checks for invalid configurations in assistant endpoints to ensure consistency with other endpoints. ## v0.4.35 * Resolved a timezone issue in the core API, ensuring accurate time data retrieval. * Introduced a new `middleware_order` setting to apply authentication middleware before custom middleware, allowing finer control over protected route configurations. * Logged the Redis URL when errors occur during Redis client creation. * Improved Go engine/runtime context propagation to ensure consistent execution flow. * Removed the unnecessary `assistants.put` call from the executor entrypoint to streamline the process. ## v0.4.34 Blocked unauthorized users from updating thread TTL settings to enhance security. ## v0.4.33 * Improved error handling for Redis locks by logging `LockNotOwnedError` and extending initial pool migration lock timeout to 60 seconds. * Updated the BaseMessage schema to align with the latest langchain-core version and synchronized build dependencies for consistent local development. ## v0.4.32 * Added a GO persistence layer to the API image, enabling GRPC server operation with PostgreSQL support and enhancing configurability. * Set the status to error when a timeout occurs to improve error handling. ## v0.4.30 * Added support for context when using `stream_mode="events"` and included new tests for this functionality. * Added support for overriding the server port using `$LANGGRAPH_SERVER_PORT` and removed an unnecessary Dockerfile `ARG` for cleaner configuration. * Applied authorization filters to all table references in thread delete CTE to enhance security. * Introduced self-hosted metrics ingestion capability, allowing metrics to be sent to an OTLP collector every minute when the corresponding environment variables are set. * Ensured that the `set_latest` function properly updates the name and description of the version. ## v0.4.29 Ensured proper cleanup of redis pubsub connections in all scenarios. ## v0.4.28 * Added a format parameter to the queue metrics server for enhanced customization. * Corrected `MOUNT_PREFIX` environment variable usage in CLI for consistency with documentation and to prevent confusion. * Added a feature to log warnings when messages are dropped due to no subscribers, controllable via a feature flag. * Added support for Bookworm and Bullseye distributions in Node images. * Consolidated executor definitions by moving them from the `langgraph-go` repository, improving manageability and updating the checkpointer setup method for server migrations. * Ensured correct response headers are sent for a2a, improving compatibility and communication. * Consolidated PostgreSQL checkpoint implementation, added CI testing for the `/core` directory, fixed RemoteStore test errors, and enhanced the Store implementation with transactions. * Added PostgreSQL migrations to the queue server to prevent errors from graphs being added before migrations are performed. ## v0.4.27 Replaced `coredis` with `redis-py` to improve connection handling and reliability under high traffic loads. ## v0.4.24 * Added functionality to return full message history for A2A calls in accordance with the A2A spec. * Added a `LANGGRAPH_SERVER_HOST` environment variable to Dockerfiles to support custom host settings for dual stack mode. ## v0.4.23 Use a faster message codec for redis streaming. ## v0.4.22 Ported long-stream handling to the run stream, join, and cancel endpoints for improved stream management. ## v0.4.21 * Added A2A streaming functionality and enhanced testing with the A2A SDK. * Added Prometheus metrics to track language usage in graphs, middleware, and authentication for improved insights. * Fixed bugs in Open Source Software related to message conversion for chunks. * Removed await from pubsub subscribes to reduce flakiness in cluster tests and added retries in the shutdown suite to enhance API stability. ## v0.4.20 Optimized Pubsub initialization to prevent overhead and address subscription timing issues, ensuring smoother run execution. ## v0.4.19 Removed warnings from psycopg by addressing function checks introduced in version 3.2.10. ## v0.4.17 Filtered out logs with mount prefix to reduce noise in logging output. ## v0.4.16 * Added support for implicit thread creation in a2a to streamline operations. * Improved error serialization and emission in distributed runtime streams, enabling more comprehensive testing. ## v0.4.13 * Monitored queue status in the health endpoint to ensure correct behavior when PostgreSQL fails to initialize. * Addressed an issue with unequal swept ID lengths to improve log clarity. * Enhanced streaming outputs by avoiding re-serialization of DR payloads, using msgpack byte inspection for json-like parsing. ## v0.4.12 * Ensured metrics are returned even when experiencing database connection issues. * Optimized update streams to prevent unnecessary data transmission. * Upgraded `hono` from version 4.9.2 to 4.9.6 in the `storage_postgres/langgraph-api-server` for improved URL path parsing security. * Added retries and an in-memory cache for LangSmith access calls to improve resilience against single failures. ## v0.4.11 Added support for TTL (time-to-live) in thread updates. ## v0.4.10 In distributed runtime, update serde logic for final checkpoint -> thread setting. ## v0.4.9 * Added support for filtering search results by IDs in the search endpoint for more precise queries. * Included configurable headers for assistant endpoints to enhance request customization. * Implemented a simple A2A endpoint with support for agent card retrieval, task creation, and task management. ## v0.4.7 Stopped the inclusion of x-api-key to enhance security. ## v0.4.6 Fixed a race condition when joining streams, preventing duplicate start events. ## v0.4.5 * Ensured the checkpointer starts and stops correctly before and after the queue to improve shutdown and startup efficiency. * Resolved an issue where workers were being prematurely cancelled when the queue was cancelled. * Prevented queue termination by adding a fallback for cases when Redis fails to wake a worker. ## v0.4.4 * Set the custom auth thread\_id to None for stateless runs to prevent conflicts. * Improved Redis signaling in the Go runtime by adding a wakeup worker and Redis lock implementation, and updated sweep logic. ## v0.4.3 * Added stream mode to thread stream for improved data processing. * Added a durability parameter to runs for improved data persistence. ## v0.4.2 Ensured pubsub is initialized before creating a run to prevent errors from missing messages. ## v0.4.0 Minor version 0.4 comes with a number of improvements as well as some breaking changes. * Emitted attempt messages correctly within the thread stream. * Reduced cluster conflicts by using only the thread ID for hashing in cluster mapping, prioritizing efficiency with stream\_thread\_cache. * Introduced a stream endpoint for threads to track all outputs across sequentially executed runs. * Made the filter query builder in PostgreSQL more robust against malformed expressions and improved validation to prevent potential security risks. This minor version also includes a couple of breaking changes to improve the usability and security of the service: * In this minor version, we stop the practice of automatically including headers as configurable values in your runs. You can opt-in to specific patterns by setting **configurable\_headers** in your agent server config. * Run stream event IDs (for resumable streams) are now in the format of `ms-seq` instead of the previous format. We retain backwards compatibility for the old format, but we recommend using the new format for new code. ## v0.3.4 * Added custom Prometheus metrics for Redis/PG connection pools and switched the queue server to Uvicorn/Starlette for improved monitoring. * Restored Wolfi image build by correcting shell command formatting and added a Makefile target for testing with nginx. ## v0.3.3 * Added timeouts to specific Redis calls to prevent workers from being left active. * Updated the Golang runtime and added pytest skips for unsupported functionalities, including initial support for passing store to node and message streaming. * Introduced a reverse proxy setup for serving combined Python and Node.js graphs, with nginx handling server routing, to facilitate a Postgres/Redis backend for the Node.js API server. ## v0.3.1 Added a statement timeout to the pool to prevent long-running queries. ## v0.3.0 * Set a default 15-minute statement timeout and implemented monitoring for long-running queries to ensure system efficiency. * Stop propagating run configurable values to the thread configuration, because this can cause issues on subsequent runs if you are specifying a checkpoint\_id. This is a **slight breaking change** in behavior, since the thread value will no longer automatically reflect the unioned configuration of the most recent run. We believe this behavior is more intuitive, however. * Enhanced compatibility with older worker versions by handling event data in channel names within ops.py. ## v0.2.137 Fixed an unbound local error and improved logging for thread interruptions or errors, along with type updates. ## v0.2.136 * Added enhanced logging to aid in debugging metaview issues. * Upgraded executor and runtime to the latest version for improved performance and stability. ## v0.2.135 Ensured async coroutines are properly awaited to prevent potential runtime errors. ## v0.2.134 Enhanced search functionality to improve performance by allowing users to select specific columns for query results. ## v0.2.133 * Added count endpoints for crons, threads, and assistants to enhance data tracking (#1132). * Improved SSH functionality for better reliability and stability. * Updated @langchain/langgraph-api to version 0.0.59 to fix an invalid state schema issue. ## v0.2.132 * Added Go language images to enhance project compatibility and functionality. * Printed internal PIDs for JS workers to facilitate process inspection via SIGUSR1 signal. * Resolved a `run_pkey` error that occurred when attempting to insert duplicate runs. * Added `ty run` command and switched to using uuid7 for generating run IDs. * Implemented the initial Golang runtime to expand language support. ## v0.2.131 Added support for `object agent spec` with descriptions in JS. ## v0.2.130 * Added a feature flag (FF\_RICH\_THREADS=false) to disable thread updates on run creation, reducing lock contention and simplifying thread status handling. * Utilized existing connections for `aput` and `apwrite` operations to improve performance. * Improved error handling for decoding issues to enhance data processing reliability. * Excluded headers from logs to improve security while maintaining runtime functionality. * Fixed an error that prevented mapping slots to a single node. * Added debug logs to track node execution in JS deployments for improved issue diagnosis. * Changed the default multitask strategy to enqueue, improving throughput by eliminating the need to fetch inflight runs during new run insertions. * Optimized database operations for `Runs.next` and `Runs.sweep` to reduce redundant queries and improve efficiency. * Improved run creation speed by skipping unnecessary inflight runs queries. ## v0.2.129 * Stopped passing internal LGP fields to context to prevent breaking type checks. * Exposed content-location headers to ensure correct resumability behavior in the API. ## v0.2.128 Ensured synchronized updates between `configurable` and `context` in assistants, preventing setup errors and supporting smoother version transitions. ## v0.2.127 Excluded unrequested stream modes from the resumable stream to optimize functionality. ## v0.2.126 * Made access logger headers configurable to enhance logging flexibility. * Debounced the Runs.stats function to reduce the frequency of expensive calls and improve performance. * Introduced debouncing for sweepers to enhance performance and efficiency (#1147). * Acquired a lock for TTL sweeping to prevent database spamming during scale-out operations. ## v0.2.125 Updated tracing context replicas to use the new format, ensuring compatibility. ## v0.2.123 Added an entrypoint to the queue replica for improved deployment management. ## v0.2.122 Utilized persisted interrupt status in `join` to ensure correct handling of user's interrupt state after completion. ## v0.2.121 * Consolidated events to a single channel to prevent race conditions and optimize startup performance. * Ensured custom lifespans are invoked on queue workers for proper setup, and added tests. ## v0.2.120 * Restored the original streaming behavior of runs, ensuring consistent inclusion of interrupt events based on `stream_mode` settings. * Optimized `Runs.next` query to reduce average execution time from \~14.43ms to \~2.42ms, improving performance. * Added support for stream mode "tasks" and "checkpoints", normalized the UI namespace, and upgraded `@langchain/langgraph-api` for enhanced functionality. ## v0.2.117 Added a composite index on threads for faster searches with owner-based authentication and updated the default sort order to `updated_at` for improved query performance. ## v0.2.116 Reduced the default number of history checkpoints from 10 to 1 to optimize performance. ## v0.2.115 Optimized cache reuse to enhance application performance and efficiency. ## v0.2.113 Improved thread search pagination by updating response headers with `X-Pagination-Total` and `X-Pagination-Next` for better navigation. ## v0.2.112 * Ensured sync logging methods are awaited and added a linter to prevent future occurrences. * Fixed an issue where JavaScript tasks were not being populated correctly for JS graphs. ## v0.2.111 Fixed JS graph streaming failure by starting the heartbeat as soon as the connection opens. ## v0.2.110 Added interrupts as default values for join operations while preserving stream behavior. ## v0.2.109 Fixed an issue where config schema was missing when `config_type` was not set, ensuring more reliable configurations. ## v0.2.108 Prepared for LangGraph v0.6 compatibility with new context API support and bug fixes. ## v0.2.107 * Implemented caching for authentication processes to enhance performance and efficiency. * Optimized database performance by merging count and select queries. ## v0.2.106 Made log streams resumable, enhancing reliability and improving user experience when reconnecting. ## v0.2.105 Added a heapdump endpoint to save memory heap information to a file. ## v0.2.103 Used the correct metadata endpoint to resolve issues with data retrieval. ## v0.2.102 * Captured interrupt events in the wait method to preserve previous behavior from langgraph 0.5.0. * Added support for SDK structlog in the JavaScript environment for enhanced logging capabilities. ## v0.2.101 Corrected the metadata endpoint for self-hosted deployments. ## v0.2.99 * Improved license check by adding an in-memory cache and handling Redis connection errors more effectively. * Reloaded assistants to preserve manually created ones while discarding those removed from the configuration file. * Reverted changes to ensure the UI namespace for gen UI is a valid JavaScript property name. * Ensured that the UI namespace for generated UI is a valid JavaScript property name, improving API compliance. * Enhanced error handling to return a 422 status code for unprocessable entity requests. ## v0.2.98 Added context to langgraph nodes to improve log filtering and trace visibility. ## v0.2.97 * Improved interoperability with the ckpt ingestion worker on the main loop to prevent task scheduling issues. * Delayed queue worker startup until after migrations are completed to prevent premature execution. * Enhanced thread state error handling by adding specific metadata and improved response codes for better clarity when state updates fail during creation. * Exposed the interrupt ID when retrieving the thread state to improve API transparency. ## v0.2.96 Added a fallback mechanism for configurable header patterns to handle exclude/include settings more effectively. ## v0.2.95 * Avoided setting the future if it is already done to prevent redundant operations. * Resolved compatibility errors in CI by switching from `typing.TypedDict` to `typing_extensions.TypedDict` for Python versions below 3.12. ## v0.2.94 * Improved performance by omitting pending sends for langgraph versions 0.5 and above. * Improved server startup logs to provide clearer warnings when the DD\_API\_KEY environment variable is set. ## v0.2.93 Removed the GIN index for run metadata to improve performance. ## v0.2.92 Enabled copying functionality for blobs and checkpoints, improving data management flexibility. ## v0.2.91 Reduced writes to the `checkpoint_blobs` table by inlining small values (null, numeric, str, etc.). This means we don't need to store extra values for channels that haven't been updated. ## v0.2.90 Improve checkpoint writes via node-local background queueing. ## v0.2.89 Decoupled checkpoint writing from thread/run state by removing foreign keys and updated logger to prevent timeout-related failures. ## v0.2.88 Removed the foreign key constraint for `thread` in the `run` table to simplify database schema. ## v0.2.87 Added more detailed logs for Redis worker signaling to improve debugging. ## v0.2.86 Honored tool descriptions in the `/mcp` endpoint to align with expected functionality. ## v0.2.85 Added support for the `on_disconnect` field to `runs/wait` and included disconnect logs for better debugging. ## v0.2.84 Removed unnecessary status updates to streamline thread handling and updated version to 0.2.84. ## v0.2.83 * Reduced the default time-to-live for resumable streams to 2 minutes. * Enhanced data submission logic to send data to both Beacon and LangSmith instance based on license configuration. * Enabled submission of self-hosted data to a LangSmith instance when the endpoint is configured. ## v0.2.82 Addressed a race condition in background runs by implementing a lock using join, ensuring reliable execution across CTEs. ## v0.2.81 Optimized run streams by reducing initial wait time to improve responsiveness for older or non-existent runs. ## v0.2.80 Corrected parameter passing in the `logger.ainfo()` API call to resolve a TypeError. ## v0.2.79 * Fixed a JsonDecodeError in checkpointing with remote graph by correcting JSON serialization to handle trailing slashes properly. * Introduced a configuration flag to disable webhooks globally across all routes. ## v0.2.78 * Added timeout retries to webhook calls to improve reliability. * Added HTTP request metrics, including a request count and latency histogram, for enhanced monitoring capabilities. ## v0.2.77 * Added HTTP metrics to improve performance monitoring. * Changed the Redis cache delimiter to reduce conflicts with subgraph message names and updated caching behavior. ## v0.2.76 Updated Redis cache delimiter to prevent conflicts with subgraph messages. ## v0.2.74 Scheduled webhooks in an isolated loop to ensure thread-safe operations and prevent errors with PYTHONASYNCIODEBUG=1. ## v0.2.73 * Fixed an infinite frame loop issue and removed the dict\_parser due to structlog's unexpected behavior. * Throw a 409 error on deadlock occurrence during run cancellations to handle lock conflicts gracefully. ## v0.2.72 * Ensured compatibility with future langgraph versions. * Implemented a 409 response status to handle deadlock issues during cancellation. ## v0.2.71 Improved logging for better clarity and detail regarding log types. ## v0.2.70 Improved error handling to better distinguish and log TimeoutErrors caused by users from internal run timeouts. ## v0.2.69 Added sorting and pagination to the crons API and updated schema definitions for improved accuracy. ## v0.2.66 Fixed a 404 error when creating multiple runs with the same thread\_id using `on_not_exist="create"`. ## v0.2.65 * Ensured that only fields from `assistant_versions` are returned when necessary. * Ensured consistent data types for in-memory and PostgreSQL users, improving internal authentication handling. ## v0.2.64 Added descriptions to version entries for better clarity. ## v0.2.62 * Improved user handling for custom authentication in the JS Studio. * Added Prometheus-format run statistics to the metrics endpoint for better monitoring. * Added run statistics in Prometheus format to the metrics endpoint. ## v0.2.61 Set a maximum idle time for Redis connections to prevent unnecessary open connections. ## v0.2.60 * Enhanced error logging to include traceback details for dictionary operations. * Added a `/metrics` endpoint to expose queue worker metrics for monitoring. ## v0.2.57 * Removed CancelledError from retriable exceptions to allow local interrupts while maintaining retriability for workers. * Introduced middleware to gracefully shut down the server after completing in-flight requests upon receiving a SIGINT. * Reduced metadata stored in checkpoint to only include necessary information. * Improved error handling in join runs to return error details when present. ## v0.2.56 Improved application stability by adding a handler for SIGTERM signals. ## v0.2.55 * Improved the handling of cancellations in the queue entrypoint. * Improved cancellation handling in the queue entry point. ## v0.2.54 * Enhanced error message for LuaLock timeout during license validation. * Fixed the \$contains filter in custom auth by requiring an explicit ::text cast and updated tests accordingly. * Ensured project and tenant IDs are formatted as UUIDs for consistency. ## v0.2.53 * Resolved a timing issue to ensure the queue starts only after the graph is registered. * Improved performance by setting thread and run status in a single query and enhanced error handling during checkpoint writes. * Reduced the default background grace period to 3 minutes. ## v0.2.52 * Now logging expected graphs when one is omitted to improve traceability. * Implemented a time-to-live (TTL) feature for resumable streams. * Improved query efficiency and consistency by adding a unique index and optimizing row locking. ## v0.2.51 * Handled `CancelledError` by marking tasks as ready to retry, improving error management in worker processes. * Added LG API version and request ID to metadata and logs for better tracking. * Added LG API version and request ID to metadata and logs to improve traceability. * Improved database performance by creating indexes concurrently. * Ensured postgres write is committed only after the Redis running marker is set to prevent race conditions. * Enhanced query efficiency and reliability by adding a unique index on thread\_id/running, optimizing row locks, and ensuring deterministic run selection. * Resolved a race condition by ensuring Postgres updates only occur after the Redis running marker is set. ## v0.2.46 Introduced a new connection for each operation while preserving transaction characteristics in Threads state `update()` and `bulk()` commands. ## v0.2.45 * Enhanced streaming feature by incorporating tracing contexts. * Removed an unnecessary query from the Crons.search function. * Resolved connection reuse issue when scheduling next run for multiple cron jobs. * Removed an unnecessary query in the Crons.search function to improve efficiency. * Resolved an issue with scheduling the next cron run by improving connection reuse. ## v0.2.44 * Enhanced the worker logic to exit the pipeline before continuing when the Redis message limit is reached. * Introduced a ceiling for Redis message size with an option to skip messages larger than 128 MB for improved performance. * Ensured the pipeline always closes properly to prevent resource leaks. ## v0.2.43 * Improved performance by omitting logs in metadata calls and ensuring output schema compliance in value streaming. * Ensured the connection is properly closed after use. * Aligned output format to strictly adhere to the specified schema. * Stopped sending internal logs in metadata requests to improve privacy. ## v0.2.42 * Added timestamps to track the start and end of a request's run. * Added tracer information to the configuration settings. * Added support for streaming with tracing contexts. ## v0.2.41 Added locking mechanism to prevent errors in pipelined executions. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-server-changelog.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Agent Server changelog Source: https://docs.langchain.com/langsmith/agent-server-changelog-link ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-server-changelog-link.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Distributed tracing with Agent Server Source: https://docs.langchain.com/langsmith/agent-server-distributed-tracing Unify traces when calling your deployed Agent Server from another service using RemoteGraph or the SDK. When you call a deployed [Agent Server](/langsmith/agent-server) from another service, you can propagate trace context so that the entire request appears as a single unified trace in LangSmith. This uses LangSmith's [distributed tracing](/langsmith/distributed-tracing) capabilities, which propagate context via HTTP headers. ## How it works Distributed tracing links runs across services using context propagation headers: 1. The **client** infers the trace context from the current run and sends it as HTTP headers. 2. The **server** reads the headers and adds them to the run's config and metadata as `langsmith-trace` and `langsmith-project` configurable values. You can choose to use these to set the tracing context for a given run when your agent is used. The headers used are: * `langsmith-trace`: Contains the trace's dotted order. * `baggage`: Specifies the LangSmith project and other optional tags and metadata. To opt-in to distributed tracing, both client and server need to opt in. ## Configure the server To accept distributed trace context, your graph must read the trace headers from the config and set the tracing context. The headers are passed through the `configurable` field as `langsmith-trace` and `langsmith-project`. Distributed-tracing headers (`langsmith-trace`, `baggage`) are consumed as trusted tracing context. Only configure your server to apply inbound trace context for deployments called by trusted, internal services. If your Agent Server receives requests directly from untrusted third parties or the public internet, do not propagate these headers into the tracing context: strip them at your gateway or proxy instead. Trusting `baggage` from an external caller lets them influence how your runs are recorded. ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import contextlib import langsmith as ls from langgraph.graph import StateGraph, MessagesState # Define your graph builder = StateGraph(MessagesState) # ... add nodes and edges ... my_graph = builder.compile() @contextlib.contextmanager async def graph(config): configurable = config.get("configurable", {}) parent_trace = configurable.get("langsmith-trace") parent_project = configurable.get("langsmith-project") # If you want to also include metadata and tags from the client metadata = configurable.get("langsmith-metadata") tags = configurable.get("langsmith-tags") with ls.tracing_context(parent=parent_trace, project_name=parent_project, metadata=metadata, tags=tags): yield my_graph ``` Export this `graph` function in your `langgraph.json`: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "graphs": { "agent": "./src/agent.py:graph" } } ``` ## Connect from the client Set `distributed_tracing=True` when initializing [`RemoteGraph`](https://reference.langchain.com/python/langgraph/pregel/remote/RemoteGraph). This automatically propagates trace headers on all requests. ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langgraph.graph import StateGraph from langgraph.pregel.remote import RemoteGraph remote_graph = RemoteGraph( "agent", url="", distributed_tracing=True, # Enable trace propagation ) def subgraph_node(query: str): # Trace context is automatically propagated return remote_graph.invoke({ "messages": [{"role": "user", "content": query}] })['messages'][-1]['content'] # The RemoteGraph is called in the context of some on going work. # This could be a parent LangGraph agent, code traced with `@ls.traceable`, # or any other instrumented code. graph = ( StateGraph(str) .add_node(subgraph_node) .add_edge("__start__", "subgraph_node") .compile() ) # The remote graph's execution will appear as a child of this trace result = graph.invoke("What's the weather in SF?") ``` If you're using the [LangGraph SDK](/langsmith/reference) directly, propagate trace headers manually using `run_tree.to_headers()`: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langgraph_sdk import get_client import langsmith as ls client = get_client(url="") with ls.trace("call_remote_agent", inputs={"query": query}) as rt: headers = rt.to_headers() async for chunk in client.runs.stream( thread_id=None, assistant_id="agent", input={"messages": [{"role": "user", "content": query}]}, stream_mode="values", headers=headers, # Pass trace headers ): pass return chunk result = await call_remote_agent("What's the weather in SF?") ``` ## Related * [Distributed tracing](/langsmith/distributed-tracing): General distributed tracing concepts and patterns * [RemoteGraph](/langsmith/use-remote-graph): Full guide to interacting with deployments using RemoteGraph ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-server-distributed-tracing.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# How to collect user feedback for Agent Server runs Source: https://docs.langchain.com/langsmith/agent-server-feedback This tutorial shows you how to collect user feedback for [Agent Server](/langsmith/agent-server) runs and automatically link them to [traces](/langsmith/observability-concepts#traces) in LangSmith. When creating a run, include the keys in the `feedback_keys` field of the request body. The response will return a pre-signed URL for each key, which your client can use to collect user feedback for the Agent Server run. LangSmith uses feedback to continuously improve the implementation of your agent. To learn more about how feedback works in LangSmith, refer to [LangSmith feedback](/langsmith/observability-concepts#feedback). ## How it works 1. Create a run and include `feedback_keys` in the request body. For example, when calling `POST /threads/{thread_id}/runs/stream`, set `feedback_keys` in the request body to: ``` ["user_liked", "user_disliked"] ``` 2. The `feedback` object from the response contains a pre-signed URL for each key. For example, the `feedback` object is: ``` { "user_liked": "https://api.smith.langchain.com/api/v1/feedback/tokens/ef19fedf-dcac-4cbb-a59c-00661efd6425", "user_disliked": "https://api.smith.langchain.com/api/v1/feedback/tokens/e952734e-c0a0-417b-a04d-fc2209691ed5" } ``` 3. Request the returned URL (e.g. `POST /api/v1/feedback/tokens/{token_id}`) to associate the feedback key with the trace generated from the Agent Server run. For more details, refer to the [LangSmith API reference](/langsmith/smith-api-ref). 4. LangSmith associates the submitted feedback with the run using the selected feedback key (e.g. `user_liked` or `user_disliked`). ## Call the streaming run API with `feedback_keys` Create a run and parse the `feedback` object from the response. ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langgraph_sdk import get_client client = get_client(url="", api_key="") thread = await client.threads.create() thread_id = thread["thread_id"] feedback_urls = {} async for event in client.runs.stream( thread_id, "agent", input={ "messages": [ {"role": "user", "content": "Tell me a joke about databases."} ] }, stream_mode="updates", feedback_keys=["user_liked", "user_disliked"], ): if event.event == "feedback": # Example: {"user_liked": ".../feedback/tokens/", "user_disliked": "..."} feedback_urls = event.data print("Feedback URLs:", feedback_urls) elif event.event == "updates": print(event.data) ``` ```javascript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { Client } from "@langchain/langgraph-sdk"; const client = new Client({ apiUrl: "", apiKey: "" }); const thread = await client.threads.create(); const threadId = thread.thread_id; let feedbackUrls = {}; const streamResponse = client.runs.stream(threadId, "agent", { input: { messages: [{ role: "user", content: "Tell me a joke about databases." }], }, streamMode: "updates", feedbackKeys: ["user_liked", "user_disliked"], }); for await (const event of streamResponse) { if (event.event === "feedback") { // Example: { user_liked: ".../feedback/tokens/", user_disliked: "..." } feedbackUrls = event.data; console.log("Feedback URLs:", feedbackUrls); } else if (event.event === "updates") { console.log(event.data); } } ``` ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url "/threads//runs/stream" \ --header "Content-Type: application/json" \ --header "x-api-key: " \ --data '{ "assistant_id": "agent", "input": { "messages": [ { "role": "user", "content": "Tell me a joke about databases." } ] }, "stream_mode": "updates", "feedback_keys": ["user_liked", "user_disliked"] }' ``` ## Handle the streamed `feedback` event The stream emits a `feedback` event like the following: ```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} event: feedback data: {"user_liked":"https://api.smith.langchain.com/api/v1/feedback/tokens/ef19fedf-dcac-4cbb-a59c-00661efd6425", "user_disliked": "https://api.smith.langchain.com/api/v1/feedback/tokens/e952734e-c0a0-417b-a04d-fc2209691ed5"} ``` Each key in `data` matches one of the values you passed in `feedback_keys`. Each value is a generated URL your client can call to submit feedback for that run. ## Submit feedback with the generated URL When the user chooses a feedback option, `POST` to the corresponding URL. `GET` is also supported. See the [LangSmith API reference](/langsmith/smith-api-ref) for more details. For example, if the user clicks a thumbs down button, call the `user_disliked` URL: ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request POST \ --url "https://api.smith.langchain.com/api/v1/feedback/tokens/e952734e-c0a0-417b-a04d-fc2209691ed5" \ --header "Content-Type: application/json" \ --data '{ "score": 1, "value": 0, "comment": "I didn't like this joke because it didn't make me laugh.", "correction": {}, "metadata": {} }' ``` `metadata` is not supported with `GET`. ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} curl --request GET \ --url "https://api.smith.langchain.com/api/v1/feedback/tokens/e952734e-c0a0-417b-a04d-fc2209691ed5?score=1&value=0&comment=I%20didn%27t%20like%20this%20joke%20because%20it%20didn%27t%20make%20me%20laugh.&correction=%7B%7D" ``` After this request succeeds, LangSmith records feedback on the trace using the key `user_disliked`. ## Optimize feedback data model The `user_liked` and `user_disliked` keys can also be modeled under a single key such as `user_score`. For example: * Use `key="user_score"` with `score=1` for `user_liked` * Use `key="user_score"` with `score=-1` for `user_disliked` This can simplify analysis because all user preference signals are grouped under one feedback key. The feedback data model is flexible and should be designed for your use case. For example, some applications may prefer separate boolean-style keys (`user_liked`, `user_disliked`), while others may prefer a single numeric score (`user_score`) or a richer rubric with multiple feedback keys. ## Productionize in a client UI A productionized solution will expose the generated feedback URLs through your frontend instead of calling them manually. Example high-level implementation: 1. Create the run from your backend or frontend. 2. Capture the `feedback` object and store the returned URLs. 3. Render feedback controls such as thumbs up/down buttons and feedback forms. 4. On feedback submission, `POST` or `GET` a feedback URL based on the user's feedback intent. 5. Optionally disable the feedback controls after submission and show confirmation to the user. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-server-feedback.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Agent Server Source: https://docs.langchain.com/langsmith/agent-server-overview Configure and operate the LangSmith Agent Server runtime, including capabilities, application structure, auth, and customization. Configure and build applications on the [Agent Server](/langsmith/agent-server) runtime. Once deployed, agents work with three primitives: [**assistants**](/langsmith/assistants) for configuration, [**threads**](/langsmith/use-threads) for state, and [**runs**](/langsmith/runs) for workloads. The pages in this tab cover the capabilities Agent Server provides, how to [structure your application](/langsmith/application-structure), and how to [secure](/langsmith/auth) and [customize](/langsmith/custom-routes) the server. ## Capabilities Structure your app, configure dependencies for Python, JavaScript, and monorepos, and connect agents with RemoteGraph, semantic search, TTLs, and CI/CD. Work with assistants, threads, runs, and cron jobs. Stream to users, pause for human review, handle concurrent input, and connect via MCP and A2A. Authenticate users, enforce resource-level access, and connect external OAuth2 identity providers. Add caching, custom stores and checkpointers, lifespan hooks, middleware, custom routes, encryption, and configurable headers and logs. ## Tutorials * [Collect user feedback for Agent Server runs](/langsmith/agent-server-feedback): Attach end-user feedback to runs and traces * [Deploy other frameworks (e.g., Strands, CrewAI)](/langsmith/deploy-other-frameworks): Wrap existing agents with Functional API and deploy * [Implement generative user interfaces with LangGraph](/langsmith/generative-ui-react): Stream UI elements to a React client * [Implement a CI/CD pipeline](/langsmith/cicd-pipeline-example): Automate tests, evaluations, and deployments with GitHub Actions ## Securing and customizing your server * [Custom auth](/langsmith/auth): Authentication and multi-tenant access control * [Server customization](/langsmith/custom-routes): Custom routes, [middleware](/langsmith/custom-middleware), [lifespan hooks](/langsmith/custom-lifespan), [encryption](/langsmith/encryption) ## Operations * [CI/CD pipelines](/langsmith/cicd-pipeline-example) * [TTL configuration](/langsmith/configure-ttl) for state and thread management * [Semantic search](/langsmith/semantic-search) ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-server-overview.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Configure Agent Server for scale Source: https://docs.langchain.com/langsmith/agent-server-scale Tune the Agent Server for self-hosted deployments—write load, read load, and example Helm configurations for different load patterns. The default configuration for the LangSmith Agent Server is designed to handle substantial read and write load across a variety of different workloads. By following the best practices outlined below, you can tune your Agent Server to perform optimally for your specific workload. This page describes scaling considerations for the Agent Server on self-hosted deployments and provides example configurations. If you're not yet familiar with how API servers and queue workers operate at the container level, read the [runtime architecture](/langsmith/agent-server#runtime-architecture) overview first. For [Cloud](/langsmith/cloud-platform-features#scaling), the platform autoscales automatically and the Helm configurations below do not apply. ## Request vs. run concurrency Two independent kinds of concurrency determine how the Agent Server scales, and they are controlled separately: * **Request concurrency** is how many API requests (creating runs, reading thread state, streaming results) the deployment serves at once. API servers handle requests asynchronously, and request concurrency scales horizontally with the number of API server replicas. * **Run concurrency** is how many runs execute at once. A single queue worker executes up to [`N_JOBS_PER_WORKER`](/langsmith/env-var-self-hosted) runs concurrently (default 10). Run concurrency is capped at the number of queue workers multiplied by `N_JOBS_PER_WORKER`. Creating a run is a fast write request: the API server persists a pending run and returns immediately, without waiting for the run to execute. If every run slot is busy, additional runs wait in the [queue](/langsmith/agent-server#run-execution-lifecycle) until a slot frees. Raising `N_JOBS_PER_WORKER` or adding queue workers increases run throughput; it does not change how many requests the deployment can serve concurrently. ## Write load Write load is primarily driven by the following factors: * Creation of new [runs](/langsmith/background-run) * Creation of new checkpoints during run execution * Writing to long term memory * Creation of new [threads](/langsmith/use-threads) * Creation of new [assistants](/langsmith/assistants) * Deletion of runs, checkpoints, threads, assistants and cron jobs The following components are primarily responsible for handling write load: * API server: Handles initial request and persistence of data to the database. * Queue worker: Handles the execution of runs. * Redis: Handles the storage of ephemeral data about on-going runs. * Postgres: Handles the storage of all data, including run, thread, assistant, cron job, checkpointing and long term memory. ### Tune `N_JOBS_PER_WORKER` based on assistant characteristics The default value of [`N_JOBS_PER_WORKER`](/langsmith/env-var-self-hosted) is 10. You can change this value to scale the maximum number of runs that can be executed at a time by a single queue worker based on the characteristics of your assistant. Some general guidelines for changing `N_JOBS_PER_WORKER`: * If your assistant is CPU bounded, the default value of 10 is likely sufficient. You might lower `N_JOBS_PER_WORKER` if you notice excessive CPU usage on queue workers or delays in run execution. * If your assistant is memory bounded, or queue workers are approaching memory limits, lower `N_JOBS_PER_WORKER` to reduce the number of concurrent runs per worker. * If your assistant is IO bounded, increase `N_JOBS_PER_WORKER` to handle more concurrent runs per worker. There is no upper limit to `N_JOBS_PER_WORKER`. However, queue workers are greedy when fetching new runs, which means they will try to pick up as many runs as they have available jobs and begin executing them immediately. Setting `N_JOBS_PER_WORKER` too high in environments with bursty traffic can lead to uneven worker utilization, increased run execution times, and high memory usage on queue workers. ### Avoid synchronous blocking operations Avoid synchronous blocking operations in your code and prefer asynchronous operations. Long synchronous operations can block the main event loop, causing longer request and run execution times and potential timeouts. For example, consider an application that needs to sleep for 1 second. Instead of using synchronous code like this: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import time def my_function(): time.sleep(1) ``` Prefer asynchronous code like this: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import asyncio async def my_function(): await asyncio.sleep(1) ``` If an assistant requires synchronous blocking operations, run those in `asyncio.to_thread()` or equivalent. ### Minimize redundant checkpointing Minimize redundant checkpointing by setting [`durability`](/oss/python/langgraph/checkpointers#durability-modes) to the minimum value necessary to ensure your data is durable. The default durability mode is `"async"`, meaning checkpoints are written after each step asynchronously. If an assistant needs to persist only the final state of the run, `durability` can be set to `"exit"`, storing only the final state of the run. This can be set when creating the run: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langgraph_sdk import get_client client = get_client(url=) thread = await client.threads.create() run = await client.runs.create( thread_id=thread["thread_id"], assistant_id="agent", durability="exit" ) ``` ### Enable queue workers By default, the API server manages the queue and does not use queue workers. Enable queue workers by setting `queue.enabled` to `true`: ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} queue: enabled: true ``` This offloads queue management from the API server to dedicated queue workers, reducing load on the API server and allowing it to focus on handling requests. ### Size jobs for expected throughput This section sizes run-execution capacity (queue workers), which is separate from request-serving capacity (API server replicas). For more information, see [Request vs. run concurrency](#request-vs-run-concurrency). The more runs you execute in parallel, the more jobs you will need to handle the load. There are two main parameters to scale the available jobs: * `number_of_queue_workers`: The number of queue workers provisioned. * `N_JOBS_PER_WORKER`: The number of runs that a single queue worker can execute at a time. Defaults to 10. You can calculate the available jobs with the following equation: ``` available_jobs = number_of_queue_workers * N_JOBS_PER_WORKER ``` Throughput is then the number of runs that can be executed per second by the available jobs: ``` throughput_per_second = available_jobs / average_run_execution_time_seconds ``` Therefore, the minimum number of queue workers you should provision to support your expected steady state throughput is: ``` number_of_queue_workers = throughput_per_second * average_run_execution_time_seconds / N_JOBS_PER_WORKER ``` ### Configure autoscaling for bursty write workloads Autoscaling is disabled by default, but should be configured for bursty workloads. Using the same calculations as the previous section, you can determine the maximum number of queue workers you should allow the autoscaler to scale to based on maximum expected throughput. ## Read load Read load is primarily driven by the following factors: * Getting the results of a [run](/langsmith/background-run) * Getting the state of a [thread](/langsmith/use-threads) * Searching for [runs](/langsmith/background-run), [threads](/langsmith/use-threads), [cron jobs](/langsmith/cron-jobs) and [assistants](/langsmith/assistants) * Retrieving checkpoints and long term memory The following components are primarily responsible for handling read load: * API server: Handles the request and direct retrieval of data from the database. * Postgres: Handles the storage of all data, including run, thread, assistant, cron job, checkpointing and long term memory. * Redis: Handles the storage of ephemeral data about on-going runs, including streaming messages from queue workers to api servers. ### Use filtering to reduce results per request [Agent Server](/langsmith/agent-server) provides a search API for each resource type. These APIs implement pagination by default and offer many filtering options. Use filtering to reduce the number of resources returned per request and improve performance. ### Set TTLs to automatically delete old data Set a [TTL on threads](/langsmith/configure-ttl) to automatically clean up old data. Runs and checkpoints are automatically deleted when the associated thread is deleted. ### Avoid polling; use `/join` to monitor a run Avoid polling the state of a run by using the `/join` API endpoint. This method returns the final state of the run once the run is complete. If you need to monitor the output of a run in real-time, use the `/stream` API endpoint. This method streams the run output including the final state of the run. ### Configure autoscaling for bursty read workloads Autoscaling is disabled by default, but should be configured for bursty workloads. Determine the maximum number of API servers you should allow the autoscaler to scale to based on maximum expected throughput. ## Example configurations The exact optimal configuration depends on your application complexity, request patterns, and data requirements. Use the following examples in combination with the information in the previous sections and your specific usage to update your deployment configuration as needed. If you have any questions, contact support via [support.langchain.com](https://support.langchain.com). The following table provides an overview comparing different Agent Server configurations for various load patterns (read requests per second / write requests per second) and standard assistant characteristics (average run execution time of 1 second, moderate CPU and memory usage). The request rates drive the required steady-state run throughput, which is sized through queue workers and `N_JOBS_PER_WORKER`, while API server replicas are sized to serve the request volume itself: | | **[Low / low](#low-reads-low-writes)** | **[Low / high](#low-reads-high-writes)** | **[High / low](#high-reads-low-writes)** | [Medium / medium](#medium-reads-medium-writes) | [High / high](#high-reads-high-writes) | | :--------------------------------------------- | :------------------------------------- | :--------------------------------------- | :--------------------------------------- | :--------------------------------------------- | :------------------------------------- | | Write requests per second | 5 | 5 | 500 | 50 | 500 | | Read requests per second | 5 | 500 | 5 | 50 | 500 | | **API servers**
(1 CPU, 2Gi per server) | 1 (default) | 6 | 10 | 3 | 15 | | **Queue workers**
(1 CPU, 2Gi per worker) | 1 (default) | 10 | 1 (default) | 5 | 10 | | **`N_JOBS_PER_WORKER`** | 10 (default) | 50 | 10 | 10 | 50 | | **Redis resources** | 2 Gi (default) | 2 Gi (default) | 2 Gi (default) | 2 Gi (default) | 2 Gi (default) | | **Postgres resources** | 2 CPU
8 Gi (default) | 4 CPU
16 Gi memory | 4 CPU
16 Gi | 4 CPU
16 Gi memory | 8 CPU
32 Gi memory | Load levels in the examples are defined as: * Low means approximately 5 requests per second * Medium means approximately 50 requests per second * High means approximately 500 requests per second ### Low reads, low writes The default [LangSmith Deployment](/langsmith/deployment) configuration will handle this load. No custom resource configuration is needed here. ### Low reads, high writes You have a high volume of write requests (500 per second) being processed by your deployment, but relatively few read requests (5 per second). For this, we recommend a configuration like this: ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} # Example configuration for low reads, high writes (5 read/500 write requests per second) api: replicas: 6 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" queue: replicas: 10 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" config: numberOfJobsPerWorker: 50 redis: resources: requests: memory: "2Gi" limits: memory: "2Gi" postgres: resources: requests: cpu: "4" memory: "16Gi" limits: cpu: "8" memory: "32Gi" ``` ### High reads, low writes You have a high volume of read requests (500 per second) but relatively few write requests (5 per second). For this, we recommend a configuration like this: ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} # Example configuration for high reads, low writes (500 read/5 write requests per second) api: replicas: 10 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" queue: replicas: 1 # Default, minimal write load resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" redis: resources: requests: memory: "2Gi" limits: memory: "2Gi" postgres: resources: requests: cpu: "4" memory: "16Gi" limits: cpu: "8" memory: "32Gi" # Consider read replicas for high read scenarios readReplicas: 2 ``` ### Medium reads, medium writes This is a balanced configuration that should handle moderate read and write loads (50 read/50 write requests per second). For this, we recommend a configuration like this: ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} # Example configuration for medium reads, medium writes (50 read/50 write requests per second) api: replicas: 3 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" queue: replicas: 5 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" redis: resources: requests: memory: "2Gi" limits: memory: "2Gi" postgres: resources: requests: cpu: "4" memory: "16Gi" limits: cpu: "8" memory: "32Gi" ``` ### High reads, high writes You have high volumes of both read and write requests (500 read/500 write requests per second). For this, we recommend a configuration like this: ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} # Example configuration for high reads, high writes (500 read/500 write requests per second) api: replicas: 15 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" queue: replicas: 10 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" config: numberOfJobsPerWorker: 50 redis: resources: requests: memory: "2Gi" limits: memory: "2Gi" postgres: resources: requests: cpu: "8" memory: "32Gi" limits: cpu: "16" memory: "64Gi" ``` ### Autoscaling If your deployment experiences bursty traffic, you can enable autoscaling to scale the number of API servers and queue workers to handle the load. Here is a sample configuration for autoscaling for high reads and high writes: ```yaml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} api: autoscaling: enabled: true minReplicas: 15 maxReplicas: 25 queue: autoscaling: enabled: true minReplicas: 10 maxReplicas: 20 ``` Ensure that your deployment environment has sufficient resources to scale to the recommended size. Monitor your applications and infrastructure to ensure optimal performance. Consider implementing monitoring and alerting to track resource usage and application performance. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/agent-server-scale.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Alerts in LangSmith Source: https://docs.langchain.com/langsmith/alerts **Self-hosted version requirement**: Access to alerts requires Helm chart version **0.10.3** or later. Effective observability in LLM applications requires proactive detection of failures, performance degradations, and regressions. LangSmith's alerts feature helps identify critical issues such as: * API rate limit violations from model providers. * Latency increases for your application. * Application changes that affect feedback scores reflecting end-user experience. * Unexpected cost spikes from LLM usage. Alerts in LangSmith are project-scoped, requiring separate configuration for each monitored project. Alerts can [route](#step-4-configure-notification-channel) to Slack, PagerDuty, Dynatrace, or any HTTP endpoint via webhook. The **Webhook** tab includes [example recipes](#example-recipes) for Microsoft Teams, email, Slack on self-hosted deployments, and Google Chat (which requires middleware). Follow these steps to configure an alert. ## Step 1: Navigate to create alert In the [UI](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=langsmith-alerts), navigate to the Tracing project that you would like to configure alerts for. Click the **Alerts** icon on the top right-hand corner of the page to view existing alerts for that project and set up a new alert. ## Step 2: Select metric type LangSmith provides threshold-based alerting on the following metrics: | Metric Type | Description | Use Case | | ------------------ | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Run Count** | Tracks the total number of [runs](/langsmith/observability-concepts#runs) over a time window. | Monitor whether a pipeline is producing runs at the expected volume and alert when it drops unexpectedly. | | **Cost** | Tracks the total cost of runs over a time window. | Monitor LLM spending to alert when costs exceed expected thresholds. Requires [cost tracking](/langsmith/cost-tracking) to be configured. | | **Errors** | Tracks runs with an error status. Alert on total error count or error percent (rate of errored runs out of all runs). | Monitor for failures in an application, or alert when the error rate exceeds an acceptable threshold. | | **Feedback Score** | Measures the average feedback score. | Track [feedback from end users](/langsmith/attach-user-feedback) or [online evaluation results](/langsmith/online-evaluations-llm-as-judge) to alert on regressions. | | **Latency** | Measures average run execution time. | Tracks the latency of your application to alert on spikes and performance bottlenecks. | Additionally, for **Errors** and **Latency**, you can use the filter builder to stack conditions on fields such as **Status**, **Run Type**, **Tag**, and **Error**. For example, you can scope an error alert to runs where **Status** is `error`, **Run Type** is `llm`, **Tag** is `support_agent`, and **Error** matches `RateLimitExceeded`. ## Step 3: Define alert conditions Alert conditions consist of several components: * **Aggregation Method**: Average, Percentage, or Count. * **Comparison Operator**: `>=`, `<=`, or exceeds threshold. * **Threshold Value**: Numerical value triggering the alert. * **Aggregation Window**: Time period for metric calculation (choose between 5 or 15 minutes). * **Feedback Key** (Feedback Score alerts only): Specific feedback metric to monitor.
Alert Condition Configuration
**Example:** The configuration in the screenshot would generate an alert when more than 5% of runs within the past 5 minutes result in errors. You can preview alert behavior over a historical time window to understand how many datapoints, and which ones, would have triggered an alert at a chosen threshold (indicated in red). For example, setting an average latency threshold of 60 seconds for a project lets you visualize potential alerts, as shown in the following screenshot.
Alert Metrics
## Step 4: Configure notification channel Send alert notifications directly to a Slack channel using LangSmith's native Slack integration. No custom webhook or Slack app configuration required. The native Slack notification type is available on LangSmith Cloud only. For self-hosted deployments, use the [webhook Slack recipe](#example-recipes) in the **Webhook** tab instead. **Prerequisites** * A Slack workspace connected to your LangSmith organization. If you haven't connected one yet, LangSmith will prompt you to do so inline when you configure this notification type. ### 1. Configure the Slack notification 1. In the **Notification Settings** section of your alert setup, select **Slack**. 2. Click the channel selector. If no Slack workspace is linked yet, click **Connect Slack** and complete the OAuth flow to authorize LangSmith. 3. Add the `@LangSmith` app to the channel you want to receive notifications in. The app must be a member of the channel — type `/invite @LangSmith` in the channel to add it. 4. Select the workspace and channel from the dropdown. Click the refresh icon if the channel does not appear immediately. 5. Click **Save** to save the notification configuration. ### 2. Test the integration Click **Send Test Notification** to verify that LangSmith can reach the channel. Check the channel for the test message. ### Notification format When an alert triggers, LangSmith posts a structured Slack message that includes: * **Headline**: The alert name and your LangSmith workspace name. * **Detail line**: The metric attribute, triggered value, comparison operator, configured threshold, aggregation method, and time window — for example: `Total Cost: $12.50 ≥ $5.00 · avg · 30 min`. * **Action buttons**: **View Alert** (links to the alert preview in LangSmith) and **View Runs** (links to the filtered runs that triggered the alert). Configure PagerDuty as a notification channel using PagerDuty's [Events API v2](https://developer.pagerduty.com/docs/events-api-v2-overview). This integration allows critical LLM application issues to trigger PagerDuty incidents, enabling rapid response through your established incident management workflow. **Prerequisites** * An active PagerDuty account with administrator access * Appropriate service-level permissions in PagerDuty If on a custom deployment of LangSmith, make sure there are no firewall settings blocking egress traffic from LangSmith services. ### 1. Create a Service in PagerDuty 1. Log in to your PagerDuty account 2. Navigate to **Services → Service Directory** 3. Click **+ New Service** 4. Complete the following fields: * **Name**: Provide a descriptive name (e.g., "LangSmith Monitoring") * **Description**: Add details about the monitored application * **Escalation Policy**: Select the appropriate team escalation policy * **Integration Type**: Select "Events API V2" 5. Click **Add Service** to create the service ### 2. Obtain integration key After creating the service, retrieve the Integration Key: 1. From the **Service Directory**, locate and click on your newly created service 2. Select the **Integrations** tab 3. Find the "Events API V2" integration 4. Copy the **Integration Key** (a 32-character alphanumeric string) PagerDuty Integration Key Location ### 3. Configure LangSmith alert with PagerDuty To receive the same alert again within an hour of it being triggered, you must resolve the active incident created by the alert in PagerDuty. PagerDuty Setup 1. In the notification section of your alert set-up in LangSmith, select **PagerDuty** 2. Click the key icon to save the Integration Key as a Workspace secret or select an existing Workspace secret. As a best practice, we recommend saving the Integration Key as a Workspace Secret rather than adding it directly. This will allow you to reuse the same key across alerts for a workspace. 3. Configure additional notification options: * **Severity**: Maps to PagerDuty incident priority 4. Send a test alert by clicking **Send Test Alert** 5. Verify the incident is triggered by PagerDuty and contains relevant LangSmith alert information ### Troubleshooting If incidents aren't being created in PagerDuty: * Verify the Integration Key is entered correctly in LangSmith * Ensure the PagerDuty service is active and not in maintenance mode * Check that your PagerDuty account has Events API v2 enabled * If an alert trigger appears to be missing in PagerDuty, check whether the expected trigger occurred within one hour of a previous trigger from the same alert rule, and whether the incident created by the previous alert is still open. * Review network connectivity if your LangSmith instance is behind a firewall ### Additional resources * [PagerDuty Events API v2 Documentation](https://developer.pagerduty.com/docs/events-api-v2/overview/) * [PagerDuty Integration Guide](https://support.pagerduty.com/docs/services-and-integrations) Configure Dynatrace as a notification channel using Dynatrace's [Events API v2](https://docs.dynatrace.com/docs/dynatrace-api/environment-api/events-v2/post-event). This integration sends LangSmith alert events to your Dynatrace environment, enabling correlation with your broader infrastructure monitoring. **Prerequisites** * An active Dynatrace environment (SaaS or Managed). * A Dynatrace API access token with the `events.ingest` scope. If you're working from a custom [deployment](/langsmith/self-hosted) of LangSmith, make sure there are no firewall settings blocking egress traffic from LangSmith services. ### 1. Create an API token in Dynatrace 1. Log in to your Dynatrace environment. 2. Navigate to **Access Tokens**. 3. Click **Generate new token**. 4. Provide a descriptive name (e.g., "LangSmith Alerts"). 5. Under **Scopes**, search for and enable `events.ingest` (Ingest events). 6. Click **Generate token**. 7. Copy the generated token and store it securely. The token is only displayed once. ### 2. Obtain your Dynatrace environment URL Your Dynatrace environment URL follows this format: ``` https://{your-environment-id}.live.dynatrace.com ``` You can find your environment ID in the browser URL bar when logged in to Dynatrace. ### 3. Configure LangSmith alert with Dynatrace 1. In the **Notifications Settings** for your alert setup in LangSmith, select **Dynatrace**. 2. Enter your Dynatrace environment URL. 3. Click the key icon to save the API token as a workspace secret or select an existing workspace secret. As a best practice, save the API token as a workspace secret rather than adding it directly. This allows you to reuse the same token across alerts for a workspace. 4. Configure additional notification options: * **Event Type**: Select the Dynatrace event type (e.g., `CUSTOM_ALERT`, `ERROR_EVENT`) 5. Send a test alert by clicking **Send Test Notification**. 6. Verify the event appears in your Dynatrace environment. ### Troubleshooting If events aren't appearing in Dynatrace: * Verify the API token has the `events.ingest` scope and is not expired. * Ensure the environment URL is correct and includes your environment ID. * Confirm the `Authorization` header format uses `Api-Token` (not `Bearer`). * Check that your Dynatrace environment is active and accessible. * Review network connectivity if your LangSmith instance is behind a firewall. ### Additional resources * [Dynatrace Events API v2 Documentation](https://docs.dynatrace.com/docs/dynatrace-api/environment-api/events-v2/post-event) * [Dynatrace Access Tokens](https://docs.dynatrace.com/docs/manage/access-control/access-tokens) Webhooks enable integration with custom services and third-party platforms by sending HTTP POST requests when alert conditions are triggered. Use webhooks to forward alert data to ticketing systems, chat applications, or custom monitoring solutions. **Prerequisites** * An endpoint that can receive HTTP POST requests * Appropriate authentication credentials for your receiving service (if required) ### 1. Prepare your receiving endpoint Before configuring the webhook in LangSmith, ensure your receiving endpoint: * Accepts HTTP POST requests * Can process JSON payloads * Is accessible from external services * Has appropriate authentication mechanisms (if required) If on a custom deployment of LangSmith, make sure there are no firewall settings blocking egress traffic from LangSmith services. ### 2. Configure webhook parameters In the **Monitoring** section of the [LangSmith UI](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=langsmith-alerts) under the **Alerts** tab, click **+ Alert** to create a. new alert. In the **Notification Settings** section, complete the webhook configuration with the following parameters: **Required fields** * **URL**: The complete URL of your receiving endpoint * Example: `https://api.example.com/incident-webhook` **Optional fields** * **Headers**: JSON key-value pairs sent with the webhook request * Common headers include: * `Authorization`: For authentication tokens * `Content-Type`: Usually set to `application/json` (default) * `X-Source`: To identify the source as LangSmith * If no headers, use `{}` * **Request Body Template**: Customize the JSON payload sent to your endpoint * Default: LangSmith sends the payload defined and the following additional key-value pairs appended to the payload: * `project_name`: Name of the LangSmith project the alert is scoped to. * `workspace_name`: Name of the LangSmith workspace. * `alert_rule_id`: A UUID to identify the LangSmith alert. This can be used as a de-duplication key in the webhook service. * `alert_rule_name`: The name of the alert rule. * `alert_rule_description`: The description of the alert rule (empty string if none set). * `alert_rule_type`: The type of alert (as of 04/01/2025 all alerts are of type `threshold`). * `alert_rule_attribute`: The attribute associated with the alert rule - `error_count`, `feedback_score`, `latency`, or `cost`. * `alert_rule_url`: A direct link to the alert rule in LangSmith. * `runs_url`: A direct link to the runs that triggered the alert in LangSmith. * `triggered_metric_value`: The value of the metric at the time the threshold was triggered. * `triggered_threshold`: The threshold that triggered the alert. * `timestamp`: The timestamp that triggered the alert. LangSmith does not perform template substitution on the request body. The auto-populated fields above are merged into the outgoing JSON as top-level keys, alongside the body you configure. Placeholder syntax like `{alert_rule_name}` is sent verbatim to the receiving service. It only resolves to a real value if the receiver itself can extract fields from the incoming JSON (for example, a Power Automate Workflow, an AWS Lambda, or a custom HTTP handler). ### 3. Test the webhook Click **Send Test Alert** to send the webhook notification to ensure the notification works as intended. ### Troubleshooting If webhook notifications aren't being delivered: * Verify the webhook URL is correct and accessible * Ensure any authentication headers are properly formatted * Check that your receiving endpoint accepts POST requests * Examine your endpoint's logs for received but rejected requests * Verify your custom payload template is valid JSON format **Send Test Alert does not validate the downstream response.** The UI reports **your configuration is working correctly and the test notification was delivered** even if the receiving endpoint returned an error (for example, a 400 or 422 rejection). Always verify receipt on the receiver side, check your endpoint's logs or the target platform's message history, rather than relying solely on the LangSmith success message. ### Security considerations * Use HTTPS for your webhook endpoints * Implement authentication for your webhook endpoint * Consider adding a shared secret in your headers to verify webhook sources * Validate incoming webhook requests before processing them ### Example recipes Here is an example for configuring LangSmith alerts to send notifications to Slack channels using the [`chat.postMessage`](https://api.slack.com/methods/chat.postMessage) API. **Prerequisites** * Access to a Slack workspace. * A LangSmith project to set up alerts. * Permissions to create Slack applications. **Step 1: Create a Slack app** 1. Visit the [Slack API Applications page](https://api.slack.com/apps). 2. Click **Create New App**. 3. Select **From scratch**. 4. Provide an **App Name** (e.g., "LangSmith Alerts"). 5. Select the workspace where you want to install the app. 6. Click **Create App**. **Step 2: Configure bot permissions** 1. In the left sidebar of your Slack app configuration, click **OAuth & Permissions**. 2. Scroll down to **Bot Token Scopes** under **Scopes** and click **Add an OAuth Scope**. 3. Add the following scopes: * `chat:write` (Send messages as the app). * `chat:write.public` (Send messages to channels the app isn't in). * `channels:read` (View basic channel information). **Step 3: Install the app to your workspace** 1. Scroll up to the top of the **OAuth & Permissions** page. 2. Click **Install to Workspace**. 3. Review the permissions and click **Allow**. 4. Copy the **Bot User OAuth Token** that appears (begins with `xoxb-`). **Step 4: Add the bot to a Slack channel** Add the bot to the specific channel you want to receive alerts in. You can add a bot to a Slack channel by mentioning it in the message field (e.g., `@botname`). You also need the channel ID to configure the webhook alert in LangSmith. You can find the channel ID by opening channel details > About. **Step 5: Configure the webhook alert in LangSmith** 1. In LangSmith, navigate to your project. 2. Select **Alerts → Create Alert**. 3. Define your alert metrics and conditions. 4. In the notification section, select **Webhook**. 5. Configure the webhook with the following settings: **Webhook URL** ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} https://slack.com/api/chat.postMessage ``` **Headers** Replace `xoxb-your-token-here` with your Bot's User OAuth Token ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "Content-Type": "application/json", "Authorization": "Bearer xoxb-your-token-here" } ``` **Request Body Template** It is required to fill in the `{channel_id}` from the value found in Step 4.

The remaining fields: `alert_name`, `project_name` and `project_url` optionally add additional context to the alert message. You can find your `project_url` in the browser's URL bar. Copy the portion up to but not including any query parameters.
```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "channel": "{channel_id}", "text": "{alert_name} triggered for {project_name}", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "🚨{alert_name} has been triggered" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "Please check the following link for more information:" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "<{project-url}|View in LangSmith>" } } ] } ``` 6. Click **Save** to activate the webhook configuration. **Step 6: Test the integration** 1. In the LangSmith alert configuration, click **Test Alert**. 2. Check your specified Slack channel for the test notification. 3. Verify that the message contains the expected alert information. **(Optional) Step 7: Link to the alert preview in the request body** After creating an alert, you can optionally link to its preview in the webhook's request body. Alert Preview Pane To configure this: 1. Save your alert. 2. Find your saved alert in the alerts table and click it. 3. Copy the displayed URL. 4. Click "Edit Alert". 5. Replace the existing project URL with the copied alert preview URL.
Here is an example for configuring LangSmith alerts to send notifications to a Microsoft Teams channel using the [Workflows app](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498) (Power Automate). This approach is recommended because it extracts fields from the incoming JSON within the flow, so the auto-populated LangSmith alert fields render correctly in the Teams message. Microsoft's legacy Office 365 Incoming Webhook connectors are being retired. Use the Workflows app for new integrations. **Prerequisites** * Access to a Microsoft Teams workspace with permissions to add Workflows. * A LangSmith project to set up alerts. **Step 1: Create a Workflow in Teams** 1. In Microsoft Teams, navigate to the channel where you want to receive alerts. 2. Click the **...** (More options) menu next to the channel name. 3. Select **Workflows**. 4. Search for and select the **Post to a channel when a webhook request is received** template. 5. Sign in to confirm the connections, then click **Next**. 6. Confirm the team and channel where alerts should be posted, then click **Add workflow**. 7. Copy the generated **HTTP POST URL**—use this in LangSmith. **Step 2: Customize the message in Power Automate (optional)** The default workflow posts the raw JSON body as a card. To format alert details, edit the flow in Power Automate: 1. Open the [Power Automate portal](https://make.powerautomate.com) and edit the workflow you created. 2. Click the **Post card in a chat or channel** action. 3. In the **Adaptive Card** field, reference incoming fields using `triggerOutputs()?['body/alert_rule_name']`, `triggerOutputs()?['body/project_name']`, `triggerOutputs()?['body/triggered_metric_value']`, `triggerOutputs()?['body/triggered_threshold']`, `triggerOutputs()?['body/timestamp']`, and `triggerOutputs()?['body/alert_rule_url']`. 4. Save the flow. **Step 3: Configure the webhook alert in LangSmith** 1. In LangSmith, navigate to your project. 2. Select **Alerts → Create Alert**. 3. Define your alert metrics and conditions. 4. In the notification section, select **Webhook**. 5. Configure the webhook with the following settings: **Webhook URL** Paste the HTTP POST URL from your Teams Workflow: ``` https://prod-XX.westus.logic.azure.com:443/workflows/.../triggers/manual/paths/invoke?... ``` **Headers** ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "Content-Type": "application/json" } ``` **Request Body Template** LangSmith automatically merges the auto-populated alert fields (`alert_rule_name`, `project_name`, `triggered_metric_value`, `triggered_threshold`, `timestamp`, `alert_rule_url`, and others) into the request body as top-level JSON keys. Power Automate reads these fields directly from the incoming payload, so an empty body is sufficient: ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} {} ``` 6. Click **Save** to activate the webhook configuration. **Step 4: Test the integration** 1. In the LangSmith alert configuration, click **Send Test Alert**. 2. Check your specified Teams channel for the test notification. 3. Verify that the card contains the expected alert information. **Reference implementation** For a working example that translates LangSmith webhook payloads (threshold alerts, run rules, and generic events) into formatted Teams Adaptive Cards, see the [langsmith-teams-webhook](https://github.com/langchain-samples/langsmith-teams-webhook) sample repo. The sample runs as a small Python service in front of a Teams Workflow URL, which avoids customizing the Power Automate flow itself. Here is an example for configuring LangSmith alerts to send email notifications using [SendGrid's Mail Send API](https://docs.sendgrid.com/api-reference/mail-send/mail-send). You can use any transactional email provider that exposes an HTTP API (e.g., Mailgun, Amazon SES, Postmark). **Prerequisites** * A SendGrid account with a verified sender identity. * A SendGrid API key with **Mail Send** permissions. * A LangSmith project to set up alerts. **Step 1: Create a SendGrid API key** 1. Log in to your [SendGrid dashboard](https://app.sendgrid.com). 2. Navigate to **Settings → API Keys**. 3. Click **Create API Key**. 4. Choose **Restricted Access** and enable **Mail Send → Full Access**. 5. Click **Create & View**, copy the key, and store it securely. **Step 2: Verify your sender email** 1. In SendGrid, navigate to **Settings → Sender Authentication**. 2. Complete either **Domain Authentication** (recommended) or **Single Sender Verification** for the address you want to send from. **Step 3: Configure the webhook alert in LangSmith** 1. In LangSmith, navigate to your project. 2. Select **Alerts → Create Alert**. 3. Define your alert metrics and conditions. 4. In the notification section, select **Webhook**. 5. Configure the webhook with the following settings: **Webhook URL** ``` https://api.sendgrid.com/v3/mail/send ``` **Headers** Replace `SG.your-api-key-here` with your SendGrid API key. ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "Content-Type": "application/json", "Authorization": "Bearer SG.your-api-key-here" } ``` **Request Body Template** Replace `alerts@your-company.com` with your verified sender address and `oncall@your-company.com` with the recipient address. SendGrid does not extract fields from arbitrary top-level JSON keys, so this example uses a fixed subject and body. To include alert-specific values in the email, route the LangSmith webhook through a middleware (such as a Power Automate flow, AWS Lambda, or Zapier webhook) that reads the incoming payload and renders the SendGrid request. ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "personalizations": [ { "to": [ { "email": "oncall@your-company.com" } ], "subject": "LangSmith alert triggered" } ], "from": { "email": "alerts@your-company.com", "name": "LangSmith Alerts" }, "content": [ { "type": "text/plain", "value": "A LangSmith alert was triggered. Open your LangSmith workspace to view the alert details, including the project, metric value, threshold, and timestamp." } ] } ``` 6. Click **Save** to activate the webhook configuration. **Step 4: Test the integration** 1. In the LangSmith alert configuration, click **Send Test Alert**. 2. Check the recipient inbox for the test notification. 3. Verify the email contains the expected alert information. **Using other email providers** The same pattern works with other transactional email APIs that accept static authentication headers. Change the **Webhook URL** and **Headers** to match your provider: | Provider | Webhook URL | Auth header format | | -------- | --------------------------------------------------- | ------------------------------------------ | | Mailgun | `https://api.mailgun.net/v3/{your-domain}/messages` | `Authorization: Basic )>` | | Postmark | `https://api.postmarkapp.com/email` | `X-Postmark-Server-Token: ` | Adjust the **Request Body Template** to match each provider's expected payload format. Amazon SES is not directly compatible because the SES API requires per-request AWS SigV4 signing, which cannot be expressed as a static header. To use SES, route through a middleware (for example, a Lambda function with an HTTP trigger). Google Chat's incoming webhook API (`spaces.messages.create`) only accepts the `text` field at the top level. Because LangSmith merges all 12 alert metadata keys into the request body as top-level fields, Google Chat rejects every request with a 400 error: ``` Invalid JSON payload received. Unknown name "project_name" at 'message': Cannot find field. ``` There is no Request Body Template that avoids this, even a minimal body such as `{"text": "hello", "project_name": "x"}` will fail. **A translation layer (middleware) is required**, similar to the Amazon SES note in the [email recipe](#configure-email-notifications-via-webhook). **Option A: Cloud Run or Cloud Functions middleware (recommended)** This approach uses a small HTTP handler that receives the LangSmith webhook, extracts the relevant fields, and forwards a clean `{"text": "..."}` payload to a Google Chat space webhook URL. **Prerequisites** * A Google Chat space with an incoming webhook configured. In Google Chat, open the space → **Apps & integrations** → **Add webhooks**, create a webhook, and copy the URL. * A Google Cloud project with Cloud Run or Cloud Functions enabled, or equivalent hosting. **Step 1: Deploy the handler** Deploy the following Python function as a Cloud Run service or Cloud Function: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import json import os import re import urllib.request from urllib.parse import urlparse ALLOWED_LINK_HOSTS = {"smith.langchain.com"} def build_text(payload): def safe(v): # Strip angle brackets to prevent link injection return re.sub(r"[<>]", "", str(v if v is not None else "")) parsed = urlparse(str(payload.get("runs_url") or "")) trusted = parsed.scheme == "https" and parsed.hostname in ALLOWED_LINK_HOSTS return ( f"*{safe(payload.get('alert_rule_name'))}* triggered for " f"`{safe(payload.get('project_name'))}`\n" f"{safe(payload.get('alert_rule_attribute'))}: " f"{safe(payload.get('triggered_metric_value'))} " f"(threshold {safe(payload.get('triggered_threshold'))})" + (f"\n<{parsed.geturl()}|View runs>" if trusted else "") ) def handler(request): if request.headers.get("X-Webhook-Secret") != os.environ["LANGSMITH_SHARED_SECRET"]: return ("forbidden", 403) payload = request.get_json(silent=True) or {} urllib.request.urlopen( urllib.request.Request( os.environ["GCHAT_WEBHOOK_URL"], data=json.dumps({"text": build_text(payload)}).encode(), headers={"Content-Type": "application/json"}, method="POST", ), timeout=10, ) return ("ok", 200) ``` Set the following environment variables for the deployed function: * `GCHAT_WEBHOOK_URL`: The Google Chat space webhook URL. * `LANGSMITH_SHARED_SECRET`: A secret string you choose (used to authenticate incoming requests from LangSmith). **Step 2: Configure the webhook alert in LangSmith** Point the LangSmith webhook at your deployed handler's URL. **Webhook URL** ``` https:/// ``` **Headers** ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} { "Content-Type": "application/json", "X-Webhook-Secret": "" } ``` **Request Body Template** ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} {} ``` The metadata fields (`alert_rule_name`, `project_name`, `runs_url`, etc.) are merged into the body by LangSmith regardless of what you put here, so an empty body is sufficient. Do not strip `*` or `_` from alert field values in your handler. These characters are also used in Google Chat's basic text formatting, but they appear in LangSmith identifiers (such as `run_count`). Stripping them will corrupt field names in the message. Google Chat enforces a write rate limit of **1 message per second per space**, shared across all webhooks writing to that space. If you have multiple LangSmith alerts routing to the same space and they fire simultaneously, some messages may be dropped. **Option B: Google Apps Script (no infrastructure required)** Google Apps Script can serve as lightweight middleware without deploying any cloud infrastructure. Create a new Apps Script project at [script.google.com](https://script.google.com), paste the following, and deploy it as a web app (execute as yourself, access to anyone): ```javascript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} function doPost(e) { var payload = JSON.parse(e.postData.contents); var secret = e.parameter.secret; // shared secret passed as query param if (secret !== PropertiesService.getScriptProperties().getProperty("LANGSMITH_SHARED_SECRET")) { return ContentService.createTextOutput("forbidden").setMimeType(ContentService.MimeType.TEXT); } var text = "*" + (payload.alert_rule_name || "") + "* triggered for `" + (payload.project_name || "") + "`\n" + (payload.alert_rule_attribute || "") + ": " + (payload.triggered_metric_value || "") + " (threshold " + (payload.triggered_threshold || "") + ")"; UrlFetchApp.fetch(PropertiesService.getScriptProperties().getProperty("GCHAT_WEBHOOK_URL"), { method: "post", contentType: "application/json", payload: JSON.stringify({ text: text }), }); return ContentService.createTextOutput("ok").setMimeType(ContentService.MimeType.TEXT); } ``` Set `GCHAT_WEBHOOK_URL` and `LANGSMITH_SHARED_SECRET` in **Project Settings → Script Properties**. Apps Script web apps cannot read custom HTTP request headers, so the shared secret must be passed as a **query string parameter** (`?secret=...`) rather than a header. Include it in the LangSmith webhook URL rather than the Headers field. ### Additional resources * [Slack chat.postMessage API Documentation](https://api.slack.com/methods/chat.postMessage) * [Slack Block Kit Builder](https://app.slack.com/block-kit-builder/) * [Create incoming webhooks with Workflows for Microsoft Teams](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498) * [Power Automate documentation](https://learn.microsoft.com/en-us/power-automate/) * [langsmith-teams-webhook sample repo](https://github.com/langchain-samples/langsmith-teams-webhook) * [SendGrid Mail Send API Documentation](https://docs.sendgrid.com/api-reference/mail-send/mail-send) * [Google Chat incoming webhooks](https://developers.google.com/chat/how-tos/webhooks)
## Best practices * Adjust sensitivity based on application criticality * Start with broader thresholds and refine based on observed patterns * Ensure alert routing reaches appropriate on-call personnel ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/alerts.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Analyze an experiment Source: https://docs.langchain.com/langsmith/analyze-an-experiment This page describes some of the essential tasks for working with [*experiments*](/langsmith/evaluation-concepts#experiment) in LangSmith: * **[Analyze a single experiment](#analyze-a-single-experiment)**: View and interpret experiment results, customize columns, filter data, and compare runs. * **[Set a baseline in the Experiments tab view](#set-a-baseline-in-the-experiments-tab-view)**: Set a baseline for a dataset that you want to outperform. * **[Filter and group by models, prompts, and tools in the Experiments tab view](#filter-and-group-by-models-prompts-and-tools-in-the-experiments-tab-view)**: Use **Models**, **Prompts**, and **Tools** columns to filter and group experiments in the **Experiments** tab view. * **[Download experiment results as a CSV](#download-experiment-results-as-a-csv)**: Export your experiment data for external analysis and sharing. * **[Rename an experiment](#rename-an-experiment)**: Update experiment names in both the Playground and experiment view. ## Analyze a single experiment After running an experiment, you can use LangSmith's experiment view to analyze the results and draw insights about your experiment's performance. ### Open the experiment view To open the experiment view, 1. Select the relevant [*dataset*](/langsmith/evaluation-concepts#datasets) from the **Dataset & Experiments** page which opens the **Experiments** tab view. 2. Click the row of the experiment you want to view. Open experiment view ### View experiment results #### Customize columns By default, the experiment view shows the input, output, and reference output for each [example](/langsmith/evaluation-concepts#examples) in the dataset, feedback scores from evaluations and experiment metrics like cost, token counts, latency and status. You can customize the columns clicking the **Columns** icon at the top right of the view to make it easier to interpret experiment results: * **Break out fields from inputs, outputs, and reference outputs** into their own columns. This is especially helpful if you have long inputs/outputs/reference outputs and want to surface important fields. * **Hide and reorder columns** to create focused views for analysis. * **Control decimal precision on feedback scores**. By default, LangSmith surfaces numerical feedback scores with a decimal precision of 2, but you can customize this setting to be up to 6 decimals. * **Set the Heat Map threshold** to high, middle, and low for numeric feedback scores in your experiment, which affects the threshold at which score chips render as red or green: Column heatmap configuration You can set default configurations for an entire dataset or temporarily save settings just for yourself. #### Sort and filter To sort rows by a feedback score, click the **Sort by** icon in the column header. Sort column To filter rows, click the icon in the column header and configure your filter settings. Filter column #### Table views Select one of three table view icons at the top right of the experiment view: * **Compact**: Shows each run as a single row for quick score comparisons. * **Full**: Shows the full output for each run. * **Diff**: Shows the text difference between the reference output and the output for each run. Diff view #### View the traces Click any row in the experiment view to open the details panel, which shows the trace alongside feedback, input, output, and attributes for that run. View trace To view the entire tracing project, click on the **View Project** icon at the top right of the experiment view. #### View evaluator runs By hovering over the evaluator score, you can view additional details about that evaluator run. For [LLM-as-a-judge evaluators](/langsmith/llm-as-judge), click the **Source** link to view the prompt used, or **Evaluator trace** to open the trace in a new browser tab. For experiments with [repetitions](/langsmith/repetition), click the aggregate average score to view links to all individual runs. View evaluator runs #### Track experiment progress For experiments run from the Playground or through the SDK, a progress bar in the experiment header tracks completion in real time. The same progress appears in the **Progress** column of the experiments table. Progress reflects both run and evaluation status. Hover over the progress bar to view the number of runs completed and runs evaluated. Progress tracking for experiments run through the SDK requires: * Python: `langsmith>=0.8.16` * TypeScript: `langsmith>=0.7.8` ### Group results by metadata You can add metadata to examples to categorize and organize them. For example, if you're evaluating factual accuracy on a question answering dataset, the metadata might include which subject area each question belongs to. Metadata can be added either [via the UI](/langsmith/manage-datasets-in-application#edit-example-metadata) or [via the SDK](/langsmith/manage-datasets-programmatically#update-single-example). To analyze results by metadata, use the **Group by** icon at the top right of the experiment view and select your desired metadata key. This displays average feedback scores, latency, total tokens, and cost for each metadata group. You will only be able to group by example metadata on experiments created after February 20th, 2025. Any experiments before that date can still be grouped by metadata, but only if the metadata is on the experiment traces themselves. ### Repetitions If you've run your experiment with [*repetitions*](/langsmith/repetition), click any row to open the details panel. The **Repetition Summary** shows a metrics table, all feedback scores, and lets you toggle through outputs or view individual repetitions with their traces. Repetitions ### Compare to another experiment In the top right of the experiment view, you can select another experiment to compare to. This will open up a comparison view, where you can see how the two experiments compare. To learn more about the comparison view, see [how to compare experiment results](/langsmith/compare-experiment-results). ## Set a baseline in the Experiments tab view While you may run dozens of tests, you typically have a specific benchmark you are trying to outperform. Setting a *baseline* anchors your results against this reference point, which allows you to identify improvements or regressions in a crowded experiment list. By designating a baseline, you can: * Highlight a reference: Explicitly mark your best-performing run so it remains visible at the top of the **Experiments** tab view as you iterate. * See instant diffs: View performance deltas across all experiments automatically, which means you don't necessarily need to perform manual side-by-side selection. * Accelerate assessment: Quickly determine if new iterations meet or exceed your current performance standards. The Experiments tab view with an experiment marked as the baseline at the top of the table. Scores show against the baseline on the rows of other experiments. The Experiments tab view with an experiment marked as the baseline at the top of the table. Scores show against the baseline on the rows of other experiments. To set a baseline for a dataset: 1. In the [LangSmith UI](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=langsmith-analyze-an-experiment), navigate to the **Datasets & Experiments** option in the left menu. 2. Select the dataset that you want to work with from the table. 3. In the **Experiments** tab view, hover over an experiment row to display the **Set baseline** button on the right end of the row. Click to select your baseline experiment. Your baseline experiment will pin to the top of the table and have the **Baseline** tag next to its name. Once an experiment is set as a baseline, the table will display scores against the baseline on each experiment for each column. When you are selecting multiple experiments for comparison, the baseline experiment will be the default source experiment to be compared to. ## Filter and group by models, prompts, and tools in the Experiments tab view The experiments table includes **Models**, **Prompts**, and **Tools** columns that show which models, prompts, and tools were used for each experiment, making it easier to understand what changed between runs at a glance. These columns are populated automatically when you run experiments from the Playground. When running experiments via the SDK, pass a `metadata` object with `models`, `prompts`, and `tools` keys to `evaluate()`: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} results = client.evaluate( target, data="my-dataset", evaluators=[...], metadata={ "models": "openai:gpt-5.4-mini", "prompts": ["my-org/my-prompt:abc12345"], "tools": [{"name": "web_search", "description": "Search the web for information"}], }, ) ``` See [how to evaluate an LLM application](/langsmith/evaluate-llm-application#run-the-evaluation) for an example using metadata. The columns only appear when at least one experiment in the dataset has the field set. Once populated, click on a value in these columns to filter or group experiments. The Experiments tab view with metadata columns for models, prompts, and tools. The Experiments tab view with metadata columns for models, prompts, and tools. You can also filter and group by models, model providers, prompts, prompt commits, tools, and other experiment metadata at the top left of the **Experiments** tab view: The Experiments tab view with metadata columns for models, prompts, and tools. The Experiments tab view with metadata columns for models, prompts, and tools. ## Download experiment results as a CSV LangSmith lets you download experiment results as a CSV file for external analysis and sharing. Click the **Download as CSV** icon at the top right of the experiment view. The CSV export always includes all columns, regardless of any column customization, sorting, or filtering you have applied in the experiment view. Column visibility settings affect only the on-screen display and are not reflected in the downloaded file. There is a 5,000 row download limit for experiment results. ## Rename an experiment Experiment names must be unique per workspace. You can rename an experiment in the LangSmith UI in the following places: * **Experiment view**: Rename an experiment by using the pencil icon beside the experiment name. Edit name in experiment view * **Playground**: A default name with the format `pg::prompt-name::model::uuid` (eg. `pg::gpt-5.4-mini::897ee630`) is automatically assigned. You can rename an experiment immediately after running it by editing its name in the Playground table header. Edit name in playground ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/analyze-an-experiment.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Custom instrumentation Source: https://docs.langchain.com/langsmith/annotate-code Instrument your code directly to control which functions are traced and how they appear in LangSmith. Adding [instrumentation](/langsmith/observability-concepts#manual-instrumentation) directly to your code gives you precise control over which functions your application traces, what inputs and outputs are logged, and how your [trace](/langsmith/observability-concepts#traces) hierarchy is structured. The three core instrumentation approaches are: * [`@traceable` decorator](#use-%40traceable-%2F-traceable): recommended for most cases * [`trace` context manager](#use-the-trace-context-manager-python-only): Python only * [`RunTree` API](#use-the-runtree-api): explicit, low-level control This page also covers: * [Specifying a custom run ID](#specify-a-custom-run-id), which is useful for attaching feedback immediately after a run or correlating with external systems. * [Ensuring all traces are submitted](#ensure-all-traces-are-submitted-before-exiting) before your process exits. For LangChain (Python or JS/TS), refer to the [LangChain-specific instructions](/langsmith/trace-with-langchain). If you're using an LLM provider or agent framework with a built-in LangSmith integration, refer to the [integrations overview](/langsmith/integrations) instead ## Prerequisites Before tracing, set the following environment variables: * `LANGSMITH_TRACING=true`: enables tracing. Set this to toggle tracing on and off without changing your code. `LANGSMITH_TRACING` controls the `@traceable` decorator and the `trace` context manager. To override this at runtime for `@traceable` without changing environment variables, use [`tracing_context(enabled=True/False)`](#use-the-trace-context-manager-python-only) (Python) or pass `tracingEnabled` directly to `traceable` (JS/TS). [`RunTree` objects](#use-the-runtree-api) are not affected by any of these controls; they always send data to LangSmith when posted. * `LANGSMITH_API_KEY`: your [LangSmith API key](/langsmith/create-account-api-key). * By default, LangSmith logs traces to a project named `default`. To log to a different project, set `LANGSMITH_PROJECT`. For more details, refer to [Log traces to a specific project](/langsmith/log-traces-to-project). ## Use `@traceable` / `traceable` Apply [`@traceable`](https://reference.langchain.com/python/langsmith/run_helpers/traceable) (Python), [`traceable`](https://reference.langchain.com/javascript/langsmith/traceable) (TypeScript), `traceable` (Kotlin) or `Tracing.traceFunction` (Java) to any function to make it a traced run. LangSmith handles context propagation across nested calls automatically. The following example traces a simple pipeline: `run_pipeline` calls `format_prompt` to build the messages, `invoke_llm` to call the model, and `parse_output` to extract the result. Each function is individually traced, and because they're called from within `run_pipeline` (also traced), LangSmith automatically nests them as child runs. `invoke_llm` uses `run_type="llm"` to mark it as an LLM call so LangSmith can render token counts and latency correctly: ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langsmith import traceable from openai import Client openai = Client() @traceable def format_prompt(subject): return [ { "role": "system", "content": "You are a helpful assistant.", }, { "role": "user", "content": f"What's a good name for a store that sells {subject}?" } ] @traceable(run_type="llm") def invoke_llm(messages): return openai.chat.completions.create( messages=messages, model="gpt-5.4-mini", temperature=0 ) @traceable def parse_output(response): return response.choices[0].message.content @traceable def run_pipeline(): messages = format_prompt("colorful socks") response = invoke_llm(messages) return parse_output(response) run_pipeline() ``` ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { traceable } from "langsmith/traceable"; import OpenAI from "openai"; const openai = new OpenAI(); const formatPrompt = traceable((subject: string) => { return [ { role: "system" as const, content: "You are a helpful assistant.", }, { role: "user" as const, content: `What's a good name for a store that sells ${subject}?`, }, ]; },{ name: "formatPrompt" }); const invokeLLM = traceable( async ({ messages }: { messages: { role: string; content: string }[] }) => { return openai.chat.completions.create({ model: "gpt-5.4-mini", messages: messages, temperature: 0, }); }, { run_type: "llm", name: "invokeLLM" } ); const parseOutput = traceable( (response: any) => { return response.choices[0].message.content; }, { name: "parseOutput" } ); const runPipeline = traceable( async () => { const messages = await formatPrompt("colorful socks"); const response = await invokeLLM({ messages }); return parseOutput(response); }, { name: "runPipeline" } ); await runPipeline(); ``` ```java Java theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import com.langchain.smith.tracing.RunType; import com.langchain.smith.tracing.TraceConfig; import com.langchain.smith.tracing.Tracing; import com.openai.client.OpenAIClient; import com.openai.client.okhttp.OpenAIOkHttpClient; import com.openai.models.ChatModel; import com.openai.models.chat.completions.ChatCompletion; import com.openai.models.chat.completions.ChatCompletionCreateParams; import com.openai.models.chat.completions.ChatCompletionMessageParam; import com.openai.models.chat.completions.ChatCompletionSystemMessageParam; import com.openai.models.chat.completions.ChatCompletionUserMessageParam; import java.util.Arrays; import java.util.List; import java.util.function.Function; public class TraceablePipeline { public static void main(String[] args) { new TraceablePipelineRunner().run(); } private static final class TraceablePipelineRunner { private final OpenAIClient openai = OpenAIOkHttpClient.fromEnv(); private final Function> formatPrompt = Tracing.traceFunction( subject -> Arrays.asList( ChatCompletionMessageParam.ofSystem( ChatCompletionSystemMessageParam.builder() .content("You are a helpful assistant.") .build()), ChatCompletionMessageParam.ofUser( ChatCompletionUserMessageParam.builder() .content("What's a good name for a store that sells " + subject + "?") .build())), TraceConfig.builder().name("format_prompt").build()); private final Function, ChatCompletion> invokeLlm = Tracing.traceFunction( messages -> openai.chat() .completions() .create( ChatCompletionCreateParams.builder() .model(ChatModel.GPT_5_CHAT_LATEST) .messages(messages) .temperature(0.0) .build()), TraceConfig.builder().name("invoke_llm").runType(RunType.LLM).build()); private final Function parseOutput = Tracing.traceFunction( response -> response.choices().get(0).message().content().orElse(""), TraceConfig.builder().name("parse_output").build()); private final Function runPipeline = Tracing.traceFunction( subject -> parseOutput.apply(invokeLlm.apply(formatPrompt.apply(subject))), TraceConfig.builder().name("run_pipeline").build()); void run() { runPipeline.apply("colorful socks"); } } } ``` ```kotlin Kotlin theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import com.langchain.smith.tracing.RunType import com.langchain.smith.tracing.TraceConfig import com.langchain.smith.tracing.traceable import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.models.ChatModel import com.openai.models.chat.completions.ChatCompletion import com.openai.models.chat.completions.ChatCompletionCreateParams import com.openai.models.chat.completions.ChatCompletionMessageParam import com.openai.models.chat.completions.ChatCompletionSystemMessageParam import com.openai.models.chat.completions.ChatCompletionUserMessageParam import kotlin.jvm.optionals.getOrNull val openai = OpenAIOkHttpClient.fromEnv() val formatPrompt = traceable( { subject: String -> listOf( ChatCompletionMessageParam.ofSystem( ChatCompletionSystemMessageParam.builder() .content("You are a helpful assistant.") .build(), ), ChatCompletionMessageParam.ofUser( ChatCompletionUserMessageParam.builder() .content("What's a good name for a store that sells $subject?") .build(), ), ) }, TraceConfig.builder().name("format_prompt").build(), ) val invokeLlm = traceable( { messages: List -> openai.chat().completions().create( ChatCompletionCreateParams.builder() .model(ChatModel.GPT_5_CHAT_LATEST) .messages(messages) .temperature(0.0) .build(), ) }, TraceConfig.builder().name("invoke_llm").runType(RunType.LLM).build(), ) val parseOutput = traceable( { response: ChatCompletion -> response.choices()[0].message().content().getOrNull().orEmpty() }, TraceConfig.builder().name("parse_output").build(), ) val runPipeline = traceable( { subject: String -> parseOutput(invokeLlm(formatPrompt(subject))) }, TraceConfig.builder().name("run_pipeline").build(), ) println(runPipeline("colorful socks")) ``` In the [UI](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=langsmith-annotate-code), you'll find a `run_pipeline` trace with `format_prompt`, `invoke_llm`, and `parse_output` as nested child runs. When you wrap a sync function with `traceable` (e.g., `formatPrompt` in the previous example), use the `await` keyword when calling it to ensure the trace is logged correctly. ## Use the `trace` context manager (Python only) In Python, you can use the `trace` context manager to log traces to LangSmith. This is useful in situations where: 1. You want to log traces for a specific block of code. 2. You want control over the inputs, outputs, and other attributes of the trace. 3. It is not feasible to use a decorator or wrapper. 4. Any or all of the above. The context manager integrates seamlessly with the `traceable` decorator and `wrap_openai` wrapper, so you can use them together in the same application. The following example shows all three used together. `wrap_openai` wraps the OpenAI client so its calls are traced automatically. `my_tool` uses `@traceable` with `run_type="tool"` and a custom `name` to appear correctly in the trace. `chat_pipeline` itself is not decorated; instead, `ls.trace` wraps the call, letting you pass the project name and inputs explicitly and set outputs manually via `rt.end()`: ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import openai import langsmith as ls from langsmith.wrappers import wrap_openai client = wrap_openai(openai.Client()) @ls.traceable(run_type="tool", name="Retrieve Context") def my_tool(question: str) -> str: return "During this morning's meeting, we solved all world conflict." def chat_pipeline(question: str): context = my_tool(question) messages = [ { "role": "system", "content": "You are a helpful assistant. Please respond to the user's request only based on the given context." }, { "role": "user", "content": f"Question: {question}\nContext: {context}"} ] chat_completion = client.chat.completions.create( model="gpt-5.4-mini", messages=messages ) return chat_completion.choices[0].message.content app_inputs = {"input": "Can you summarize this morning's meetings?"} with ls.trace("Chat Pipeline", "chain", project_name="my_test", inputs=app_inputs) as rt: output = chat_pipeline("Can you summarize this morning's meetings?") rt.end(outputs={"output": output}) ``` ## Use the `RunTree` API Another, more explicit way to log traces to LangSmith is via the `RunTree` API. This API allows you more control over your tracing. You can manually create runs and children runs to assemble your trace. You still need to set your `LANGSMITH_API_KEY`, but `LANGSMITH_TRACING` is not necessary for this method. This method is not recommended for most use cases; manually managing trace context is error-prone compared to `@traceable`, which handles context propagation automatically. ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import openai from langsmith.run_trees import RunTree # This can be a user input to your app question = "Can you summarize this morning's meetings?" # Create a top-level run pipeline = RunTree( name="Chat Pipeline", run_type="chain", inputs={"question": question} ) pipeline.post() # This can be retrieved in a retrieval step context = "During this morning's meeting, we solved all world conflict." messages = [ { "role": "system", "content": "You are a helpful assistant. Please respond to the user's request only based on the given context." }, { "role": "user", "content": f"Question: {question}\nContext: {context}"} ] # Create a child run child_llm_run = pipeline.create_child( name="OpenAI Call", run_type="llm", inputs={"messages": messages}, ) child_llm_run.post() # Generate a completion client = openai.Client() chat_completion = client.chat.completions.create( model="gpt-5.4-mini", messages=messages ) # End the runs and log them child_llm_run.end(outputs=chat_completion) child_llm_run.patch() pipeline.end(outputs={"answer": chat_completion.choices[0].message.content}) pipeline.patch() ``` ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import OpenAI from "openai"; import { RunTree } from "langsmith"; // This can be a user input to your app const question = "Can you summarize this morning's meetings?"; const pipeline = new RunTree({ name: "Chat Pipeline", run_type: "chain", inputs: { question } }); await pipeline.postRun(); // This can be retrieved in a retrieval step const context = "During this morning's meeting, we solved all world conflict."; const messages = [ { role: "system", content: "You are a helpful assistant. Please respond to the user's request only based on the given context." }, { role: "user", content: `Question: ${question}Context: ${context}` } ]; // Create a child run const childRun = await pipeline.createChild({ name: "OpenAI Call", run_type: "llm", inputs: { messages }, }); await childRun.postRun(); // Generate a completion const client = new OpenAI(); const chatCompletion = await client.chat.completions.create({ model: "gpt-5.4-mini", messages: messages, }); // End the runs and log them childRun.end(chatCompletion); await childRun.patchRun(); pipeline.end({ outputs: { answer: chatCompletion.choices[0].message.content } }); await pipeline.patchRun(); ``` ```java Java theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import com.langchain.smith.client.LangsmithClient; import com.langchain.smith.client.okhttp.LangsmithOkHttpClient; import com.langchain.smith.tracing.RunTree; import com.langchain.smith.tracing.RunType; import com.langchain.smith.tracing.TraceConfig; import com.openai.client.OpenAIClient; import com.openai.client.okhttp.OpenAIOkHttpClient; import com.openai.models.ChatModel; import com.openai.models.chat.completions.ChatCompletion; import com.openai.models.chat.completions.ChatCompletionCreateParams; import com.openai.models.chat.completions.ChatCompletionMessageParam; import com.openai.models.chat.completions.ChatCompletionSystemMessageParam; import com.openai.models.chat.completions.ChatCompletionUserMessageParam; import java.time.Instant; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class RunTreeExample { public static void main(String[] args) throws InterruptedException { LangsmithClient langsmith = LangsmithOkHttpClient.fromEnv(); OpenAIClient openai = OpenAIOkHttpClient.fromEnv(); ExecutorService executor = Executors.newSingleThreadExecutor(); try { String question = "Can you summarize this morning's meetings?"; String runId = "01990f3e-7f97-74c5-a9b6-8d3f7e8e2f11"; RunTree pipeline = RunTree.builder() .id(runId) .name("Chat Pipeline") .runType(RunType.CHAIN) .inputs(Collections.singletonMap("question", question)) .client(langsmith) .executor(executor) .build(); pipeline.postRun(); String context = "During this morning's meeting, we solved all world conflict."; List messages = Arrays.asList( ChatCompletionMessageParam.ofSystem( ChatCompletionSystemMessageParam.builder() .content( "You are a helpful assistant. Please respond to the user's " + "request only based on the given context.") .build()), ChatCompletionMessageParam.ofUser( ChatCompletionUserMessageParam.builder() .content("Question: " + question + "\nContext: " + context) .build())); RunTree childRun = pipeline.createChild( TraceConfig.builder().name("OpenAI Call").runType(RunType.LLM).build()); childRun.setInputs(Collections.singletonMap("messages", messages)); childRun.postRun(); ChatCompletion chatCompletion = openai.chat().completions().create( ChatCompletionCreateParams.builder() .model(ChatModel.GPT_5_CHAT_LATEST) .messages(messages) .build()); String answer = chatCompletion.choices().get(0).message().content().orElse(""); System.out.println(answer); childRun.setOutputs(Collections.singletonMap("response", chatCompletion.toString())); childRun.setEndTime(Instant.now().toString()); childRun.patchRun(); pipeline.setOutputs(Collections.singletonMap( "answer", answer)); pipeline.setEndTime(Instant.now().toString()); pipeline.patchRun(); } finally { executor.shutdown(); if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { throw new IllegalStateException( "Timed out waiting for LangSmith traces to submit"); } } } } ``` ```kotlin Kotlin theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import com.langchain.smith.client.okhttp.LangsmithOkHttpClient import com.langchain.smith.tracing.RunTree import com.langchain.smith.tracing.RunType import com.langchain.smith.tracing.TraceConfig import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.models.ChatModel import com.openai.models.chat.completions.ChatCompletionCreateParams import com.openai.models.chat.completions.ChatCompletionMessageParam import com.openai.models.chat.completions.ChatCompletionSystemMessageParam import com.openai.models.chat.completions.ChatCompletionUserMessageParam import java.time.Instant import java.util.concurrent.Executors import java.util.concurrent.TimeUnit val langsmith = LangsmithOkHttpClient.fromEnv() val openai = OpenAIOkHttpClient.fromEnv() val executor = Executors.newSingleThreadExecutor() try { val question = "Can you summarize this morning's meetings?" val runId = "01990f3e-7f97-74c5-a9b6-8d3f7e8e2f11" val pipeline = RunTree.builder() .id(runId) .name("Chat Pipeline") .runType(RunType.CHAIN) .inputs(mapOf("question" to question)) .client(langsmith) .executor(executor) .build() println("[run-tree-example] Posting parent run to LangSmith…") pipeline.postRun() val context = "During this morning's meeting, we solved all world conflict." val messages = listOf( ChatCompletionMessageParam.ofSystem( ChatCompletionSystemMessageParam.builder() .content( "You are a helpful assistant. Please respond to the user's " + "request only based on the given context.", ) .build(), ), ChatCompletionMessageParam.ofUser( ChatCompletionUserMessageParam.builder() .content("Question: $question\nContext: $context") .build(), ), ) val childRun = pipeline.createChild( TraceConfig.builder().name("OpenAI Call").runType(RunType.LLM).build(), ) childRun.inputs = mapOf("messages" to messages) println("[run-tree-example] Posting child run to LangSmith…") childRun.postRun() val chatCompletion = openai.chat().completions().create( ChatCompletionCreateParams.builder() .model(ChatModel.GPT_5_CHAT_LATEST) .messages(messages) .build(), ) val answer = chatCompletion.choices()[0].message().content().orElse("") println("[run-tree-example] Answer:") println(answer) childRun.outputs = mapOf("response" to chatCompletion.toString()) childRun.endTime = Instant.now().toString() childRun.patchRun() pipeline.outputs = mapOf( "answer" to answer, ) pipeline.endTime = Instant.now().toString() pipeline.patchRun() } finally { executor.shutdown() check(executor.awaitTermination(10, TimeUnit.SECONDS)) { "Timed out waiting for LangSmith traces to submit" } } ``` The Java and Kotlin examples use a custom root run ID and a dedicated executor. Shutting down the executor and awaiting termination ensures the background run submissions complete before the process exits. ## Example usage You can extend the utilities explained in the previous section to trace any code. The following code shows some example extensions. Trace any public method in a class: ```python expandable theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from typing import Any, Callable, Type, TypeVar T = TypeVar("T") def traceable_cls(cls: Type[T]) -> Type[T]: """Instrument all public methods in a class.""" def wrap_method(name: str, method: Any) -> Any: if callable(method) and not name.startswith("__"): return traceable(name=f"{cls.__name__}.{name}")(method) return method # Handle __dict__ case for name in dir(cls): if not name.startswith("_"): try: method = getattr(cls, name) setattr(cls, name, wrap_method(name, method)) except AttributeError: # Skip attributes that can't be set (e.g., some descriptors) pass # Handle __slots__ case if hasattr(cls, "__slots__"): for slot in cls.__slots__: # type: ignore[attr-defined] if not slot.startswith("__"): try: method = getattr(cls, slot) setattr(cls, slot, wrap_method(slot, method)) except AttributeError: # Skip slots that don't have a value yet pass return cls @traceable_cls class MyClass: def __init__(self, some_val: int): self.some_val = some_val def combine(self, other_val: int): return self.some_val + other_val # See trace: https://smith.langchain.com/public/882f9ecf-5057-426a-ae98-0edf84fdcaf9/r MyClass(13).combine(29) ``` ## Specify a custom run ID By default, LangSmith assigns a random ID to each run. You can override this when you need to know the run ID ahead of time (for example, to attach [feedback](/langsmith/attach-user-feedback) immediately after a run), correlate LangSmith runs with IDs from an external system, or make runs idempotent using a deterministic ID. Use **UUID v7** for custom run IDs. UUIDv7 embeds a timestamp, which preserves correct time-ordering of runs in a trace. The LangSmith SDK exports a `uuid7` helper (Python v0.4.43+, JS v0.3.80+): * **Python**: `from langsmith import uuid7` * **JS/TS**: `import { uuid7 } from 'langsmith'` Any UUID v7 string is accepted — you can use the SDK helper or your own if your system already uses UUID v7 identifiers. Use one of the following: * `@traceable`: pass `run_id` inside `langsmith_extra` when calling a `@traceable` function (Python), or pass `id` in the config object passed to `traceable` (TypeScript): ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langsmith import traceable, uuid7 @traceable def my_pipeline(question: str) -> str: return "answer" run_id = uuid7() my_pipeline("What is the capital of France?", langsmith_extra={"run_id": run_id}) # run_id can now be used to attach feedback, query the run, etc. ``` ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { traceable } from "langsmith/traceable"; import { uuid7 } from "langsmith"; const runId = uuid7(); const myPipeline = traceable( async (question: string) => { return "answer"; }, { name: "my-pipeline", id: runId } ); await myPipeline("What is the capital of France?"); // runId can now be used to attach feedback, query the run, etc. ``` * `trace` context manager (Python only): Pass `run_id` directly to the [trace](https://reference.langchain.com/python/langsmith/run_helpers/trace) context manager constructor: ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langsmith import trace, uuid7 run_id = uuid7() with trace("my-pipeline", run_id=run_id) as run: result = "answer" run.end(outputs={"result": result}) # run_id can now be used to attach feedback, query the run, etc. ``` ## Ensure all traces are submitted before exiting LangSmith performs tracing in a background thread to avoid obstructing your production application. This means that your process may end before all traces are successfully posted to LangSmith. Refer to the following options: * If you are using LangChain, refer to the [LangChain tracing guide](/langsmith/trace-with-langchain#ensure-all-traces-are-submitted-before-exiting). * If you are using the [LangSmith SDK](/langsmith/reference) standalone, you can use the `flush` method before exit: ```python Python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} from langsmith import Client client = Client() @traceable(client=client) async def my_traced_func(): # Your code here... pass try: await my_traced_func() finally: await client.flush() ``` ```typescript TypeScript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} import { Client } from "langsmith"; const langsmithClient = new Client({}); const myTracedFunc = traceable(async () => { // Your code here... },{ client: langsmithClient }); try { await myTracedFunc(); } finally { await langsmithClient.flush(); } ``` ## Related * [Observability concepts](/langsmith/observability-concepts): background on runs, traces, and the LangSmith data model * [Run (span) data format](/langsmith/run-data-format): schema reference for run fields including `dotted_order`, `trace_id`, and `parent_run_id` * [Log user feedback using the SDK](/langsmith/attach-user-feedback): common use case for pre-specifying a run ID * [Access the current run (span) within a traced function](/langsmith/access-current-span): read or modify the active run from inside a trace * [Log traces to a specific project](/langsmith/log-traces-to-project): route traces to a named project instead of `default` * [Trace with API](/langsmith/trace-with-api): low-level REST API alternative to the SDK * [Tracing Basics video](https://academy.langchain.com/pages/intro-to-langsmith-preview) from the Introduction to LangSmith Course ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/annotate-code.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Annotate traces and runs inline Source: https://docs.langchain.com/langsmith/annotate-traces-inline LangSmith allows you to manually annotate traces with feedback within the application. This can be useful for adding context to a trace, such as a user's comment or a note about a specific issue. You can annotate a trace either inline or by sending the trace to an annotation queue, which allows you to closely inspect and log feedbacks to runs one at a time. Feedback tags are associated with your [workspace](/langsmith/administration-overview#workspaces). **You can attach user feedback to ANY intermediate run (span) of the trace, not just the root span.** This is useful for critiquing specific parts of the LLM application, such as the retrieval step or generation step of the RAG pipeline. To annotate a trace inline, open the three-dot menu (`...`) in the trace view for any particular run that is part of the trace, then click **Notes**. This will open up a pane that allows you to choose from feedback tags associated with your workspace and add a score for particular tags. You can also add a standalone comment. Follow [Set up feedback criteria](/langsmith/set-up-feedback-criteria) to set up feedback tags for your workspace. You can also set up new feedback criteria from within the pane itself. Inline feedback and notes in the LangSmith UI do not change the trace's [retention tier](/langsmith/usage-and-billing#data-retention-auto-upgrades); the trace keeps the retention configured for its project unless another action explicitly extends retention. Annotation sidebar You can use the labeled keyboard shortcuts to streamline the annotation process. ***
[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/annotate-traces-inline.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
# Use annotation queues Source: https://docs.langchain.com/langsmith/annotation-queues *Annotation queues* give human reviewers a focused workflow for attaching feedback to specific [runs](/langsmith/observability-concepts#runs) or [threads](/langsmith/observability-concepts#threads). While you can always annotate [traces](/langsmith/observability-concepts#traces) inline, annotation queues let you group runs and threads together, prescribe rubrics, and track reviewer progress. Reviewing an entire thread lets you evaluate a full multi-turn conversation, capturing quality signals that a single run cannot. You can also manage annotation queues and feedback configs programmatically with the SDK. Refer to [Manage feedback & annotation queues programmatically](/langsmith/annotation-queues-sdk). To customize how run outputs appear during review, [configure custom output rendering for annotation queues](/langsmith/custom-output-rendering#for-annotation-queues). LangSmith supports two queue styles: * [**Single-run annotation queues**](#single-run-annotation-queues) present one queue item at a time, either a run or a thread, and let reviewers submit any rubric feedback you configure. For **run** items, single-run queues also support [assertions](/langsmith/assertions) to capture acceptance criteria for offline evaluation. * [**Pairwise annotation queues (PAQs)**](#pairwise-annotation-queues) present two runs side-by-side so reviewers can quickly decide which output is better (or if they are equivalent) against the rubric items you define. For a demonstration of using annotation queues, watch the [Getting started with annotation queues](#video-guide) video guide. ## Single-run annotation queues Single-run queues present one item at a time and let reviewers submit any rubric feedback you configure. They can be created directly from the **Annotation queues** section in the [LangSmith UI](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=langsmith-annotation-queues). A queue can contain a mix of run items and thread items. A *thread item* represents an entire conversation and is reviewed against the same rubric as a run item. Run items and thread items support different capabilities: | Capability | Run items | Thread items | | ---------------- | --------- | ------------ | | Rubric feedback | Yes | Yes | | Reviewer notes | Yes | No | | Assertions | Yes | No | | Add to Dataset | Yes | No | | Default dataset | Yes | No | | Automation rules | Yes | No | ### Create a single-run queue 1. Navigate to **Annotation Queues** in the left navigation. 2. Click **+ Annotation Queue** in the top-left corner to open the **Create Annotation Queue** panel. #### Basic details 1. Fill in the **Name** and **Description** of the queue. 2. Optionally select an **Application**. 3. Optionally **Select a default dataset** to streamline exporting reviewed runs into a dataset in your LangSmith [workspace](/langsmith/administration-overview#workspaces). Default datasets apply when you use **Add to Dataset** on run items; thread items do not support adding to a dataset. #### Annotation rubric 1. Draft some high-level **Instructions** for your annotators, which will be shown in the sidebar on every item. 2. Click **+ Add a feedback rubric** to add feedback keys to your annotation queue. Annotators will be presented with these feedback keys on each item. 3. Add a description for each, as well as a short description of each category, if the feedback type is categorical. Reviewers see the **Instructions** and **Feedback** details in the right-hand pane of the UI. #### Collaborator settings Set a number of reviewers or the maximum time you want to reserve the item to a collaborator. When there are multiple annotators for an item, you can choose to have the item stay in the queue until all reviewers have marked it as **Done**. In these settings, "run" refers to any queue item, including thread items. The settings are as follows: * **All workspace members review each run**: When enabled, an item remains in the queue until every [workspace](/langsmith/administration-overview#workspaces) member has marked their review as **Done**. * **Enable reservations on runs**: Reserving an item locks it for your review for a set amount of time. While an item is reserved, other reviewers can view it but cannot add feedback or notes. Reservations are disabled if all workspace members review each run. If a reviewer has viewed an item and then leaves without marking it **Done**, the reservation will expire after the specified **Reservation length**. The item is then released back into the queue and can be reserved by another reviewer. Clicking **Requeue** for an item's annotation will only move the current item to the end of the current user's queue; it won't affect the queue order of any other user. It will also release the reservation that the current user has on that item. * **Number of reviewers per run**: This determines the number of reviewers that must mark an item as **Done** for it to be removed from the queue. * Reviewers cannot view the feedback left by other reviewers. * Comments on items are visible to all reviewers. The **Number of reviewers per run** setting is hidden when **Use assigned reviewers** is enabled (see below). * **Use assigned reviewers**: Enable this toggle to use specific workspace members instead of a count-based threshold. When enabled: * A multi-select user picker appears so you can choose specific workspace members as assigned reviewers. * An item is marked **Completed** only when every assigned reviewer has submitted their review. Queue items progress through three states: **Needs Review** → **Needs Others' Review** → **Completed**. * Non-assigned workspace members can still annotate items, but their submissions do not count toward completion. * Any workspace member can edit the assigned reviewers list in the queue settings. When you add a new assigned reviewer to a queue that already has completed items, those items do not revert to pending. If you remove an assigned reviewer, any items they had not yet reviewed recalculate their completion status. Because of these settings, the number of items visible to each reviewer can differ from the total queue size. ### Edit a queue's settings 1. Open the **Edit Annotation Queue** panel for the annotation queue you want to edit. You can access this panel in two ways: * In the **Annotation queues** list, click the **Actions** icon at the right of the queue's row. Select **Edit** from the dropdown. * In the annotation queue view, click the **Settings** icon in the top-right corner. 2. In the **Edit Annotation Queue** panel, modify any of the settings you configured during queue creation and click **Save**. ### Assign runs and threads to a single-run queue There are several ways to populate a single-run queue with items: * **From the Details view**: In a [tracing project](/langsmith/observability-concepts#projects), click into any row to open the side panel in the [Details view](/langsmith/view-traces#details-view). Click **+ Add**, then **Add to Annotation Queue** in the top-right. In the popover, under **What to add**, choose **Selected run** (the current run) or **Entire thread** (the full conversation that run belongs to). You can add any intermediate [run](/langsmith/observability-concepts#runs) as a run item, but not the root run. **Entire thread** requires the run to be part of a thread (instrumented with `thread_id` / `session_id` metadata). Add to Annotation Queue popover with What to add tabs for Selected run and Entire thread, and a queue picker. Add to Annotation Queue popover with What to add tabs for Selected run and Entire thread, and a queue picker. If the **Entire thread** option is unavailable or the **Threads** tab is empty, the runs are not instrumented with `thread_id` / `session_id` metadata. * **From the Traces or Runs tab**: In a tracing project, select either the **Traces** or **Runs** tab. Use the row checkboxes to select one or more items. Click **Add to Annotation Queue** at the bottom of the page. Use **What to add** to enqueue each selection as a **Selected run** or as its **Entire thread**. View of the runs table with runs selected. Add to Annotation Queue button at the bottom of the page. View of the runs table with runs selected. Add to Annotation Queue button at the bottom of the page. * **From the Threads tab**: In a tracing project, select the **Threads** tab. Use the row checkboxes to select one or more items. Click **Add to Annotation Queue** at the bottom of the page. Selected threads are added as thread items. Threads tab with selected threads and the Add to Annotation Queue bulk action. Threads tab with selected threads and the Add to Annotation Queue bulk action. * **Automation rules**: [Set up a rule](/langsmith/rules) to automatically assign **runs** that match a filter (for example, errors or low user scores) into a queue. Automation rules enqueue run items only. They do not add entire threads as thread items. * **Datasets & Experiments**: Select one or more [experiments](/langsmith/evaluation-concepts#experiment) within a dataset and click ** Annotate**. Select **Add to Annotation Queue**, then choose an existing queue or create a new one. Experiment annotate flows add run items. Selected experiments with the Annotate button at the bottom of the page. Selected experiments with the Annotate button at the bottom of the page. You can add at most **100** runs or threads to an annotation queue in a single action. To enqueue more, repeat the add flow in batches of 100 or fewer. Manually adding runs or threads to an annotation queue does not change trace retention by default. The trace keeps the retention configured for its project unless another action explicitly extends retention. For the full retention model, see [data retention auto-upgrades](/langsmith/usage-and-billing#data-retention-auto-upgrades). ### Review a single-run queue 1. Navigate to the **Annotation Queues** section through the left-hand navigation bar. The queue list includes an **Assigned Reviewers** column showing which reviewers are assigned to each queue. To see only queues assigned to you, click the **Assigned to me** filter at the top of the list. 2. Click on the queue you want to review. This will take you to a focused, cyclical view of the items in the queue that require review. A left side panel lists queue items (runs and threads) and shows the status of each (**Needs Review**, **Needs Others' Review**, **Completed**). Use **View all items** to open the full queue list. 3. Review the current item: * **Run items**: Inspect inputs and outputs in the center pane. Add **Reviewer Notes**, score [**Feedback**](/langsmith/observability-concepts#feedback) criteria, or mark the item as reviewed. To build a dataset, edit the run's input and output to create a corrected reference example and click **Add to Dataset**. Instead of crafting a corrected reference output by hand, you can [write **Assertions**](/langsmith/assertions) directly in the review side panel and save them as the example's expected output. * **Thread items**: The center pane shows the conversation transcript for that thread. Read the transcript and score the same rubric **Feedback** keys. Use **View item** to open the thread in the conversation peek. Click **Delete** to remove the item from the queue for all users, regardless of any current reservations or queue settings. Thread items support rubric feedback only. See the [capability table](#single-run-annotation-queues) for what differs between run and thread items. Annotation queue reviewing a thread item with the conversation transcript and rubric feedback pane. Annotation queue reviewing a thread item with the conversation transcript and rubric feedback pane. Feedback and notes submitted while reviewing an annotation queue do not change the trace's [retention tier](/langsmith/usage-and-billing#data-retention-auto-upgrades). Use the keyboard shortcuts next to each option to review items faster. ## Pairwise annotation queues Pairwise annotation queues (PAQs) present two runs side-by-side so reviewers can quickly decide which output is better (or if they are equivalent) against the rubric items you define. They are designed for fast A/B comparisons between two experiments (often a baseline vs. a candidate model) and must be created from the **Datasets & Experiments** pages. Pairwise queues use run comparisons only; they do not enqueue thread items. ### Create a pairwise queue 1. Navigate to **Datasets & Experiments**, open a dataset, and select **exactly two experiments** you want to compare. 2. Click **Annotate**. In the popover, choose **Add to Pairwise Annotation Queue**. (The button is disabled until exactly two experiments are selected.) Popover showing the "Add to Pairwise Annotation Queue" card highlighted after two experiments are selected. 3. Decide whether to send the experiments to an existing pairwise queue or create a new one. 4. Provide the queue details: * **Basic details** (name and description) * **Instructions & rubrics** tailored to pairwise scoring * **Collaborator settings** (reviewer count, reservations, reservation length) 5. Submit the form to create the queue. LangSmith immediately pairs runs from the two experiments and populates the queue. Creating or populating a pairwise annotation queue does not change trace retention by default. Runs keep the [retention tier](/langsmith/usage-and-billing#data-retention-auto-upgrades) they had before they were added to the queue. Key differences for PAQs: * **Experiments**: You must provide two experiment sessions up front. LangSmith automatically pairs their runs in chronological order and populates the queue during creation. * **Rubric**: Pairwise rubric items only require a feedback key and (optionally) a description. Annotators decide whether Run A, Run B, or both are better for each rubric item. * **Dataset**: Pairwise queues do not use a default dataset, because comparisons span two experiments. * **Reservations & reviewers**: The same collaborator controls apply. Reservations help prevent two people from judging the same comparison simultaneously. ### Add more comparisons to a pairwise queue If you need to add more comparisons later, return to **Datasets & Experiments**, select the two experiments again, and choose **Add to Pairwise Annotation Queue** to append new pairs. Selecting two experiments and creating a PAQ automatically pairs the runs. When augmenting an existing PAQ, LangSmith preserves historical comparisons and appends new pairs to the queue. ### Review a pairwise queue 1. From **Annotation queues**, select the pairwise queue you want to review. 2. Each queue item displays Run A on the left and Run B on the right, along with your rubric. 3. For every rubric item: * Choose **A is better**, **B is better**, or **Equal**. The UI records binary feedback on both runs behind the scenes. * Use hotkeys `A`, `B`, or `E` to lock in your choice. 4. Once you finish all rubric items, press **Done** (or `Enter` on the final rubric item) to advance to the next comparison. 5. Optional actions: * Leave comments tied to either run. * Requeue the comparison if you need to revisit it later. * Open the Details view for deeper debugging. Reservations, reviewer thresholds, and comments behave identically to those in single-run queues, enabling teams to use different queue types without modifying their existing workflow. Pairwise review screen showing runs side-by-side with the feedback pane containing A/B/Equal buttons and keyboard shortcuts. Consider routing runs that already have user feedback (e.g., thumbs-down) into a single-run queue for triage and a pairwise queue for head-to-head comparisons against a stronger baseline. This helps you identify regressions quickly. To learn more about how to capture user feedback from your LLM application, follow the guide on [attaching user feedback](/langsmith/attach-user-feedback). ## Video guide