mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-11-12 17:07:43 +01:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62c4e502f7 |
No files matched your search
+11
-20
@@ -40,7 +40,7 @@ langgraph new [PATH] --template TEMPLATE_NAME
|
||||
|
||||
### `langgraph dev` 🏃♀️
|
||||
|
||||
Run Agent Server in development mode with hot reloading.
|
||||
Run LangGraph API server in development mode with hot reloading.
|
||||
|
||||
```bash
|
||||
langgraph dev [OPTIONS]
|
||||
@@ -54,7 +54,7 @@ langgraph dev [OPTIONS]
|
||||
|
||||
### `langgraph up` 🚀
|
||||
|
||||
Launch Agent Server in Docker.
|
||||
Launch LangGraph API server in Docker.
|
||||
|
||||
```bash
|
||||
langgraph up [OPTIONS]
|
||||
@@ -86,23 +86,14 @@ 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 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.
|
||||
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.
|
||||
|
||||
```bash
|
||||
langgraph deploy metrics AGENT_SERVER_ID \
|
||||
langgraph deploy metrics DEPLOYMENT_ID \
|
||||
--start-time 2026-10-28T00:00:00Z \
|
||||
--end-time 2026-10-28T01:00:00Z \
|
||||
--metrics cpu_usage,memory_usage,worker_count
|
||||
@@ -117,8 +108,8 @@ langgraph deploy metrics AGENT_SERVER_ID \
|
||||
- Queue: `run_queue_wait_time`
|
||||
|
||||
These are exact metric names, not free-text search terms. Availability depends on
|
||||
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`.
|
||||
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`.
|
||||
|
||||
Omit `--metrics` to fetch all available metrics. Time bounds default to the last
|
||||
15 minutes, and the backend picks the resolution from the time
|
||||
@@ -145,8 +136,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 AGENT_SERVER_ID --summary
|
||||
langgraph deploy metrics AGENT_SERVER_ID --raw
|
||||
langgraph deploy metrics DEPLOYMENT_ID --summary
|
||||
langgraph deploy metrics DEPLOYMENT_ID --raw
|
||||
```
|
||||
|
||||
`--summary` returns the same shape without `points`. `--raw` returns the original
|
||||
@@ -154,7 +145,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 Agent Server benchmark):
|
||||
serialization for all modes; not a live deployment benchmark):
|
||||
|
||||
| Before: raw UI schema | After: default | After: summary |
|
||||
| --- | --- | --- |
|
||||
|
||||
@@ -107,10 +107,6 @@ 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 @@
|
||||
__version__ = "0.4.43"
|
||||
__version__ = "0.4.42"
|
||||
@@ -1,4 +1,4 @@
|
||||
"""CLI entrypoint for Agent Server."""
|
||||
"""CLI entrypoint for LangGraph API 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 the Agent Server. Dependencies can be one of the following:
|
||||
- "dependencies": array of dependencies for langgraph API 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=(
|
||||
"Agent Server version to use for the base image. If unspecified, the "
|
||||
"API 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 Agent Server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
|
||||
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."
|
||||
"\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 Agent Server.")
|
||||
@cli.command(help="🚀 Launch LangGraph API 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 Agent Server...", fg="green")
|
||||
click.secho("Starting LangGraph API server...", fg="green")
|
||||
click.secho(
|
||||
"""For local dev, requires env var LANGSMITH_API_KEY with access to LangSmith Agent Server.
|
||||
"""For local dev, requires env var LANGSMITH_API_KEY with access to LangSmith Deployment.
|
||||
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 Agent Server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
|
||||
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."
|
||||
"\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 Agent Server Docker image.",
|
||||
help="📦 Build LangGraph API 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 Agent Server, with Docker Compose options."
|
||||
help="🐳 Generate a Dockerfile for the LangGraph API server, with Docker Compose options."
|
||||
)
|
||||
@click.option(
|
||||
"--add-docker-compose",
|
||||
help=(
|
||||
"Add additional files for running the Agent Server with "
|
||||
"Add additional files for running the LangGraph API 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 Agent Server. Pin to specific versions using version tags. Defaults to langchain/langgraph-api or langchain/langgraphjs-api."
|
||||
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."
|
||||
"\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 an Agent Server license key, "
|
||||
"# Or if you have a LangSmith Deployment 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 Agent Server.",
|
||||
help="Set the log level for the API server.",
|
||||
)
|
||||
@click.option(
|
||||
"--ssl-certfile",
|
||||
@@ -820,7 +820,7 @@ def dockerfile(
|
||||
)
|
||||
@cli.command(
|
||||
"dev",
|
||||
help="🏃♀️➡️ Run Agent Server in development mode with hot reloading and debugging support",
|
||||
help="🏃♀️➡️ Run LangGraph API 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 Agent Server."""
|
||||
"""CLI entrypoint for running the LangGraph API 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 Agent Server."""
|
||||
"""Prepare the arguments and stdin for running the LangGraph API server."""
|
||||
config_json = langgraph_cli.config.validate_config_file(config_path)
|
||||
warn_non_wolfi_distro(config_json)
|
||||
# pull latest images
|
||||
|
||||
@@ -138,7 +138,7 @@ class BuildResult:
|
||||
progress_message: str = ""
|
||||
timeout_seconds: int = 300
|
||||
poll_interval_seconds: int = 1
|
||||
no_result_message: str = "Agent Server updated"
|
||||
no_result_message: str = "Deployment 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 --agent-server-id or --name is required.")
|
||||
raise click.UsageError("Either --deployment-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 an Agent Server/directory name into a valid Agent Server name.
|
||||
"""Sanitize a deployment/directory name into a valid deployment name.
|
||||
|
||||
Agent Server names only allow lowercase
|
||||
LangSmith Deployment 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 Agent Server "
|
||||
f"hosting requires {_DEPLOYMENT_PLATFORM}. Rebuild or pull the image for "
|
||||
f"Docker image '{image}' targets {detected}, but LangSmith Deployment "
|
||||
f"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 an Agent Server without an id."
|
||||
"The control plane returned a deployment without an id."
|
||||
)
|
||||
return cls(identifier, _text(deployment, "name"), _deployment_url(deployment))
|
||||
|
||||
@@ -655,7 +655,7 @@ class DeploymentRow:
|
||||
|
||||
|
||||
DEPLOYMENTS_TABLE = Table(
|
||||
("Agent Server ID", "Agent Server Name", "Agent Server URL"), DeploymentRow.cells
|
||||
("Deployment ID", "Deployment Name", "Deployment 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 Agent Server {selector.deployment_id}")
|
||||
_log_deploy_step(step, f"Using deployment {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 Agent Servers by agent, so the "
|
||||
"This control plane does not filter deployments 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 Agent Server '{selector.name}'")
|
||||
_log_deploy_step(step, f"Looking up deployment '{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 Agent Server (ID: {found.id})")
|
||||
em.info(f"Found existing deployment (ID: {found.id})")
|
||||
return found, step + 1
|
||||
|
||||
|
||||
@@ -877,9 +877,9 @@ def _create_deployment(
|
||||
) -> tuple[CreatedDeployment, int]:
|
||||
_log_deploy_step(
|
||||
step,
|
||||
f"Creating Agent Server for agent '{agent['agent_id']}' in {agent['environment']}"
|
||||
f"Creating deployment for agent '{agent['agent_id']}' in {agent['environment']}"
|
||||
if agent is not None
|
||||
else f"Creating Agent Server '{name}'",
|
||||
else f"Creating deployment '{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 an Agent Server in this environment.",
|
||||
"This agent already has a deployment 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"Agent Server name: {created.get('name')}")
|
||||
_get_emitter().info(f"Agent Server ID: {created_id}", deployment_id=created_id)
|
||||
_get_emitter().info(f"Deployment name: {created.get('name')}")
|
||||
_get_emitter().info(f"Deployment 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"Agent Server '{deployment_id}' was not created by 'langgraph deploy' "
|
||||
f"Deployment '{deployment_id}' was not created by 'langgraph deploy' "
|
||||
"and cannot be updated with this command.\n"
|
||||
"Please create a new Agent Server by running 'langgraph deploy' "
|
||||
"without --agent-server-id, or use a different --name."
|
||||
"Please create a new deployment by running 'langgraph deploy' "
|
||||
"without --deployment-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 Agent Server {deployment_id}")
|
||||
_log_deploy_step(step, f"Updating deployment {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, "Agent Server updated")
|
||||
return _image_revision_result(updated, "Deployment updated")
|
||||
|
||||
|
||||
def _run_remote_build(
|
||||
@@ -1488,7 +1488,7 @@ def _run_remote_build(
|
||||
|
||||
def _handle_interrupt(revision_id: str) -> None:
|
||||
em.warn(
|
||||
f"\nInterrupted. Agent Server ID: {deployment_id}, Revision ID: {revision_id}"
|
||||
f"\nInterrupted. Deployment 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 --agent-server-type dev instead.",
|
||||
f"{err.message}\nUse --deployment-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"Agent Server {existing.id} was not created from an external image "
|
||||
f"Deployment {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 Agent Server."
|
||||
"--name to create a new deployment."
|
||||
)
|
||||
|
||||
|
||||
@@ -1630,7 +1630,7 @@ class ManagedRegistrySource:
|
||||
deployment_id, step = _resolve_or_create(
|
||||
ctx,
|
||||
source="internal_docker",
|
||||
not_found_message="No Agent Server found. Will create after build.",
|
||||
not_found_message="No deployment 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 Agent Server found. Will create.",
|
||||
not_found_message="No deployment 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 an Agent Server with --push-to or --image-uri."
|
||||
"apply when creating a deployment 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 an Agent Server is "
|
||||
f"created. Agent Server {deployment_id} already exists, so "
|
||||
"Listener and namespace are fixed when a deployment is "
|
||||
f"created. Deployment {deployment_id} already exists, so "
|
||||
"drop --listener-id and --k8s-namespace, or create a new "
|
||||
"Agent Server with a different --name."
|
||||
"deployment with a different --name."
|
||||
)
|
||||
if self.image_pull_secrets:
|
||||
raise click.UsageError(
|
||||
"langgraph deploy does not update image pull secrets on an "
|
||||
f"existing Agent Server. Agent Server {deployment_id} already "
|
||||
f"existing deployment. Deployment {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 Agent Server with a different --name."
|
||||
"new deployment with a different --name."
|
||||
)
|
||||
|
||||
|
||||
@@ -1757,7 +1757,7 @@ class CustomerRegistrySource:
|
||||
ctx.client,
|
||||
1,
|
||||
ctx.selector,
|
||||
not_found_message="No Agent Server found. Will create after push.",
|
||||
not_found_message="No deployment 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 Agent Server {existing.id}")
|
||||
_log_deploy_step(step, f"Updating deployment {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, "Agent Server updated")
|
||||
existing.id, _image_revision_result(updated, "Deployment 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, "Agent Server created")
|
||||
created.id, _image_revision_result(created.resource, "Deployment 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 Agent Server hosting is not enabled for this organization. "
|
||||
"LangSmith Deployment 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,15 +2064,6 @@ 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",
|
||||
@@ -2086,9 +2077,9 @@ OPT_HOST_DEPLOYMENT_NAME = click.option(
|
||||
"--name",
|
||||
envvar=_DEPLOYMENT_NAME_ENV,
|
||||
help=(
|
||||
"Agent Server name. Can also be set via LANGSMITH_DEPLOYMENT_NAME "
|
||||
"Deployment name. Can also be set via LANGSMITH_DEPLOYMENT_NAME "
|
||||
"environment variable or .env file. Defaults to current directory name "
|
||||
"if --agent-server-id is not provided."
|
||||
"if --deployment-id is not provided."
|
||||
),
|
||||
)
|
||||
|
||||
@@ -2216,21 +2207,19 @@ def _deploy_base_options(
|
||||
OPT_AGENT_ID,
|
||||
OPT_AGENT_ENVIRONMENT,
|
||||
click.option(
|
||||
"--agent-server-id",
|
||||
"deployment_id",
|
||||
"--deployment-id",
|
||||
help=(
|
||||
"ID of an existing Agent Server to update. If omitted, "
|
||||
"--name is used to find or create the Agent Server."
|
||||
"ID of an existing deployment to update. If omitted, "
|
||||
"--name is used to find or create the deployment."
|
||||
),
|
||||
),
|
||||
click.option(
|
||||
"--agent-server-type",
|
||||
"deployment_type",
|
||||
"--deployment-type",
|
||||
type=click.Choice(["serverless", "dedicated", "dev", "prod"]),
|
||||
default="serverless",
|
||||
show_default=True,
|
||||
help=(
|
||||
"Agent Server type (used when creating a new Agent Server): "
|
||||
"Deployment type (used when creating a new deployment): "
|
||||
"serverless (scale to zero) or dedicated (prod); "
|
||||
"legacy dev and prod are also accepted. "
|
||||
"Ignored with --push-to."
|
||||
@@ -2249,7 +2238,7 @@ def _deploy_base_options(
|
||||
"--tag",
|
||||
"-t",
|
||||
default=None,
|
||||
help="Tag to use for the pushed Agent Server image. [default: latest]",
|
||||
help="Tag to use for the pushed deployment image. [default: latest]",
|
||||
),
|
||||
click.option(
|
||||
"--image",
|
||||
@@ -2282,16 +2271,16 @@ def _deploy_base_options(
|
||||
click.option(
|
||||
"--listener-id",
|
||||
help=(
|
||||
"Listener that will run the Agent Server, for workspaces that "
|
||||
"Listener that will run the deployment, for workspaces that "
|
||||
"deploy through a listener in your own cluster. Only used when "
|
||||
"creating an Agent Server with --push-to or --image-uri."
|
||||
"creating a deployment with --push-to or --image-uri."
|
||||
),
|
||||
),
|
||||
click.option(
|
||||
"--k8s-namespace",
|
||||
help=(
|
||||
"Kubernetes namespace the listener deploys into. Only used when "
|
||||
"creating an Agent Server with --push-to or --image-uri."
|
||||
"creating a deployment with --push-to or --image-uri."
|
||||
),
|
||||
),
|
||||
click.option(
|
||||
@@ -2300,9 +2289,9 @@ def _deploy_base_options(
|
||||
multiple=True,
|
||||
help=(
|
||||
"Name of a Kubernetes secret (type kubernetes.io/"
|
||||
"dockerconfigjson) in the Agent Server's namespace to use for "
|
||||
"dockerconfigjson) in the deployment's namespace to use for "
|
||||
"pulling the image from a private registry. Repeat to give "
|
||||
"more than one. Only used when creating an Agent Server with "
|
||||
"more than one. Only used when creating a deployment with "
|
||||
"--push-to or --image-uri."
|
||||
),
|
||||
),
|
||||
@@ -2369,18 +2358,14 @@ def _deploy_base_options(
|
||||
@click.group(
|
||||
cls=DeployGroup,
|
||||
help=(
|
||||
"[Beta] Build and deploy an Agent Server image to LangSmith.\n\n"
|
||||
"[Beta] Build and deploy a LangGraph image to LangSmith Deployment.\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,
|
||||
token_normalize_func=_normalize_deploy_token,
|
||||
),
|
||||
context_settings=dict(ignore_unknown_options=True, allow_extra_args=True),
|
||||
invoke_without_command=True, # allow `deploy` click group to execute without command
|
||||
)
|
||||
@_deploy_base_options(include_docker_args=False, validate_config_path=False)
|
||||
@@ -2404,11 +2389,7 @@ def deploy(ctx: click.Context, **_: object):
|
||||
|
||||
|
||||
@_deploy_base_options()
|
||||
@click.command(
|
||||
context_settings=dict(
|
||||
ignore_unknown_options=True, token_normalize_func=_normalize_deploy_token
|
||||
)
|
||||
)
|
||||
@click.command(context_settings=dict(ignore_unknown_options=True))
|
||||
def _deploy_cmd(
|
||||
config: pathlib.Path,
|
||||
pull: bool,
|
||||
@@ -2458,13 +2439,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 --agent-server-id."
|
||||
"--agent-id and --agent-environment cannot be combined with --name or --deployment-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 your LangGraph project. To get started, visit "
|
||||
"the root of a LangSmith Deployment project. To get started, visit "
|
||||
"https://docs.langchain.com/langsmith/deployment-quickstart."
|
||||
)
|
||||
if json_output:
|
||||
@@ -2483,14 +2464,14 @@ def _deploy_cmd(
|
||||
if no_input:
|
||||
name = default_name
|
||||
else:
|
||||
name = click.prompt("Agent Server name", default=default_name)
|
||||
name = click.prompt("Deployment 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 Agent Server name to {env_path}")
|
||||
em.info(f"Saved deployment name to {env_path}")
|
||||
|
||||
secrets = _secrets_from_env(_env_without_deployment_name(env_vars))
|
||||
|
||||
@@ -2587,7 +2568,7 @@ def _deploy_cmd(
|
||||
client,
|
||||
outcome.deployment_id,
|
||||
last_status,
|
||||
dashboard_label="Agent Server dashboard",
|
||||
dashboard_label="Deployment dashboard",
|
||||
status_url=dep_status_url,
|
||||
)
|
||||
|
||||
@@ -2604,16 +2585,16 @@ def _deploy_cmd(
|
||||
@click.option(
|
||||
"--name-contains",
|
||||
default="",
|
||||
help="Only show Agent Servers whose names contain this value.",
|
||||
help="Only show deployments whose names contain this value.",
|
||||
)
|
||||
@click.option(
|
||||
"--json",
|
||||
"json_output",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Print the Agent Servers as a JSON array of {id, name} instead of a table.",
|
||||
help="Print the deployments as a JSON array of {id, name} instead of a table.",
|
||||
)
|
||||
@deploy.command("list", help="[Beta] List Agent Servers.")
|
||||
@deploy.command("list", help="[Beta] List LangSmith Deployments.")
|
||||
def deploy_list(
|
||||
api_key: str | None,
|
||||
host_url: str | None,
|
||||
@@ -2645,7 +2626,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 Agent Servers found.")
|
||||
click.echo("No deployments found.")
|
||||
else:
|
||||
click.echo(DEPLOYMENTS_TABLE.render(deployments))
|
||||
|
||||
@@ -2657,7 +2638,7 @@ def deploy_list(
|
||||
|
||||
@OPT_HOST_API_KEY
|
||||
@OPT_HOST_URL
|
||||
@click.argument("deployment_id", type=click.UUID, metavar="AGENT_SERVER_ID")
|
||||
@click.argument("deployment_id", type=click.UUID)
|
||||
@click.option(
|
||||
"--start-time",
|
||||
help="ISO8601 start time. Defaults to 15 minutes before --end-time.",
|
||||
@@ -2670,7 +2651,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 Agent Server and platform."
|
||||
"Availability depends on the deployment and platform."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
@@ -2680,7 +2661,7 @@ def deploy_list(
|
||||
"--raw", is_flag=True, help="Print the original host-backend chart schema."
|
||||
)
|
||||
@deploy.command(
|
||||
"metrics", help="[Beta] Fetch Agent Server monitoring time series as compact JSON."
|
||||
"metrics", help="[Beta] Fetch deployment monitoring time series as compact JSON."
|
||||
)
|
||||
def deploy_metrics(
|
||||
api_key: str | None,
|
||||
@@ -2781,7 +2762,7 @@ def deploy_listeners_list(
|
||||
|
||||
|
||||
@deploy.group(
|
||||
"revisions", cls=NestedHelpGroup, help="[Beta] Manage Agent Server revisions."
|
||||
"revisions", cls=NestedHelpGroup, help="[Beta] Manage deployment revisions."
|
||||
)
|
||||
def deploy_revisions() -> None:
|
||||
pass
|
||||
@@ -2796,12 +2777,12 @@ def deploy_revisions() -> None:
|
||||
show_default=True,
|
||||
help="Maximum number of revisions to return.",
|
||||
)
|
||||
@click.argument("deployment_id", metavar="AGENT_SERVER_ID")
|
||||
@click.argument("deployment_id")
|
||||
@deploy_revisions.command(
|
||||
"list",
|
||||
help=(
|
||||
"[Beta] List revisions for an Agent Server.\n\n"
|
||||
"Use the `deploy list` command to list Agent Server IDs."
|
||||
"[Beta] List revisions for a LangSmith Deployment.\n\n"
|
||||
"Use the `deploy list` command to list deployment IDs."
|
||||
),
|
||||
)
|
||||
def deploy_revisions_list(
|
||||
@@ -2813,7 +2794,7 @@ def deploy_revisions_list(
|
||||
lambda c: c.list_revisions(deployment_id, limit=limit),
|
||||
)
|
||||
if not revisions:
|
||||
click.echo(f"No revisions found for Agent Server {deployment_id}.")
|
||||
click.echo(f"No revisions found for deployment {deployment_id}.")
|
||||
return
|
||||
click.echo(REVISIONS_TABLE.render(_with_replaced_status(revisions)))
|
||||
|
||||
@@ -2831,12 +2812,12 @@ def deploy_revisions_list(
|
||||
default=False,
|
||||
help="Delete without prompting for confirmation.",
|
||||
)
|
||||
@click.argument("deployment_id", metavar="AGENT_SERVER_ID")
|
||||
@click.argument("deployment_id")
|
||||
@deploy.command(
|
||||
"delete",
|
||||
help=(
|
||||
"[Beta] Delete an Agent Server.\n\n"
|
||||
"Use the `deploy list` command to list Agent Server IDs."
|
||||
"[Beta] Delete a LangSmith Deployment.\n\n"
|
||||
"Use the `deploy list` command to list deployment IDs."
|
||||
),
|
||||
)
|
||||
def deploy_delete(
|
||||
@@ -2845,7 +2826,7 @@ def deploy_delete(
|
||||
if not force:
|
||||
response = click.prompt(
|
||||
click.style(
|
||||
f"Are you sure you want to delete Agent Server ID {deployment_id}? (Y/n)",
|
||||
f"Are you sure you want to delete deployment ID {deployment_id}? (Y/n)",
|
||||
fg="yellow",
|
||||
),
|
||||
default="Y",
|
||||
@@ -2858,7 +2839,7 @@ def deploy_delete(
|
||||
client,
|
||||
lambda c: c.delete_deployment(deployment_id),
|
||||
)
|
||||
click.secho(f"Deleted Agent Server {deployment_id}.", fg="green")
|
||||
click.secho(f"Deleted deployment {deployment_id}.", fg="green")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -2869,9 +2850,8 @@ def deploy_delete(
|
||||
@OPT_HOST_API_KEY
|
||||
@OPT_HOST_DEPLOYMENT_NAME
|
||||
@click.option(
|
||||
"--agent-server-id",
|
||||
"deployment_id",
|
||||
help="Agent Server ID. If omitted, --name is used to find the Agent Server.",
|
||||
"--deployment-id",
|
||||
help="Deployment ID. If omitted, --name is used to find the deployment.",
|
||||
)
|
||||
@click.option(
|
||||
"--type",
|
||||
@@ -2880,8 +2860,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 Agent Servers built remotely)."
|
||||
"Log stream to fetch: 'deploy' shows agent server runtime logs; "
|
||||
"'build' shows build logs (for deployments built remotely)."
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
@@ -2926,7 +2906,7 @@ def deploy_delete(
|
||||
@deploy.command(
|
||||
"logs",
|
||||
help=(
|
||||
"[Beta] Fetch Agent Server logs. Use 'deploy' for Agent Server runtime "
|
||||
"[Beta] Fetch LangSmith Deployment logs. Use 'deploy' for agent runtime "
|
||||
"logs, or 'build' for remote build logs."
|
||||
),
|
||||
)
|
||||
@@ -2958,14 +2938,14 @@ def deploy_logs(
|
||||
client, lambda c: find_deployment_by_name(c, name_to_find)
|
||||
)
|
||||
if found is None:
|
||||
raise click.ClickException(f"Agent Server '{name_to_find}' not found.")
|
||||
raise click.ClickException(f"Deployment '{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 Agent Server. Cannot fetch build logs."
|
||||
"No revisions found for this deployment. Cannot fetch build logs."
|
||||
)
|
||||
revision_id = str(revisions[0]["id"])
|
||||
click.secho(f"Using latest revision: {revision_id}", fg="cyan")
|
||||
|
||||
@@ -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 Agent Server
|
||||
# Base image to use for the LangGraph API server
|
||||
base_image: str | None = None,
|
||||
# API version of the base image
|
||||
api_version: str | None = None,
|
||||
|
||||
@@ -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 Agent Server's workspace. If `True`, all requests will go through your custom
|
||||
value is a valid API key for the deployment's workspace. If `True`, all requests will go through your custom
|
||||
authentication logic, regardless of origin of the request.
|
||||
"""
|
||||
openapi: SecurityConfig
|
||||
@@ -461,7 +461,7 @@ class ConfigurableHeaderConfig(TypedDict, total=False):
|
||||
|
||||
|
||||
class HttpConfig(TypedDict, total=False):
|
||||
"""Configuration for the built-in HTTP server that powers your Agent Server's routes and endpoints."""
|
||||
"""Configuration for the built-in HTTP server that powers your deployment's routes and endpoints."""
|
||||
|
||||
app: str
|
||||
"""Optional. Import path to a custom Starlette/FastAPI application to mount.
|
||||
@@ -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 Agent Server as an MCP server.
|
||||
"""Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.
|
||||
|
||||
Default is False.
|
||||
"""
|
||||
disable_a2a: bool
|
||||
"""Optional. If `True`, /a2a routes are removed, disabling default support to expose the Agent Server as an agent-to-agent (A2A) server.
|
||||
"""Optional. If `True`, /a2a routes are removed, disabling default support to expose the deployment 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 Agent Server to function properly.
|
||||
Must be at least 3.11 or greater for this deployment to function properly.
|
||||
"""
|
||||
|
||||
node_version: str | None
|
||||
"""Optional. Node.js version as a major version (e.g. '20'), if your Agent Server needs Node.
|
||||
"""Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.
|
||||
Must be >= 20 if provided.
|
||||
"""
|
||||
|
||||
api_version: str | None
|
||||
"""Optional. Which semantic version of the Agent Server to use.
|
||||
"""Optional. Which semantic version of the LangGraph API server to use.
|
||||
|
||||
Defaults to latest. Check the
|
||||
[changelog](https://docs.langchain.com/langgraph-platform/langgraph-server-changelog)
|
||||
@@ -658,7 +658,7 @@ class Config(TypedDict, total=False):
|
||||
"""
|
||||
|
||||
base_image: str | None
|
||||
"""Optional. Base image to use for the Agent Server.
|
||||
"""Optional. Base image to use for the LangGraph API 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 Agent Server.
|
||||
"""Optional. Environment variables to set for your deployment.
|
||||
|
||||
- If given as a dict, keys are variable names and values are their values.
|
||||
- If given as a string, it must be a path to a file containing lines in KEY=VALUE format.
|
||||
|
||||
@@ -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 Agent Server 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 deployment to function properly.\n",
|
||||
"enum": [
|
||||
"3.11",
|
||||
"3.12",
|
||||
@@ -50,7 +50,7 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
|
||||
"description": "Optional. Which semantic version of the LangGraph API 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 Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
|
||||
"description": "Optional. Base image to use for the LangGraph API 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 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"
|
||||
"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"
|
||||
},
|
||||
"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 Agent Server 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 deployment to function properly.\n",
|
||||
"enum": [
|
||||
"3.11",
|
||||
"3.12",
|
||||
@@ -303,7 +303,7 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
|
||||
"description": "Optional. Which semantic version of the LangGraph API 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 Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
|
||||
"description": "Optional. Base image to use for the LangGraph API 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 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"
|
||||
"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"
|
||||
},
|
||||
"graphs": {
|
||||
"type": "object",
|
||||
@@ -520,7 +520,7 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Node.js version as a major version (e.g. '20'), if your Agent Server needs Node.\nMust be >= 20 if provided.\n"
|
||||
"description": "Optional. Node.js version as a major version (e.g. '20'), if your deployment 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 Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
|
||||
"description": "Optional. Which semantic version of the LangGraph API 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 Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
|
||||
"description": "Optional. Base image to use for the LangGraph API 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 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"
|
||||
"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"
|
||||
},
|
||||
"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 Agent Server'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 deployment'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 Agent Server's routes and endpoints.",
|
||||
"description": "Configuration for the built-in HTTP server that powers your deployment'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 Agent Server 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 deployment 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 Agent Server as an MCP server.\n\nDefault is False.\n"
|
||||
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.\n\nDefault is False.\n"
|
||||
},
|
||||
"disable_meta": {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -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 Agent Server 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 deployment to function properly.\n",
|
||||
"enum": [
|
||||
"3.11",
|
||||
"3.12",
|
||||
@@ -50,7 +50,7 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
|
||||
"description": "Optional. Which semantic version of the LangGraph API 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 Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
|
||||
"description": "Optional. Base image to use for the LangGraph API 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 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"
|
||||
"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"
|
||||
},
|
||||
"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 Agent Server 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 deployment to function properly.\n",
|
||||
"enum": [
|
||||
"3.11",
|
||||
"3.12",
|
||||
@@ -303,7 +303,7 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Which semantic version of the Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
|
||||
"description": "Optional. Which semantic version of the LangGraph API 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 Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
|
||||
"description": "Optional. Base image to use for the LangGraph API 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 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"
|
||||
"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"
|
||||
},
|
||||
"graphs": {
|
||||
"type": "object",
|
||||
@@ -520,7 +520,7 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Node.js version as a major version (e.g. '20'), if your Agent Server needs Node.\nMust be >= 20 if provided.\n"
|
||||
"description": "Optional. Node.js version as a major version (e.g. '20'), if your deployment 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 Agent Server to use.\n\nDefaults to latest. Check the\nfor more information.\n"
|
||||
"description": "Optional. Which semantic version of the LangGraph API 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 Agent Server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
|
||||
"description": "Optional. Base image to use for the LangGraph API 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 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"
|
||||
"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"
|
||||
},
|
||||
"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 Agent Server'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 deployment'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 Agent Server's routes and endpoints.",
|
||||
"description": "Configuration for the built-in HTTP server that powers your deployment'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 Agent Server 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 deployment 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 Agent Server as an MCP server.\n\nDefault is False.\n"
|
||||
"description": "Optional. If `True`, /mcp routes are removed, disabling default support to expose the deployment as an MCP server.\n\nDefault is False.\n"
|
||||
},
|
||||
"disable_meta": {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -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": "Agent Server configuration (when building via the langgraph-cli).",
|
||||
"title": "LangGraph Platform configuration (when building via the langgraph-cli).",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -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 Agent Servers." in result.output
|
||||
assert "[Beta] List LangSmith Deployments." 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 Agent Servers." in deploy_list_line
|
||||
assert "[Beta] List LangSmith Deployments." 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 "Agent Server ID" in result.output
|
||||
assert "Agent Server Name" in result.output
|
||||
assert "Agent Server URL" in result.output
|
||||
assert "Deployment ID" in result.output
|
||||
assert "Deployment Name" in result.output
|
||||
assert "Deployment 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 Agent Servers found."
|
||||
assert result.output.strip() == "No deployments 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() == [
|
||||
"Agent Server ID Agent Server Name Agent Server URL ",
|
||||
"--------------- ----------------- -------------------------",
|
||||
"dep-123 alpha https://alpha.example.com",
|
||||
"dep-4567 beta-agent - ",
|
||||
"Deployment ID Deployment Name Deployment 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 "Agent Server without an id" in result.output
|
||||
assert "deployment 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 Agent Server dep-123."
|
||||
assert result.output.strip() == "No revisions found for deployment 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 Agent Server dep-123."
|
||||
assert result.output.strip() == "No revisions found for deployment 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 'AGENT_SERVER_ID'" in result.output
|
||||
assert "Missing argument 'DEPLOYMENT_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 Agent Server ID dep-123? (Y/n):"
|
||||
"Are you sure you want to delete deployment ID dep-123? (Y/n):"
|
||||
in click.unstyle(result.output)
|
||||
)
|
||||
assert result.output.strip().endswith("Deleted Agent Server dep-123.")
|
||||
assert result.output.strip().endswith("Deleted deployment dep-123.")
|
||||
|
||||
|
||||
def test_deploy_delete_command_cancelled(monkeypatch) -> None:
|
||||
@@ -1266,16 +1266,14 @@ def test_deploy_delete_command_force(monkeypatch) -> None:
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert (
|
||||
"Are you sure you want to delete Agent Server ID dep-123?" not in result.output
|
||||
)
|
||||
assert "Are you sure you want to delete deployment 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 Agent Server dep-123."
|
||||
assert result.output.strip() == "Deleted deployment dep-123."
|
||||
|
||||
|
||||
def test_dockerfile_command_basic() -> None:
|
||||
|
||||
@@ -359,68 +359,6 @@ 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"),
|
||||
@@ -438,7 +376,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 --agent-server-type dev instead." in result.output) is expects_dev_hint
|
||||
assert ("Use --deployment-type dev instead." in result.output) is expects_dev_hint
|
||||
assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == {
|
||||
"deployment_type": "dev_zero"
|
||||
}
|
||||
@@ -477,7 +415,7 @@ def test_first_local_deploy_creates_then_builds_pushes_and_updates_in_order(
|
||||
"docker inspect-digest",
|
||||
_patch(CREATED_ID),
|
||||
]
|
||||
assert "Agent Server updated" in result.output
|
||||
assert "Deployment updated" in result.output
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -490,15 +428,10 @@ 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,
|
||||
option: str,
|
||||
deploy_project: DeployProject, deployment_type: str, wire_type: str, build_mode: str
|
||||
) -> None:
|
||||
result = deploy_project.run(build_mode, option, deployment_type)
|
||||
result = deploy_project.run(build_mode, "--deployment-type", deployment_type)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == {
|
||||
@@ -837,7 +770,7 @@ def test_push_to_builds_pushes_then_creates_an_external_deployment(
|
||||
"source_revision_config": {"image_uri": EXTERNAL_DIGEST},
|
||||
"secrets": [],
|
||||
}
|
||||
assert "Agent Server created" in result.output
|
||||
assert "Deployment created" in result.output
|
||||
|
||||
|
||||
def test_push_to_builds_directly_with_the_push_reference(
|
||||
@@ -940,17 +873,16 @@ 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, option: str, equals: bool
|
||||
deploy_project: DeployProject,
|
||||
) -> None:
|
||||
deploy_project.control_plane.existing_deployments = [
|
||||
{"id": "dep-ext", "name": "another-name", "source": "external_docker"}
|
||||
]
|
||||
|
||||
id_args = [f"{option}=dep-ext"] if equals else [option, "dep-ext"]
|
||||
result = deploy_project.run(*id_args, "--push-to", PUSH_REPOSITORY)
|
||||
result = deploy_project.run(
|
||||
"--deployment-id", "dep-ext", "--push-to", PUSH_REPOSITORY
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert deploy_project.timeline == [
|
||||
@@ -1227,7 +1159,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 an Agent Server with --push-to" in result.output
|
||||
assert "only apply when creating a deployment with --push-to" in result.output
|
||||
assert deploy_project.timeline == []
|
||||
|
||||
|
||||
@@ -1309,7 +1241,7 @@ def test_listener_flags_are_refused_for_a_deployment_id_without_any_call(
|
||||
)
|
||||
|
||||
assert result.exit_code != 0
|
||||
assert "fixed when an Agent Server is created" in result.output
|
||||
assert "fixed when a deployment is created" in result.output
|
||||
assert deploy_project.timeline == []
|
||||
|
||||
|
||||
@@ -1330,7 +1262,7 @@ def test_listener_flags_are_refused_on_an_existing_deployment(
|
||||
)
|
||||
|
||||
assert result.exit_code != 0
|
||||
assert "fixed when an Agent Server is created" in result.output
|
||||
assert "fixed when a deployment is created" in result.output
|
||||
assert deploy_project.docker.verbs() == []
|
||||
|
||||
|
||||
|
||||
@@ -1204,7 +1204,7 @@ ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
|
||||
assert additional_contexts == {}
|
||||
|
||||
|
||||
# node.js build used for LangSmith Agent Server
|
||||
# node.js build used for LangSmith Deployment
|
||||
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 Agent Servers by agent" in result.output
|
||||
assert "does not filter deployments 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="hosting requires linux/amd64"):
|
||||
with pytest.raises(click.ClickException, match="requires linux/amd64"):
|
||||
_validate_prebuilt_image(_FakeRunner(), "repo/app:arm", verbose=False)
|
||||
|
||||
|
||||
@@ -327,9 +327,6 @@ 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 = (
|
||||
@@ -874,17 +871,17 @@ class TestSelectSource:
|
||||
),
|
||||
pytest.param(
|
||||
{"placement": RequestedPlacement(listener_id="listener-1")},
|
||||
"only apply when creating an Agent Server with --push-to",
|
||||
"only apply when creating a deployment with --push-to",
|
||||
id="listener_without_push_to",
|
||||
),
|
||||
pytest.param(
|
||||
{"placement": RequestedPlacement(k8s_namespace="agents")},
|
||||
"only apply when creating an Agent Server with --push-to",
|
||||
"only apply when creating a deployment with --push-to",
|
||||
id="namespace_without_push_to",
|
||||
),
|
||||
pytest.param(
|
||||
{"image_pull_secrets": ("regcred",)},
|
||||
"only apply when creating an Agent Server with --push-to",
|
||||
"only apply when creating a deployment with --push-to",
|
||||
id="image_pull_secret_without_push_to",
|
||||
),
|
||||
pytest.param(
|
||||
@@ -1409,7 +1406,7 @@ class TestCreateOnlySettings:
|
||||
settings = CreateOnlySettings(RequestedPlacement(listener_id="listener-1"))
|
||||
with pytest.raises(
|
||||
click.UsageError,
|
||||
match="Listener and namespace are fixed when an Agent Server is created",
|
||||
match="Listener and namespace are fixed when a deployment is created",
|
||||
):
|
||||
settings.ensure_not_existing("dep-1")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph"
|
||||
version = "1.2.20"
|
||||
version = "1.2.21"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Generated
+1
-1
@@ -1440,7 +1440,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "1.2.20"
|
||||
version = "1.2.21"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
Generated
+1
-1
@@ -381,7 +381,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "1.2.20"
|
||||
version = "1.2.21"
|
||||
source = { editable = "../langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
Generated
+1
-1
@@ -394,7 +394,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "1.2.20"
|
||||
version = "1.2.21"
|
||||
source = { editable = "../langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
Reference in new issue
Block a user