mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-31 20:29:46 +02:00
feat(cli): add internal docker tag support (#4333)
This commit is contained in:
@@ -326,6 +326,10 @@ class Config(TypedDict, total=False):
|
||||
Must be >= 20 if provided.
|
||||
"""
|
||||
|
||||
_INTERNAL_docker_tag: Optional[str]
|
||||
"""Optional. Internal use only.
|
||||
"""
|
||||
|
||||
pip_config_file: Optional[str]
|
||||
"""Optional. Path to a pip config file (e.g., "/etc/pip.conf" or "pip.ini") for controlling
|
||||
package installation (custom indices, credentials, etc.).
|
||||
@@ -480,6 +484,7 @@ def validate_config(config: Config) -> Config:
|
||||
"node_version": node_version,
|
||||
"python_version": python_version,
|
||||
"pip_config_file": config.get("pip_config_file"),
|
||||
"_INTERNAL_docker_tag": config.get("_INTERNAL_docker_tag"),
|
||||
"dependencies": config.get("dependencies", []),
|
||||
"dockerfile_lines": config.get("dockerfile_lines", []),
|
||||
"graphs": config.get("graphs", {}),
|
||||
@@ -1025,7 +1030,9 @@ def _get_node_pm_install_cmd(config_path: pathlib.Path, config: Config) -> str:
|
||||
|
||||
|
||||
def python_config_to_docker(
|
||||
config_path: pathlib.Path, config: Config, base_image: str
|
||||
config_path: pathlib.Path,
|
||||
config: Config,
|
||||
base_image: str,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
"""Generate a Dockerfile from the configuration."""
|
||||
# configure pip
|
||||
@@ -1040,6 +1047,8 @@ def python_config_to_docker(
|
||||
else ""
|
||||
)
|
||||
|
||||
docker_tag = config.get("_INTERNAL_docker_tag") or config["python_version"]
|
||||
|
||||
# collect dependencies
|
||||
pypi_deps = [dep for dep in config["dependencies"] if not dep.startswith(".")]
|
||||
local_deps = _assemble_local_deps(config_path, config)
|
||||
@@ -1160,7 +1169,7 @@ ADD {relpath} /deps/{name}
|
||||
)
|
||||
|
||||
docker_file_contents = [
|
||||
f"FROM {base_image}:{config['python_version']}",
|
||||
f"FROM {base_image}:{docker_tag}",
|
||||
"",
|
||||
os.linesep.join(config["dockerfile_lines"]),
|
||||
"",
|
||||
@@ -1192,10 +1201,13 @@ ADD {relpath} /deps/{name}
|
||||
|
||||
|
||||
def node_config_to_docker(
|
||||
config_path: pathlib.Path, config: Config, base_image: str
|
||||
config_path: pathlib.Path,
|
||||
config: Config,
|
||||
base_image: str,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
faux_path = f"/deps/{config_path.parent.name}"
|
||||
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
||||
docker_tag = config.get("_INTERNAL_docker_tag") or config["node_version"]
|
||||
|
||||
env_vars: list[str] = []
|
||||
|
||||
@@ -1222,7 +1234,7 @@ def node_config_to_docker(
|
||||
env_vars.append(f"ENV LANGSERVE_GRAPHS='{json.dumps(config['graphs'])}'")
|
||||
|
||||
docker_file_contents = [
|
||||
f"FROM {base_image}:{config['node_version']}",
|
||||
f"FROM {base_image}:{docker_tag}",
|
||||
"",
|
||||
os.linesep.join(config["dockerfile_lines"]),
|
||||
"",
|
||||
@@ -1246,8 +1258,13 @@ def default_base_image(config: Config) -> str:
|
||||
return "langchain/langgraph-api"
|
||||
|
||||
|
||||
def docker_tag(config: Config, base_image: Optional[str] = None) -> str:
|
||||
def docker_tag(
|
||||
config: Config,
|
||||
base_image: Optional[str] = None,
|
||||
) -> str:
|
||||
base_image = base_image or default_base_image(config)
|
||||
if config.get("_INTERNAL_docker_tag"):
|
||||
return f"{base_image}:{config['_INTERNAL_docker_tag']}"
|
||||
|
||||
if config.get("node_version") and not config.get("python_version"):
|
||||
return f"{base_image}:{config['node_version']}"
|
||||
@@ -1255,7 +1272,9 @@ def docker_tag(config: Config, base_image: Optional[str] = None) -> str:
|
||||
|
||||
|
||||
def config_to_docker(
|
||||
config_path: pathlib.Path, config: Config, base_image: Optional[str] = None
|
||||
config_path: pathlib.Path,
|
||||
config: Config,
|
||||
base_image: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
base_image = base_image or default_base_image(config)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "langgraph-cli"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
description = "CLI for interacting with LangGraph API"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
|
||||
@@ -29,6 +29,17 @@
|
||||
],
|
||||
"description": "Optional. Path to a pip config file (e.g., \"/etc/pip.conf\" or \"pip.ini\") for controlling\npackage installation (custom indices, credentials, etc.).\n\nOnly relevant if Python dependencies are installed via pip. If omitted, default pip settings are used.\n"
|
||||
},
|
||||
"_INTERNAL_docker_tag": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Internal use only.\n"
|
||||
},
|
||||
"auth": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -145,6 +156,17 @@
|
||||
],
|
||||
"description": "Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.\nMust be >= 20 if provided.\n"
|
||||
},
|
||||
"_INTERNAL_docker_tag": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Internal use only.\n"
|
||||
},
|
||||
"auth": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
@@ -29,6 +29,17 @@
|
||||
],
|
||||
"description": "Optional. Path to a pip config file (e.g., \"/etc/pip.conf\" or \"pip.ini\") for controlling\npackage installation (custom indices, credentials, etc.).\n\nOnly relevant if Python dependencies are installed via pip. If omitted, default pip settings are used.\n"
|
||||
},
|
||||
"_INTERNAL_docker_tag": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Internal use only.\n"
|
||||
},
|
||||
"auth": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -145,6 +156,17 @@
|
||||
],
|
||||
"description": "Optional. Node.js version as a major version (e.g. '20'), if your deployment needs Node.\nMust be >= 20 if provided.\n"
|
||||
},
|
||||
"_INTERNAL_docker_tag": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional. Internal use only.\n"
|
||||
},
|
||||
"auth": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ def test_validate_config():
|
||||
}
|
||||
actual_config = validate_config(expected_config)
|
||||
expected_config = {
|
||||
"_INTERNAL_docker_tag": None,
|
||||
"python_version": "3.11",
|
||||
"node_version": None,
|
||||
"pip_config_file": None,
|
||||
@@ -47,6 +48,7 @@ def test_validate_config():
|
||||
# full config
|
||||
env = ".env"
|
||||
expected_config = {
|
||||
"_INTERNAL_docker_tag": None,
|
||||
"python_version": "3.12",
|
||||
"node_version": None,
|
||||
"pip_config_file": "pipconfig.txt",
|
||||
@@ -567,6 +569,39 @@ RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not foun
|
||||
assert additional_contexts == {}
|
||||
|
||||
|
||||
def test_config_to_docker_nodejs_internal_docker_tag():
|
||||
graphs = {"agent": "./graphs/agent.js:graph"}
|
||||
actual_docker_stdin, additional_contexts = config_to_docker(
|
||||
PATH_TO_CONFIG,
|
||||
validate_config(
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": graphs,
|
||||
"dockerfile_lines": ["ARG meow", "ARG foo"],
|
||||
"auth": {"path": "./graphs/auth.mts:auth"},
|
||||
"ui": {"agent": "./graphs/agent.ui.jsx"},
|
||||
"ui_config": {"shared": ["nuqs"]},
|
||||
"_INTERNAL_docker_tag": "my-tag",
|
||||
}
|
||||
),
|
||||
"langchain/langgraphjs-api",
|
||||
)
|
||||
expected_docker_stdin = """FROM langchain/langgraphjs-api:my-tag
|
||||
ARG meow
|
||||
ARG foo
|
||||
ADD . /deps/unit_tests
|
||||
RUN cd /deps/unit_tests && npm i
|
||||
ENV LANGGRAPH_AUTH='{"path": "./graphs/auth.mts:auth"}'
|
||||
ENV LANGGRAPH_UI='{"agent": "./graphs/agent.ui.jsx"}'
|
||||
ENV LANGGRAPH_UI_CONFIG='{"shared": ["nuqs"]}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "./graphs/agent.js:graph"}'
|
||||
WORKDIR /deps/unit_tests
|
||||
RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts"""
|
||||
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
|
||||
|
||||
def test_config_to_docker_gen_ui_python():
|
||||
graphs = {"agent": "./agent.py:graph"}
|
||||
actual_docker_stdin, additional_contexts = config_to_docker(
|
||||
|
||||
Reference in New Issue
Block a user