From 74c91eb4e5a228b286c83b29e84feec159ea77d5 Mon Sep 17 00:00:00 2001 From: Hari Dhanushkodi Date: Fri, 16 Oct 2026 20:11:22 +0000 Subject: [PATCH] fix(cli): defer AWS serverless support to the backend Co-authored-by: open-swe[bot] --- libs/cli/langgraph_cli/deploy.py | 22 +++--- libs/cli/langgraph_cli/host_backend.py | 4 - .../unit_tests/cli/test_deploy_command.py | 75 +++++-------------- .../cli/tests/unit_tests/test_host_backend.py | 26 ------- 4 files changed, 30 insertions(+), 97 deletions(-) diff --git a/libs/cli/langgraph_cli/deploy.py b/libs/cli/langgraph_cli/deploy.py index dbe231d65..24eda4e03 100644 --- a/libs/cli/langgraph_cli/deploy.py +++ b/libs/cli/langgraph_cli/deploy.py @@ -1548,11 +1548,6 @@ def _resolve_or_create( ) if found is not None: return found.id, step - if ctx.endpoints.is_aws and ctx.deployment_type == "dev_zero": - raise click.UsageError( - "Serverless deployments are not supported on AWS yet. " - "Use --deployment-type dev instead." - ) try: created, step = _create_deployment( ctx.client, @@ -1565,6 +1560,14 @@ def _resolve_or_create( secrets=ctx.secrets, ) except HostBackendError as err: + if err.status_code == 400 and err.detail == ( + "Scale to zero deployments are not supported in AWS environments" + ): + raise HostBackendError( + f"{err.message}\nUse --deployment-type dev instead.", + status_code=err.status_code, + detail=err.detail, + ) from None if _needs_a_listener(err): raise ListenerRequiredError( "The image has to come from a registry you manage, so re-run with " @@ -2219,8 +2222,8 @@ def _deploy_base_options( click.option( "--deployment-type", type=click.Choice(["serverless", "dedicated", "dev", "prod"]), - default=None, - show_default="serverless; dev on AWS", + default="serverless", + show_default=True, help=( "Deployment type (used when creating a new deployment): " "serverless (scale to zero) or dedicated (prod); " @@ -2401,7 +2404,7 @@ def _deploy_cmd( host_url: str | None, api_key: str | None, deployment_id: str | None, - deployment_type: str | None, + deployment_type: str, name: str | None, agent_id: str | None, environment: str | None, @@ -2493,9 +2496,6 @@ def _deploy_cmd( ) client = _create_host_backend_client(host_url, api_key, env_vars=env_vars) - deployment_type = deployment_type or ( - "dev" if client.endpoints.is_aws else "serverless" - ) try: tracked_packages = find_tracked_packages(config, config_json) or None except Exception as exc: diff --git a/libs/cli/langgraph_cli/host_backend.py b/libs/cli/langgraph_cli/host_backend.py index c129123e6..377b261e7 100644 --- a/libs/cli/langgraph_cli/host_backend.py +++ b/libs/cli/langgraph_cli/host_backend.py @@ -44,10 +44,6 @@ class ControlPlaneEndpoints: f".{CLOUD_CONTROL_PLANE_HOST}" ) - @property - def is_aws(self) -> bool: - return urlparse(self.control_plane_url).hostname == "aws.api.host.langchain.com" - @classmethod def from_control_plane_url(cls, url: str) -> ControlPlaneEndpoints: control_plane_url = url.rstrip("/") diff --git a/libs/cli/tests/unit_tests/cli/test_deploy_command.py b/libs/cli/tests/unit_tests/cli/test_deploy_command.py index 5d06c00f2..4b23d7022 100644 --- a/libs/cli/tests/unit_tests/cli/test_deploy_command.py +++ b/libs/cli/tests/unit_tests/cli/test_deploy_command.py @@ -345,74 +345,37 @@ def deploy_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> DeployPro @pytest.mark.parametrize("build_mode", ["--no-remote", "--remote"]) -@pytest.mark.parametrize("deployment_args", [(), ("--deployment-type", "dev")]) -def test_aws_deploy_uses_dev( - deploy_project: DeployProject, build_mode: str, deployment_args: tuple[str, ...] +@pytest.mark.parametrize( + ("detail", "expects_dev_hint"), + [ + ("Scale to zero deployments are not supported in AWS environments", True), + ("No eligible cluster is currently available to host this deployment.", False), + ], +) +def test_create_failure_preserves_backend_error_and_only_hints_for_aws_rejection( + deploy_project: DeployProject, build_mode: str, detail: str, expects_dev_hint: bool ) -> None: - result = deploy_project.run( - build_mode, *deployment_args, host_url="https://aws.api.host.langchain.com" - ) + deploy_project.control_plane.create_error = detail - assert result.exit_code == 0, result.output + result = deploy_project.run(build_mode) + + 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 deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == { - "deployment_type": "dev" + "deployment_type": "dev_zero" } + assert deploy_project.timeline == [LIST_DEPLOYMENTS, CREATE_DEPLOYMENT] @pytest.mark.parametrize("build_mode", ["--no-remote", "--remote"]) -def test_aws_deploy_rejects_serverless_before_creating_or_building( +def test_aws_serverless_creation_succeeds_when_backend_accepts_it( deploy_project: DeployProject, build_mode: str ) -> None: result = deploy_project.run( - build_mode, - "--deployment-type", - "serverless", - host_url="https://aws.api.host.langchain.com", + build_mode, host_url="https://aws.api.host.langchain.com" ) - assert result.exit_code == 2, result.output - assert "Serverless deployments are not supported on AWS yet" in result.output - assert "Use --deployment-type dev instead" in result.output - assert deploy_project.timeline == [LIST_DEPLOYMENTS] - - -@pytest.mark.parametrize("endpoint_source", ["environment", "dotenv"]) -def test_aws_default_is_resolved_from_langsmith_endpoint( - deploy_project: DeployProject, - monkeypatch: pytest.MonkeyPatch, - endpoint_source: str, -) -> None: - endpoint = "https://aws.api.smith.langchain.com" - if endpoint_source == "environment": - monkeypatch.setenv("LANGSMITH_ENDPOINT", endpoint) - else: - Path(".env").write_text(f"LANGSMITH_ENDPOINT={endpoint}\n") - result = CliRunner().invoke( - cli, - [ - "deploy", - "--api-key", - "test-key", - "--name", - "my-app", - "--no-input", - "--no-wait", - "--remote", - ], - ) - - assert result.exit_code == 0, result.output - assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == { - "deployment_type": "dev" - } - - -def test_explicit_host_takes_precedence_over_aws_langsmith_endpoint( - deploy_project: DeployProject, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.setenv("LANGSMITH_ENDPOINT", "https://aws.api.smith.langchain.com") - result = deploy_project.run("--remote", host_url=CLOUD_CONTROL_PLANE_URL) - assert result.exit_code == 0, result.output assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == { "deployment_type": "dev_zero" diff --git a/libs/cli/tests/unit_tests/test_host_backend.py b/libs/cli/tests/unit_tests/test_host_backend.py index daef66564..bf9f922f7 100644 --- a/libs/cli/tests/unit_tests/test_host_backend.py +++ b/libs/cli/tests/unit_tests/test_host_backend.py @@ -10,32 +10,6 @@ from langgraph_cli.host_backend import ( ) -@pytest.mark.parametrize( - ("host_url", "langsmith_endpoint", "expected"), - [ - ("https://aws.api.host.langchain.com/", None, True), - ("https://AWS.api.host.langchain.com:443", None, True), - (None, "https://aws.api.smith.langchain.com", True), - (None, None, False), - ("https://eu.api.host.langchain.com", None, False), - ("https://apac.api.host.langchain.com", None, False), - ("https://aws.example.com/api-host", None, False), - ("https://aws.api.host.langchain.com.example.com", None, False), - ( - "https://api.host.langchain.com", - "https://aws.api.smith.langchain.com", - False, - ), - ], -) -def test_aws_detection_uses_the_resolved_control_plane_host( - host_url: str | None, langsmith_endpoint: str | None, expected: bool -) -> None: - endpoints = ControlPlaneEndpoints.resolve(host_url, langsmith_endpoint) - - assert endpoints.is_aws is expected - - def test_constructor_strips_trailing_slash(): c = HostBackendClient("https://api.example.com/", "key") assert c.base_url == "https://api.example.com"