mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
WIP: monorepo support in CLI (#6028)
This PR introduces the `--build-command` and `--install-command` arguments to `langgraph build`. `--install-command` is a custom install command. If passed, it will be run from wherever the `langgraph build` call was made, i.e. NOT where the langgraph.json file lives (except if these are the same place). This will override the detected install command that we previously used. `--build-command` is a custom build command. This will run from wherever the langgraph.json file lives, and will be done after the install has been run. You don't need to provide both. Just providing one will make the install (detected or supplied) run in the directory from where `langgraph build was called` and then have the build command (if one exists) run in the directory where langgraph.json exists. I think we should probably allow configuring the directories from which these commands get run, but I don't think this needs to be part of the MVP. --------- Co-authored-by: William FH <13333726+hinthornw@users.noreply.github.com>
This commit is contained in:
co-authored by
William FH
parent
25ba4c3bda
commit
d503c0bf33
@@ -87,3 +87,18 @@ jobs:
|
||||
working-directory: libs/cli/js-examples
|
||||
run: |
|
||||
langgraph build -t langgraph-test-e
|
||||
|
||||
- name: Build JS monorepo service
|
||||
if: steps.changed-files.outputs.all
|
||||
working-directory: libs/cli/js-monorepo-example
|
||||
run: |
|
||||
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json --build-command "yarn run turbo build" --install-command "yarn install"
|
||||
|
||||
- name: Build Python monorepo service
|
||||
if: steps.changed-files.outputs.all
|
||||
working-directory: libs/cli/python-monorepo-example
|
||||
run: |
|
||||
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
cp apps/agent/.env.example apps/agent/.env
|
||||
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env; fi
|
||||
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
|
||||
@@ -483,19 +483,19 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt langchain_community langchain_anthropic langchain_openai wikipedia scikit-learn
|
||||
|
||||
ADD ./graphs /deps/__outer_graphs/src
|
||||
ADD ./graphs /deps/outer-graphs/src
|
||||
RUN set -ex && \
|
||||
for line in '[project]' \
|
||||
'name = "graphs"' \
|
||||
'version = "0.1"' \
|
||||
'[tool.setuptools.package-data]' \
|
||||
'"*" = ["**/*"]'; do \
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \
|
||||
done
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-graphs/src/agent.py:graph", "storm": "/deps/outer-graphs/src/storm.py:graph"}'
|
||||
```
|
||||
|
||||
???+ note "Updating your langgraph.json file"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"prettier",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
parser: "@typescript-eslint/parser",
|
||||
project: "./tsconfig.json",
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["import", "@typescript-eslint", "no-instanceof"],
|
||||
ignorePatterns: [
|
||||
".eslintrc.cjs",
|
||||
"scripts",
|
||||
"src/utils/lodash/*",
|
||||
"node_modules",
|
||||
"dist",
|
||||
"dist-cjs",
|
||||
"*.js",
|
||||
"*.cjs",
|
||||
"*.d.ts",
|
||||
],
|
||||
rules: {
|
||||
"no-process-env": 2,
|
||||
"no-instanceof/no-instanceof": 2,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-shadow": 0,
|
||||
"@typescript-eslint/no-empty-interface": 0,
|
||||
"@typescript-eslint/no-use-before-define": ["error", "nofunc"],
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { args: "none" }],
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
camelcase: 0,
|
||||
"class-methods-use-this": 0,
|
||||
"import/extensions": [2, "ignorePackages"],
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{ devDependencies: ["**/*.test.ts"] },
|
||||
],
|
||||
"import/no-unresolved": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"keyword-spacing": "error",
|
||||
"max-classes-per-file": 0,
|
||||
"max-len": 0,
|
||||
"no-await-in-loop": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-console": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"no-shadow": 0,
|
||||
"no-continue": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-useless-constructor": 0,
|
||||
"no-return-await": 0,
|
||||
"consistent-return": 0,
|
||||
"no-else-return": 0,
|
||||
"new-cap": ["error", { properties: false, capIsNew: false }],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": {
|
||||
"agent": "./src/graph.ts:graph"
|
||||
},
|
||||
"env": "../../.env"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@js-monorepo-example/agent",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "src/graph.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@js-monorepo-example/shared": "*",
|
||||
"@langchain/core": "^0.3.2",
|
||||
"@langchain/langgraph": "^0.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Simple LangGraph.js example for monorepo testing
|
||||
*/
|
||||
import { StateGraph } from "@langchain/langgraph";
|
||||
import { RunnableConfig } from "@langchain/core/runnables";
|
||||
import { StateAnnotation } from "./state.js";
|
||||
import { getGreeting } from "@js-monorepo-example/shared";
|
||||
|
||||
/**
|
||||
* Simple node that uses the shared library
|
||||
*/
|
||||
const callModel = async (
|
||||
state: typeof StateAnnotation.State,
|
||||
_config: RunnableConfig,
|
||||
): Promise<typeof StateAnnotation.Update> => {
|
||||
// Use functions from the shared library
|
||||
const greeting = getGreeting();
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: `${greeting}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple routing function
|
||||
*/
|
||||
export const route = (
|
||||
state: typeof StateAnnotation.State,
|
||||
): "__end__" | "callModel" => {
|
||||
if (state.messages.length > 0) {
|
||||
return "__end__";
|
||||
}
|
||||
return "callModel";
|
||||
};
|
||||
|
||||
// Create the graph
|
||||
const builder = new StateGraph(StateAnnotation)
|
||||
.addNode("callModel", callModel)
|
||||
.addEdge("__start__", "callModel")
|
||||
.addConditionalEdges("callModel", route);
|
||||
|
||||
export const graph = builder.compile();
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
|
||||
import { Annotation, messagesStateReducer } from "@langchain/langgraph";
|
||||
|
||||
/**
|
||||
* Simple state annotation for the agent
|
||||
*/
|
||||
export const StateAnnotation = Annotation.Root({
|
||||
/**
|
||||
* Messages track the primary execution state of the agent.
|
||||
*/
|
||||
messages: Annotation<BaseMessage[], BaseMessageLike[]>({
|
||||
reducer: messagesStateReducer,
|
||||
default: () => [],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@js-monorepo-example/shared",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Simple utility functions for monorepo testing
|
||||
*/
|
||||
export function getGreeting(): string {
|
||||
return "Hello from shared library!";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "js-monorepo-example",
|
||||
"version": "0.0.1",
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"description": "A simple monorepo example for LangGraph integration testing.",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"libs/*",
|
||||
"apps/*"
|
||||
],
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "turbo build",
|
||||
"clean": "turbo clean",
|
||||
"test": "turbo test",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint 'apps/**/*.ts' 'libs/**/*.ts'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"turbo": "^2.5.0",
|
||||
"typescript": "^5.3.3",
|
||||
"@tsconfig/recommended": "^1.0.7",
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "^9.9.1",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-no-instanceof": "^1.0.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||
"@typescript-eslint/parser": "^5.59.8",
|
||||
"prettier": "^3.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@tsconfig/recommended",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["apps/**/*", "libs/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"clean": {
|
||||
"dependsOn": ["^clean"]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["^test"]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
__version__ = "0.4.0"
|
||||
__version__ = "0.4.1"
|
||||
|
||||
@@ -303,6 +303,8 @@ def _build(
|
||||
pull: bool,
|
||||
tag: str,
|
||||
passthrough: Sequence[str] = (),
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
):
|
||||
# pull latest images
|
||||
if pull:
|
||||
@@ -322,22 +324,38 @@ def _build(
|
||||
"-t",
|
||||
tag,
|
||||
]
|
||||
# determine build context: use current directory for JS projects, config parent for Python
|
||||
is_js_project = config_json.get("node_version") and not config_json.get(
|
||||
"python_version"
|
||||
)
|
||||
# build/install commands only apply to JS projects for now
|
||||
# without install/build command, JS projects will follow the old behavior
|
||||
if is_js_project and (build_command or install_command):
|
||||
build_context = str(pathlib.Path.cwd())
|
||||
else:
|
||||
build_context = str(config.parent)
|
||||
|
||||
# apply config
|
||||
stdin, additional_contexts = langgraph_cli.config.config_to_docker(
|
||||
config, config_json, base_image, api_version
|
||||
config,
|
||||
config_json,
|
||||
base_image,
|
||||
api_version,
|
||||
install_command,
|
||||
build_command,
|
||||
build_context,
|
||||
)
|
||||
# add additional_contexts
|
||||
if additional_contexts:
|
||||
for k, v in additional_contexts.items():
|
||||
args.extend(["--build-context", f"{k}={v}"])
|
||||
# run docker build
|
||||
runner.run(
|
||||
subp_exec(
|
||||
"docker",
|
||||
"build",
|
||||
*args,
|
||||
*passthrough,
|
||||
str(config.parent),
|
||||
build_context,
|
||||
input=stdin,
|
||||
verbose=True,
|
||||
)
|
||||
@@ -366,6 +384,14 @@ def _build(
|
||||
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
|
||||
)
|
||||
@OPT_API_VERSION
|
||||
@click.option(
|
||||
"--install-command",
|
||||
help="Custom install command to run from the build context root. If not provided, auto-detects based on package manager files.",
|
||||
)
|
||||
@click.option(
|
||||
"--build-command",
|
||||
help="Custom build command to run from the langgraph.json directory. If not provided, uses default build process.",
|
||||
)
|
||||
@click.argument("docker_build_args", nargs=-1, type=click.UNPROCESSED)
|
||||
@cli.command(
|
||||
help="📦 Build LangGraph API server Docker image.",
|
||||
@@ -381,6 +407,8 @@ def build(
|
||||
api_version: Optional[str],
|
||||
pull: bool,
|
||||
tag: str,
|
||||
install_command: Optional[str],
|
||||
build_command: Optional[str],
|
||||
):
|
||||
with Runner() as runner, Progress(message="Pulling...") as set:
|
||||
if shutil.which("docker") is None:
|
||||
@@ -397,6 +425,8 @@ def build(
|
||||
pull,
|
||||
tag,
|
||||
docker_build_args,
|
||||
install_command,
|
||||
build_command,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -913,10 +913,10 @@ def _assemble_local_deps(config_path: pathlib.Path, config: Config) -> LocalDeps
|
||||
"Rename the directory to use it as flat-layout package."
|
||||
)
|
||||
check_reserved(resolved.name, local_dep)
|
||||
container_path = f"/deps/__outer_{resolved.name}/{resolved.name}"
|
||||
container_path = f"/deps/outer-{resolved.name}/{resolved.name}"
|
||||
else:
|
||||
# src layout
|
||||
container_path = f"/deps/__outer_{resolved.name}/src"
|
||||
container_path = f"/deps/outer-{resolved.name}/src"
|
||||
for file in files:
|
||||
rfile = resolved / file
|
||||
if (
|
||||
@@ -1286,7 +1286,7 @@ def python_config_to_docker(
|
||||
if local_deps.pip_reqs:
|
||||
pip_reqs_str = os.linesep.join(
|
||||
(
|
||||
f"COPY --from=__outer_{reqpath.name} requirements.txt {destpath}"
|
||||
f"COPY --from=outer-{reqpath.name} requirements.txt {destpath}"
|
||||
if reqpath.parent in local_deps.additional_contexts
|
||||
else f"ADD {reqpath.relative_to(config_path.parent)} {destpath}"
|
||||
)
|
||||
@@ -1305,7 +1305,7 @@ def python_config_to_docker(
|
||||
faux_pkgs_str = f"{os.linesep}{os.linesep}".join(
|
||||
(
|
||||
f"""# -- Adding non-package dependency {fullpath.name} --
|
||||
COPY --from=__outer_{fullpath.name} . {destpath}"""
|
||||
COPY --from=outer-{fullpath.name} . {destpath}"""
|
||||
if fullpath in local_deps.additional_contexts
|
||||
else f"""# -- Adding non-package dependency {fullpath.name} --
|
||||
ADD {relpath} {destpath}"""
|
||||
@@ -1320,7 +1320,7 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_{fullpath.name}/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-{fullpath.name}/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency {fullpath.name} --"""
|
||||
for fullpath, (relpath, destpath) in local_deps.faux_pkgs.items()
|
||||
@@ -1423,7 +1423,7 @@ ADD {relpath} /deps/{name}
|
||||
if p in local_deps.real_pkgs:
|
||||
name = local_deps.real_pkgs[p][1]
|
||||
elif p in local_deps.faux_pkgs:
|
||||
name = f"__outer_{p.name}"
|
||||
name = f"outer-{p.name}"
|
||||
else:
|
||||
raise RuntimeError(f"Unknown additional context: {p}")
|
||||
additional_contexts[name] = str(p)
|
||||
@@ -1436,9 +1436,28 @@ def node_config_to_docker(
|
||||
config: Config,
|
||||
base_image: str,
|
||||
api_version: Optional[str] = None,
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
build_context: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
faux_path = f"/deps/{config_path.parent.name}"
|
||||
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
||||
# Calculate paths for monorepo support
|
||||
if build_context:
|
||||
relative_workdir = _calculate_relative_workdir(config_path, build_context)
|
||||
container_name = pathlib.Path(build_context).name
|
||||
if relative_workdir:
|
||||
faux_path = f"/deps/{container_name}/{relative_workdir}"
|
||||
else:
|
||||
faux_path = f"/deps/{container_name}"
|
||||
else:
|
||||
# Backward compatibility: use the original behavior
|
||||
faux_path = f"/deps/{config_path.parent.name}"
|
||||
|
||||
# Use custom install command or auto-detect
|
||||
if install_command:
|
||||
install_cmd = install_command
|
||||
else:
|
||||
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
||||
|
||||
image_str = docker_tag(config, base_image, api_version)
|
||||
|
||||
env_vars: list[str] = []
|
||||
@@ -1465,20 +1484,35 @@ def node_config_to_docker(
|
||||
|
||||
env_vars.append(f"ENV LANGSERVE_GRAPHS='{json.dumps(config['graphs'])}'")
|
||||
|
||||
# For monorepo support, we need to handle install and build commands differently
|
||||
if build_context:
|
||||
# Monorepo case: install from root, build from config directory
|
||||
container_root = f"/deps/{pathlib.Path(build_context).name}"
|
||||
install_step = f"RUN cd {container_root} && {install_cmd}"
|
||||
|
||||
if build_command:
|
||||
build_step = f"RUN cd {faux_path} && {build_command}"
|
||||
else:
|
||||
build_step = 'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts'
|
||||
else:
|
||||
# Original behavior: everything happens in the same directory
|
||||
install_step = f"RUN cd {faux_path} && {install_cmd}"
|
||||
build_step = 'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts'
|
||||
|
||||
docker_file_contents = [
|
||||
f"FROM {image_str}",
|
||||
"",
|
||||
os.linesep.join(config["dockerfile_lines"]),
|
||||
"",
|
||||
f"ADD . {faux_path}",
|
||||
f"ADD . {faux_path if not build_context else container_root}",
|
||||
"",
|
||||
f"RUN cd {faux_path} && {install_cmd}",
|
||||
install_step,
|
||||
"",
|
||||
os.linesep.join(env_vars),
|
||||
"",
|
||||
f"WORKDIR {faux_path}",
|
||||
"",
|
||||
'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts',
|
||||
build_step,
|
||||
]
|
||||
|
||||
return os.linesep.join(docker_file_contents), {}
|
||||
@@ -1526,16 +1560,42 @@ def docker_tag(
|
||||
return f"{base_image}:{full_tag}"
|
||||
|
||||
|
||||
def _calculate_relative_workdir(config_path: pathlib.Path, build_context: str) -> str:
|
||||
"""Calculate the relative path from build context to langgraph.json directory."""
|
||||
config_dir = config_path.parent.resolve()
|
||||
build_context_path = pathlib.Path(build_context).resolve()
|
||||
|
||||
try:
|
||||
relative_path = config_dir.relative_to(build_context_path)
|
||||
return str(relative_path) if str(relative_path) != "." else ""
|
||||
except ValueError as _:
|
||||
raise ValueError(
|
||||
f"Configuration file {config_path} is not under the build context {build_context}. "
|
||||
f"Please run the command from a directory that contains your langgraph.json file, "
|
||||
) from None
|
||||
|
||||
|
||||
def config_to_docker(
|
||||
config_path: pathlib.Path,
|
||||
config: Config,
|
||||
base_image: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
build_context: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
base_image = base_image or default_base_image(config)
|
||||
|
||||
if config.get("node_version") and not config.get("python_version"):
|
||||
return node_config_to_docker(config_path, config, base_image, api_version)
|
||||
return node_config_to_docker(
|
||||
config_path,
|
||||
config,
|
||||
base_image,
|
||||
api_version,
|
||||
install_command,
|
||||
build_command,
|
||||
build_context,
|
||||
)
|
||||
|
||||
return python_config_to_docker(config_path, config, base_image, api_version)
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": [".", "../../libs/shared", "../../libs/common"],
|
||||
"graphs": {
|
||||
"agent": "./src/agent/graph.py:graph"
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
[project]
|
||||
name = "agent"
|
||||
version = "0.0.1"
|
||||
description = "Agent for the Python monorepo"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["agent"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"agent" = "src/agent"
|
||||
@@ -0,0 +1 @@
|
||||
"""Agent package."""
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Simple LangGraph agent for monorepo testing."""
|
||||
|
||||
from common import get_common_prefix
|
||||
from langchain_core.messages import AIMessage
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from shared import get_dummy_message
|
||||
|
||||
from agent.state import State
|
||||
|
||||
|
||||
def call_model(state: State) -> dict:
|
||||
"""Simple node that uses the shared libraries."""
|
||||
# Use functions from both shared packages
|
||||
dummy_message = get_dummy_message()
|
||||
prefix = get_common_prefix()
|
||||
|
||||
message = AIMessage(content=f"{prefix} Agent says: {dummy_message}")
|
||||
|
||||
return {"messages": [message]}
|
||||
|
||||
|
||||
def should_continue(state: State):
|
||||
"""Conditional edge - end after first message."""
|
||||
messages = state["messages"]
|
||||
if len(messages) > 0:
|
||||
return END
|
||||
return "call_model"
|
||||
|
||||
|
||||
# Build the graph
|
||||
workflow = StateGraph(State)
|
||||
|
||||
# Add the node
|
||||
workflow.add_node("call_model", call_model)
|
||||
|
||||
# Add edges
|
||||
workflow.add_edge(START, "call_model")
|
||||
workflow.add_conditional_edges("call_model", should_continue)
|
||||
|
||||
graph = workflow.compile()
|
||||
@@ -0,0 +1,13 @@
|
||||
"""State definition for the agent."""
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Annotated, TypedDict
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langgraph.graph.message import add_messages
|
||||
|
||||
|
||||
class State(TypedDict):
|
||||
"""The state of the agent."""
|
||||
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Common helper functions package."""
|
||||
|
||||
from .helpers import get_common_prefix
|
||||
|
||||
__all__ = ["get_common_prefix"]
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Common helper functions."""
|
||||
|
||||
|
||||
def get_common_prefix() -> str:
|
||||
"""Get a common prefix for messages."""
|
||||
return "[COMMON]"
|
||||
@@ -0,0 +1,20 @@
|
||||
[project]
|
||||
name = "shared"
|
||||
version = "0.0.1"
|
||||
description = "Shared utilities for the Python monorepo"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
dependencies = []
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["shared"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"shared" = "src/shared"
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Shared utilities package."""
|
||||
|
||||
from .utils import get_dummy_message
|
||||
|
||||
__all__ = ["get_dummy_message"]
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Shared utility functions."""
|
||||
|
||||
|
||||
def get_dummy_message() -> str:
|
||||
"""Get a dummy message for testing."""
|
||||
return "Hello from shared library!"
|
||||
@@ -0,0 +1,46 @@
|
||||
[project]
|
||||
name = "python-monorepo-example"
|
||||
version = "0.0.1"
|
||||
description = "A Python monorepo example with LangGraph agents and shared packages"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
dependencies = [
|
||||
"langgraph>=0.6.0,<0.7.0",
|
||||
"langchain-core>=0.2.14",
|
||||
]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["apps/*", "libs/shared"]
|
||||
|
||||
[tool.uv.sources]
|
||||
shared = { workspace = true }
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["mypy>=1.11.1", "ruff>=0.6.1"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"D", # pydocstyle
|
||||
"UP",
|
||||
]
|
||||
lint.ignore = [
|
||||
"D100", # Missing docstring in public module
|
||||
"D101", # Missing docstring in public class
|
||||
"D102", # Missing docstring in public method
|
||||
"D103", # Missing docstring in public function
|
||||
"D104", # Missing docstring in public package
|
||||
"D105", # Missing docstring in magic method
|
||||
]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
@@ -570,7 +570,7 @@ def test_build_generate_proper_build_context():
|
||||
catch_exceptions=True,
|
||||
)
|
||||
|
||||
build_context_pattern = re.compile(r"--build-context\s+(\w+)=([^\s]+)")
|
||||
build_context_pattern = re.compile(r"--build-context\s+([\w-]+)=([^\s]+)")
|
||||
|
||||
build_contexts = re.findall(build_context_pattern, result.output)
|
||||
assert len(build_contexts) == 2, (
|
||||
|
||||
@@ -421,14 +421,14 @@ def test_config_to_docker_simple():
|
||||
expected_docker_stdin = f"""\
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Installing local requirements --
|
||||
COPY --from=__outer_requirements.txt requirements.txt /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -r /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
COPY --from=outer-requirements.txt requirements.txt /deps/outer-graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -r /deps/outer-graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
# -- End of local requirements install --
|
||||
# -- Adding local package ../../examples --
|
||||
COPY --from=examples . /deps/examples
|
||||
# -- End of local package ../../examples --
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -438,11 +438,11 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Adding non-package dependency graphs_reqs_a --
|
||||
COPY --from=__outer_graphs_reqs_a . /deps/__outer_graphs_reqs_a/graphs_reqs_a
|
||||
COPY --from=outer-graphs_reqs_a . /deps/outer-graphs_reqs_a/graphs_reqs_a
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs_reqs_a"' \\
|
||||
@@ -452,21 +452,21 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs_reqs_a/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs_reqs_a/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs_reqs_a --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGGRAPH_HTTP='{{"app": "/deps/examples/my_app.py:app"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
|
||||
assert additional_contexts == {
|
||||
"__outer_graphs_reqs_a": str(
|
||||
"outer-graphs_reqs_a": str(
|
||||
(pathlib.Path(__file__).parent / "../../examples/graphs_reqs_a").resolve()
|
||||
),
|
||||
"examples": str((pathlib.Path(__file__).parent / "../../examples").resolve()),
|
||||
@@ -484,7 +484,7 @@ def test_config_to_docker_outside_path():
|
||||
"""\
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -494,11 +494,11 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Adding non-package dependency tests --
|
||||
COPY --from=__outer_tests . /deps/__outer_tests/tests
|
||||
COPY --from=outer-tests . /deps/outer-tests/tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "tests"' \\
|
||||
@@ -508,22 +508,22 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}'
|
||||
"""
|
||||
+ FORMATTED_CLEANUP_LINES
|
||||
+ """
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
)
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {
|
||||
"__outer_tests": str(pathlib.Path(__file__).parent.parent.absolute()),
|
||||
"outer-tests": str(pathlib.Path(__file__).parent.parent.absolute()),
|
||||
}
|
||||
|
||||
|
||||
@@ -545,7 +545,7 @@ def test_config_to_docker_pipconfig():
|
||||
FROM langchain/langgraph-api:3.11
|
||||
ADD pipconfig.txt /pipconfig.txt
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -555,17 +555,17 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}'
|
||||
"""
|
||||
+ FORMATTED_CLEANUP_LINES
|
||||
+ """
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
)
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
@@ -607,7 +607,7 @@ def test_config_to_docker_local_deps():
|
||||
expected_docker_stdin = f"""\
|
||||
FROM langchain/langgraph-api-custom:3.11
|
||||
# -- Adding non-package dependency graphs --
|
||||
ADD ./graphs /deps/__outer_graphs/src
|
||||
ADD ./graphs /deps/outer-graphs/src
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs"' \\
|
||||
@@ -617,13 +617,13 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_graphs/src/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}\
|
||||
"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
@@ -691,7 +691,7 @@ ARG foo
|
||||
ADD pipconfig.txt /pipconfig.txt
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt langchain langchain_openai
|
||||
# -- Adding non-package dependency graphs --
|
||||
ADD ./graphs/ /deps/__outer_graphs/src
|
||||
ADD ./graphs/ /deps/outer-graphs/src
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs"' \\
|
||||
@@ -701,13 +701,13 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_graphs/src/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -797,7 +797,7 @@ def test_config_to_docker_gen_ui_python():
|
||||
expected_docker_stdin = f"""FROM langchain/langgraph-api:3.11
|
||||
RUN /storage/install-node.sh
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -807,7 +807,7 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
@@ -815,13 +815,13 @@ RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/con
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGGRAPH_UI='{{"agent": "./graphs/agent.ui.jsx"}}'
|
||||
ENV LANGGRAPH_UI_CONFIG='{{"shared": ["nuqs"]}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
# -- Installing JS dependencies --
|
||||
ENV NODE_VERSION=20
|
||||
RUN cd /deps/__outer_unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
RUN cd /deps/outer-unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
# -- End of JS dependencies install --
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests"""
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests"""
|
||||
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -843,7 +843,7 @@ def test_config_to_docker_multiplatform():
|
||||
expected_docker_stdin = f"""FROM langchain/langgraph-api:3.11
|
||||
RUN /storage/install-node.sh
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -853,19 +853,19 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"python": "/deps/__outer_unit_tests/unit_tests/multiplatform/python.py:graph", "js": "/deps/__outer_unit_tests/unit_tests/multiplatform/js.mts:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"python": "/deps/outer-unit_tests/unit_tests/multiplatform/python.py:graph", "js": "/deps/outer-unit_tests/unit_tests/multiplatform/js.mts:graph"}}'
|
||||
# -- Installing JS dependencies --
|
||||
ENV NODE_VERSION=22
|
||||
RUN cd /deps/__outer_unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
RUN cd /deps/outer-unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
# -- End of JS dependencies install --
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests"""
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests"""
|
||||
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -984,7 +984,7 @@ def test_config_to_compose_simple_config():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -994,15 +994,15 @@ def test_config_to_compose_simple_config():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
actual_compose_stdin = config_to_compose(
|
||||
PATH_TO_CONFIG,
|
||||
@@ -1025,7 +1025,7 @@ def test_config_to_compose_env_vars():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api-custom:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1035,15 +1035,15 @@ def test_config_to_compose_env_vars():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
openai_api_key = "key"
|
||||
actual_compose_stdin = config_to_compose(
|
||||
@@ -1070,7 +1070,7 @@ def test_config_to_compose_env_file():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1080,15 +1080,15 @@ def test_config_to_compose_env_file():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
actual_compose_stdin = config_to_compose(
|
||||
PATH_TO_CONFIG,
|
||||
@@ -1108,7 +1108,7 @@ def test_config_to_compose_watch():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1118,15 +1118,15 @@ def test_config_to_compose_watch():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
|
||||
develop:
|
||||
watch:
|
||||
@@ -1155,7 +1155,7 @@ def test_config_to_compose_end_to_end():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1165,15 +1165,15 @@ def test_config_to_compose_end_to_end():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
|
||||
develop:
|
||||
watch:
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
"Bash(python:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(sed:*)",
|
||||
"Bash(awk:*)"
|
||||
"Bash(awk:*)",
|
||||
"Bash(uv run mypy:*)",
|
||||
"Bash(uv run:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user