feat(cli): add building support for JS

This commit is contained in:
Tat Dat Duong
2024-08-28 02:07:21 +02:00
parent a8758661bc
commit f4dea9c5f7
3 changed files with 109 additions and 27 deletions
+19 -5
View File
@@ -372,7 +372,11 @@ def _build(
pull: bool,
tag: str,
):
base_image = base_image or "langchain/langgraph-api"
base_image = base_image or (
"langchain/langgraphjs-api"
if config_json.get("node_version")
else "langchain/langgraph-api"
)
# pull latest images
if pull:
@@ -380,7 +384,9 @@ def _build(
subp_exec(
"docker",
"pull",
f"{base_image}:{config_json['python_version']}",
f"{base_image}:{config_json['node_version']}"
if config_json.get("node_version")
else f"{base_image}:{config_json['python_version']}",
verbose=True,
)
)
@@ -462,7 +468,11 @@ def dockerfile(save_path: pathlib.Path, config: pathlib.Path):
with open(save_path, "w") as f:
f.write(
langgraph_cli.config.config_to_docker(
config, config_json, "langchain/langgraph-api"
config,
config_json,
"langchain/langgraphjs-api"
if config_json.get("node_version")
else "langchain/langgraph-api",
)
)
@@ -500,7 +510,9 @@ def prepare_args_and_stdin(
config_path,
config,
watch=watch,
base_image="langchain/langgraph-api",
base_image="langchain/langgraphjs-api"
if config.get("node_version")
else "langchain/langgraph-api",
)
return args, stdin
@@ -527,7 +539,9 @@ def prepare(
subp_exec(
"docker",
"pull",
f"langchain/langgraph-api:{config['python_version']}",
f"langchain/langgraphjs-api:{config['node_version']}"
if config.get("node_version")
else f"langchain/langgraph-api:{config['python_version']}",
verbose=verbose,
)
)
+65 -22
View File
@@ -9,6 +9,7 @@ import click
class Config(TypedDict):
python_version: str
node_version: Optional[str]
pip_config_file: Optional[str]
dockerfile_lines: list[str]
dependencies: list[str]
@@ -17,27 +18,46 @@ class Config(TypedDict):
def validate_config(config: Config) -> Config:
config = {
"python_version": config.get("python_version", "3.11"),
"pip_config_file": config.get("pip_config_file"),
"dockerfile_lines": config.get("dockerfile_lines", []),
"dependencies": config.get("dependencies", []),
"graphs": config.get("graphs", {}),
"env": config.get("env", {}),
}
if config["python_version"] not in (
"3.11",
"3.12",
):
raise click.UsageError(
f"Unsupported Python version: {config['python_version']}. "
"Supported versions are 3.11 and 3.12."
)
if not config["dependencies"]:
raise click.UsageError(
"No dependencies found in config. "
"Add at least one dependency to 'dependencies' list."
)
config = (
{
"node_version": config.get("node_version"),
"dockerfile_lines": config.get("dockerfile_lines", []),
"graphs": config.get("graphs", {}),
"env": config.get("env", {}),
}
if config.get("node_version")
else {
"python_version": config.get("python_version", "3.11"),
"pip_config_file": config.get("pip_config_file"),
"dockerfile_lines": config.get("dockerfile_lines", []),
"dependencies": config.get("dependencies", []),
"graphs": config.get("graphs", {}),
"env": config.get("env", {}),
}
)
if config.get("node_version"):
if config["node_version"] not in ("20"):
raise click.UsageError(
f"Unsupported Node.js version: {config['node_version']}. "
"Currently only `node_version: \"20\"` is supported."
)
if config.get("python_version"):
if config["python_version"] not in (
"3.11",
"3.12",
):
raise click.UsageError(
f"Unsupported Python version: {config['python_version']}. "
"Supported versions are 3.11 and 3.12."
)
if not config["dependencies"]:
raise click.UsageError(
"No dependencies found in config. "
"Add at least one dependency to 'dependencies' list."
)
if not config["graphs"]:
raise click.UsageError(
"No graphs found in config. "
@@ -191,7 +211,7 @@ def _update_graph_paths(
config["graphs"][graph_id] = f"{module_str}:{attr_str}"
def config_to_docker(config_path: pathlib.Path, config: Config, base_image: str):
def python_config_to_docker(config_path: pathlib.Path, config: Config, base_image: str):
# configure pip
pip_install = (
"PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt"
@@ -266,6 +286,29 @@ ENV LANGSERVE_GRAPHS='{json.dumps(config["graphs"])}'
{f"WORKDIR {local_deps.working_dir}" if local_deps.working_dir else ""}"""
def node_config_to_docker(config_path: pathlib.Path, config: Config, base_image: str):
faux_path = f"/deps/{config_path.parent.name}"
return f"""FROM {base_image}:{config['node_version']}
{os.linesep.join(config["dockerfile_lines"])}
ADD . {faux_path}
RUN cd {faux_path} && yarn install --frozen-lockfile
ENV LANGSERVE_GRAPHS='{json.dumps(config["graphs"])}'
WORKDIR {faux_path}"""
def config_to_docker(config_path: pathlib.Path, config: Config, base_image: str):
if config.get("node_version"):
return node_config_to_docker(config_path, config, base_image)
return python_config_to_docker(config_path, config, base_image)
def config_to_compose(
config_path: pathlib.Path,
config: Config,
+25
View File
@@ -232,6 +232,31 @@ ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph"}'"""
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
# node.js build used for LangGraph Cloud
def test_config_to_docker_nodejs():
graphs = {"agent": "./graphs/agent.js:graph"}
actual_docker_stdin = config_to_docker(
PATH_TO_CONFIG,
validate_config(
{
"node_version": "20",
"graphs": graphs,
"dockerfile_lines": ["ARG meow", "ARG foo"],
}
),
"langchain/langgraphjs-api",
)
expected_docker_stdin = """FROM langchain/langgraphjs-api:20
ARG meow
ARG foo
ADD . /deps/unit_tests
RUN cd /deps/unit_tests && yarn install --frozen-lockfile
ENV LANGSERVE_GRAPHS='{"agent": "./graphs/agent.js:graph"}'
WORKDIR /deps/unit_tests"""
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
# config_to_compose
def test_config_to_compose_simple_config():
graphs = {"agent": "./agent.py:graph"}