Skip to content

Commit 182f090

Browse files
authored
Merge branch 'release-1.11.0' into fix/admin-auth-pages-a11y
2 parents f6026d8 + d796ea6 commit 182f090

21 files changed

Lines changed: 2051 additions & 152 deletions

.secrets.baseline

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7034,23 +7034,23 @@
70347034
"filename": "src/lfx/tests/unit/inputs/test_max_tokens_propagation.py",
70357035
"hashed_secret": "665b1e3851eefefa3fb878654292f16597d25155",
70367036
"is_verified": false,
7037-
"line_number": 121,
7037+
"line_number": 122,
70387038
"is_secret": false
70397039
},
70407040
{
70417041
"type": "Secret Keyword",
70427042
"filename": "src/lfx/tests/unit/inputs/test_max_tokens_propagation.py",
70437043
"hashed_secret": "e9b4dce312643ee0e1bd0561a50d9d5a7e5a2be1",
70447044
"is_verified": false,
7045-
"line_number": 154,
7045+
"line_number": 155,
70467046
"is_secret": false
70477047
},
70487048
{
70497049
"type": "Secret Keyword",
70507050
"filename": "src/lfx/tests/unit/inputs/test_max_tokens_propagation.py",
70517051
"hashed_secret": "3f2df46921dd8e2c36e2ce85238705ac0774c74a",
70527052
"is_verified": false,
7053-
"line_number": 234,
7053+
"line_number": 235,
70547054
"is_secret": false
70557055
}
70567056
],
@@ -7222,5 +7222,5 @@
72227222
}
72237223
]
72247224
},
7225-
"generated_at": "2026-07-13T20:51:12Z"
7225+
"generated_at": "2026-07-13T20:58:07Z"
72267226
}

src/lfx/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,13 @@ uv run lfx serve my-flow.json --upgrade-flow=safe
435435
| `--no-env-fallback` / `--env-fallback` | Disable process-environment fallback for credential resolution. Use with per-request `LANGFLOW_REQUEST_VARIABLES`. Default: `--env-fallback`. |
436436
| `--port`, `-p` | Port to bind the server to. Default: `8000`. |
437437
| `--stdin` | Read JSON flow content from `stdin`. Example: `cat flow.json | uv run lfx serve --stdin`. |
438+
| `--max-requests` | Recycle each worker after N requests to bound memory (gunicorn, Unix, `--workers > 1`). Default: every ~1000 (10% jitter); `0` disables; `1` = every request. Worker hygiene, **not** per-request isolation. Not applied on Windows. |
439+
| `--reset-environ` / `--no-reset-environ` | Snapshot/restore `os.environ` around each flow run so one request's env mutations or request-scoped credentials can't leak into the next on a warm worker. **A strict per-request isolation mechanism** (any worker, any platform). Default: `--no-reset-environ` (off). |
440+
| `--use-sync-workers` / `--use-async-workers` | Multi-worker only (`--workers > 1`, Unix). gunicorn's blocking `sync` worker serves one request at a time per worker, so the kernel routes each to an idle worker. With `--max-requests 1` it is **the other strict per-request isolation mechanism** (each request gets a fresh process). Uses the `a2wsgi` bridge, which ships with lfx on Unix. Default: `--use-async-workers`. |
441+
| `--timeout` | Worker timeout in seconds (gunicorn, Unix, `--workers > 1`): a worker that doesn't finish a request in this many seconds is killed and restarted. Raise it for long flows, especially with `--use-sync-workers`. Default: `120`. No effect on Windows. |
438442
| `--upgrade-flow` | Compatibility mode: `check` reports issues and fails, `safe` applies safe upgrades in memory. |
439443
| `--verbose`, `-v` | Show diagnostic output and execution details. |
440-
| `--workers`, `-w` | Number of uvicorn worker processes. Default: `1`. Use with `--flow-dir` for multi-worker flow sharing. |
444+
| `--workers`, `-w` | Number of worker processes. Use with `--flow-dir` for multi-worker flow sharing. Default: `1`. |
441445

442446
## Run the simple agent flow with `lfx run`
443447

src/lfx/pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ dependencies = [
1818
"pydantic>=2.0.0,<3.0.0",
1919
"pillow>=11.1.0,<13.0.0",
2020
"fastapi>=0.135.0,<1.0.0",
21-
"uvicorn>=0.34.3,<1.0.0",
21+
# [standard] adds uvloop + httptools (fast loop + HTTP parser) for the `lfx serve` worker path.
22+
"uvicorn[standard]>=0.34.3,<1.0.0",
23+
# gunicorn powers `lfx serve --workers N` on Unix; Windows falls back to uvicorn and rejects gunicorn-only flags.
24+
"gunicorn>=22.0; sys_platform != 'win32'",
25+
# a2wsgi bridges the ASGI app onto gunicorn's sync worker for `lfx serve --use-sync-workers` (Unix).
26+
"a2wsgi>=1.10.0; sys_platform != 'win32'",
2227
"typer>=0.16.0,<1.0.0",
2328
"platformdirs>=4.3.8,<5.0.0",
2429
"aiofiles>=24.1.0,<25.0.0",

src/lfx/src/lfx/_assets/component_index.json

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

src/lfx/src/lfx/base/models/unified_models/credentials.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,11 @@ async def _get_all_variables():
271271
var_key = var_info.get("variable_key")
272272
if not var_key or db_values.get(var_key):
273273
continue
274+
# Honor the request's no-env-fallback contract: a served flow under
275+
# no_env_fallback must stay isolated from process-wide credentials even on
276+
# this post-DB-miss rotation fallback.
277+
if is_env_fallback_disabled():
278+
continue
274279
env_value = _env_value_for(var_key)
275280
if env_value:
276281
db_values[var_key] = env_value

src/lfx/src/lfx/cli/_running_commands.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import typer
44

5+
from lfx.cli import _serve_help
56
from lfx.upgrade.cli_gate import UpgradeFlowMode
67

78

@@ -126,7 +127,8 @@ def serve_command_wrapper(
126127
1,
127128
"--workers",
128129
"-w",
129-
help="Number of uvicorn worker processes. Use with --flow-dir for multi-worker flow sharing.",
130+
help="Number of worker processes (gunicorn on Unix, uvicorn on Windows). "
131+
"Use with --flow-dir for multi-worker flow sharing.",
130132
),
131133
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show diagnostic output and execution details"),
132134
env_file: str | None = typer.Option(
@@ -147,12 +149,17 @@ def serve_command_wrapper(
147149
flow_dir: str | None = typer.Option(
148150
None,
149151
"--flow-dir",
150-
help=(
151-
"Directory for filesystem-backed flow storage. "
152-
"All uvicorn workers sharing this path will serve the same flows. "
153-
"Use /tmp/lfx-flows for single-pod sharing or a PVC mount for cross-pod. "
154-
"Defaults to in-memory only when omitted."
155-
),
152+
help=_serve_help.FLOW_DIR,
153+
),
154+
max_requests: int | None = typer.Option(
155+
None,
156+
"--max-requests",
157+
help=_serve_help.MAX_REQUESTS,
158+
),
159+
timeout: int | None = typer.Option(
160+
None,
161+
"--timeout",
162+
help=_serve_help.TIMEOUT,
156163
),
157164
*,
158165
stdin: bool = typer.Option(
@@ -177,11 +184,17 @@ def serve_command_wrapper(
177184
no_env_fallback: bool = typer.Option(
178185
False,
179186
"--no-env-fallback/--env-fallback",
180-
help=(
181-
"Disable os.environ fallback for credential variables. "
182-
"Variables not supplied via global_vars on each request resolve to None "
183-
"instead of reading from the process environment."
184-
),
187+
help=_serve_help.NO_ENV_FALLBACK,
188+
),
189+
reset_environ: bool = typer.Option(
190+
False,
191+
"--reset-environ/--no-reset-environ",
192+
help=_serve_help.RESET_ENVIRON,
193+
),
194+
sync_workers: bool = typer.Option(
195+
False,
196+
"--use-sync-workers/--use-async-workers",
197+
help=_serve_help.SYNC_WORKERS,
185198
),
186199
identity_mode: str = typer.Option(
187200
"off",
@@ -251,6 +264,10 @@ def serve_command_wrapper(
251264
check_variables=check_variables,
252265
upgrade_flow=upgrade_flow,
253266
no_env_fallback=no_env_fallback,
267+
max_requests=max_requests,
268+
reset_environ=reset_environ,
269+
sync_workers=sync_workers,
270+
timeout=timeout,
254271
identity_mode=identity_mode,
255272
identity_jwt_issuer=identity_jwt_issuer,
256273
identity_jwt_audience=identity_jwt_audience,

src/lfx/src/lfx/cli/_serve_help.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Shared ``--help`` text for the multi-line ``lfx serve`` options.
2+
3+
These options are declared twice — in the lazy CLI wrapper (``_running_commands``) and in
4+
the implementation ``commands.serve_command`` (which is also registered as a standalone
5+
typer command in tests). Defining the substantial help text once here keeps the two from
6+
drifting. Trivial one-line help (host, port, ...) stays inline at each call site.
7+
8+
Kept dependency-free so importing it never defeats the wrapper's lazy import of the heavy
9+
``commands`` module.
10+
"""
11+
12+
FLOW_DIR = (
13+
"Directory for filesystem-backed flow storage. "
14+
"All uvicorn workers sharing this path will serve the same flows. "
15+
"Use /tmp/lfx-flows for single-pod sharing or a PVC mount for cross-pod. "
16+
"Defaults to in-memory only when omitted."
17+
)
18+
19+
MAX_REQUESTS = (
20+
"Recycle each worker after N requests to bound memory (gunicorn, Unix, --workers > 1). "
21+
"Default: recycle every ~1000 (10% jitter); 0 disables; 1 = every request. Not applied "
22+
"on Windows. Worker hygiene, NOT per-request isolation — for that use --reset-environ, "
23+
"or --use-sync-workers together with --max-requests 1 (a sync worker alone only prevents "
24+
"in-worker overlap; a warm process still carries os.environ mutations to later requests)."
25+
)
26+
27+
TIMEOUT = (
28+
"Worker timeout in seconds (gunicorn, Unix, --workers > 1): a worker that does not "
29+
"complete a request within this many seconds is killed and restarted. Raise it for "
30+
"long-running flows, especially with --use-sync-workers (a blocking sync worker cannot "
31+
"heartbeat mid-request). Default: 120. No effect on Windows (uvicorn fallback)."
32+
)
33+
34+
NO_ENV_FALLBACK = (
35+
"Disable os.environ fallback for credential variables. "
36+
"Variables not supplied via global_vars on each request resolve to None "
37+
"instead of reading from the process environment."
38+
)
39+
40+
RESET_ENVIRON = (
41+
"Snapshot os.environ before each flow run and restore it afterward, so a "
42+
"flow's environment mutations (or request-scoped credentials) cannot leak "
43+
"into the next request served by the same warm worker. Off by default."
44+
)
45+
46+
SYNC_WORKERS = (
47+
"Use gunicorn's blocking 'sync' worker (Unix, --workers > 1) so the kernel "
48+
"routes each request to an idle worker instead of queueing it behind an "
49+
"in-flight request on a busy async worker. Uses the 'a2wsgi' bridge bundled "
50+
"with lfx on Unix. Off by default (async worker)."
51+
)

0 commit comments

Comments
 (0)