mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-10-18 14:07:31 +02:00
fix(cli): defer AWS serverless support to the backend
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
co-authored by
open-swe[bot] <open-swe@users.noreply.github.com>
parent
dcdcb13a99
commit
74c91eb4e5
@@ -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:
|
||||
|
||||
@@ -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("/")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user