From 67ad6e232f9c9b007189585299ed5b08e580efb7 Mon Sep 17 00:00:00 2001 From: zgenu <2023020285@bistu.edu.cn> Date: Mon, 8 Jun 2026 22:54:23 +0800 Subject: [PATCH] fix(dev): exclude runtime state from gateway reload (#3426) --- backend/tests/test_dev_entrypoint.py | 13 +++++++++++++ backend/tests/test_gateway_runtime_cleanup.py | 13 +++++++++++++ docker/dev-entrypoint.sh | 14 +++++++++++++- scripts/serve.sh | 19 ++++++++++++++++++- 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/backend/tests/test_dev_entrypoint.py b/backend/tests/test_dev_entrypoint.py index b587e0dfe..5ca8f3443 100644 --- a/backend/tests/test_dev_entrypoint.py +++ b/backend/tests/test_dev_entrypoint.py @@ -40,6 +40,19 @@ def test_entrypoint_script_exists_and_is_posix_sh(): assert proc.returncode == 0, proc.stderr +def test_entrypoint_excludes_runtime_state_from_uvicorn_reload(): + content = ENTRYPOINT.read_text(encoding="utf-8") + + assert ': "${DEER_FLOW_HOME:=/app/backend/.deer-flow}"' in content + assert 'mkdir -p "$DEER_FLOW_HOME" /app/backend/.deer-flow' in content + assert "--reload-include='*.yaml .env'" not in content + assert "--reload-include='*.yaml'" in content + assert "--reload-include='.env'" in content + assert "--reload-exclude=/app/backend/sandbox" in content + assert '--reload-exclude="$DEER_FLOW_HOME"' in content + assert "--reload-exclude=/app/backend/.deer-flow" in content + + def test_no_uv_extras_yields_empty_flags(): proc = _run(None) assert proc.returncode == 0 diff --git a/backend/tests/test_gateway_runtime_cleanup.py b/backend/tests/test_gateway_runtime_cleanup.py index ae640267d..4642559e5 100644 --- a/backend/tests/test_gateway_runtime_cleanup.py +++ b/backend/tests/test_gateway_runtime_cleanup.py @@ -43,6 +43,19 @@ def test_service_launchers_always_use_gateway_runtime(): assert "LANGGRAPH_REWRITE" not in content, path +def test_local_dev_gateway_reload_excludes_runtime_state_with_absolute_dirs(): + serve_sh = _read("scripts/serve.sh") + + assert 'export DEER_FLOW_PROJECT_ROOT="$REPO_ROOT"' in serve_sh + assert 'BACKEND_RUNTIME_HOME="$REPO_ROOT/backend/.deer-flow"' in serve_sh + assert 'export DEER_FLOW_HOME="$BACKEND_RUNTIME_HOME"' in serve_sh + assert 'mkdir -p "$DEER_FLOW_HOME" "$BACKEND_RUNTIME_HOME"' in serve_sh + assert "--reload-exclude='$DEER_FLOW_HOME'" in serve_sh + assert "--reload-exclude='$BACKEND_RUNTIME_HOME'" in serve_sh + assert "--reload-exclude='sandbox/'" not in serve_sh + assert "--reload-exclude='.deer-flow/'" not in serve_sh + + def test_backend_container_only_exposes_gateway_port(): dockerfile = _read("backend/Dockerfile") diff --git a/docker/dev-entrypoint.sh b/docker/dev-entrypoint.sh index 872e16022..c7f2a1b31 100755 --- a/docker/dev-entrypoint.sh +++ b/docker/dev-entrypoint.sh @@ -64,6 +64,13 @@ if [ -n "$EXTRAS_FLAGS" ]; then echo "[startup] uv extras:$EXTRAS_FLAGS" fi +# Keep runtime-owned files out of uvicorn's reload watcher. The directory must +# exist before uvicorn starts so watchfiles treats it as an excluded directory, +# not as a plain glob pattern. +: "${DEER_FLOW_HOME:=/app/backend/.deer-flow}" +export DEER_FLOW_HOME +mkdir -p "$DEER_FLOW_HOME" /app/backend/.deer-flow + # ── Sync dependencies (with self-heal) ────────────────────────────────────── cd /app/backend @@ -82,4 +89,9 @@ fi PYTHONPATH=. exec uv run uvicorn app.gateway.app:app \ --host 0.0.0.0 --port 8001 \ - --reload --reload-include='*.yaml .env' + --reload \ + --reload-include='*.yaml' \ + --reload-include='.env' \ + --reload-exclude=/app/backend/sandbox \ + --reload-exclude="$DEER_FLOW_HOME" \ + --reload-exclude=/app/backend/.deer-flow diff --git a/scripts/serve.sh b/scripts/serve.sh index 3eb2ac833..39dbb6679 100755 --- a/scripts/serve.sh +++ b/scripts/serve.sh @@ -285,9 +285,26 @@ else FRONTEND_CMD="env BETTER_AUTH_SECRET=$($PYTHON_BIN -c 'import secrets; print(secrets.token_hex(16))') pnpm run preview" fi +# Runtime path defaults. Local `make dev` launches Gateway from `backend/`, +# so pin DeerFlow-owned state to the expected backend runtime directory and +# create it before uvicorn builds its reload exclude filter. +if [ -z "$DEER_FLOW_PROJECT_ROOT" ]; then + export DEER_FLOW_PROJECT_ROOT="$REPO_ROOT" +fi + +BACKEND_RUNTIME_HOME="$REPO_ROOT/backend/.deer-flow" +if [ -z "$DEER_FLOW_HOME" ]; then + export DEER_FLOW_HOME="$BACKEND_RUNTIME_HOME" +fi + +mkdir -p "$DEER_FLOW_HOME" "$BACKEND_RUNTIME_HOME" +DEER_FLOW_HOME="$(cd "$DEER_FLOW_HOME" && pwd -P)" +BACKEND_RUNTIME_HOME="$(cd "$BACKEND_RUNTIME_HOME" && pwd -P)" +export DEER_FLOW_HOME + # Extra flags for uvicorn if $DEV_MODE && ! $DAEMON_MODE; then - GATEWAY_EXTRA_FLAGS="--reload --reload-include='*.yaml' --reload-include='.env' --reload-exclude='*.pyc' --reload-exclude='__pycache__' --reload-exclude='sandbox/' --reload-exclude='.deer-flow/'" + GATEWAY_EXTRA_FLAGS="--reload --reload-include='*.yaml' --reload-include='.env' --reload-exclude='*.pyc' --reload-exclude='__pycache__' --reload-exclude='$REPO_ROOT/backend/sandbox' --reload-exclude='$DEER_FLOW_HOME' --reload-exclude='$BACKEND_RUNTIME_HOME'" else GATEWAY_EXTRA_FLAGS="" fi