mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-19 11:06:18 +00:00
fix(nginx): allow model-bound /api/ and /api/skills requests past 60 seconds (#5524)
* fix(nginx): allow model-bound /api/ and /api/skills requests past 60 seconds Two locations were left on nginx's 60s default while the routes behind them wait on Gateway. The /api/ catch-all carries the stateless POST /api/runs/wait, which blocks on wait_for_run_completion and cancels its run when the client disconnects, so a caller waiting on a longer run got a 504 and lost the run; it also carries POST /api/input-polish, which waits for a one-shot model call. /api/skills carries POST /api/skills/install, which runs one LLM security scan per file in the archive, and the custom-skill edit and rollback routes, which run one more each. None of them sets an application-level timeout, and only the sibling /api/skills/install/upload endpoint had been given the longer timeout, so the same archive failed at 60s depending on which endpoint installed it. Allow 600s on both locations, matching /api/langgraph/ and /api/threads, in all three copies of the nginx config. Each directive is pinned by its own test that parses the active directive per config. * docs(changelog): link the nginx /api/ and /api/skills timeout entry to #5524
This commit is contained in:
parent
6d725f1ccb
commit
9f79ddf9b6
14
CHANGELOG.md
14
CHANGELOG.md
@ -941,6 +941,19 @@ This release closes that milestone with **765 merged pull requests**.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **nginx:** Extend the 600-second read timeout to the two remaining locations
|
||||
whose routes wait on the Gateway, both left on nginx's 60-second default by
|
||||
the thread-route fix. Behind the `/api/` catch-all, the stateless
|
||||
`POST /api/runs/wait` blocks on the same run-completion wait and cancels its
|
||||
run when the client disconnects, so an API consumer waiting on a run longer
|
||||
than 60 seconds got a 504 *and* a cancelled run, and the composer's
|
||||
`POST /api/input-polish` waits for a one-shot model call. Behind
|
||||
`/api/skills`, installing a `.skill` archive runs one LLM security scan per
|
||||
file in it, and a custom-skill edit or rollback runs one more; none of them
|
||||
sets its own timeout, and only the sibling `/api/skills/install/upload`
|
||||
endpoint had been given the longer timeout, so the same install through
|
||||
`POST /api/skills/install` failed at 60 seconds. Applied to the Docker,
|
||||
local, and Helm configs. ([#5524])
|
||||
- **nginx:** Stop thread routes that wait on a model call from failing at 60
|
||||
seconds. The browser calls `/api/threads/*` directly, and that location had
|
||||
no `proxy_read_timeout`, so nginx's 60-second default applied while
|
||||
@ -4255,3 +4268,4 @@ with **180 merged pull requests** since the first 2.0 milestone tag.
|
||||
[#5501]: https://github.com/bytedance/deer-flow/pull/5501
|
||||
[#5504]: https://github.com/bytedance/deer-flow/pull/5504
|
||||
[#5505]: https://github.com/bytedance/deer-flow/pull/5505
|
||||
[#5524]: https://github.com/bytedance/deer-flow/pull/5524
|
||||
|
||||
@ -727,6 +727,14 @@
|
||||
|
||||
### 修复
|
||||
|
||||
- **nginx:** 把 600 秒读取超时扩展到其余两个会等待 Gateway 的 location,它们在线程路由的修复
|
||||
之后仍沿用 nginx 默认的 60 秒。`/api/` 兜底 location 之后:无状态的 `POST /api/runs/wait`
|
||||
阻塞在同一套运行完成等待上,并在客户端断开时取消该运行,因此等待超过 60 秒的 API 调用方会
|
||||
同时收到 504 **并且**运行被取消;输入框的 `POST /api/input-polish` 则等待一次性模型调用。
|
||||
`/api/skills` 之后:安装 `.skill` 压缩包会对其中每个文件各做一次 LLM 安全扫描,自定义技能的
|
||||
编辑与回滚各再做一次,它们都没有自己的超时;此前只有同级的 `/api/skills/install/upload`
|
||||
拿到了更长的超时,因此同样的安装经由 `POST /api/skills/install` 会在 60 秒失败。
|
||||
Docker、本地开发与 Helm 配置均已应用。([#5524])
|
||||
- **nginx:** 需要等待模型调用的线程路由不再在 60 秒时失败。浏览器直接调用 `/api/threads/*`,
|
||||
而该 location 没有设置 `proxy_read_timeout`,因此沿用 nginx 默认的 60 秒,而 `/api/langgraph/`
|
||||
允许 600 秒。较慢的 `/compact` 会返回 504,但 Gateway 仍会继续执行并保存压缩结果,于是 UI
|
||||
@ -3483,3 +3491,4 @@ DeerFlow 2.0 是围绕"超级智能体"框架的彻底重写,核心包含子
|
||||
[#5501]: https://github.com/bytedance/deer-flow/pull/5501
|
||||
[#5504]: https://github.com/bytedance/deer-flow/pull/5504
|
||||
[#5505]: https://github.com/bytedance/deer-flow/pull/5505
|
||||
[#5524]: https://github.com/bytedance/deer-flow/pull/5524
|
||||
|
||||
@ -159,6 +159,22 @@ def test_threads_route_outlasts_blocking_gateway_calls(path):
|
||||
assert timeout_seconds >= _MIN_BLOCKING_READ_TIMEOUT_SECONDS, f"{path}: the generic /api/threads location allows {timeout_seconds}s, expected at least {_MIN_BLOCKING_READ_TIMEOUT_SECONDS}s like /api/langgraph/"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", NGINX_CONFIGS)
|
||||
def test_api_catchall_outlasts_blocking_gateway_calls(path):
|
||||
"""The routes that wait on Gateway are not all under ``/api/threads``.
|
||||
The stateless ``POST /api/runs/wait`` blocks on the same
|
||||
``wait_for_run_completion`` and cancels its run when the client
|
||||
disconnects, and the composer's ``POST /api/input-polish`` waits for a
|
||||
one-shot model call. Both fall through to this catch-all, so it needs the
|
||||
same read timeout as the thread routes."""
|
||||
content = _read(path)
|
||||
block = _extract_location_block(content, "/api/")
|
||||
|
||||
timeout_seconds = _parse_read_timeout_seconds(block)
|
||||
|
||||
assert timeout_seconds >= _MIN_BLOCKING_READ_TIMEOUT_SECONDS, f"{path}: the /api/ catch-all allows {timeout_seconds}s, expected at least {_MIN_BLOCKING_READ_TIMEOUT_SECONDS}s like /api/langgraph/ and /api/threads"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", NGINX_CONFIGS)
|
||||
def test_skills_upload_route_allows_archive_plus_multipart_framing(path):
|
||||
"""The upload route must stream archives and allow slow validation."""
|
||||
@ -170,6 +186,21 @@ def test_skills_upload_route_allows_archive_plus_multipart_framing(path):
|
||||
assert "proxy_read_timeout 600s;" in block
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", NGINX_CONFIGS)
|
||||
def test_skills_prefix_outlasts_the_llm_security_scan(path):
|
||||
"""Installing a ``.skill`` archive runs one LLM security scan per file in
|
||||
it, and editing or rolling back a custom skill runs one more; none of them
|
||||
sets an application-level timeout. The upload endpoint above already gets
|
||||
600s, but ``POST /api/skills/install`` and the custom-skill writes land
|
||||
here, so this prefix needs it too."""
|
||||
content = _read(path)
|
||||
block = _extract_location_block(content, "/api/skills")
|
||||
|
||||
timeout_seconds = _parse_read_timeout_seconds(block)
|
||||
|
||||
assert timeout_seconds >= _MIN_BLOCKING_READ_TIMEOUT_SECONDS, f"{path}: /api/skills allows {timeout_seconds}s, expected at least {_MIN_BLOCKING_READ_TIMEOUT_SECONDS}s like its own /api/skills/install/upload endpoint"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", NGINX_CONFIGS)
|
||||
def test_skills_prefix_keeps_default_request_body_policy(path):
|
||||
"""Large bodies must be allowed only on the admin upload endpoint."""
|
||||
|
||||
@ -121,12 +121,14 @@ secrets:
|
||||
|
||||
The default ingress annotations permit a 100 MiB local `.skill` archive plus
|
||||
multipart framing, stream request bodies without ingress buffering, and allow
|
||||
up to 600 seconds for a response, which skill validation and thread requests
|
||||
that wait on a model call (such as `/compact`) both need. If you replace
|
||||
`ingress.annotations`, preserve equivalent size, streaming, and
|
||||
response-timeout settings for your ingress controller, or local skill uploads
|
||||
may fail before DeerFlow completes the installation and those thread requests
|
||||
may time out while Gateway is still working.
|
||||
up to 600 seconds for a response, which the API requests that wait on a model
|
||||
call or a whole run all need — skill install and custom-skill edits (each file
|
||||
is scanned by an LLM), `/api/threads/{id}/compact`, `/api/input-polish`, and
|
||||
`/api/runs/wait`. If you replace `ingress.annotations`, preserve equivalent
|
||||
size, streaming, and response-timeout settings for your ingress controller, or
|
||||
local skill uploads may fail before DeerFlow completes the installation and
|
||||
those requests may time out while Gateway is still working — for
|
||||
`/api/runs/wait` the disconnect also cancels the run.
|
||||
|
||||
Provide your model config under `config` (keep secrets as `$VAR` references —
|
||||
they resolve from the `secrets` map):
|
||||
|
||||
@ -133,6 +133,10 @@ data:
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||||
# Installing a .skill archive runs one LLM security scan per file, and a
|
||||
# custom-skill edit or rollback runs one more; none sets its own timeout.
|
||||
# The upload endpoint above already allows 600s for the same work.
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
location /api/agents {
|
||||
@ -227,6 +231,10 @@ data:
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||||
# /api/runs/wait blocks on a whole run and /api/input-polish on a model
|
||||
# call. nginx's 60s default would 504 them mid-work, and the waited run is
|
||||
# cancelled on the disconnect.
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# Everything else -> frontend (with WebSocket upgrade for HMR/sockets).
|
||||
|
||||
@ -167,6 +167,11 @@ http {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $forwarded_proto;
|
||||
|
||||
# Installing a .skill archive runs one LLM security scan per file, and a
|
||||
# custom-skill edit or rollback runs one more; none sets its own timeout.
|
||||
# The upload endpoint above already allows 600s for the same work.
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# Custom API: Agents endpoint
|
||||
@ -301,6 +306,11 @@ http {
|
||||
|
||||
# Disable buffering to avoid permission errors when nginx
|
||||
# runs as a non-root user (e.g. local development).
|
||||
|
||||
# /api/runs/wait blocks on a whole run and /api/input-polish on a model
|
||||
# call. nginx's 60s default would 504 them mid-work, and the waited run is
|
||||
# cancelled on the disconnect.
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# All other requests go to frontend
|
||||
|
||||
@ -168,6 +168,11 @@ http {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# Installing a .skill archive runs one LLM security scan per file, and a
|
||||
# custom-skill edit or rollback runs one more; none sets its own timeout.
|
||||
# The upload endpoint above already allows 600s for the same work.
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# Custom API: Agents endpoint
|
||||
@ -315,6 +320,11 @@ http {
|
||||
# runs as a non-root user (e.g. local development).
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# /api/runs/wait blocks on a whole run and /api/input-polish on a model
|
||||
# call. nginx's 60s default would 504 them mid-work, and the waited run is
|
||||
# cancelled on the disconnect.
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# All other requests go to frontend
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user