a7d6d88f6f
CI / changes (push) Has been cancelled
CI / cd libs/checkpoint (push) Has been cancelled
CI / cd libs/checkpoint-conformance (push) Has been cancelled
CI / cd libs/checkpoint-postgres (push) Has been cancelled
CI / cd libs/checkpoint-sqlite (push) Has been cancelled
CI / cd libs/cli (push) Has been cancelled
CI / cd libs/prebuilt (push) Has been cancelled
CI / cd libs/sdk-py (push) Has been cancelled
CI / cd libs/langgraph (push) Has been cancelled
CI / Check SDK methods matching (push) Has been cancelled
CI / Check CLI schema hasn't changed #3.13 (push) Has been cancelled
CI / CLI integration test (push) Has been cancelled
CI / sdk-py integration test (push) Has been cancelled
CI / CI Success (push) Has been cancelled
baseline / benchmark (push) Has been cancelled
Deploy Redirects to GitHub Pages / deploy (push) Has been cancelled
443 lines
13 KiB
Python
443 lines
13 KiB
Python
import pytest
|
|
|
|
from langgraph_cli.docker import (
|
|
DEFAULT_POSTGRES_URI,
|
|
DockerCapabilities,
|
|
Version,
|
|
_parse_version,
|
|
compose,
|
|
)
|
|
from langgraph_cli.util import clean_empty_lines
|
|
|
|
DEFAULT_DOCKER_CAPABILITIES = DockerCapabilities(
|
|
version_docker=Version(26, 1, 1),
|
|
version_compose=Version(2, 27, 0),
|
|
healthcheck_start_interval=False,
|
|
)
|
|
|
|
|
|
def test_compose_with_no_debugger_and_custom_db():
|
|
port = 8123
|
|
custom_postgres_uri = "custom_postgres_uri"
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES, port=port, postgres_uri=custom_postgres_uri
|
|
)
|
|
expected_compose_str = f"""services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {custom_postgres_uri}"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_with_no_debugger_and_custom_db_with_healthcheck():
|
|
port = 8123
|
|
custom_postgres_uri = "custom_postgres_uri"
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES._replace(healthcheck_start_interval=True),
|
|
port=port,
|
|
postgres_uri=custom_postgres_uri,
|
|
)
|
|
expected_compose_str = f"""services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {custom_postgres_uri}
|
|
healthcheck:
|
|
test: python /api/healthcheck.py
|
|
interval: 60s
|
|
start_interval: 1s
|
|
start_period: 10s"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_with_debugger_and_custom_db():
|
|
port = 8123
|
|
custom_postgres_uri = "custom_postgres_uri"
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES,
|
|
port=port,
|
|
postgres_uri=custom_postgres_uri,
|
|
)
|
|
expected_compose_str = f"""services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {custom_postgres_uri}"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_with_debugger_and_default_db():
|
|
port = 8123
|
|
actual_compose_str = compose(DEFAULT_DOCKER_CAPABILITIES, port=port)
|
|
expected_compose_str = f"""volumes:
|
|
langgraph-data:
|
|
driver: local
|
|
services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-postgres:
|
|
image: pgvector/pgvector:pg16
|
|
ports:
|
|
- "5433:5432"
|
|
environment:
|
|
POSTGRES_DB: postgres
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: postgres
|
|
command:
|
|
- postgres
|
|
- -c
|
|
- shared_preload_libraries=vector
|
|
volumes:
|
|
- langgraph-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: pg_isready -U postgres
|
|
start_period: 10s
|
|
timeout: 1s
|
|
retries: 5
|
|
interval: 5s
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
langgraph-postgres:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {DEFAULT_POSTGRES_URI}"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_with_api_version():
|
|
"""Test compose function with api_version parameter."""
|
|
port = 8123
|
|
api_version = "0.2.74"
|
|
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES, port=port, api_version=api_version
|
|
)
|
|
|
|
# The compose function should generate a compose file that doesn't directly
|
|
# reference the api_version, since it's handled in the docker tag creation
|
|
# when building the image. The compose function mainly sets up services.
|
|
expected_compose_str = f"""volumes:
|
|
langgraph-data:
|
|
driver: local
|
|
services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-postgres:
|
|
image: pgvector/pgvector:pg16
|
|
ports:
|
|
- "5433:5432"
|
|
environment:
|
|
POSTGRES_DB: postgres
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: postgres
|
|
command:
|
|
- postgres
|
|
- -c
|
|
- shared_preload_libraries=vector
|
|
volumes:
|
|
- langgraph-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: pg_isready -U postgres
|
|
start_period: 10s
|
|
timeout: 1s
|
|
retries: 5
|
|
interval: 5s
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
langgraph-postgres:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {DEFAULT_POSTGRES_URI}"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_with_api_version_and_base_image():
|
|
"""Test compose function with both api_version and base_image parameters."""
|
|
port = 8123
|
|
api_version = "1.0.0"
|
|
base_image = "my-registry/custom-api"
|
|
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES,
|
|
port=port,
|
|
api_version=api_version,
|
|
base_image=base_image,
|
|
)
|
|
|
|
# Similar to the previous test - the compose function doesn't directly embed
|
|
# the api_version or base_image into the compose file since those are handled
|
|
# during the docker build process
|
|
expected_compose_str = f"""volumes:
|
|
langgraph-data:
|
|
driver: local
|
|
services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-postgres:
|
|
image: pgvector/pgvector:pg16
|
|
ports:
|
|
- "5433:5432"
|
|
environment:
|
|
POSTGRES_DB: postgres
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: postgres
|
|
command:
|
|
- postgres
|
|
- -c
|
|
- shared_preload_libraries=vector
|
|
volumes:
|
|
- langgraph-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: pg_isready -U postgres
|
|
start_period: 10s
|
|
timeout: 1s
|
|
retries: 5
|
|
interval: 5s
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
langgraph-postgres:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {DEFAULT_POSTGRES_URI}"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_with_api_version_and_custom_postgres():
|
|
"""Test compose function with api_version and custom postgres URI."""
|
|
port = 8123
|
|
api_version = "0.2.74"
|
|
custom_postgres_uri = "postgresql://user:pass@external-db:5432/mydb"
|
|
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES,
|
|
port=port,
|
|
api_version=api_version,
|
|
postgres_uri=custom_postgres_uri,
|
|
)
|
|
|
|
expected_compose_str = f"""services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {custom_postgres_uri}"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_with_api_version_and_debugger():
|
|
"""Test compose function with api_version and debugger port."""
|
|
port = 8123
|
|
debugger_port = 8001
|
|
api_version = "0.2.74"
|
|
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES,
|
|
port=port,
|
|
api_version=api_version,
|
|
debugger_port=debugger_port,
|
|
)
|
|
|
|
expected_compose_str = f"""volumes:
|
|
langgraph-data:
|
|
driver: local
|
|
services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-postgres:
|
|
image: pgvector/pgvector:pg16
|
|
ports:
|
|
- "5433:5432"
|
|
environment:
|
|
POSTGRES_DB: postgres
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: postgres
|
|
command:
|
|
- postgres
|
|
- -c
|
|
- shared_preload_libraries=vector
|
|
volumes:
|
|
- langgraph-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: pg_isready -U postgres
|
|
start_period: 10s
|
|
timeout: 1s
|
|
retries: 5
|
|
interval: 5s
|
|
langgraph-debugger:
|
|
image: langchain/langgraph-debugger
|
|
restart: on-failure
|
|
depends_on:
|
|
langgraph-postgres:
|
|
condition: service_healthy
|
|
ports:
|
|
- "{debugger_port}:3968"
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
langgraph-postgres:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {DEFAULT_POSTGRES_URI}"""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_distributed_mode_with_custom_db():
|
|
"""Test compose with engine_runtime_mode='distributed' adds N_JOBS_PER_WORKER=0."""
|
|
port = 8123
|
|
custom_postgres_uri = "custom_postgres_uri"
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES,
|
|
port=port,
|
|
postgres_uri=custom_postgres_uri,
|
|
engine_runtime_mode="distributed",
|
|
)
|
|
expected_compose_str = f"""services:
|
|
langgraph-redis:
|
|
image: redis:6
|
|
healthcheck:
|
|
test: redis-cli ping
|
|
interval: 5s
|
|
timeout: 1s
|
|
retries: 5
|
|
langgraph-api:
|
|
ports:
|
|
- "{port}:8000"
|
|
depends_on:
|
|
langgraph-redis:
|
|
condition: service_healthy
|
|
environment:
|
|
REDIS_URI: redis://langgraph-redis:6379
|
|
POSTGRES_URI: {custom_postgres_uri}
|
|
N_JOBS_PER_WORKER: "0\""""
|
|
assert clean_empty_lines(actual_compose_str) == expected_compose_str
|
|
|
|
|
|
def test_compose_distributed_mode_with_default_db():
|
|
"""Test compose distributed mode with default DB includes N_JOBS_PER_WORKER=0."""
|
|
port = 8123
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES,
|
|
port=port,
|
|
engine_runtime_mode="distributed",
|
|
)
|
|
assert 'N_JOBS_PER_WORKER: "0"' in actual_compose_str
|
|
assert "langgraph-postgres:" in actual_compose_str
|
|
assert "langgraph-redis:" in actual_compose_str
|
|
|
|
|
|
def test_compose_combined_mode_has_no_n_jobs():
|
|
"""Test compose with default combined_queue_worker mode does NOT set N_JOBS_PER_WORKER."""
|
|
port = 8123
|
|
actual_compose_str = compose(
|
|
DEFAULT_DOCKER_CAPABILITIES,
|
|
port=port,
|
|
engine_runtime_mode="combined_queue_worker",
|
|
)
|
|
assert "N_JOBS_PER_WORKER" not in actual_compose_str
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"input_str,expected",
|
|
[
|
|
("1.2.3", Version(1, 2, 3)),
|
|
("v1.2.3", Version(1, 2, 3)),
|
|
("1.2.3-alpha", Version(1, 2, 3)),
|
|
("1.2.3+1", Version(1, 2, 3)),
|
|
("1.2.3-alpha+build", Version(1, 2, 3)),
|
|
("1.2", Version(1, 2, 0)),
|
|
("1", Version(1, 0, 0)),
|
|
("v28.1.1+1", Version(28, 1, 1)),
|
|
("2.0.0-beta.1+exp.sha.5114f85", Version(2, 0, 0)),
|
|
("v3.4.5-rc1+build.123", Version(3, 4, 5)),
|
|
],
|
|
)
|
|
def test_parse_version_w_edge_cases(input_str, expected):
|
|
assert _parse_version(input_str) == expected
|