From 8537ea94d3580423df14be4d42fad874c90c6cbe Mon Sep 17 00:00:00 2001 From: Josh Rogers Date: Mon, 11 May 2026 20:20:17 -0400 Subject: [PATCH] fix(cli): add support for prerelease api_versions (#7771) Today, pre-release versions aren't supported for the `api_version` config parameter in langgraph.json. This adds support for them to enable latest and rc images going forward. --- libs/cli/langgraph_cli/config.py | 22 ++++++++++++++++++++-- libs/cli/tests/unit_tests/test_config.py | 17 +++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/libs/cli/langgraph_cli/config.py b/libs/cli/langgraph_cli/config.py index ac66470d7..a30e6449e 100644 --- a/libs/cli/langgraph_cli/config.py +++ b/libs/cli/langgraph_cli/config.py @@ -35,6 +35,12 @@ DISALLOWED_BUILD_COMMAND_CHARS = [ # This blocks background execution (cmd &) while allowing command # chaining (cmd1 && cmd2) which is common in build commands. _SINGLE_AMPERSAND_RE = re.compile(r"(?\d+)" + r"(?:\.(?P\d+))?" + r"(?:\.(?P\d+))?" + r"(?:(?:\.|)(?:[A-Za-z][0-9A-Za-z]*))?$" +) def has_disallowed_build_command_content(command: str) -> bool: @@ -123,6 +129,18 @@ def _parse_node_version(version_str: str) -> int: ) from None +def _parse_api_version_parts(version_str: str) -> tuple[int, ...]: + """Parse an API version into numeric components. + + Supports optional prerelease suffixes, e.g. `0.9.0rc1`. + """ + version_core = version_str.split("-", 1)[0] + match = _API_VERSION_PATTERN.fullmatch(version_core) + if not match: + raise ValueError("Version must be major or major.minor or major.minor.patch.") + return tuple(int(part) for part in match.groups() if part is not None) + + def _is_node_graph(spec: str | dict) -> bool: """Check if a graph is a Node.js graph based on the file extension.""" if isinstance(spec, dict): @@ -176,12 +194,12 @@ def validate_config(config: Config) -> Config: ) if api_version: try: - parts = tuple(map(int, api_version.split("-")[0].split("."))) + parts = _parse_api_version_parts(api_version) if len(parts) > 3: raise ValueError( "Version must be major or major.minor or major.minor.patch." ) - except TypeError: + except (TypeError, ValueError): raise click.UsageError( f"Invalid version format: {api_version}.\n\n" "Pin to a minor version, e.g.:\n" diff --git a/libs/cli/tests/unit_tests/test_config.py b/libs/cli/tests/unit_tests/test_config.py index c68edf382..ea8f5a6e2 100644 --- a/libs/cli/tests/unit_tests/test_config.py +++ b/libs/cli/tests/unit_tests/test_config.py @@ -2944,6 +2944,23 @@ def test_docker_tag_with_api_version(in_config: bool): assert tag == f"langchain/langgraph-server:{version}-py3.11" +@pytest.mark.parametrize("in_config", [False, True]) +@pytest.mark.parametrize("version", ["0.9.0rc1", "0.9.0.dev1"]) +def test_docker_tag_with_prerelease_api_version(version: str, in_config: bool): + """Test docker_tag with prerelease and dev api_version values.""" + config = validate_config( + { + "python_version": "3.11", + "dependencies": ["."], + "graphs": {"agent": "./agent.py:graph"}, + "api_version": version if in_config else None, + } + ) + + tag = docker_tag(config, api_version=version if not in_config else None) + assert tag == f"langchain/langgraph-api:{version}-py3.11" + + def test_config_to_docker_with_api_version(): """Test config_to_docker function with api_version parameter."""