mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-09 11:17:53 +02:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d263a20ae | ||
|
|
10d9721aed |
@@ -0,0 +1,61 @@
|
|||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from contextvars import ContextVar
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from starlette import Starlette
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
from starlette.responses import JSONResponse
|
||||||
|
from starlette.routing import Route
|
||||||
|
|
||||||
|
my_context_var: ContextVar[str] = ContextVar("my_context_var", default="")
|
||||||
|
LIFESPAN_VAL = ""
|
||||||
|
other_context_var = ContextVar("other_context_var", default="")
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
@asynccontextmanager
|
||||||
|
async def my_lifespan(app):
|
||||||
|
global LIFESPAN_VAL
|
||||||
|
LIFESPAN_VAL = "foobar-lifespan"
|
||||||
|
yield
|
||||||
|
assert LIFESPAN_VAL == "foobar-lifespan"
|
||||||
|
LIFESPAN_VAL = ""
|
||||||
|
|
||||||
|
|
||||||
|
class MyContextMiddleware(BaseHTTPMiddleware):
|
||||||
|
async def dispatch(self, request: Any, call_next: Any) -> Any:
|
||||||
|
token = my_context_var.set("Foobar")
|
||||||
|
try:
|
||||||
|
response = await call_next(request)
|
||||||
|
return response
|
||||||
|
finally:
|
||||||
|
my_context_var.reset(token)
|
||||||
|
|
||||||
|
|
||||||
|
async def custom_my_route(request):
|
||||||
|
assert my_context_var.get() == "Foobar"
|
||||||
|
assert LIFESPAN_VAL == "foobar-lifespan"
|
||||||
|
return JSONResponse({"foo": "bar"})
|
||||||
|
|
||||||
|
|
||||||
|
async def runs_afakeroute(request):
|
||||||
|
assert my_context_var.get() == "Foobar"
|
||||||
|
assert LIFESPAN_VAL == "foobar-lifespan"
|
||||||
|
return JSONResponse({"foo": "afakeroute"})
|
||||||
|
|
||||||
|
|
||||||
|
async def other_middleware(request: Any, call_next: Any) -> Any:
|
||||||
|
other_context_var.set("foobar")
|
||||||
|
response = await call_next(request)
|
||||||
|
other_context_var.reset()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
app = Starlette(
|
||||||
|
middleware=[MyContextMiddleware],
|
||||||
|
routes=[
|
||||||
|
Route("/custom/my-route", custom_my_route),
|
||||||
|
Route("/runs/afakeroute", runs_afakeroute),
|
||||||
|
],
|
||||||
|
lifespan=my_lifespan,
|
||||||
|
)
|
||||||
@@ -86,6 +86,33 @@ class AuthConfig(TypedDict, total=False):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CorsConfig(TypedDict, total=False):
|
||||||
|
allow_origins: list[str]
|
||||||
|
allow_methods: list[str]
|
||||||
|
allow_headers: list[str]
|
||||||
|
allow_credentials: bool
|
||||||
|
allow_origin_regex: str
|
||||||
|
expose_headers: list[str]
|
||||||
|
max_age: int
|
||||||
|
|
||||||
|
|
||||||
|
class HttpConfig(TypedDict, total=False):
|
||||||
|
app: str
|
||||||
|
"""Import path for a custom Starlette/FastAPI app to mount"""
|
||||||
|
disable_assistants: bool
|
||||||
|
"""Disable /assistants routes"""
|
||||||
|
disable_threads: bool
|
||||||
|
"""Disable /threads routes"""
|
||||||
|
disable_runs: bool
|
||||||
|
"""Disable /runs routes"""
|
||||||
|
disable_store: bool
|
||||||
|
"""Disable /store routes"""
|
||||||
|
disable_meta: bool
|
||||||
|
"""Disable /ok, /info, /metrics, and /docs routes"""
|
||||||
|
cors: Optional[CorsConfig]
|
||||||
|
"""Cross-Origin Resource Sharing (CORS) configuration"""
|
||||||
|
|
||||||
|
|
||||||
class Config(TypedDict, total=False):
|
class Config(TypedDict, total=False):
|
||||||
"""Configuration for langgraph-cli."""
|
"""Configuration for langgraph-cli."""
|
||||||
|
|
||||||
@@ -124,6 +151,9 @@ class Config(TypedDict, total=False):
|
|||||||
auth: Optional[AuthConfig]
|
auth: Optional[AuthConfig]
|
||||||
"""Configuration for authentication."""
|
"""Configuration for authentication."""
|
||||||
|
|
||||||
|
http: Optional[HttpConfig]
|
||||||
|
"""Configuration for HTTP server."""
|
||||||
|
|
||||||
|
|
||||||
def _parse_version(version_str: str) -> tuple[int, int]:
|
def _parse_version(version_str: str) -> tuple[int, int]:
|
||||||
"""Parse a version string into a tuple of (major, minor)."""
|
"""Parse a version string into a tuple of (major, minor)."""
|
||||||
@@ -158,6 +188,7 @@ def validate_config(config: Config) -> Config:
|
|||||||
"env": config.get("env", {}),
|
"env": config.get("env", {}),
|
||||||
"store": config.get("store"),
|
"store": config.get("store"),
|
||||||
"auth": config.get("auth"),
|
"auth": config.get("auth"),
|
||||||
|
"http": config.get("http"),
|
||||||
}
|
}
|
||||||
if config.get("node_version")
|
if config.get("node_version")
|
||||||
else {
|
else {
|
||||||
@@ -169,6 +200,7 @@ def validate_config(config: Config) -> Config:
|
|||||||
"env": config.get("env", {}),
|
"env": config.get("env", {}),
|
||||||
"store": config.get("store"),
|
"store": config.get("store"),
|
||||||
"auth": config.get("auth"),
|
"auth": config.get("auth"),
|
||||||
|
"http": config.get("http"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -221,7 +253,13 @@ def validate_config(config: Config) -> Config:
|
|||||||
f"Invalid auth.path format: '{auth_conf['path']}'. "
|
f"Invalid auth.path format: '{auth_conf['path']}'. "
|
||||||
"Must be in format './path/to/file.py:attribute_name'"
|
"Must be in format './path/to/file.py:attribute_name'"
|
||||||
)
|
)
|
||||||
|
if http_conf := config.get("http"):
|
||||||
|
if "app" in http_conf:
|
||||||
|
if ":" not in http_conf["app"]:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid http.app format: '{http_conf['app']}'. "
|
||||||
|
"Must be in format './path/to/file.py:attribute_name'"
|
||||||
|
)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
@@ -441,6 +479,47 @@ def _assemble_local_deps(config_path: pathlib.Path, config: Config) -> LocalDeps
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if auth_conf := config.get("auth"):
|
||||||
|
if auth_path := auth_conf.get("path"):
|
||||||
|
module_str, _, _ = auth_path.partition(":")
|
||||||
|
if module_str.startswith("."):
|
||||||
|
auth_file = (config_path.parent / module_str).resolve()
|
||||||
|
auth_dir = auth_file.parent
|
||||||
|
if (config_path.parent not in auth_dir.parents) and (
|
||||||
|
auth_dir not in additional_contexts
|
||||||
|
):
|
||||||
|
additional_contexts.append(auth_dir)
|
||||||
|
# Also add auth_dir to faux_pkgs if not already added.
|
||||||
|
if auth_dir not in real_pkgs and auth_dir not in faux_pkgs:
|
||||||
|
files = os.listdir(auth_dir)
|
||||||
|
if "__init__.py" in files:
|
||||||
|
container_path = (
|
||||||
|
f"/deps/__outer_{auth_dir.name}/{auth_dir.name}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
container_path = f"/deps/__outer_{auth_dir.name}/src"
|
||||||
|
faux_pkgs[auth_dir] = (str(auth_dir), container_path)
|
||||||
|
if http_conf := config.get("http"):
|
||||||
|
if http_path := http_conf.get("app"):
|
||||||
|
module_str, _, _ = http_path.partition(":")
|
||||||
|
if module_str.startswith("."):
|
||||||
|
http_file = (config_path.parent / module_str).resolve()
|
||||||
|
http_dir = http_file.parent
|
||||||
|
if (config_path.parent not in http_dir.parents) and (
|
||||||
|
http_dir not in additional_contexts
|
||||||
|
):
|
||||||
|
additional_contexts.append(http_dir)
|
||||||
|
# Also add http_dir to faux_pkgs if not already added.
|
||||||
|
if http_dir not in real_pkgs and http_dir not in faux_pkgs:
|
||||||
|
files = os.listdir(http_dir)
|
||||||
|
if "__init__.py" in files:
|
||||||
|
container_path = (
|
||||||
|
f"/deps/__outer_{http_dir.name}/{http_dir.name}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
container_path = f"/deps/__outer_{http_dir.name}/src"
|
||||||
|
faux_pkgs[http_dir] = (str(http_dir), container_path)
|
||||||
|
|
||||||
return LocalDeps(pip_reqs, real_pkgs, faux_pkgs, working_dir, additional_contexts)
|
return LocalDeps(pip_reqs, real_pkgs, faux_pkgs, working_dir, additional_contexts)
|
||||||
|
|
||||||
|
|
||||||
@@ -560,13 +639,80 @@ def _update_auth_path(
|
|||||||
auth_conf["path"] = new_path
|
auth_conf["path"] = new_path
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# -- New: Check additional contexts for auth --
|
||||||
|
for add_ctx in local_deps.additional_contexts:
|
||||||
|
if resolved.is_relative_to(add_ctx):
|
||||||
|
new_path = f"/deps/__outer_{add_ctx.name}/{resolved.relative_to(add_ctx)}:{attr_str}"
|
||||||
|
auth_conf["path"] = new_path
|
||||||
|
return
|
||||||
|
# ------------------------------------------------
|
||||||
|
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Auth file '{resolved}' not covered by dependencies.\n"
|
f"Auth file '{resolved}' not covered by dependencies or additional contexts.\n"
|
||||||
"Add its parent directory to the 'dependencies' array in your config.\n"
|
"Add its parent directory to the 'dependencies' array in your config, or let the auto-include logic add it.\n"
|
||||||
f"Current dependencies: {config['dependencies']}"
|
f"Current dependencies: {config['dependencies']}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _update_http_app_path(
|
||||||
|
config_path: pathlib.Path, config: Config, local_deps: LocalDeps
|
||||||
|
) -> None:
|
||||||
|
"""Update the HTTP app path to point to the correct location in the Docker container.
|
||||||
|
|
||||||
|
Similar to _update_graph_paths, this ensures that if a custom app is specified via
|
||||||
|
a local file path, that file is included in the Docker build context and its path
|
||||||
|
is updated to point to the correct location in the container.
|
||||||
|
"""
|
||||||
|
if not (http_config := config.get("http")) or not (
|
||||||
|
app_str := http_config.get("app")
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
module_str, _, attr_str = app_str.partition(":")
|
||||||
|
if not module_str or not attr_str:
|
||||||
|
message = (
|
||||||
|
'Import string "{import_str}" must be in format "<module>:<attribute>".'
|
||||||
|
)
|
||||||
|
raise ValueError(message.format(import_str=app_str))
|
||||||
|
|
||||||
|
# Check if it's a file path
|
||||||
|
if "/" in module_str or "\\" in module_str:
|
||||||
|
# Resolve the local path properly on the current OS
|
||||||
|
resolved = (config_path.parent / module_str).resolve()
|
||||||
|
if not resolved.exists():
|
||||||
|
raise FileNotFoundError(f"Could not find HTTP app module: {resolved}")
|
||||||
|
elif not resolved.is_file():
|
||||||
|
raise IsADirectoryError(f"HTTP app module must be a file: {resolved}")
|
||||||
|
else:
|
||||||
|
for path in local_deps.real_pkgs:
|
||||||
|
if resolved.is_relative_to(path):
|
||||||
|
container_path = (
|
||||||
|
pathlib.Path("/deps") / path.name / resolved.relative_to(path)
|
||||||
|
)
|
||||||
|
module_str = container_path.as_posix()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
for faux_pkg, (_, destpath) in local_deps.faux_pkgs.items():
|
||||||
|
if resolved.is_relative_to(faux_pkg):
|
||||||
|
container_subpath = resolved.relative_to(faux_pkg)
|
||||||
|
# Construct the final path, ensuring POSIX style
|
||||||
|
module_str = f"{destpath}/{container_subpath.as_posix()}"
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# -- New: Check additional contexts for HTTP app --
|
||||||
|
for add_ctx in local_deps.additional_contexts:
|
||||||
|
if resolved.is_relative_to(add_ctx):
|
||||||
|
module_str = f"/deps/__outer_{add_ctx.name}/{resolved.relative_to(add_ctx)}"
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"HTTP app module '{app_str}' not found in 'dependencies' or additional contexts. "
|
||||||
|
"Add its containing package to 'dependencies' list."
|
||||||
|
)
|
||||||
|
# update the config
|
||||||
|
http_config["app"] = f"{module_str}:{attr_str}"
|
||||||
|
|
||||||
|
|
||||||
def python_config_to_docker(
|
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]]:
|
) -> tuple[str, dict[str, str]]:
|
||||||
@@ -590,13 +736,17 @@ def python_config_to_docker(
|
|||||||
_update_graph_paths(config_path, config, local_deps)
|
_update_graph_paths(config_path, config, local_deps)
|
||||||
# Rewrite auth path, so it points to the correct location in the Docker container
|
# Rewrite auth path, so it points to the correct location in the Docker container
|
||||||
_update_auth_path(config_path, config, local_deps)
|
_update_auth_path(config_path, config, local_deps)
|
||||||
|
# Rewrite HTTP app path, so it points to the correct location in the Docker container
|
||||||
|
_update_http_app_path(config_path, config, local_deps)
|
||||||
|
|
||||||
pip_pkgs_str = f"RUN {pip_install} {' '.join(pypi_deps)}" if pypi_deps else ""
|
pip_pkgs_str = f"RUN {pip_install} {' '.join(pypi_deps)}" if pypi_deps else ""
|
||||||
if local_deps.pip_reqs:
|
if local_deps.pip_reqs:
|
||||||
pip_reqs_str = os.linesep.join(
|
pip_reqs_str = os.linesep.join(
|
||||||
f"COPY --from=__outer_{reqpath.name} requirements.txt {destpath}"
|
(
|
||||||
if reqpath.parent in local_deps.additional_contexts
|
f"COPY --from=__outer_{reqpath.name} requirements.txt {destpath}"
|
||||||
else f"ADD {reqpath.relative_to(config_path.parent)} {destpath}"
|
if reqpath.parent in local_deps.additional_contexts
|
||||||
|
else f"ADD {reqpath.relative_to(config_path.parent)} {destpath}"
|
||||||
|
)
|
||||||
for reqpath, destpath in local_deps.pip_reqs
|
for reqpath, destpath in local_deps.pip_reqs
|
||||||
)
|
)
|
||||||
pip_reqs_str += f'{os.linesep}RUN {pip_install} {" ".join("-r " + r for _,r in local_deps.pip_reqs)}'
|
pip_reqs_str += f'{os.linesep}RUN {pip_install} {" ".join("-r " + r for _,r in local_deps.pip_reqs)}'
|
||||||
@@ -631,13 +781,15 @@ RUN set -ex && \\
|
|||||||
)
|
)
|
||||||
|
|
||||||
local_pkgs_str = os.linesep.join(
|
local_pkgs_str = os.linesep.join(
|
||||||
f"""# -- Adding local package {relpath} --
|
(
|
||||||
|
f"""# -- Adding local package {relpath} --
|
||||||
COPY --from={name} . /deps/{name}
|
COPY --from={name} . /deps/{name}
|
||||||
# -- End of local package {relpath} --"""
|
# -- End of local package {relpath} --"""
|
||||||
if fullpath in local_deps.additional_contexts
|
if fullpath in local_deps.additional_contexts
|
||||||
else f"""# -- Adding local package {relpath} --
|
else f"""# -- Adding local package {relpath} --
|
||||||
ADD {relpath} /deps/{name}
|
ADD {relpath} /deps/{name}
|
||||||
# -- End of local package {relpath} --"""
|
# -- End of local package {relpath} --"""
|
||||||
|
)
|
||||||
for fullpath, (relpath, name) in local_deps.real_pkgs.items()
|
for fullpath, (relpath, name) in local_deps.real_pkgs.items()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -662,6 +814,9 @@ ADD {relpath} /deps/{name}
|
|||||||
if (auth_config := config.get("auth")) is not None:
|
if (auth_config := config.get("auth")) is not None:
|
||||||
env_vars.append(f"ENV LANGGRAPH_AUTH='{json.dumps(auth_config)}'")
|
env_vars.append(f"ENV LANGGRAPH_AUTH='{json.dumps(auth_config)}'")
|
||||||
|
|
||||||
|
if (http_config := config.get("http")) is not None:
|
||||||
|
env_vars.append(f"ENV LANGGRAPH_HTTP='{json.dumps(http_config)}'")
|
||||||
|
|
||||||
graphs = config["graphs"]
|
graphs = config["graphs"]
|
||||||
env_vars.append(f"ENV LANGSERVE_GRAPHS='{json.dumps(graphs)}'")
|
env_vars.append(f"ENV LANGSERVE_GRAPHS='{json.dumps(graphs)}'")
|
||||||
|
|
||||||
@@ -733,6 +888,10 @@ ENV LANGGRAPH_STORE='{json.dumps(store_config)}'
|
|||||||
if (auth_config := config.get("auth")) is not None:
|
if (auth_config := config.get("auth")) is not None:
|
||||||
env_additional_config += f"""
|
env_additional_config += f"""
|
||||||
ENV LANGGRAPH_AUTH='{json.dumps(auth_config)}'
|
ENV LANGGRAPH_AUTH='{json.dumps(auth_config)}'
|
||||||
|
"""
|
||||||
|
if (http_config := config.get("http")) is not None:
|
||||||
|
env_additional_config += f"""
|
||||||
|
ENV LANGGRAPH_HTTP='{json.dumps(http_config)}'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,10 +6,14 @@
|
|||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"langchain_openai",
|
"langchain_openai",
|
||||||
|
"starlette",
|
||||||
"."
|
"."
|
||||||
],
|
],
|
||||||
"graphs": {
|
"graphs": {
|
||||||
"agent": "graphs/agent.py:graph"
|
"agent": "graphs/agent.py:graph"
|
||||||
},
|
},
|
||||||
"env": ".env"
|
"env": ".env",
|
||||||
|
"http": {
|
||||||
|
"app": "../../examples/my_app.py:app"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ def test_validate_config():
|
|||||||
"env": {},
|
"env": {},
|
||||||
"store": None,
|
"store": None,
|
||||||
"auth": None,
|
"auth": None,
|
||||||
|
"http": None,
|
||||||
**expected_config,
|
**expected_config,
|
||||||
}
|
}
|
||||||
actual_config = validate_config(expected_config)
|
actual_config = validate_config(expected_config)
|
||||||
@@ -50,6 +51,7 @@ def test_validate_config():
|
|||||||
"env": env,
|
"env": env,
|
||||||
"store": None,
|
"store": None,
|
||||||
"auth": None,
|
"auth": None,
|
||||||
|
"http": None,
|
||||||
}
|
}
|
||||||
actual_config = validate_config(expected_config)
|
actual_config = validate_config(expected_config)
|
||||||
assert actual_config == expected_config
|
assert actual_config == expected_config
|
||||||
@@ -108,6 +110,18 @@ def test_validate_config():
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
assert config["python_version"] == "3.12-slim"
|
assert config["python_version"] == "3.12-slim"
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError,
|
||||||
|
match="Invalid http.app format",
|
||||||
|
):
|
||||||
|
validate_config(
|
||||||
|
{
|
||||||
|
"python_version": "3.12",
|
||||||
|
"dependencies": ["."],
|
||||||
|
"graphs": {"agent": "./agent.py:graph"},
|
||||||
|
"http": {"app": "../../examples/my_app.py"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_config_file():
|
def test_validate_config_file():
|
||||||
@@ -180,7 +194,11 @@ def test_config_to_docker_simple():
|
|||||||
actual_docker_stdin, additional_contexts = config_to_docker(
|
actual_docker_stdin, additional_contexts = config_to_docker(
|
||||||
PATH_TO_CONFIG,
|
PATH_TO_CONFIG,
|
||||||
validate_config(
|
validate_config(
|
||||||
{"dependencies": [".", "../../examples/graphs_reqs_a"], "graphs": graphs}
|
{
|
||||||
|
"dependencies": [".", "../../examples/graphs_reqs_a"],
|
||||||
|
"graphs": graphs,
|
||||||
|
"http": {"app": "../../examples/my_app.py:app"},
|
||||||
|
}
|
||||||
),
|
),
|
||||||
"langchain/langgraph-api",
|
"langchain/langgraph-api",
|
||||||
)
|
)
|
||||||
@@ -190,6 +208,9 @@ FROM langchain/langgraph-api:3.11
|
|||||||
COPY --from=__outer_requirements.txt requirements.txt /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 pip install --no-cache-dir -c /api/constraints.txt -r /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
RUN PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -r /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||||
# -- End of local requirements install --
|
# -- 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 --
|
# -- Adding non-package dependency unit_tests --
|
||||||
ADD . /deps/__outer_unit_tests/unit_tests
|
ADD . /deps/__outer_unit_tests/unit_tests
|
||||||
RUN set -ex && \\
|
RUN set -ex && \\
|
||||||
@@ -215,6 +236,7 @@ RUN set -ex && \\
|
|||||||
# -- Installing all local dependencies --
|
# -- Installing all local dependencies --
|
||||||
RUN PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
|
RUN PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||||
# -- End of local dependencies install --
|
# -- 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"}'
|
||||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||||
"""
|
"""
|
||||||
@@ -223,7 +245,8 @@ WORKDIR /deps/__outer_unit_tests/unit_tests\
|
|||||||
assert additional_contexts == {
|
assert additional_contexts == {
|
||||||
"__outer_graphs_reqs_a": str(
|
"__outer_graphs_reqs_a": str(
|
||||||
(pathlib.Path(__file__).parent / "../../examples/graphs_reqs_a").resolve()
|
(pathlib.Path(__file__).parent / "../../examples/graphs_reqs_a").resolve()
|
||||||
)
|
),
|
||||||
|
"examples": str((pathlib.Path(__file__).parent / "../../examples").resolve()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user