mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
Merge branch 'main' into v1
This commit is contained in:
@@ -1,15 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
version: 2.1
|
||||
contact_links:
|
||||
- name: 🤔 Question or Problem
|
||||
about: Ask a question or ask about a problem in GitHub Discussions.
|
||||
url: https://github.com/langchain-ai/langgraph/discussions/categories/q-a
|
||||
- name: Feature Request
|
||||
url: https://github.com/langchain-ai/langgraph/discussions/categories/ideas
|
||||
about: Suggest a feature or an idea
|
||||
- name: Show and tell
|
||||
about: Show what you built with LangChain
|
||||
url: https://github.com/langchain-ai/langgraph/discussions/categories/show-and-tell
|
||||
- name: LangChain Forum
|
||||
url: https://forum.langchain.com/
|
||||
about: General community discussions and support
|
||||
|
||||
@@ -8,4 +8,4 @@ This section contains additional resources for LangGraph.
|
||||
- [FAQ](../concepts/faq.md): A collection of frequently asked questions about LangGraph.
|
||||
- [llms.txt](../llms-txt-overview.md): A list of documentation files in the `llms.txt` format that allow LLMs and agents to access our documentation.
|
||||
- [LangChain Forum](https://forum.langchain.com/): A place to ask questions and get help from other LangGraph users.
|
||||
- [Troubleshooting](../troubleshooting/errors/index.md.md): A collection of troubleshooting guides for common issues.
|
||||
- [Troubleshooting](../troubleshooting/errors/index.md): A collection of troubleshooting guides for common issues.
|
||||
@@ -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> |
|
||||
|
||||
@@ -10,6 +10,10 @@ This environment variable should be set to `True` if the implementation of a gra
|
||||
|
||||
Defaults to `False`.
|
||||
|
||||
## `BG_JOB_SHUTDOWN_GRACE_PERIOD_SECS`
|
||||
|
||||
Specifies, in seconds, how long the server will wait for background jobs to finish after the queue receives a shutdown signal. After this period, the server will force termination. Defaults to `180` seconds. Set this to ensure jobs have enough time to complete cleanly during shutdown. Added in `langgraph-api==0.2.16`.
|
||||
|
||||
## `BG_JOB_TIMEOUT_SECS`
|
||||
|
||||
The timeout of a background run can be increased. However, the infrastructure for a Cloud SaaS deployment enforces a 1 hour timeout limit for API requests. This means the connection between client and server will timeout after 1 hour. This is not configurable.
|
||||
@@ -18,10 +22,6 @@ A background run can execute for longer than 1 hour, but a client must reconnect
|
||||
|
||||
Defaults to `3600`.
|
||||
|
||||
## `BG_JOB_SHUTDOWN_GRACE_PERIOD_SECS`
|
||||
|
||||
Specifies, in seconds, how long the server will wait for background jobs to finish after the queue receives a shutdown signal. After this period, the server will force termination. Defaults to `3600` seconds. Set this to ensure jobs have enough time to complete cleanly during shutdown. Added in `langgraph-api==0.2.16`.
|
||||
|
||||
## `DD_API_KEY`
|
||||
|
||||
Specify `DD_API_KEY` (your [Datadog API Key](https://docs.datadoghq.com/account_management/api-app-keys/)) to automatically enable Datadog tracing for the deployment. Specify other [`DD_*` environment variables](https://ddtrace.readthedocs.io/en/stable/configuration.html) to configure the tracing instrumentation.
|
||||
@@ -40,6 +40,14 @@ Type of authentication for the LangGraph Server deployment. Valid values: `langs
|
||||
|
||||
For deployments to LangGraph Platform, this environment variable is set automatically. For local development or deployments where authentication is handled externally (e.g. self-hosted), set this environment variable to `noop`.
|
||||
|
||||
## `LANGGRAPH_POSTGRES_POOL_MAX_SIZE`
|
||||
|
||||
Beginning with langgraph-api version `0.2.12`, the maximum size of the Postgres connection pool (per replica) can be controlled using the `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` environment variable. By setting this variable, you can determine the upper bound on the number of simultaneous connections the server will establish with the Postgres database.
|
||||
|
||||
For example, if a deployment is scaled up to 10 replicas and `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` is configured to `150`, then up to `1500` connections to Postgres can be established. This is particularly useful for deployments where database resources are limited (or more available) or where you need to tune connection behavior for performance or scaling reasons.
|
||||
|
||||
Defaults to `150` connections.
|
||||
|
||||
## `LANGSMITH_RUNS_ENDPOINTS`
|
||||
|
||||
For deployments with [self-hosted LangSmith](https://docs.smith.langchain.com/self_hosting) only.
|
||||
@@ -54,6 +62,10 @@ Set `LANGSMITH_TRACING` to `false` to disable tracing to LangSmith.
|
||||
|
||||
Defaults to `true`.
|
||||
|
||||
## `LOG_COLOR`
|
||||
|
||||
This is mainly relevant in the context of using the dev server via the `langgraph dev` command. Set `LOG_COLOR` to `true` to enable ANSI-colored console output when using the default console renderer. Disabling color output by setting this variable to `false` produces monochrome logs. Defaults to `true`.
|
||||
|
||||
## `LOG_LEVEL`
|
||||
|
||||
Configure [log level](https://docs.python.org/3/library/logging.html#logging-levels). Defaults to `INFO`.
|
||||
@@ -62,9 +74,14 @@ Configure [log level](https://docs.python.org/3/library/logging.html#logging-lev
|
||||
|
||||
Set `LOG_JSON` to `true` to render all log messages as JSON objects using the configured `JSONRenderer`. This produces structured logs that can be easily parsed or ingested by log management systems. Defaults to `false`.
|
||||
|
||||
## `LOG_COLOR`
|
||||
## `MOUNT_PREFIX`
|
||||
|
||||
This is mainly relevant in the context of using the dev server via the `langgraph dev` command. Set `LOG_COLOR` to `true` to enable ANSI-colored console output when using the default console renderer. Disabling color output by setting this variable to `false` produces monochrome logs. Defaults to `true`.
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
The `MOUNT_PREFIX` environment variable is only allowed in Self-Hosted Deployment models, LangGraph Platform SaaS will not allow this environment variable.
|
||||
|
||||
Set `MOUNT_PREFIX` to serve the LangGraph Server under a specific path prefix. This is useful for deployments where the server is behind a reverse proxy or load balancer that requires a specific path prefix.
|
||||
|
||||
For example, if the server is to be served under `https://example.com/langgraph`, set `MOUNT_PREFIX` to `/langgraph`.
|
||||
|
||||
## `N_JOBS_PER_WORKER`
|
||||
|
||||
@@ -94,16 +111,14 @@ Database Connectivity:
|
||||
|
||||
- The custom Postgres instance must be accessible by the LangGraph Server. The user is responsible for ensuring connectivity.
|
||||
|
||||
## `LANGGRAPH_POSTGRES_POOL_MAX_SIZE`
|
||||
## `REDIS_CLUSTER`
|
||||
|
||||
Beginning with langgraph-api version `0.2.12`, the maximum size of the Postgres connection pool can be controlled using the `LANGGRAPH_POSTGRES_POOL_MAX_SIZE` environment variable. By setting this variable, you can determine the upper bound on the number of simultaneous connections the server will establish with the Postgres database. This is particularly useful for deployments where database resources are limited (or more available) or where you need to tune connection behavior for performance or scaling reasons. If not specified, the pool size defaults to 150 connections.
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
Redis Cluster mode is only available in Self-Hosted Deployment models, LangGraph Platform SaaS will provision a redis instance for you by default.
|
||||
|
||||
## `REDIS_URI_CUSTOM`
|
||||
Set `REDIS_CLUSTER` to `True` to enable Redis Cluster mode. When enabled, the system will connect to Redis using cluster mode. This is useful when connecting to a Redis Cluster deployment.
|
||||
|
||||
!!! info "Only for Self-Hosted Data Plane and Self-Hosted Control Plane"
|
||||
Custom Redis instances are only available for [Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
Specify `REDIS_URI_CUSTOM` to use a custom Redis instance. The value of `REDIS_URI_CUSTOM` must be a valid [Redis connection URI](https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url).
|
||||
Defaults to `False`.
|
||||
|
||||
## `REDIS_KEY_PREFIX`
|
||||
|
||||
@@ -114,20 +129,19 @@ Specify a prefix for Redis keys. This allows multiple LangGraph Server instances
|
||||
|
||||
Defaults to `''`.
|
||||
|
||||
## `REDIS_CLUSTER`
|
||||
## `REDIS_URI_CUSTOM`
|
||||
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
Redis Cluster mode is only available in Self-Hosted Deployment models, LangGraph Platform SaaS will provision a redis instance for you by default.
|
||||
!!! info "Only for Self-Hosted Data Plane and Self-Hosted Control Plane"
|
||||
Custom Redis instances are only available for [Self-Hosted Data Plane](../../concepts/langgraph_self_hosted_data_plane.md) and [Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployments.
|
||||
|
||||
Set `REDIS_CLUSTER` to `True` to enable Redis Cluster mode. When enabled, the system will connect to Redis using cluster mode. This is useful when connecting to a Redis Cluster deployment.
|
||||
Specify `REDIS_URI_CUSTOM` to use a custom Redis instance. The value of `REDIS_URI_CUSTOM` must be a valid [Redis connection URI](https://redis-py.readthedocs.io/en/stable/connections.html#redis.Redis.from_url).
|
||||
|
||||
Defaults to `False`.
|
||||
## `RESUMABLE_STREAM_TTL_SECONDS`
|
||||
|
||||
## `MOUNT_PREFIX`
|
||||
Time-to-live in seconds for resumable stream data in Redis.
|
||||
|
||||
!!! info "Only Allowed in Self-Hosted Deployments"
|
||||
The `MOUNT_PREFIX` environment variable is only allowed in Self-Hosted Deployment models, LangGraph Platform SaaS will not allow this environment variable.
|
||||
When a run is created and the output is streamed, the stream can be configured to be resumable (e.g. `stream_resumable=True`). If a stream is resumable, output from the stream is temporarily stored in Redis. The TTL for this data can be configured by setting `RESUMABLE_STREAM_TTL_SECONDS`.
|
||||
|
||||
Set `MOUNT_PREFIX` to serve the LangGraph Server under a specific path prefix. This is useful for deployments where the server is behind a reverse proxy or load balancer that requires a specific path prefix.
|
||||
See the [Python](https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#langgraph_sdk.client.RunsClient.stream) and [JS/TS](https://langchain-ai.github.io/langgraphjs/reference/classes/sdk_client.RunsClient.html#stream) SDKs for more details on how to implement resumable streams.
|
||||
|
||||
For example, if the server is to be served under `https://example.com/langgraph`, set `MOUNT_PREFIX` to `/langgraph`.
|
||||
Defaults to `120` seconds.
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* This file is used to override the navigation title for the LangGraph documentation.
|
||||
* It is used to change the title of the first and second items in the navigation menu.
|
||||
* The first item is the Guides page, and the second item is the Reference page.
|
||||
*/
|
||||
|
||||
.md-nav--primary > .md-nav__list > .md-nav__item:nth-child(1) > .md-nav__link .md-ellipsis {
|
||||
visibility: hidden !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.md-nav--primary > .md-nav__list > .md-nav__item:nth-child(1) > .md-nav__link .md-ellipsis::after {
|
||||
content: "Home";
|
||||
visibility: visible;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.md-nav--primary > .md-nav__list > .md-nav__item:nth-child(2) > .md-nav__link .md-ellipsis {
|
||||
visibility: hidden !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.md-nav--primary > .md-nav__list > .md-nav__item:nth-child(2) > .md-nav__link .md-ellipsis::after {
|
||||
content: "Home";
|
||||
visibility: visible;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
@@ -381,7 +381,6 @@ validation:
|
||||
copyright: >
|
||||
Copyright © 2025 LangChain, Inc | <a href="#__consent">Consent Preferences</a>
|
||||
extra_css:
|
||||
- stylesheets/navigation_title_ovverides.css
|
||||
- stylesheets/version_admonitions.css
|
||||
- stylesheets/logos.css
|
||||
- stylesheets/sticky_navigation.css
|
||||
|
||||
@@ -17,7 +17,7 @@ model_oai = ChatOpenAI(temperature=0)
|
||||
model_anth = model_anth.bind_tools(tools)
|
||||
model_oai = model_oai.bind_tools(tools)
|
||||
|
||||
prompt = open("prompt.txt").read()
|
||||
prompt = open(Path(__file__).parent.parent / "prompt.txt").read()
|
||||
subprompt = open(Path(__file__).parent / "subprompt.txt").read()
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ model_oai = ChatOpenAI(temperature=0)
|
||||
model_anth = model_anth.bind_tools(tools)
|
||||
model_oai = model_oai.bind_tools(tools)
|
||||
|
||||
prompt = open("prompt.txt").read()
|
||||
prompt = open(Path(__file__).parent.parent / "prompt.txt").read()
|
||||
subprompt = open(Path(__file__).parent / "subprompt.txt").read()
|
||||
|
||||
|
||||
|
||||
@@ -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,10 @@ class HttpConfig(TypedDict, total=False):
|
||||
Default is False.
|
||||
"""
|
||||
disable_meta: bool
|
||||
"""Optional. If True, all meta endpoints (/ok, /info, /metrics, /docs) are disabled.
|
||||
"""Optional. Remove meta endpoints.
|
||||
|
||||
Set to True to disable the following endpoints: /openapi.json, /info, /metrics, /docs.
|
||||
This will also make the /ok endpoint skip any DB or other checks, always returning {"ok": True}.
|
||||
|
||||
Default is False.
|
||||
"""
|
||||
@@ -471,21 +474,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 +606,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 +679,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 +1188,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 +1216,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 +1376,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. Remove meta endpoints.\n\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. Remove meta endpoints.\n\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'" },
|
||||
|
||||
@@ -382,7 +382,7 @@ def interrupt(value: Any) -> Any:
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.constants import START
|
||||
from langgraph.graph import StateGraph
|
||||
from langgraph.types import interrupt
|
||||
from langgraph.types import interrupt, Command
|
||||
|
||||
|
||||
class State(TypedDict):
|
||||
|
||||
Reference in New Issue
Block a user