mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-10 11:47:51 +02:00
feat: [CLI] Add arg to retain build deps (setuptools, pip, wheel) (#5404)
This commit is contained in:
@@ -51,6 +51,7 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
| <span style="white-space: nowrap;">`node_version`</span> | Specify `node_version: 20` to use LangGraph.js. |
|
||||
| <span style="white-space: nowrap;">`pip_config_file`</span> | Path to `pip` config file. |
|
||||
| <span style="white-space: nowrap;">`pip_installer`</span> | _(Added in v0.3)_ Optional. Python package installer selector. It can be set to `"auto"`, `"pip"`, or `"uv"`. From version 0.3 onward the default strategy is to run `uv pip`, which typically delivers faster builds while remaining a drop-in replacement. In the uncommon situation where `uv` cannot handle your dependency graph or the structure of your `pyproject.toml`, specify `"pip"` here to revert to the earlier behaviour. |
|
||||
| <span style="white-space: nowrap;">`keep_pkg_tools`</span> | _(Added in v0.3.4)_ Optional. Control whether to retain Python packaging tools (`pip`, `setuptools`, `wheel`) in the final image. Accepted values: <ul><li><code>true</code> : Keep all three tools (skip uninstall).</li><li><code>false</code> / omitted : Uninstall all three tools (default behaviour).</li><li><code>list[str]</code> : Names of tools <strong>to retain</strong>. Each value must be one of "pip", "setuptools", "wheel".</li></ul>. By default, all three tools are uninstalled. |
|
||||
| <span style="white-space: nowrap;">`dockerfile_lines`</span> | Array of additional lines to add to Dockerfile following the import from parent image. |
|
||||
| <span style="white-space: nowrap;">`checkpointer`</span> | Configuration for the checkpointer. Contains a `ttl` field which is an object with the following keys: <ul><li>`strategy`: How to handle expired checkpoints (e.g., `"delete"`).</li><li>`sweep_interval_minutes`: How often to check for expired checkpoints (integer).</li><li>`default_ttl`: Default time-to-live for checkpoints in **minutes** (integer). Defines how long checkpoints are kept before the specified strategy is applied.</li></ul> |
|
||||
| <span style="white-space: nowrap;">`http`</span> | HTTP server configuration with the following fields: <ul><li>`app`: Path to custom Starlette/FastAPI app (e.g., `"./src/agent/webapp.py:app"`). See [custom routes guide](../../how-tos/http/custom_routes.md).</li><li>`cors`: CORS configuration with fields for `allow_origins`, `allow_methods`, `allow_headers`, etc.</li><li>`configurable_headers`: Define which request headers to exclude or include as a run's configurable values.</li><li>`disable_assistants`: Disable `/assistants` routes</li><li>`disable_mcp`: Disable `/mcp` routes</li><li>`disable_meta`: Disable `/ok`, `/info`, `/metrics`, and `/docs` routes</li><li>`disable_runs`: Disable `/runs` routes</li><li>`disable_store`: Disable `/store` routes</li><li>`disable_threads`: Disable `/threads` routes</li><li>`disable_ui`: Disable `/ui` routes</li><li>`disable_webhooks`: Disable webhooks calls on run completion in all routes</li><li>`mount_prefix`: Prefix for mounted routes (e.g., "/my-deployment/api")</li></ul> |
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"scikit-learn",
|
||||
"./graphs"
|
||||
],
|
||||
"keep_pkg_tools": false,
|
||||
"graphs": {
|
||||
"agent": "./graphs/agent.py:graph",
|
||||
"storm": "./graphs/storm.py:graph"
|
||||
|
||||
@@ -338,7 +338,7 @@ class HttpConfig(TypedDict, total=False):
|
||||
Default is False.
|
||||
"""
|
||||
disable_meta: bool
|
||||
"""Optional. If True, all meta endpoints (/ok, /info, /metrics, /docs) are disabled.
|
||||
"""Optional. If True, all meta endpoints (/openapi.json, /info, /metrics, /docs) are disabled.
|
||||
|
||||
Default is False.
|
||||
"""
|
||||
@@ -471,21 +471,61 @@ class Config(TypedDict, total=False):
|
||||
"""Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file.
|
||||
"""
|
||||
|
||||
keep_pkg_tools: Optional[Union[bool, list[str]]]
|
||||
"""Optional. Control whether to retain Python packaging tools in the final image.
|
||||
|
||||
Allowed tools are: "pip", "setuptools", "wheel".
|
||||
You can also set to true to include all packaging tools.
|
||||
"""
|
||||
|
||||
PIP_CLEANUP_LINES = """# -- Ensure user deps didn't inadvertently overwrite langgraph-api
|
||||
|
||||
_BUILD_TOOLS = ("pip", "setuptools", "wheel")
|
||||
|
||||
|
||||
def _get_pip_cleanup_lines(
|
||||
install_cmd: str,
|
||||
to_uninstall: Optional[tuple[str]],
|
||||
pip_installer: Literal["uv", "pip"],
|
||||
) -> str:
|
||||
commands = [
|
||||
f"""# -- Ensure user deps didn't inadvertently overwrite langgraph-api
|
||||
RUN mkdir -p /api/langgraph_api /api/langgraph_runtime /api/langgraph_license && \
|
||||
touch /api/langgraph_api/__init__.py /api/langgraph_runtime/__init__.py /api/langgraph_license/__init__.py
|
||||
touch /api/langgraph_api/__init__.py /api/langgraph_runtime/__init__.py /api/langgraph_license/__init__.py
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 {install_cmd} --no-cache-dir --no-deps -e /api
|
||||
# -- End of ensuring user deps didn't inadvertently overwrite langgraph-api --
|
||||
# -- Removing pip from the final image ~<:===~~~ --
|
||||
RUN pip uninstall -y pip setuptools wheel && \
|
||||
rm -rf /usr/local/lib/python*/site-packages/pip* /usr/local/lib/python*/site-packages/setuptools* /usr/local/lib/python*/site-packages/wheel* && \
|
||||
find /usr/local/bin -name "pip*" -delete || true
|
||||
# pip removal for wolfi
|
||||
RUN rm -rf /usr/lib/python*/site-packages/pip* /usr/lib/python*/site-packages/setuptools* /usr/lib/python*/site-packages/wheel* && \
|
||||
find /usr/bin -name "pip*" -delete || true
|
||||
{uv_removal}
|
||||
# -- End of pip removal --"""
|
||||
# -- Removing build deps from the final image ~<:===~~~ --"""
|
||||
]
|
||||
if to_uninstall:
|
||||
for pack in to_uninstall:
|
||||
if pack not in _BUILD_TOOLS:
|
||||
raise ValueError(
|
||||
f"Invalid build tool: {pack}; must be one of {', '.join(_BUILD_TOOLS)}"
|
||||
)
|
||||
packs_str = " ".join(sorted(to_uninstall))
|
||||
commands.append(f"RUN pip uninstall -y {packs_str}")
|
||||
# Ensure the directories are removed entirely
|
||||
packages_rm = " ".join(
|
||||
f"/usr/local/lib/python*/site-packages/{pack}*" for pack in to_uninstall
|
||||
)
|
||||
if "pip" in to_uninstall:
|
||||
packages_rm += ' && find /usr/local/bin -name "pip*" -delete || true'
|
||||
commands.append(f"RUN rm -rf {packages_rm}")
|
||||
wolfi_packages_rm = " ".join(
|
||||
f"/usr/lib/python*/site-packages/{pack}*" for pack in to_uninstall
|
||||
)
|
||||
if "pip" in to_uninstall:
|
||||
wolfi_packages_rm += ' && find /usr/bin -name "pip*" -delete || true'
|
||||
commands.append(f"RUN rm -rf {wolfi_packages_rm}")
|
||||
if pip_installer == "uv":
|
||||
commands.append(
|
||||
f"RUN uv pip uninstall --system {packs_str} && rm /usr/bin/uv /usr/bin/uvx"
|
||||
)
|
||||
else:
|
||||
if pip_installer == "uv":
|
||||
commands.append(
|
||||
"RUN rm /usr/bin/uv /usr/bin/uvx\n# -- End of build deps removal --"
|
||||
)
|
||||
return "\n".join(commands)
|
||||
|
||||
|
||||
def _parse_version(version_str: str) -> tuple[int, int]:
|
||||
@@ -563,6 +603,7 @@ def validate_config(config: Config) -> Config:
|
||||
"checkpointer": config.get("checkpointer"),
|
||||
"ui": config.get("ui"),
|
||||
"ui_config": config.get("ui_config"),
|
||||
"keep_pkg_tools": config.get("keep_pkg_tools"),
|
||||
}
|
||||
|
||||
if config.get("node_version"):
|
||||
@@ -635,6 +676,22 @@ def validate_config(config: Config) -> Config:
|
||||
f"Invalid http.app format: '{http_conf['app']}'. "
|
||||
"Must be in format './path/to/file.py:attribute_name'"
|
||||
)
|
||||
if keep_pkg_tools := config.get("keep_pkg_tools"):
|
||||
if isinstance(keep_pkg_tools, list):
|
||||
for tool in keep_pkg_tools:
|
||||
if tool not in _BUILD_TOOLS:
|
||||
raise ValueError(
|
||||
f"Invalid keep_pkg_tools: '{tool}'. "
|
||||
"Must be one of 'pip', 'setuptools', 'wheel'."
|
||||
)
|
||||
elif keep_pkg_tools is True:
|
||||
pass
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid keep_pkg_tools: '{keep_pkg_tools}'. "
|
||||
"Must be bool or list[str] (with values"
|
||||
" 'pip', 'setuptools', and/or 'wheel')."
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
@@ -1128,6 +1185,27 @@ def _image_supports_uv(base_image: str) -> bool:
|
||||
return version >= min_uv
|
||||
|
||||
|
||||
def get_build_tools_to_uninstall(config: Config) -> tuple[str]:
|
||||
keep_pkg_tools = config.get("keep_pkg_tools")
|
||||
if not keep_pkg_tools:
|
||||
return _BUILD_TOOLS
|
||||
if keep_pkg_tools is True:
|
||||
return ()
|
||||
expected = _BUILD_TOOLS
|
||||
if isinstance(keep_pkg_tools, list):
|
||||
for tool in keep_pkg_tools:
|
||||
if tool not in expected:
|
||||
raise ValueError(
|
||||
f"Invalid build tool to uninstall: {tool}. Expected one of {expected}"
|
||||
)
|
||||
return tuple(sorted(set(_BUILD_TOOLS) - set(keep_pkg_tools)))
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid value for keep_pkg_tools: {keep_pkg_tools}."
|
||||
" Expected True or a list containing any of {expected}."
|
||||
)
|
||||
|
||||
|
||||
def python_config_to_docker(
|
||||
config_path: pathlib.Path,
|
||||
config: Config,
|
||||
@@ -1135,20 +1213,18 @@ def python_config_to_docker(
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
"""Generate a Dockerfile from the configuration."""
|
||||
pip_installer = config.get("pip_installer", "auto")
|
||||
|
||||
build_tools_to_uninstall = get_build_tools_to_uninstall(config)
|
||||
if pip_installer == "auto":
|
||||
if _image_supports_uv(base_image):
|
||||
pip_installer = "uv"
|
||||
else:
|
||||
pip_installer = "pip"
|
||||
if pip_installer == "uv":
|
||||
install_cmd = "uv pip install --system"
|
||||
uv_removal = "RUN uv pip uninstall --system pip setuptools wheel && rm /usr/bin/uv /usr/bin/uvx"
|
||||
elif pip_installer == "pip":
|
||||
install_cmd = "pip install"
|
||||
uv_removal = ""
|
||||
else:
|
||||
if _image_supports_uv(base_image):
|
||||
install_cmd = "uv pip install --system"
|
||||
uv_removal = "RUN uv pip uninstall --system pip setuptools wheel && rm /usr/bin/uv /usr/bin/uvx"
|
||||
else:
|
||||
install_cmd = "pip install"
|
||||
uv_removal = ""
|
||||
raise ValueError(f"Invalid pip_installer: {pip_installer}")
|
||||
|
||||
# configure pip
|
||||
pip_install = f"PYTHONDONTWRITEBYTECODE=1 {install_cmd} --no-cache-dir -c /api/constraints.txt"
|
||||
@@ -1297,7 +1373,11 @@ ADD {relpath} /deps/{name}
|
||||
js_inst_str,
|
||||
"",
|
||||
# Add pip cleanup after all installations are complete
|
||||
PIP_CLEANUP_LINES.format(install_cmd=install_cmd, uv_removal=uv_removal),
|
||||
_get_pip_cleanup_lines(
|
||||
install_cmd=install_cmd,
|
||||
to_uninstall=build_tools_to_uninstall,
|
||||
pip_installer=pip_installer,
|
||||
),
|
||||
"",
|
||||
f"WORKDIR {local_deps.working_dir}" if local_deps.working_dir else "",
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-cli"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
description = "CLI for interacting with LangGraph API"
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -134,6 +134,23 @@
|
||||
],
|
||||
"description": "Optional. Linux distribution for the base image.\n\nMust be either 'debian' or 'wolfi'. If omitted, defaults to 'debian'.\n"
|
||||
},
|
||||
"keep_pkg_tools": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Control whether to retain Python packaging tools in the final image.\n\nYou can also set to true to include all packaging tools.\n"
|
||||
},
|
||||
"pip_installer": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -298,6 +315,23 @@
|
||||
],
|
||||
"description": "Optional. Linux distribution for the base image.\n\nMust be either 'debian' or 'wolfi'. If omitted, defaults to 'debian'.\n"
|
||||
},
|
||||
"keep_pkg_tools": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Control whether to retain Python packaging tools in the final image.\n\nYou can also set to true to include all packaging tools.\n"
|
||||
},
|
||||
"pip_installer": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -505,7 +539,7 @@
|
||||
},
|
||||
"disable_meta": {
|
||||
"type": "boolean",
|
||||
"description": "Optional. If True, all meta endpoints (/ok, /info, /metrics, /docs) are disabled.\n\nDefault is False.\n"
|
||||
"description": "Optional. If True, all meta endpoints (/openapi.json, /info, /metrics, /docs) are disabled.\n\nDefault is False.\n"
|
||||
},
|
||||
"disable_runs": {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -134,6 +134,23 @@
|
||||
],
|
||||
"description": "Optional. Linux distribution for the base image.\n\nMust be either 'debian' or 'wolfi'. If omitted, defaults to 'debian'.\n"
|
||||
},
|
||||
"keep_pkg_tools": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Control whether to retain Python packaging tools in the final image.\n\nYou can also set to true to include all packaging tools.\n"
|
||||
},
|
||||
"pip_installer": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -298,6 +315,23 @@
|
||||
],
|
||||
"description": "Optional. Linux distribution for the base image.\n\nMust be either 'debian' or 'wolfi'. If omitted, defaults to 'debian'.\n"
|
||||
},
|
||||
"keep_pkg_tools": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Control whether to retain Python packaging tools in the final image.\n\nYou can also set to true to include all packaging tools.\n"
|
||||
},
|
||||
"pip_installer": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -505,7 +539,7 @@
|
||||
},
|
||||
"disable_meta": {
|
||||
"type": "boolean",
|
||||
"description": "Optional. If True, all meta endpoints (/ok, /info, /metrics, /docs) are disabled.\n\nDefault is False.\n"
|
||||
"description": "Optional. If True, all meta endpoints (/openapi.json, /info, /metrics, /docs) are disabled.\n\nDefault is False.\n"
|
||||
},
|
||||
"disable_runs": {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"$id": "https://github.com/langchain-ai/langgraph/libs/cli/schemas/version.schema.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "LangGraph Platform configuration (when building via the langgraph-cli).",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"maxLength": 0
|
||||
}
|
||||
},
|
||||
"required": ["version"]
|
||||
},
|
||||
{
|
||||
"not": {
|
||||
"required": ["version"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "https://raw.githubusercontent.com/langchain-ai/langgraph/main/libs/cli/schemas/schema.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"properties": {
|
||||
"version": { "const": "v0" }
|
||||
},
|
||||
"required": ["version"]
|
||||
},
|
||||
{
|
||||
"$ref": "https://raw.githubusercontent.com/langchain-ai/langgraph/main/libs/cli/schemas/schema.v0.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,13 +10,14 @@ from pathlib import Path
|
||||
from click.testing import CliRunner
|
||||
|
||||
from langgraph_cli.cli import cli, prepare_args_and_stdin
|
||||
from langgraph_cli.config import PIP_CLEANUP_LINES, Config, validate_config
|
||||
from langgraph_cli.config import Config, _get_pip_cleanup_lines, validate_config
|
||||
from langgraph_cli.docker import DEFAULT_POSTGRES_URI, DockerCapabilities, Version
|
||||
from langgraph_cli.util import clean_empty_lines
|
||||
|
||||
FORMATTED_CLEANUP_LINES = PIP_CLEANUP_LINES.format(
|
||||
FORMATTED_CLEANUP_LINES = _get_pip_cleanup_lines(
|
||||
install_cmd="uv pip install --system",
|
||||
uv_removal="RUN uv pip uninstall --system pip setuptools wheel && rm /usr/bin/uv /usr/bin/uvx",
|
||||
to_uninstall=("pip", "setuptools", "wheel"),
|
||||
pip_installer="uv",
|
||||
)
|
||||
DEFAULT_DOCKER_CAPABILITIES = DockerCapabilities(
|
||||
version_docker=Version(26, 1, 1),
|
||||
|
||||
@@ -9,7 +9,8 @@ import click
|
||||
import pytest
|
||||
|
||||
from langgraph_cli.config import (
|
||||
PIP_CLEANUP_LINES,
|
||||
_BUILD_TOOLS,
|
||||
_get_pip_cleanup_lines,
|
||||
config_to_compose,
|
||||
config_to_docker,
|
||||
docker_tag,
|
||||
@@ -18,9 +19,10 @@ from langgraph_cli.config import (
|
||||
)
|
||||
from langgraph_cli.util import clean_empty_lines
|
||||
|
||||
FORMATTED_CLEANUP_LINES = PIP_CLEANUP_LINES.format(
|
||||
FORMATTED_CLEANUP_LINES = _get_pip_cleanup_lines(
|
||||
install_cmd="uv pip install --system",
|
||||
uv_removal="RUN uv pip uninstall --system pip setuptools wheel && rm /usr/bin/uv /usr/bin/uvx",
|
||||
to_uninstall=("pip", "setuptools", "wheel"),
|
||||
pip_installer="uv",
|
||||
)
|
||||
|
||||
PATH_TO_CONFIG = pathlib.Path(__file__).parent / "test_config.json"
|
||||
@@ -51,6 +53,7 @@ def test_validate_config():
|
||||
"http": None,
|
||||
"ui": None,
|
||||
"ui_config": None,
|
||||
"keep_pkg_tools": None,
|
||||
**expected_config,
|
||||
}
|
||||
assert actual_config == expected_config
|
||||
@@ -77,6 +80,7 @@ def test_validate_config():
|
||||
"http": None,
|
||||
"ui": None,
|
||||
"ui_config": None,
|
||||
"keep_pkg_tools": None,
|
||||
}
|
||||
actual_config = validate_config(expected_config)
|
||||
assert actual_config == expected_config
|
||||
@@ -925,6 +929,53 @@ def test_config_to_docker_pip_installer():
|
||||
assert "uv pip install --system" in docker_default
|
||||
|
||||
|
||||
def test_config_retain_build_tools():
|
||||
graphs = {"agent": "./graphs/agent.py:graph"}
|
||||
base_config = {
|
||||
"python_version": "3.11",
|
||||
"dependencies": ["."],
|
||||
"graphs": graphs,
|
||||
}
|
||||
config_true = validate_config(
|
||||
{**copy.deepcopy(base_config), "keep_pkg_tools": True}
|
||||
)
|
||||
docker_true, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_true, "langchain/langgraph-api:0.2.47"
|
||||
)
|
||||
assert not any(
|
||||
"/usr/local/lib/python*/site-packages/" + pckg + "*" in docker_true
|
||||
for pckg in _BUILD_TOOLS
|
||||
)
|
||||
assert "RUN pip uninstall -y pip setuptools wheel" not in docker_true
|
||||
config_false = validate_config(
|
||||
{**copy.deepcopy(base_config), "keep_pkg_tools": False}
|
||||
)
|
||||
docker_false, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_false, "langchain/langgraph-api:0.2.47"
|
||||
)
|
||||
assert all(
|
||||
"/usr/local/lib/python*/site-packages/" + pckg + "*" in docker_false
|
||||
for pckg in _BUILD_TOOLS
|
||||
)
|
||||
assert "RUN pip uninstall -y pip setuptools wheel" in docker_false
|
||||
config_list = validate_config(
|
||||
{**copy.deepcopy(base_config), "keep_pkg_tools": ["pip", "setuptools"]}
|
||||
)
|
||||
docker_list, _ = config_to_docker(
|
||||
PATH_TO_CONFIG, config_list, "langchain/langgraph-api:0.2.47"
|
||||
)
|
||||
assert all(
|
||||
"/usr/local/lib/python*/site-packages/" + pckg + "*" in docker_list
|
||||
for pckg in ("wheel",)
|
||||
)
|
||||
assert not any(
|
||||
"/usr/local/lib/python*/site-packages/" + pckg + "*" in docker_list
|
||||
for pckg in ("pip", "setuptools")
|
||||
)
|
||||
assert "RUN pip uninstall -y wheel" in docker_list
|
||||
assert "RUN pip uninstall -y pip setuptools" not in docker_list
|
||||
|
||||
|
||||
# config_to_compose
|
||||
def test_config_to_compose_simple_config():
|
||||
graphs = {"agent": "./agent.py:graph"}
|
||||
|
||||
Generated
+1
-1
@@ -522,7 +522,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-cli"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
|
||||
Reference in New Issue
Block a user