Compare commits

..
Author SHA1 Message Date
595223d46a release(cli): 0.4.43 (#9682)
Bump langgraph-cli from 0.4.42 to 0.4.43 to prepare a release including
the Agent Server naming and flag changes in #9666. Publishing remains a
manual dispatch of the release workflow with `working-directory` set to
`libs/cli`.

Made by [Robin](https://github.com/langchain-ai/open-swe) · [view
thread](https://robin.langchain.dev/agents/aee825b9-b145-4de3-9752-6811b1372e96)
· openai:gpt-6.1-sol (medium)

Co-authored-by: Hari Dhanushkodi <hari-dhanushkodi@users.noreply.github.com>
Co-authored-by: Robin <open-swe@users.noreply.github.com>
2026-11-12 16:04:37 +00:00
a0175fdb1c feat(cli): use Agent Server flags with hidden deployment aliases (#9666)
Align CLI copy with **LangSmith Agent Server**, using **Agent Servers**
for multiple resources and **Agent Server hosting** for entitlement
messages.

- Advertise `--agent-server-id` and `--agent-server-type`; retain
`--deployment-id` and `--deployment-type` as hidden, supported
compatibility aliases, including `--flag=value`. No alias removal is
scheduled.
- Display `AGENT_SERVER_ID` for positional IDs without changing internal
parameters. Preserve commands, environment variables, API/JSON
contracts, URLs, backend error matching, deployment-operation outcomes,
and self-hosted/hybrid LangSmith terminology.
- Release only after the renamed UI is confirmed live in production, not
immediately on merge.
- Follow-ups: matching JavaScript CLI flags in langgraphjs and SDK/core
docstring updates. Older CLIs may still require the legacy spellings.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Victor Moreira <victorm-lc@users.noreply.github.com>
Co-authored-by: Robin <open-swe@users.noreply.github.com>
Co-authored-by: Hari Dhanushkodi <hari-dhanushkodi@users.noreply.github.com>
2026-11-12 09:51:38 -05:00
19 changed files with 303 additions and 197 deletions

No files matched your search

+20 -11
View File
@@ -40,7 +40,7 @@ langgraph new [PATH] --template TEMPLATE_NAME
### `langgraph dev` 🏃‍♀️
Run LangGraph API server in development mode with hot reloading.
Run Agent Server in development mode with hot reloading.
```bash
langgraph dev [OPTIONS]
@@ -54,7 +54,7 @@ langgraph dev [OPTIONS]
### `langgraph up` 🚀
Launch LangGraph API server in Docker.
Launch Agent Server in Docker.
```bash
langgraph up [OPTIONS]
@@ -86,14 +86,23 @@ langgraph dockerfile SAVE_PATH [OPTIONS]
-c, --config FILE Config file path
```
### `langgraph deploy`
Build and deploy an Agent Server image to LangSmith. Use `--agent-server-id` to
update an existing Agent Server and `--agent-server-type` to select its type.
The legacy `--deployment-id` and `--deployment-type` spellings remain supported
as hidden compatibility aliases, with no removal scheduled. Older Python CLI
versions and the JavaScript CLI may still require the legacy spellings; check
command help for your installed version.
### `langgraph deploy metrics`
Fetch deployment monitoring time series as JSON through the LangSmith control plane.
Use `langgraph deploy list` to find the deployment ID. Authentication uses the same
API key and workspace configuration as the other deployment commands.
Fetch Agent Server monitoring time series as JSON through the LangSmith control plane.
Use `langgraph deploy list` to find the Agent Server ID. Authentication uses the same
API key and workspace configuration as the other `deploy` commands.
```bash
langgraph deploy metrics DEPLOYMENT_ID \
langgraph deploy metrics AGENT_SERVER_ID \
--start-time 2026-10-28T00:00:00Z \
--end-time 2026-10-28T01:00:00Z \
--metrics cpu_usage,memory_usage,worker_count
@@ -108,8 +117,8 @@ langgraph deploy metrics DEPLOYMENT_ID \
- Queue: `run_queue_wait_time`
These are exact metric names, not free-text search terms. Availability depends on
the deployment and platform; a supported metric may have no data. Use
`lg_run_count` for deployment runs, not the UI schema's placeholder `run_count`.
the Agent Server and platform; a supported metric may have no data. Use
`lg_run_count` for Agent Server runs, not the UI schema's placeholder `run_count`.
Omit `--metrics` to fetch all available metrics. Time bounds default to the last
15 minutes, and the backend picks the resolution from the time
@@ -136,8 +145,8 @@ series has fewer than two timestamps. UI metadata and repeated series IDs are
omitted. The host-backend response itself is unchanged.
```bash
langgraph deploy metrics DEPLOYMENT_ID --summary
langgraph deploy metrics DEPLOYMENT_ID --raw
langgraph deploy metrics AGENT_SERVER_ID --summary
langgraph deploy metrics AGENT_SERVER_ID --raw
```
`--summary` returns the same shape without `points`. `--raw` returns the original
@@ -145,7 +154,7 @@ UI chart schema without reshaping or rounding. These flags are mutually exclusiv
Neither mode returns traces.
Size comparison using the realistic synthetic unit-test fixture (compact JSON
serialization for all modes; not a live deployment benchmark):
serialization for all modes; not a live Agent Server benchmark):
| Before: raw UI schema | After: default | After: summary |
| --- | --- | --- |
+4
View File
@@ -107,6 +107,10 @@ def add_descriptions_to_schema(schema, cls):
# Recursively process nested definitions
if "$defs" in schema:
for def_name, def_schema in schema["$defs"].items():
# msgspec copies raw __doc__, which keeps indentation before Python
# 3.13, so clean it here too for classes not listed below.
if def_schema.get("description"):
def_schema["description"] = inspect.cleandoc(def_schema["description"])
# Find the class that corresponds to this definition
for potential_cls in [
Config,
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.4.42"
__version__ = "0.4.43"
+17 -17
View File
@@ -1,4 +1,4 @@
"""CLI entrypoint for LangGraph API server."""
"""CLI entrypoint for Agent Server."""
import os
import pathlib
@@ -46,7 +46,7 @@ OPT_CONFIG = click.option(
\b
Config file must be a JSON file that has the following keys:
- "dependencies": array of dependencies for langgraph API server. Dependencies can be one of the following:
- "dependencies": array of dependencies for the Agent Server. Dependencies can be one of the following:
- ".", which would look for local python packages, as well as pyproject.toml, setup.py or requirements.txt in the app directory
- "./local_package"
- "<package_name>
@@ -150,7 +150,7 @@ OPT_API_VERSION = click.option(
"--api-version",
type=str,
help=(
"API server version to use for the base image. If unspecified, the "
"Agent Server version to use for the base image. If unspecified, the "
"latest stable version will be used. Compatible ranges like "
"~=0.11.0.dev5 stay on 0.11.0.dev5 while only newer dev builds exist, "
"then resolve to the newest matching rc or stable release, for example "
@@ -330,7 +330,7 @@ def _studio_link(
@click.option(
"--base-image",
default=None,
help="Base image to use for the LangGraph API server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
help="Base image to use for the Agent Server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
"\n\n \b\nExamples:\n --base-image langchain/langgraph-server:0.2.18 # Pin to a specific patch version"
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
)
@@ -339,7 +339,7 @@ def _studio_link(
is_flag=True,
help="Wait for services to start before returning. Implies --detach",
)
@cli.command(help="🚀 Launch LangGraph API server.")
@cli.command(help="🚀 Launch Agent Server.")
@log_command
def up(
config: pathlib.Path,
@@ -365,9 +365,9 @@ def up(
api_url=api_url,
debugger_base_url=debugger_base_url,
)
click.secho("Starting LangGraph API server...", fg="green")
click.secho("Starting Agent Server...", fg="green")
click.secho(
"""For local dev, requires env var LANGSMITH_API_KEY with access to LangSmith Deployment.
"""For local dev, requires env var LANGSMITH_API_KEY with access to LangSmith Agent Server.
For production use, requires a license key in env var LANGGRAPH_CLOUD_LICENSE_KEY.""",
)
with Runner() as runner, Progress(message="Pulling...") as set:
@@ -457,7 +457,7 @@ For production use, requires a license key in env var LANGGRAPH_CLOUD_LICENSE_KE
)
@click.option(
"--base-image",
help="Base image to use for the LangGraph API server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
help="Base image to use for the Agent Server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
"\n\n \b\nExamples:\n --base-image langchain/langgraph-server:0.2.18 # Pin to a specific patch version"
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
)
@@ -473,7 +473,7 @@ For production use, requires a license key in env var LANGGRAPH_CLOUD_LICENSE_KE
)
@click.argument("docker_build_args", nargs=-1, type=click.UNPROCESSED)
@cli.command(
help="📦 Build LangGraph API server Docker image.",
help="📦 Build Agent Server Docker image.",
context_settings=dict(
ignore_unknown_options=True,
),
@@ -590,12 +590,12 @@ tests
@OPT_CONFIG
@click.argument("save_path", type=click.Path(resolve_path=True))
@cli.command(
help="🐳 Generate a Dockerfile for the LangGraph API server, with Docker Compose options."
help="🐳 Generate a Dockerfile for the Agent Server, with Docker Compose options."
)
@click.option(
"--add-docker-compose",
help=(
"Add additional files for running the LangGraph API server with "
"Add additional files for running the Agent Server with "
"docker-compose. These files include a docker-compose.yml, .env file, "
"and a .dockerignore file."
),
@@ -603,7 +603,7 @@ tests
)
@click.option(
"--base-image",
help="Base image to use for the LangGraph API server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
help="Base image to use for the Agent Server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
"\n\n \b\nExamples:\n --base-image langchain/langgraph-server:0.2.18 # Pin to a specific patch version"
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
)
@@ -695,7 +695,7 @@ def dockerfile(
"\n",
"# LANGSMITH_API_KEY=your-api-key",
"\n",
"# Or if you have a LangSmith Deployment license key, "
"# Or if you have an Agent Server license key, "
"then uncomment the following line: ",
"\n",
"# LANGGRAPH_CLOUD_LICENSE_KEY=your-license-key",
@@ -792,7 +792,7 @@ def dockerfile(
"--server-log-level",
type=str,
default="WARNING",
help="Set the log level for the API server.",
help="Set the log level for the Agent Server.",
)
@click.option(
"--ssl-certfile",
@@ -820,7 +820,7 @@ def dockerfile(
)
@cli.command(
"dev",
help="🏃‍♀️‍➡️ Run LangGraph API server in development mode with hot reloading and debugging support",
help="🏃‍♀️‍➡️ Run Agent Server in development mode with hot reloading and debugging support",
)
@log_command
def dev(
@@ -839,7 +839,7 @@ def dev(
ssl_certfile: pathlib.Path | None,
ssl_keyfile: pathlib.Path | None,
):
"""CLI entrypoint for running the LangGraph API server."""
"""CLI entrypoint for running the Agent Server."""
if (ssl_certfile is None) != (ssl_keyfile is None):
raise click.UsageError(
"Both --ssl-certfile and --ssl-keyfile must be provided to enable HTTPS."
@@ -1055,7 +1055,7 @@ def prepare(
image: str | None = None,
base_image: str | None = None,
) -> tuple[list[str], str]:
"""Prepare the arguments and stdin for running the LangGraph API server."""
"""Prepare the arguments and stdin for running the Agent Server."""
config_json = langgraph_cli.config.validate_config_file(config_path)
warn_non_wolfi_distro(config_json)
# pull latest images
+104 -84
View File
@@ -138,7 +138,7 @@ class BuildResult:
progress_message: str = ""
timeout_seconds: int = 300
poll_interval_seconds: int = 1
no_result_message: str = "Deployment updated"
no_result_message: str = "Agent Server updated"
on_poll: Callable[[str, str, Callable[[str], None]], None] | None = None
on_interrupt: Callable[[str], None] | None = None
show_build_logs_on_failure: bool = False
@@ -465,7 +465,7 @@ def deployment_selector(deployment_id: str | None, name: str | None) -> ById | B
return ById(deployment_id)
if name:
return ByName(name)
raise click.UsageError("Either --deployment-id or --name is required.")
raise click.UsageError("Either --agent-server-id or --name is required.")
def validate_deploy_commands(
@@ -513,9 +513,9 @@ def find_deployment_by_name(
def normalize_name(value: str | None) -> str:
"""Sanitize a deployment/directory name into a valid deployment name.
"""Sanitize an Agent Server/directory name into a valid Agent Server name.
LangSmith Deployment names only allow lowercase
Agent Server names only allow lowercase
alphanumeric characters and hyphens ([a-z0-9-]).
Invalid characters are replaced with hyphens.
"""
@@ -571,8 +571,8 @@ def _validate_prebuilt_image(
if image_platform != _DEPLOYMENT_PLATFORM:
detected = image_platform or "unknown"
raise click.ClickException(
f"Docker image '{image}' targets {detected}, but LangSmith Deployment "
f"requires {_DEPLOYMENT_PLATFORM}. Rebuild or pull the image for "
f"Docker image '{image}' targets {detected}, but LangSmith Agent Server "
f"hosting requires {_DEPLOYMENT_PLATFORM}. Rebuild or pull the image for "
f"{_DEPLOYMENT_PLATFORM} before deploying with --image."
)
_get_emitter().info(f"Image is available for {_DEPLOYMENT_PLATFORM}")
@@ -643,7 +643,7 @@ class DeploymentRow:
identifier = _text(deployment, "id")
if identifier is None:
raise HostBackendError(
"The control plane returned a deployment without an id."
"The control plane returned an Agent Server without an id."
)
return cls(identifier, _text(deployment, "name"), _deployment_url(deployment))
@@ -655,7 +655,7 @@ class DeploymentRow:
DEPLOYMENTS_TABLE = Table(
("Deployment ID", "Deployment Name", "Deployment URL"), DeploymentRow.cells
("Agent Server ID", "Agent Server Name", "Agent Server URL"), DeploymentRow.cells
)
LISTENERS_TABLE = Table(("Listener ID", "Compute ID", "Namespaces"), Listener.cells)
@@ -806,7 +806,7 @@ def _log_deploy_step(step: int, message: str, **extra: object) -> None:
def _fetch_deployment(
client: HostBackendClient, step: int, selector: ById
) -> tuple[ExistingDeployment, int]:
_log_deploy_step(step, f"Using deployment {selector.deployment_id}")
_log_deploy_step(step, f"Using Agent Server {selector.deployment_id}")
resource = _call_host_backend_with_optional_tenant(
client, lambda c: c.get_deployment(selector.deployment_id)
)
@@ -833,7 +833,7 @@ def _find_deployment(
)
if len(existing) > 1:
raise click.ClickException(
"This control plane does not filter deployments by agent, so the "
"This control plane does not filter Agent Servers by agent, so the "
f"CLI cannot tell which one belongs to '{selector.agent_id}' in "
f"{selector.environment}. Deploy by --name instead."
)
@@ -846,7 +846,7 @@ def _find_deployment(
None,
)
else:
_log_deploy_step(step, f"Looking up deployment '{selector.name}'")
_log_deploy_step(step, f"Looking up Agent Server '{selector.name}'")
found = _call_host_backend_with_optional_tenant(
client, lambda c: find_deployment_by_name(c, selector.name)
)
@@ -854,7 +854,7 @@ def _find_deployment(
if found is None:
em.warn(not_found_message)
else:
em.info(f"Found existing deployment (ID: {found.id})")
em.info(f"Found existing Agent Server (ID: {found.id})")
return found, step + 1
@@ -877,9 +877,9 @@ def _create_deployment(
) -> tuple[CreatedDeployment, int]:
_log_deploy_step(
step,
f"Creating deployment for agent '{agent['agent_id']}' in {agent['environment']}"
f"Creating Agent Server for agent '{agent['agent_id']}' in {agent['environment']}"
if agent is not None
else f"Creating deployment '{name}'",
else f"Creating Agent Server '{name}'",
)
try:
created = client.create_deployment(
@@ -893,7 +893,7 @@ def _create_deployment(
except HostBackendError as err:
if agent is not None and err.status_code == 409:
raise HostBackendError(
"This agent already has a deployment in this environment.",
"This agent already has an Agent Server in this environment.",
status_code=409,
) from None
raise
@@ -903,8 +903,8 @@ def _create_deployment(
"POST /v2/deployments succeeded but response missing a valid 'id'"
)
if agent is not None:
_get_emitter().info(f"Deployment name: {created.get('name')}")
_get_emitter().info(f"Deployment ID: {created_id}", deployment_id=created_id)
_get_emitter().info(f"Agent Server name: {created.get('name')}")
_get_emitter().info(f"Agent Server ID: {created_id}", deployment_id=created_id)
return CreatedDeployment(created_id, created), step + 1
@@ -1324,10 +1324,10 @@ def _run_local_build(
in err.message
):
raise click.ClickException(
f"Deployment '{deployment_id}' was not created by 'langgraph deploy' "
f"Agent Server '{deployment_id}' was not created by 'langgraph deploy' "
"and cannot be updated with this command.\n"
"Please create a new deployment by running 'langgraph deploy' "
"without --deployment-id, or use a different --name."
"Please create a new Agent Server by running 'langgraph deploy' "
"without --agent-server-id, or use a different --name."
) from None
raise
deployment_token = push_data.get("token")
@@ -1391,7 +1391,7 @@ def _run_local_build(
verbose=verbose,
)
_log_deploy_step(step, f"Updating deployment {deployment_id}")
_log_deploy_step(step, f"Updating Agent Server {deployment_id}")
updated = client.update_deployment(
deployment_id,
resolved_image,
@@ -1400,7 +1400,7 @@ def _run_local_build(
tracked_packages=tracked_packages,
)
return _image_revision_result(updated, "Deployment updated")
return _image_revision_result(updated, "Agent Server updated")
def _run_remote_build(
@@ -1488,7 +1488,7 @@ def _run_remote_build(
def _handle_interrupt(revision_id: str) -> None:
em.warn(
f"\nInterrupted. Deployment ID: {deployment_id}, Revision ID: {revision_id}"
f"\nInterrupted. Agent Server ID: {deployment_id}, Revision ID: {revision_id}"
)
em.warn("The build will continue remotely.")
@@ -1558,7 +1558,7 @@ def _resolve_or_create(
"Scale to zero deployments are not supported in AWS environments"
):
raise HostBackendError(
f"{err.message}\nUse --deployment-type dev instead.",
f"{err.message}\nUse --agent-server-type dev instead.",
status_code=err.status_code,
detail=err.detail,
) from None
@@ -1613,10 +1613,10 @@ def _available_listeners(client: HostBackendClient) -> tuple[Listener, ...]:
def _ensure_customer_registry_source(existing: ExistingDeployment) -> None:
if existing.source != _CUSTOMER_REGISTRY_SOURCE:
raise click.UsageError(
f"Deployment {existing.id} was not created from an external image "
f"Agent Server {existing.id} was not created from an external image "
"and cannot be updated with --push-to or --image-uri. Run without "
"either flag to keep its current build mode, or use a different "
"--name to create a new deployment."
"--name to create a new Agent Server."
)
@@ -1630,7 +1630,7 @@ class ManagedRegistrySource:
deployment_id, step = _resolve_or_create(
ctx,
source="internal_docker",
not_found_message="No deployment found. Will create after build.",
not_found_message="No Agent Server found. Will create after build.",
)
build_result = _run_local_build(
client=ctx.client,
@@ -1654,7 +1654,7 @@ class RemoteBuildSource:
deployment_id, step = _resolve_or_create(
ctx,
source="internal_source",
not_found_message="No deployment found. Will create.",
not_found_message="No Agent Server found. Will create.",
)
build_result = _run_remote_build(
client=ctx.client,
@@ -1723,24 +1723,24 @@ class CreateOnlySettings:
if push_to is None and image_uri is None and self.requested:
raise click.UsageError(
"--listener-id, --k8s-namespace, and --image-pull-secret only "
"apply when creating a deployment with --push-to or --image-uri."
"apply when creating an Agent Server with --push-to or --image-uri."
)
def ensure_not_existing(self, deployment_id: str) -> None:
if self.placement.requested:
raise click.UsageError(
"Listener and namespace are fixed when a deployment is "
f"created. Deployment {deployment_id} already exists, so "
"Listener and namespace are fixed when an Agent Server is "
f"created. Agent Server {deployment_id} already exists, so "
"drop --listener-id and --k8s-namespace, or create a new "
"deployment with a different --name."
"Agent Server with a different --name."
)
if self.image_pull_secrets:
raise click.UsageError(
"langgraph deploy does not update image pull secrets on an "
f"existing deployment. Deployment {deployment_id} already "
f"existing Agent Server. Agent Server {deployment_id} already "
"exists, so drop --image-pull-secret and update resource_spec "
"through the API or the LangSmith UI instead, or create a "
"new deployment with a different --name."
"new Agent Server with a different --name."
)
@@ -1757,7 +1757,7 @@ class CustomerRegistrySource:
ctx.client,
1,
ctx.selector,
not_found_message="No deployment found. Will create after push.",
not_found_message="No Agent Server found. Will create after push.",
)
if found is not None:
return self._update(ctx, found, step)
@@ -1771,7 +1771,7 @@ class CustomerRegistrySource:
_ensure_customer_registry_source(existing)
self.create_only.ensure_not_existing(existing.id)
image_uri, step = self._publish(ctx, step)
_log_deploy_step(step, f"Updating deployment {existing.id}")
_log_deploy_step(step, f"Updating Agent Server {existing.id}")
updated = ctx.client.update_deployment(
existing.id,
image_uri,
@@ -1780,7 +1780,7 @@ class CustomerRegistrySource:
tracked_packages=ctx.tracked_packages,
)
return DeployOutcome(
existing.id, _image_revision_result(updated, "Deployment updated")
existing.id, _image_revision_result(updated, "Agent Server updated")
)
def _resolve_placement(self, ctx: DeployContext) -> Placement:
@@ -1829,7 +1829,7 @@ class CustomerRegistrySource:
) from None
raise
return DeployOutcome(
created.id, _image_revision_result(created.resource, "Deployment created")
created.id, _image_revision_result(created.resource, "Agent Server created")
)
def _publish(self, ctx: DeployContext, step: int) -> tuple[str, int]:
@@ -2052,7 +2052,7 @@ def _call_host_backend_with_optional_tenant(
if err.status_code == 403 and "not enabled" in err.message.lower():
smith_base = client.endpoints.dashboard_url
raise HostBackendError(
"LangSmith Deployment is not enabled for this organization. "
"LangSmith Agent Server hosting is not enabled for this organization. "
f"Enable it at {smith_base}/host/deployments"
" (ensure this matches the organization for your API key).",
status_code=403,
@@ -2064,6 +2064,15 @@ def _call_host_backend_with_optional_tenant(
# Click options shared by deploy commands
# ---------------------------------------------------------------------------
def _normalize_deploy_token(token: str) -> str:
"""Accept legacy option names without advertising them in help or errors."""
return {
"deployment-id": "agent-server-id",
"deployment-type": "agent-server-type",
}.get(token, token)
OPT_HOST_API_KEY = click.option(
"--api-key",
envvar="LANGGRAPH_HOST_API_KEY",
@@ -2077,9 +2086,9 @@ OPT_HOST_DEPLOYMENT_NAME = click.option(
"--name",
envvar=_DEPLOYMENT_NAME_ENV,
help=(
"Deployment name. Can also be set via LANGSMITH_DEPLOYMENT_NAME "
"Agent Server name. Can also be set via LANGSMITH_DEPLOYMENT_NAME "
"environment variable or .env file. Defaults to current directory name "
"if --deployment-id is not provided."
"if --agent-server-id is not provided."
),
)
@@ -2207,19 +2216,21 @@ def _deploy_base_options(
OPT_AGENT_ID,
OPT_AGENT_ENVIRONMENT,
click.option(
"--deployment-id",
"--agent-server-id",
"deployment_id",
help=(
"ID of an existing deployment to update. If omitted, "
"--name is used to find or create the deployment."
"ID of an existing Agent Server to update. If omitted, "
"--name is used to find or create the Agent Server."
),
),
click.option(
"--deployment-type",
"--agent-server-type",
"deployment_type",
type=click.Choice(["serverless", "dedicated", "dev", "prod"]),
default="serverless",
show_default=True,
help=(
"Deployment type (used when creating a new deployment): "
"Agent Server type (used when creating a new Agent Server): "
"serverless (scale to zero) or dedicated (prod); "
"legacy dev and prod are also accepted. "
"Ignored with --push-to."
@@ -2238,7 +2249,7 @@ def _deploy_base_options(
"--tag",
"-t",
default=None,
help="Tag to use for the pushed deployment image. [default: latest]",
help="Tag to use for the pushed Agent Server image. [default: latest]",
),
click.option(
"--image",
@@ -2271,16 +2282,16 @@ def _deploy_base_options(
click.option(
"--listener-id",
help=(
"Listener that will run the deployment, for workspaces that "
"Listener that will run the Agent Server, for workspaces that "
"deploy through a listener in your own cluster. Only used when "
"creating a deployment with --push-to or --image-uri."
"creating an Agent Server with --push-to or --image-uri."
),
),
click.option(
"--k8s-namespace",
help=(
"Kubernetes namespace the listener deploys into. Only used when "
"creating a deployment with --push-to or --image-uri."
"creating an Agent Server with --push-to or --image-uri."
),
),
click.option(
@@ -2289,9 +2300,9 @@ def _deploy_base_options(
multiple=True,
help=(
"Name of a Kubernetes secret (type kubernetes.io/"
"dockerconfigjson) in the deployment's namespace to use for "
"dockerconfigjson) in the Agent Server's namespace to use for "
"pulling the image from a private registry. Repeat to give "
"more than one. Only used when creating a deployment with "
"more than one. Only used when creating an Agent Server with "
"--push-to or --image-uri."
),
),
@@ -2358,14 +2369,18 @@ def _deploy_base_options(
@click.group(
cls=DeployGroup,
help=(
"[Beta] Build and deploy a LangGraph image to LangSmith Deployment.\n\n"
"[Beta] Build and deploy an Agent Server image to LangSmith.\n\n"
"This command is in beta and under active development. "
"Expect frequent updates and improvements.\n\n"
"Run from the root of your LangGraph project (where langgraph.json "
"is located). This command also accepts build flags (--base-image, "
"--config, --pull, etc.). See 'langgraph build --help' for details."
),
context_settings=dict(ignore_unknown_options=True, allow_extra_args=True),
context_settings=dict(
ignore_unknown_options=True,
allow_extra_args=True,
token_normalize_func=_normalize_deploy_token,
),
invoke_without_command=True, # allow `deploy` click group to execute without command
)
@_deploy_base_options(include_docker_args=False, validate_config_path=False)
@@ -2389,7 +2404,11 @@ def deploy(ctx: click.Context, **_: object):
@_deploy_base_options()
@click.command(context_settings=dict(ignore_unknown_options=True))
@click.command(
context_settings=dict(
ignore_unknown_options=True, token_normalize_func=_normalize_deploy_token
)
)
def _deploy_cmd(
config: pathlib.Path,
pull: bool,
@@ -2439,13 +2458,13 @@ def _deploy_cmd(
)
if name is not None or deployment_id is not None:
raise click.UsageError(
"--agent-id and --agent-environment cannot be combined with --name or --deployment-id."
"--agent-id and --agent-environment cannot be combined with --name or --agent-server-id."
)
agent = {"agent_id": agent_id, "environment": environment}
if not config.exists():
message = (
"We couldn't find a langgraph.json file. Run `langgraph deploy` from "
"the root of a LangSmith Deployment project. To get started, visit "
"the root of your LangGraph project. To get started, visit "
"https://docs.langchain.com/langsmith/deployment-quickstart."
)
if json_output:
@@ -2464,14 +2483,14 @@ def _deploy_cmd(
if no_input:
name = default_name
else:
name = click.prompt("Deployment name", default=default_name)
name = click.prompt("Agent Server name", default=default_name)
if name and not deployment_id:
name = normalize_name(name)
if not no_input:
env_path = _resolve_env_path(config_json, config)
if env_path is not None:
set_key(str(env_path), _DEPLOYMENT_NAME_ENV, name)
em.info(f"Saved deployment name to {env_path}")
em.info(f"Saved Agent Server name to {env_path}")
secrets = _secrets_from_env(_env_without_deployment_name(env_vars))
@@ -2568,7 +2587,7 @@ def _deploy_cmd(
client,
outcome.deployment_id,
last_status,
dashboard_label="Deployment dashboard",
dashboard_label="Agent Server dashboard",
status_url=dep_status_url,
)
@@ -2585,16 +2604,16 @@ def _deploy_cmd(
@click.option(
"--name-contains",
default="",
help="Only show deployments whose names contain this value.",
help="Only show Agent Servers whose names contain this value.",
)
@click.option(
"--json",
"json_output",
is_flag=True,
default=False,
help="Print the deployments as a JSON array of {id, name} instead of a table.",
help="Print the Agent Servers as a JSON array of {id, name} instead of a table.",
)
@deploy.command("list", help="[Beta] List LangSmith Deployments.")
@deploy.command("list", help="[Beta] List Agent Servers.")
def deploy_list(
api_key: str | None,
host_url: str | None,
@@ -2626,7 +2645,7 @@ def deploy_list(
if json_output:
click.echo(json_mod.dumps([deployment.as_json() for deployment in deployments]))
elif not deployments:
click.echo("No deployments found.")
click.echo("No Agent Servers found.")
else:
click.echo(DEPLOYMENTS_TABLE.render(deployments))
@@ -2638,7 +2657,7 @@ def deploy_list(
@OPT_HOST_API_KEY
@OPT_HOST_URL
@click.argument("deployment_id", type=click.UUID)
@click.argument("deployment_id", type=click.UUID, metavar="AGENT_SERVER_ID")
@click.option(
"--start-time",
help="ISO8601 start time. Defaults to 15 minutes before --end-time.",
@@ -2651,7 +2670,7 @@ def deploy_list(
"memory_usage, cpu_usage, disk_usage, restart_count, replica_count, "
"worker_count, lg_run_count, responses_per_second, "
"error_responses_per_second, p95_latency, run_queue_wait_time. "
"Availability depends on the deployment and platform."
"Availability depends on the Agent Server and platform."
),
)
@click.option(
@@ -2661,7 +2680,7 @@ def deploy_list(
"--raw", is_flag=True, help="Print the original host-backend chart schema."
)
@deploy.command(
"metrics", help="[Beta] Fetch deployment monitoring time series as compact JSON."
"metrics", help="[Beta] Fetch Agent Server monitoring time series as compact JSON."
)
def deploy_metrics(
api_key: str | None,
@@ -2762,7 +2781,7 @@ def deploy_listeners_list(
@deploy.group(
"revisions", cls=NestedHelpGroup, help="[Beta] Manage deployment revisions."
"revisions", cls=NestedHelpGroup, help="[Beta] Manage Agent Server revisions."
)
def deploy_revisions() -> None:
pass
@@ -2777,12 +2796,12 @@ def deploy_revisions() -> None:
show_default=True,
help="Maximum number of revisions to return.",
)
@click.argument("deployment_id")
@click.argument("deployment_id", metavar="AGENT_SERVER_ID")
@deploy_revisions.command(
"list",
help=(
"[Beta] List revisions for a LangSmith Deployment.\n\n"
"Use the `deploy list` command to list deployment IDs."
"[Beta] List revisions for an Agent Server.\n\n"
"Use the `deploy list` command to list Agent Server IDs."
),
)
def deploy_revisions_list(
@@ -2794,7 +2813,7 @@ def deploy_revisions_list(
lambda c: c.list_revisions(deployment_id, limit=limit),
)
if not revisions:
click.echo(f"No revisions found for deployment {deployment_id}.")
click.echo(f"No revisions found for Agent Server {deployment_id}.")
return
click.echo(REVISIONS_TABLE.render(_with_replaced_status(revisions)))
@@ -2812,12 +2831,12 @@ def deploy_revisions_list(
default=False,
help="Delete without prompting for confirmation.",
)
@click.argument("deployment_id")
@click.argument("deployment_id", metavar="AGENT_SERVER_ID")
@deploy.command(
"delete",
help=(
"[Beta] Delete a LangSmith Deployment.\n\n"
"Use the `deploy list` command to list deployment IDs."
"[Beta] Delete an Agent Server.\n\n"
"Use the `deploy list` command to list Agent Server IDs."
),
)
def deploy_delete(
@@ -2826,7 +2845,7 @@ def deploy_delete(
if not force:
response = click.prompt(
click.style(
f"Are you sure you want to delete deployment ID {deployment_id}? (Y/n)",
f"Are you sure you want to delete Agent Server ID {deployment_id}? (Y/n)",
fg="yellow",
),
default="Y",
@@ -2839,7 +2858,7 @@ def deploy_delete(
client,
lambda c: c.delete_deployment(deployment_id),
)
click.secho(f"Deleted deployment {deployment_id}.", fg="green")
click.secho(f"Deleted Agent Server {deployment_id}.", fg="green")
# ---------------------------------------------------------------------------
@@ -2850,8 +2869,9 @@ def deploy_delete(
@OPT_HOST_API_KEY
@OPT_HOST_DEPLOYMENT_NAME
@click.option(
"--deployment-id",
help="Deployment ID. If omitted, --name is used to find the deployment.",
"--agent-server-id",
"deployment_id",
help="Agent Server ID. If omitted, --name is used to find the Agent Server.",
)
@click.option(
"--type",
@@ -2860,8 +2880,8 @@ def deploy_delete(
default="deploy",
show_default=True,
help=(
"Log stream to fetch: 'deploy' shows agent server runtime logs; "
"'build' shows build logs (for deployments built remotely)."
"Log stream to fetch: 'deploy' shows Agent Server runtime logs; "
"'build' shows build logs (for Agent Servers built remotely)."
),
)
@click.option(
@@ -2906,7 +2926,7 @@ def deploy_delete(
@deploy.command(
"logs",
help=(
"[Beta] Fetch LangSmith Deployment logs. Use 'deploy' for agent runtime "
"[Beta] Fetch Agent Server logs. Use 'deploy' for Agent Server runtime "
"logs, or 'build' for remote build logs."
),
)
@@ -2938,14 +2958,14 @@ def deploy_logs(
client, lambda c: find_deployment_by_name(c, name_to_find)
)
if found is None:
raise click.ClickException(f"Deployment '{name_to_find}' not found.")
raise click.ClickException(f"Agent Server '{name_to_find}' not found.")
dep_id = found.id
if log_type == "build" and not revision_id:
revisions = client.list_revisions(dep_id, limit=1)
if not revisions:
raise click.ClickException(
"No revisions found for this deployment. Cannot fetch build logs."
"No revisions found for this Agent Server. Cannot fetch build logs."
)
revision_id = str(revisions[0]["id"])
click.secho(f"Using latest revision: {revision_id}", fg="cyan")
+1 -1
View File
@@ -172,7 +172,7 @@ def compose_as_dict(
postgres_uri: str | None = None,
# If you are running against an already-built image, you can pass it here
image: str | None = None,
# Base image to use for the LangGraph API server
# Base image to use for the Agent Server
base_image: str | None = None,
# API version of the base image
api_version: str | None = None,
+9 -9
View File
@@ -317,7 +317,7 @@ class AuthConfig(TypedDict, total=False):
"""Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.
Defaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header
value is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom
value is a valid API key for the Agent Server's workspace. If `True`, all requests will go through your custom
authentication logic, regardless of origin of the request.
"""
openapi: SecurityConfig
@@ -461,7 +461,7 @@ class ConfigurableHeaderConfig(TypedDict, total=False):
class HttpConfig(TypedDict, total=False):
"""Configuration for the built-in HTTP server that powers your deployment's routes and endpoints."""
"""Configuration for the built-in HTTP server that powers your Agent Server's routes and endpoints."""
app: str
"""Optional. Import path to a custom Starlette/FastAPI application to mount.
@@ -490,12 +490,12 @@ class HttpConfig(TypedDict, total=False):
Default is False.
"""
disable_mcp: bool
"""Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.
"""Optional. If `True`, /mcp routes are removed, disabling default support to expose the Agent Server as an MCP server.
Default is False.
"""
disable_a2a: bool
"""Optional. If `True`, /a2a routes are removed, disabling default support to expose the deployment as an agent-to-agent (A2A) server.
"""Optional. If `True`, /a2a routes are removed, disabling default support to expose the Agent Server as an agent-to-agent (A2A) server.
Default is False.
"""
@@ -638,16 +638,16 @@ class Config(TypedDict, total=False):
python_version: str
"""Optional. Python version in 'major.minor' format (e.g. '3.11').
Must be at least 3.11 or greater for this deployment to function properly.
Must be at least 3.11 or greater for this Agent Server to function properly.
"""
node_version: str | None
"""Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.
"""Optional. Node.js version as a major version (e.g. '20'), if your Agent Server needs Node.
Must be >= 20 if provided.
"""
api_version: str | None
"""Optional. Which semantic version of the LangGraph API server to use.
"""Optional. Which semantic version of the Agent Server to use.
Defaults to latest. Check the
[changelog](https://docs.langchain.com/langgraph-platform/langgraph-server-changelog)
@@ -658,7 +658,7 @@ class Config(TypedDict, total=False):
"""
base_image: str | None
"""Optional. Base image to use for the LangGraph API server.
"""Optional. Base image to use for the Agent Server.
Defaults to langchain/langgraph-api or langchain/langgraphjs-api."""
@@ -740,7 +740,7 @@ class Config(TypedDict, total=False):
"""
env: dict[str, str] | str
"""Optional. Environment variables to set for your deployment.
"""Optional. Environment variables to set for your Agent Server.
- If given as a dict, keys are variable names and values are their values.
- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.
+16 -16
View File
@@ -12,7 +12,7 @@
"properties": {
"python_version": {
"type": "string",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this deployment to function properly.\n",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this Agent Server to function properly.\n",
"enum": [
"3.11",
"3.12",
@@ -50,7 +50,7 @@
"type": "null"
}
],
"description": "Optional. Which semantic version of the LangGraph API server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
},
"auth": {
"anyOf": [
@@ -72,7 +72,7 @@
"type": "null"
}
],
"description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
"description": "Optional. Base image to use for the Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
},
"checkpointer": {
"anyOf": [
@@ -122,7 +122,7 @@
"type": "string"
}
],
"description": "Optional. Environment variables to set for your deployment.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
"description": "Optional. Environment variables to set for your Agent Server.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
},
"graphs": {
"type": "object",
@@ -265,7 +265,7 @@
"properties": {
"python_version": {
"type": "string",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this deployment to function properly.\n",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this Agent Server to function properly.\n",
"enum": [
"3.11",
"3.12",
@@ -303,7 +303,7 @@
"type": "null"
}
],
"description": "Optional. Which semantic version of the LangGraph API server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
},
"auth": {
"anyOf": [
@@ -325,7 +325,7 @@
"type": "null"
}
],
"description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
"description": "Optional. Base image to use for the Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
},
"checkpointer": {
"anyOf": [
@@ -375,7 +375,7 @@
"type": "string"
}
],
"description": "Optional. Environment variables to set for your deployment.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
"description": "Optional. Environment variables to set for your Agent Server.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
},
"graphs": {
"type": "object",
@@ -520,7 +520,7 @@
"type": "null"
}
],
"description": "Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.\nMust be >= 20 if provided.\n"
"description": "Optional. Node.js version as a major version (e.g. '20'), if your Agent Server needs Node.\nMust be >= 20 if provided.\n"
},
"_INTERNAL_docker_tag": {
"anyOf": [
@@ -542,7 +542,7 @@
"type": "null"
}
],
"description": "Optional. Which semantic version of the LangGraph API server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
},
"auth": {
"anyOf": [
@@ -564,7 +564,7 @@
"type": "null"
}
],
"description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
"description": "Optional. Base image to use for the Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
},
"checkpointer": {
"anyOf": [
@@ -614,7 +614,7 @@
"type": "string"
}
],
"description": "Optional. Environment variables to set for your deployment.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
"description": "Optional. Environment variables to set for your Agent Server.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
},
"graphs": {
"type": "object",
@@ -765,7 +765,7 @@
},
"disable_studio_auth": {
"type": "boolean",
"description": "Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.\n\nDefaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header\nvalue is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom\nauthentication logic, regardless of origin of the request.\n"
"description": "Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.\n\nDefaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header\nvalue is a valid API key for the Agent Server's workspace. If `True`, all requests will go through your custom\nauthentication logic, regardless of origin of the request.\n"
},
"openapi": {
"$ref": "#/$defs/SecurityConfig",
@@ -1018,7 +1018,7 @@
},
"HttpConfig": {
"title": "HttpConfig",
"description": "Configuration for the built-in HTTP server that powers your deployment's routes and endpoints.",
"description": "Configuration for the built-in HTTP server that powers your Agent Server's routes and endpoints.",
"type": "object",
"properties": {
"app": {
@@ -1049,7 +1049,7 @@
},
"disable_a2a": {
"type": "boolean",
"description": "Optional. If `True`, /a2a routes are removed, disabling default support to expose the deployment as an agent-to-agent (A2A) server.\n\nDefault is False.\n"
"description": "Optional. If `True`, /a2a routes are removed, disabling default support to expose the Agent Server as an agent-to-agent (A2A) server.\n\nDefault is False.\n"
},
"disable_assistants": {
"type": "boolean",
@@ -1057,7 +1057,7 @@
},
"disable_mcp": {
"type": "boolean",
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.\n\nDefault is False.\n"
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the Agent Server as an MCP server.\n\nDefault is False.\n"
},
"disable_meta": {
"type": "boolean",
+16 -16
View File
@@ -12,7 +12,7 @@
"properties": {
"python_version": {
"type": "string",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this deployment to function properly.\n",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this Agent Server to function properly.\n",
"enum": [
"3.11",
"3.12",
@@ -50,7 +50,7 @@
"type": "null"
}
],
"description": "Optional. Which semantic version of the LangGraph API server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
},
"auth": {
"anyOf": [
@@ -72,7 +72,7 @@
"type": "null"
}
],
"description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
"description": "Optional. Base image to use for the Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
},
"checkpointer": {
"anyOf": [
@@ -122,7 +122,7 @@
"type": "string"
}
],
"description": "Optional. Environment variables to set for your deployment.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
"description": "Optional. Environment variables to set for your Agent Server.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
},
"graphs": {
"type": "object",
@@ -265,7 +265,7 @@
"properties": {
"python_version": {
"type": "string",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this deployment to function properly.\n",
"description": "Optional. Python version in 'major.minor' format (e.g. '3.11').\nMust be at least 3.11 or greater for this Agent Server to function properly.\n",
"enum": [
"3.11",
"3.12",
@@ -303,7 +303,7 @@
"type": "null"
}
],
"description": "Optional. Which semantic version of the LangGraph API server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
},
"auth": {
"anyOf": [
@@ -325,7 +325,7 @@
"type": "null"
}
],
"description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
"description": "Optional. Base image to use for the Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
},
"checkpointer": {
"anyOf": [
@@ -375,7 +375,7 @@
"type": "string"
}
],
"description": "Optional. Environment variables to set for your deployment.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
"description": "Optional. Environment variables to set for your Agent Server.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
},
"graphs": {
"type": "object",
@@ -520,7 +520,7 @@
"type": "null"
}
],
"description": "Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.\nMust be >= 20 if provided.\n"
"description": "Optional. Node.js version as a major version (e.g. '20'), if your Agent Server needs Node.\nMust be >= 20 if provided.\n"
},
"_INTERNAL_docker_tag": {
"anyOf": [
@@ -542,7 +542,7 @@
"type": "null"
}
],
"description": "Optional. Which semantic version of the LangGraph API server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
},
"auth": {
"anyOf": [
@@ -564,7 +564,7 @@
"type": "null"
}
],
"description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
"description": "Optional. Base image to use for the Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
},
"checkpointer": {
"anyOf": [
@@ -614,7 +614,7 @@
"type": "string"
}
],
"description": "Optional. Environment variables to set for your deployment.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
"description": "Optional. Environment variables to set for your Agent Server.\n\n- If given as a dict, keys are variable names and values are their values.\n- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.\n\nenv=\".env\n"
},
"graphs": {
"type": "object",
@@ -765,7 +765,7 @@
},
"disable_studio_auth": {
"type": "boolean",
"description": "Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.\n\nDefaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header\nvalue is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom\nauthentication logic, regardless of origin of the request.\n"
"description": "Optional. Whether to disable LangSmith API-key authentication for requests originating the Studio.\n\nDefaults to False, meaning that if a particular header is set, the server will verify the `x-api-key` header\nvalue is a valid API key for the Agent Server's workspace. If `True`, all requests will go through your custom\nauthentication logic, regardless of origin of the request.\n"
},
"openapi": {
"$ref": "#/$defs/SecurityConfig",
@@ -1018,7 +1018,7 @@
},
"HttpConfig": {
"title": "HttpConfig",
"description": "Configuration for the built-in HTTP server that powers your deployment's routes and endpoints.",
"description": "Configuration for the built-in HTTP server that powers your Agent Server's routes and endpoints.",
"type": "object",
"properties": {
"app": {
@@ -1049,7 +1049,7 @@
},
"disable_a2a": {
"type": "boolean",
"description": "Optional. If `True`, /a2a routes are removed, disabling default support to expose the deployment as an agent-to-agent (A2A) server.\n\nDefault is False.\n"
"description": "Optional. If `True`, /a2a routes are removed, disabling default support to expose the Agent Server as an agent-to-agent (A2A) server.\n\nDefault is False.\n"
},
"disable_assistants": {
"type": "boolean",
@@ -1057,7 +1057,7 @@
},
"disable_mcp": {
"type": "boolean",
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.\n\nDefault is False.\n"
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the Agent Server as an MCP server.\n\nDefault is False.\n"
},
"disable_meta": {
"type": "boolean",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$id": "https://github.com/langchain-ai/langgraph/libs/cli/schemas/version.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "LangGraph Platform configuration (when building via the langgraph-cli).",
"title": "Agent Server configuration (when building via the langgraph-cli).",
"type": "object",
"oneOf": [
{
+20 -18
View File
@@ -352,7 +352,7 @@ def test_top_level_help_shows_deploy_subcommands() -> None:
assert "deploy delete" in result.output
assert "deploy revisions" in result.output
assert "deploy revisions list" in result.output
assert "[Beta] List LangSmith Deployments." in result.output
assert "[Beta] List Agent Servers." in result.output
def test_top_level_help_truncates_command_descriptions_to_single_line() -> None:
@@ -369,7 +369,7 @@ def test_top_level_help_truncates_command_descriptions_to_single_line() -> None:
assert not lines[lines.index(deploy_line) + 1].startswith(" ")
assert "..." in deploy_line
assert "[Beta] List LangSmith Deployments." in deploy_list_line
assert "[Beta] List Agent Servers." in deploy_list_line
def test_deploy_missing_config_shows_actionable_error(tmp_path, monkeypatch) -> None:
@@ -496,9 +496,9 @@ def test_deploy_list_command(monkeypatch) -> None:
"tenant_id": "",
"name_contains": "alp",
}
assert "Deployment ID" in result.output
assert "Deployment Name" in result.output
assert "Deployment URL" in result.output
assert "Agent Server ID" in result.output
assert "Agent Server Name" in result.output
assert "Agent Server URL" in result.output
assert "dep-123" in result.output
assert "https://beta.example.com" in result.output
@@ -528,7 +528,7 @@ def test_deploy_list_command_no_results(monkeypatch) -> None:
)
assert result.exit_code == 0, result.output
assert result.output.strip() == "No deployments found."
assert result.output.strip() == "No Agent Servers found."
def test_deploy_list_prints_an_aligned_table(monkeypatch) -> None:
@@ -562,10 +562,10 @@ def test_deploy_list_prints_an_aligned_table(monkeypatch) -> None:
assert result.exit_code == 0, result.output
assert result.output.splitlines() == [
"Deployment ID Deployment Name Deployment URL ",
"------------- --------------- -------------------------",
"dep-123 alpha https://alpha.example.com",
"dep-4567 beta-agent - ",
"Agent Server ID Agent Server Name Agent Server URL ",
"--------------- ----------------- -------------------------",
"dep-123 alpha https://alpha.example.com",
"dep-4567 beta-agent - ",
]
@@ -649,7 +649,7 @@ def test_deploy_list_rejects_a_deployment_without_an_id(monkeypatch) -> None:
result = _deploy_list_json(monkeypatch, [{"name": "alpha"}])
assert result.exit_code != 0
assert "deployment without an id" in result.output
assert "Agent Server without an id" in result.output
def test_deploy_list_json_keeps_the_agent_note_off_stdout(monkeypatch) -> None:
@@ -1061,7 +1061,7 @@ def test_deploy_revisions_list_command_no_results(monkeypatch) -> None:
)
assert result.exit_code == 0, result.output
assert result.output.strip() == "No revisions found for deployment dep-123."
assert result.output.strip() == "No revisions found for Agent Server dep-123."
def test_deploy_revisions_list_marks_older_deployed_revisions_as_replaced(
@@ -1145,7 +1145,7 @@ def test_deploy_revisions_list_command_with_explicit_limit(monkeypatch) -> None:
assert result.exit_code == 0, result.output
assert captured == {"deployment_id": "dep-123", "limit": "25"}
assert result.output.strip() == "No revisions found for deployment dep-123."
assert result.output.strip() == "No revisions found for Agent Server dep-123."
def test_deploy_revisions_list_missing_deployment_id_shows_usage() -> None:
@@ -1154,7 +1154,7 @@ def test_deploy_revisions_list_missing_deployment_id_shows_usage() -> None:
result = runner.invoke(cli, ["deploy", "revisions", "list"])
assert result.exit_code == 2, result.output
assert "Missing argument 'DEPLOYMENT_ID'" in result.output
assert "Missing argument 'AGENT_SERVER_ID'" in result.output
def test_deploy_delete_command(monkeypatch) -> None:
@@ -1195,10 +1195,10 @@ def test_deploy_delete_command(monkeypatch) -> None:
"deployment_id": "dep-123",
}
assert (
"Are you sure you want to delete deployment ID dep-123? (Y/n):"
"Are you sure you want to delete Agent Server ID dep-123? (Y/n):"
in click.unstyle(result.output)
)
assert result.output.strip().endswith("Deleted deployment dep-123.")
assert result.output.strip().endswith("Deleted Agent Server dep-123.")
def test_deploy_delete_command_cancelled(monkeypatch) -> None:
@@ -1266,14 +1266,16 @@ def test_deploy_delete_command_force(monkeypatch) -> None:
)
assert result.exit_code == 0, result.output
assert "Are you sure you want to delete deployment ID dep-123?" not in result.output
assert (
"Are you sure you want to delete Agent Server ID dep-123?" not in result.output
)
assert captured == {
"host_url": "https://api.example.com",
"api_key": "test-key",
"tenant_id": "",
"deployment_id": "dep-123",
}
assert result.output.strip() == "Deleted deployment dep-123."
assert result.output.strip() == "Deleted Agent Server dep-123."
def test_dockerfile_command_basic() -> None:
@@ -359,6 +359,68 @@ def deploy_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> DeployPro
return DeployProject(control_plane, docker, timeline, uploads)
@pytest.mark.parametrize(
("command", "expected"),
[
([], ["--agent-server-id", "--agent-server-type"]),
(["logs"], ["--agent-server-id"]),
(["delete"], ["AGENT_SERVER_ID"]),
(["revisions", "list"], ["AGENT_SERVER_ID"]),
(["metrics"], ["AGENT_SERVER_ID"]),
],
)
def test_deploy_help_uses_agent_server_names(command: list[str], expected: list[str]):
result = CliRunner().invoke(cli, ["deploy", *command, "--help"])
assert result.exit_code == 0, result.output
assert all(name in result.output for name in expected)
assert "Agent Server" in result.output
assert "--deployment-" not in result.output
assert "DEPLOYMENT_ID" not in result.output
@pytest.mark.parametrize("command", [[], ["logs"]])
@pytest.mark.parametrize("option", ["--agent-server-id", "--deployment-id"])
def test_missing_agent_server_id_uses_public_option_name(
command: list[str], option: str
):
result = CliRunner().invoke(cli, ["deploy", *command, option])
assert result.exit_code == 2, result.output
assert "Option '--agent-server-id' requires an argument" in result.output
assert "--deployment-id" not in result.output
@pytest.mark.parametrize("option", ["--agent-server-type", "--deployment-type"])
@pytest.mark.parametrize("values", [[], ["invalid"]])
def test_invalid_agent_server_type_uses_public_option_name(
option: str, values: list[str]
):
result = CliRunner().invoke(cli, ["deploy", option, *values])
assert result.exit_code == 2, result.output
assert "'--agent-server-type'" in result.output
assert "--deployment-type" not in result.output
@pytest.mark.parametrize("option", ["--agent-server-id", "--deployment-id"])
def test_deploy_logs_accepts_agent_server_id_aliases(
deploy_project, mocker, option: str
):
fetch_logs = mocker.patch.object(
HostBackendClient, "get_deploy_logs", return_value={"logs": []}
)
result = CliRunner().invoke(
cli, ["deploy", "logs", "--api-key", "test-key", option, "dep-existing"]
)
assert result.exit_code == 0, result.output
fetch_logs.assert_called_once_with(
"dep-existing", {"limit": 100, "order": "desc"}, None
)
@pytest.mark.parametrize("build_mode", ["--no-remote", "--remote"])
@pytest.mark.parametrize(
("detail", "expects_dev_hint"),
@@ -376,7 +438,7 @@ def test_create_failure_preserves_backend_error_and_only_hints_for_aws_rejection
assert result.exit_code == 1, result.output
assert detail in result.output
assert ("Use --deployment-type dev instead." in result.output) is expects_dev_hint
assert ("Use --agent-server-type dev instead." in result.output) is expects_dev_hint
assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == {
"deployment_type": "dev_zero"
}
@@ -415,7 +477,7 @@ def test_first_local_deploy_creates_then_builds_pushes_and_updates_in_order(
"docker inspect-digest",
_patch(CREATED_ID),
]
assert "Deployment updated" in result.output
assert "Agent Server updated" in result.output
@pytest.mark.parametrize(
@@ -428,10 +490,15 @@ def test_first_local_deploy_creates_then_builds_pushes_and_updates_in_order(
],
)
@pytest.mark.parametrize("build_mode", ["--no-remote", "--remote"])
@pytest.mark.parametrize("option", ["--agent-server-type", "--deployment-type"])
def test_deployment_type_aliases(
deploy_project: DeployProject, deployment_type: str, wire_type: str, build_mode: str
deploy_project: DeployProject,
deployment_type: str,
wire_type: str,
build_mode: str,
option: str,
) -> None:
result = deploy_project.run(build_mode, "--deployment-type", deployment_type)
result = deploy_project.run(build_mode, option, deployment_type)
assert result.exit_code == 0, result.output
assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == {
@@ -770,7 +837,7 @@ def test_push_to_builds_pushes_then_creates_an_external_deployment(
"source_revision_config": {"image_uri": EXTERNAL_DIGEST},
"secrets": [],
}
assert "Deployment created" in result.output
assert "Agent Server created" in result.output
def test_push_to_builds_directly_with_the_push_reference(
@@ -873,16 +940,17 @@ def test_push_to_rejects_a_non_external_deployment_before_any_docker_work(
assert deploy_project.docker.verbs() == []
@pytest.mark.parametrize("option", ["--agent-server-id", "--deployment-id"])
@pytest.mark.parametrize("equals", [False, True])
def test_push_to_with_deployment_id_fetches_the_deployment_once(
deploy_project: DeployProject,
deploy_project: DeployProject, option: str, equals: bool
) -> None:
deploy_project.control_plane.existing_deployments = [
{"id": "dep-ext", "name": "another-name", "source": "external_docker"}
]
result = deploy_project.run(
"--deployment-id", "dep-ext", "--push-to", PUSH_REPOSITORY
)
id_args = [f"{option}=dep-ext"] if equals else [option, "dep-ext"]
result = deploy_project.run(*id_args, "--push-to", PUSH_REPOSITORY)
assert result.exit_code == 0, result.output
assert deploy_project.timeline == [
@@ -1159,7 +1227,7 @@ def test_image_pull_secret_without_push_to_or_image_uri_is_refused(
result = deploy_project.run("--image-pull-secret", "regcred")
assert result.exit_code != 0
assert "only apply when creating a deployment with --push-to" in result.output
assert "only apply when creating an Agent Server with --push-to" in result.output
assert deploy_project.timeline == []
@@ -1241,7 +1309,7 @@ def test_listener_flags_are_refused_for_a_deployment_id_without_any_call(
)
assert result.exit_code != 0
assert "fixed when a deployment is created" in result.output
assert "fixed when an Agent Server is created" in result.output
assert deploy_project.timeline == []
@@ -1262,7 +1330,7 @@ def test_listener_flags_are_refused_on_an_existing_deployment(
)
assert result.exit_code != 0
assert "fixed when a deployment is created" in result.output
assert "fixed when an Agent Server is created" in result.output
assert deploy_project.docker.verbs() == []
+1 -1
View File
@@ -1204,7 +1204,7 @@ ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
assert additional_contexts == {}
# node.js build used for LangSmith Deployment
# node.js build used for LangSmith Agent Server
def test_config_to_docker_nodejs():
graphs = {"agent": "./graphs/agent.js:graph"}
actual_docker_stdin, additional_contexts = config_to_docker(
@@ -118,5 +118,5 @@ def test_agent_lookup_refuses_a_control_plane_that_ignores_the_filter(deployment
result = CliRunner().invoke(cli, AGENT_ARGS)
assert result.exit_code != 0
assert "does not filter deployments by agent" in result.output
assert "does not filter Agent Servers by agent" in result.output
assert len(requests) == 1
@@ -187,7 +187,7 @@ class TestValidatePrebuiltImage:
monkeypatch.setattr(deploy_mod, "subp_exec", fake_subp_exec)
with pytest.raises(click.ClickException, match="requires linux/amd64"):
with pytest.raises(click.ClickException, match="hosting requires linux/amd64"):
_validate_prebuilt_image(_FakeRunner(), "repo/app:arm", verbose=False)
@@ -327,6 +327,9 @@ class TestCallHostBackendWithOptionalTenant:
)
assert exc_info.value.status_code == 403
assert "smith.langchain.com" in exc_info.value.message
assert exc_info.value.message.startswith(
"LangSmith Agent Server hosting is not enabled for this organization."
)
def test_403_not_enabled_eu_url(self):
detail = (
@@ -871,17 +874,17 @@ class TestSelectSource:
),
pytest.param(
{"placement": RequestedPlacement(listener_id="listener-1")},
"only apply when creating a deployment with --push-to",
"only apply when creating an Agent Server with --push-to",
id="listener_without_push_to",
),
pytest.param(
{"placement": RequestedPlacement(k8s_namespace="agents")},
"only apply when creating a deployment with --push-to",
"only apply when creating an Agent Server with --push-to",
id="namespace_without_push_to",
),
pytest.param(
{"image_pull_secrets": ("regcred",)},
"only apply when creating a deployment with --push-to",
"only apply when creating an Agent Server with --push-to",
id="image_pull_secret_without_push_to",
),
pytest.param(
@@ -1406,7 +1409,7 @@ class TestCreateOnlySettings:
settings = CreateOnlySettings(RequestedPlacement(listener_id="listener-1"))
with pytest.raises(
click.UsageError,
match="Listener and namespace are fixed when a deployment is created",
match="Listener and namespace are fixed when an Agent Server is created",
):
settings.ensure_not_existing("dep-1")
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "langgraph"
version = "1.2.21"
version = "1.2.20"
description = "Building stateful, multi-actor applications with LLMs"
authors = []
requires-python = ">=3.10"
+1 -1
View File
@@ -1440,7 +1440,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.21"
version = "1.2.20"
source = { editable = "." }
dependencies = [
{ name = "langchain-core" },
+1 -1
View File
@@ -381,7 +381,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.21"
version = "1.2.20"
source = { editable = "../langgraph" }
dependencies = [
{ name = "langchain-core" },
+1 -1
View File
@@ -394,7 +394,7 @@ wheels = [
[[package]]
name = "langgraph"
version = "1.2.21"
version = "1.2.20"
source = { editable = "../langgraph" }
dependencies = [
{ name = "langchain-core" },