diff --git a/Makefile b/Makefile index e74a02db3..d190de3e6 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # DeerFlow - Unified Development Environment -.PHONY: help config config-upgrade check install dev dev-daemon start stop up down clean docker-init docker-start docker-stop docker-logs docker-logs-frontend docker-logs-gateway +.PHONY: help config config-upgrade check install dev dev-pro dev-daemon dev-daemon-pro start start-pro start-daemon start-daemon-pro stop up up-pro down clean docker-init docker-start docker-start-pro docker-stop docker-logs docker-logs-frontend docker-logs-gateway BASH ?= bash @@ -20,18 +20,25 @@ help: @echo " make install - Install all dependencies (frontend + backend)" @echo " make setup-sandbox - Pre-pull sandbox container image (recommended)" @echo " make dev - Start all services in development mode (with hot-reloading)" - @echo " make dev-daemon - Start all services in background (daemon mode)" + @echo " make dev-pro - Start in dev + Gateway mode (experimental, no LangGraph server)" + @echo " make dev-daemon - Start dev services in background (daemon mode)" + @echo " make dev-daemon-pro - Start dev daemon + Gateway mode (experimental)" @echo " make start - Start all services in production mode (optimized, no hot-reloading)" + @echo " make start-pro - Start in prod + Gateway mode (experimental)" + @echo " make start-daemon - Start prod services in background (daemon mode)" + @echo " make start-daemon-pro - Start prod daemon + Gateway mode (experimental)" @echo " make stop - Stop all running services" @echo " make clean - Clean up processes and temporary files" @echo "" @echo "Docker Production Commands:" @echo " make up - Build and start production Docker services (localhost:2026)" + @echo " make up-pro - Build and start production Docker in Gateway mode (experimental)" @echo " make down - Stop and remove production Docker containers" @echo "" @echo "Docker Development Commands:" @echo " make docker-init - Pull the sandbox image" @echo " make docker-start - Start Docker services (mode-aware from config.yaml, localhost:2026)" + @echo " make docker-start-pro - Start Docker in Gateway mode (experimental, no LangGraph container)" @echo " make docker-stop - Stop Docker development services" @echo " make docker-logs - View Docker development logs" @echo " make docker-logs-frontend - View Docker frontend logs" @@ -105,6 +112,15 @@ else @./scripts/serve.sh --dev endif +# Start all services in dev + Gateway mode (experimental: agent runtime embedded in Gateway) +dev-pro: + @$(PYTHON) ./scripts/check.py +ifeq ($(OS),Windows_NT) + @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --dev --gateway +else + @./scripts/serve.sh --dev --gateway +endif + # Start all services in production mode (with optimizations) start: @$(PYTHON) ./scripts/check.py @@ -114,30 +130,54 @@ else @./scripts/serve.sh --prod endif +# Start all services in prod + Gateway mode (experimental) +start-pro: + @$(PYTHON) ./scripts/check.py +ifeq ($(OS),Windows_NT) + @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --prod --gateway +else + @./scripts/serve.sh --prod --gateway +endif + # Start all services in daemon mode (background) dev-daemon: @$(PYTHON) ./scripts/check.py ifeq ($(OS),Windows_NT) - @call scripts\run-with-git-bash.cmd ./scripts/start-daemon.sh + @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --dev --daemon else - @./scripts/start-daemon.sh + @./scripts/serve.sh --dev --daemon +endif + +# Start daemon + Gateway mode (experimental) +dev-daemon-pro: + @$(PYTHON) ./scripts/check.py +ifeq ($(OS),Windows_NT) + @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --dev --gateway --daemon +else + @./scripts/serve.sh --dev --gateway --daemon +endif + +# Start prod services in daemon mode (background) +start-daemon: + @$(PYTHON) ./scripts/check.py +ifeq ($(OS),Windows_NT) + @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --prod --daemon +else + @./scripts/serve.sh --prod --daemon +endif + +# Start prod daemon + Gateway mode (experimental) +start-daemon-pro: + @$(PYTHON) ./scripts/check.py +ifeq ($(OS),Windows_NT) + @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --prod --gateway --daemon +else + @./scripts/serve.sh --prod --gateway --daemon endif # Stop all services stop: - @echo "Stopping all services..." - @-pkill -f "langgraph dev" 2>/dev/null || true - @-pkill -f "uvicorn app.gateway.app:app" 2>/dev/null || true - @-pkill -f "next dev" 2>/dev/null || true - @-pkill -f "next start" 2>/dev/null || true - @-pkill -f "next-server" 2>/dev/null || true - @-pkill -f "next-server" 2>/dev/null || true - @-nginx -c $(PWD)/docker/nginx/nginx.local.conf -p $(PWD) -s quit 2>/dev/null || true - @sleep 1 - @-pkill -9 nginx 2>/dev/null || true - @echo "Cleaning up sandbox containers..." - @-./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true - @echo "✓ All services stopped" + @./scripts/serve.sh --stop # Clean up clean: stop @@ -159,6 +199,10 @@ docker-init: docker-start: @./scripts/docker.sh start +# Start Docker in Gateway mode (experimental) +docker-start-pro: + @./scripts/docker.sh start --gateway + # Stop Docker development environment docker-stop: @./scripts/docker.sh stop @@ -181,6 +225,10 @@ docker-logs-gateway: up: @./scripts/deploy.sh +# Build and start production services in Gateway mode +up-pro: + @./scripts/deploy.sh --gateway + # Stop and remove production containers down: @./scripts/deploy.sh down diff --git a/README.md b/README.md index 1c1f6dcdf..14aec9fc6 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,60 @@ On Windows, run the local development flow from Git Bash. Native `cmd.exe` and P 6. **Access**: http://localhost:2026 +#### Startup Modes + +DeerFlow supports multiple startup modes across two dimensions: + +- **Dev / Prod** — dev enables hot-reload; prod uses pre-built frontend +- **Standard / Gateway** — standard uses a separate LangGraph server (4 processes); Gateway mode (experimental) embeds the agent runtime in the Gateway API (3 processes) + +| | **Local Foreground** | **Local Daemon** | **Docker Dev** | **Docker Prod** | +|---|---|---|---|---| +| **Dev** | `./scripts/serve.sh --dev`
`make dev` | `./scripts/serve.sh --dev --daemon`
`make dev-daemon` | `./scripts/docker.sh start`
`make docker-start` | — | +| **Dev + Gateway** | `./scripts/serve.sh --dev --gateway`
`make dev-pro` | `./scripts/serve.sh --dev --gateway --daemon`
`make dev-daemon-pro` | `./scripts/docker.sh start --gateway`
`make docker-start-pro` | — | +| **Prod** | `./scripts/serve.sh --prod`
`make start` | `./scripts/serve.sh --prod --daemon`
`make start-daemon` | — | `./scripts/deploy.sh`
`make up` | +| **Prod + Gateway** | `./scripts/serve.sh --prod --gateway`
`make start-pro` | `./scripts/serve.sh --prod --gateway --daemon`
`make start-daemon-pro` | — | `./scripts/deploy.sh --gateway`
`make up-pro` | + +| Action | Local | Docker Dev | Docker Prod | +|---|---|---|---| +| **Stop** | `./scripts/serve.sh --stop`
`make stop` | `./scripts/docker.sh stop`
`make docker-stop` | `./scripts/deploy.sh down`
`make down` | +| **Restart** | `./scripts/serve.sh --restart [flags]` | `./scripts/docker.sh restart` | — | + +> **Gateway mode** eliminates the LangGraph server process — the Gateway API handles agent execution directly via async tasks, managing its own concurrency. + +#### Why Gateway Mode? + +In standard mode, DeerFlow runs a dedicated [LangGraph Platform](https://langchain-ai.github.io/langgraph/) server alongside the Gateway API. This architecture works well but has trade-offs: + +| | Standard Mode | Gateway Mode | +|---|---|---| +| **Architecture** | Gateway (REST API) + LangGraph (agent runtime) | Gateway embeds agent runtime | +| **Concurrency** | `--n-jobs-per-worker` per worker (requires license) | `--workers` × async tasks (no per-worker cap) | +| **Containers / Processes** | 4 (frontend, gateway, langgraph, nginx) | 3 (frontend, gateway, nginx) | +| **Resource usage** | Higher (two Python runtimes) | Lower (single Python runtime) | +| **LangGraph Platform license** | Required for production images | Not required | +| **Cold start** | Slower (two services to initialize) | Faster | + +Both modes are functionally equivalent — the same agents, tools, and skills work in either mode. + +#### Docker Production Deployment + +`deploy.sh` supports building and starting separately. Images are mode-agnostic — runtime mode is selected at start time: + +```bash +# One-step (build + start) +deploy.sh # standard mode (default) +deploy.sh --gateway # gateway mode + +# Two-step (build once, start with any mode) +deploy.sh build # build all images +deploy.sh start # start in standard mode +deploy.sh start --gateway # start in gateway mode + +# Stop +deploy.sh down +``` + ### Advanced #### Sandbox Mode diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md index a45b14253..846429e40 100644 --- a/backend/CLAUDE.md +++ b/backend/CLAUDE.md @@ -13,6 +13,10 @@ DeerFlow is a LangGraph-based AI super agent system with a full-stack architectu - **Nginx** (port 2026): Unified reverse proxy entry point - **Provisioner** (port 8002, optional in Docker dev): Started only when sandbox is configured for provisioner/Kubernetes mode +**Runtime Modes**: +- **Standard mode** (`make dev`): LangGraph Server handles agent execution as a separate process. 4 processes total. +- **Gateway mode** (`make dev-pro`, experimental): Agent runtime embedded in Gateway via `RunManager` + `run_agent()` + `StreamBridge` (`packages/harness/deerflow/runtime/`). Service manages its own concurrency via async tasks. 3 processes total, no LangGraph Server. + **Project Structure**: ``` deer-flow/ @@ -80,6 +84,8 @@ When making code changes, you MUST update the relevant documentation: make check # Check system requirements make install # Install all dependencies (frontend + backend) make dev # Start all services (LangGraph + Gateway + Frontend + Nginx), with config.yaml preflight +make dev-pro # Gateway mode (experimental): skip LangGraph, agent runtime embedded in Gateway +make start-pro # Production + Gateway mode (experimental) make stop # Stop all services ``` @@ -436,8 +442,25 @@ make dev This starts all services and makes the application available at `http://localhost:2026`. +**All startup modes:** + +| | **Local Foreground** | **Local Daemon** | **Docker Dev** | **Docker Prod** | +|---|---|---|---|---| +| **Dev** | `./scripts/serve.sh --dev`
`make dev` | `./scripts/serve.sh --dev --daemon`
`make dev-daemon` | `./scripts/docker.sh start`
`make docker-start` | — | +| **Dev + Gateway** | `./scripts/serve.sh --dev --gateway`
`make dev-pro` | `./scripts/serve.sh --dev --gateway --daemon`
`make dev-daemon-pro` | `./scripts/docker.sh start --gateway`
`make docker-start-pro` | — | +| **Prod** | `./scripts/serve.sh --prod`
`make start` | `./scripts/serve.sh --prod --daemon`
`make start-daemon` | — | `./scripts/deploy.sh`
`make up` | +| **Prod + Gateway** | `./scripts/serve.sh --prod --gateway`
`make start-pro` | `./scripts/serve.sh --prod --gateway --daemon`
`make start-daemon-pro` | — | `./scripts/deploy.sh --gateway`
`make up-pro` | + +| Action | Local | Docker Dev | Docker Prod | +|---|---|---|---| +| **Stop** | `./scripts/serve.sh --stop`
`make stop` | `./scripts/docker.sh stop`
`make docker-stop` | `./scripts/deploy.sh down`
`make down` | +| **Restart** | `./scripts/serve.sh --restart [flags]` | `./scripts/docker.sh restart` | — | + +Gateway mode embeds the agent runtime in Gateway, no LangGraph server. + **Nginx routing**: -- `/api/langgraph/*` → LangGraph Server (2024) +- Standard mode: `/api/langgraph/*` → LangGraph Server (2024) +- Gateway mode: `/api/langgraph/*` → Gateway embedded runtime (8001) (via envsubst) - `/api/*` (other) → Gateway API (8001) - `/` (non-API) → Frontend (3000) diff --git a/docker/docker-compose-dev.yaml b/docker/docker-compose-dev.yaml index c0749ba9d..53dcb80b2 100644 --- a/docker/docker-compose-dev.yaml +++ b/docker/docker-compose-dev.yaml @@ -19,8 +19,6 @@ services: # cluster via the K8s API. # Backend accesses sandboxes directly via host.docker.internal:{NodePort}. provisioner: - profiles: - - provisioner build: context: ./provisioner dockerfile: Dockerfile @@ -59,20 +57,25 @@ services: # ── Reverse Proxy ────────────────────────────────────────────────────── # Routes API traffic to gateway/langgraph and (optionally) provisioner. - # Select nginx config via NGINX_CONF: - # - nginx.local.conf (default): no provisioner route (local/aio modes) - # - nginx.conf: includes provisioner route (provisioner mode) + # LANGGRAPH_UPSTREAM and LANGGRAPH_REWRITE control gateway vs standard + # routing (processed by envsubst at container start). nginx: image: nginx:alpine container_name: deer-flow-nginx ports: - "2026:2026" volumes: - - ./nginx/${NGINX_CONF:-nginx.conf}:/etc/nginx/nginx.conf:ro + - ./nginx/nginx.conf:/etc/nginx/nginx.conf.template:ro + environment: + - LANGGRAPH_UPSTREAM=${LANGGRAPH_UPSTREAM:-langgraph:2024} + - LANGGRAPH_REWRITE=${LANGGRAPH_REWRITE:-/} + command: > + sh -c "envsubst '$$LANGGRAPH_UPSTREAM $$LANGGRAPH_REWRITE' + < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf + && nginx -g 'daemon off;'" depends_on: - frontend - gateway - - langgraph networks: - deer-flow-dev restart: unless-stopped diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 98d549878..8c1432da7 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -29,11 +29,17 @@ services: ports: - "${PORT:-2026}:2026" volumes: - - ./nginx/${NGINX_CONF:-nginx.conf}:/etc/nginx/nginx.conf:ro + - ./nginx/nginx.conf:/etc/nginx/nginx.conf.template:ro + environment: + - LANGGRAPH_UPSTREAM=${LANGGRAPH_UPSTREAM:-langgraph:2024} + - LANGGRAPH_REWRITE=${LANGGRAPH_REWRITE:-/} + command: > + sh -c "envsubst '$$LANGGRAPH_UPSTREAM $$LANGGRAPH_REWRITE' + < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf + && nginx -g 'daemon off;'" depends_on: - frontend - gateway - - langgraph networks: - deer-flow restart: unless-stopped @@ -68,7 +74,7 @@ services: UV_IMAGE: ${UV_IMAGE:-ghcr.io/astral-sh/uv:0.7.20} UV_INDEX_URL: ${UV_INDEX_URL:-https://pypi.org/simple} container_name: deer-flow-gateway - command: sh -c "cd backend && PYTHONPATH=. uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 --workers 2" + command: sh -c "cd backend && PYTHONPATH=. uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 --workers ${GATEWAY_WORKERS:-4}" volumes: - ${DEER_FLOW_CONFIG_PATH}:/app/backend/config.yaml:ro - ${DEER_FLOW_EXTENSIONS_CONFIG_PATH}:/app/backend/extensions_config.json:ro @@ -160,13 +166,12 @@ services: # ── Sandbox Provisioner (optional, Kubernetes mode) ──────────────────────── provisioner: - profiles: - - provisioner build: context: ./provisioner dockerfile: Dockerfile args: APT_MIRROR: ${APT_MIRROR:-} + PIP_INDEX_URL: ${PIP_INDEX_URL:-} container_name: deer-flow-provisioner volumes: - ~/.kube/config:/root/.kube/config:ro diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index da570f709..c9a7be32b 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -27,7 +27,7 @@ http { } upstream langgraph { - server langgraph:2024; + server ${LANGGRAPH_UPSTREAM}; } upstream frontend { @@ -57,9 +57,11 @@ http { } # LangGraph API routes - # Rewrites /api/langgraph/* to /* before proxying + # In standard mode: /api/langgraph/* → langgraph:2024 (rewrite to /*) + # In gateway mode: /api/langgraph/* → gateway:8001 (rewrite to /api/*) + # Controlled by LANGGRAPH_UPSTREAM and LANGGRAPH_REWRITE env vars. location /api/langgraph/ { - rewrite ^/api/langgraph/(.*) /$1 break; + rewrite ^/api/langgraph/(.*) ${LANGGRAPH_REWRITE}$1 break; proxy_pass http://langgraph; proxy_http_version 1.1; @@ -84,34 +86,6 @@ http { chunked_transfer_encoding on; } - # Experimental: Gateway-backed LangGraph-compatible API - # Frontend can opt-in via NEXT_PUBLIC_LANGGRAPH_BASE_URL=/api/langgraph-compat - location /api/langgraph-compat/ { - rewrite ^/api/langgraph-compat/(.*) /api/$1 break; - proxy_pass http://gateway; - proxy_http_version 1.1; - - # Headers - proxy_set_header Host $host; - 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 $scheme; - proxy_set_header Connection ''; - - # SSE/Streaming support - proxy_buffering off; - proxy_cache off; - proxy_set_header X-Accel-Buffering no; - - # Timeouts for long-running requests - proxy_connect_timeout 600s; - proxy_send_timeout 600s; - proxy_read_timeout 600s; - - # Chunked transfer encoding - chunked_transfer_encoding on; - } - # Custom API: Models endpoint location /api/models { proxy_pass http://gateway; diff --git a/docker/provisioner/Dockerfile b/docker/provisioner/Dockerfile index 6f30e778d..96ef93156 100644 --- a/docker/provisioner/Dockerfile +++ b/docker/provisioner/Dockerfile @@ -1,6 +1,7 @@ FROM python:3.12-slim-bookworm ARG APT_MIRROR +ARG PIP_INDEX_URL # Optionally override apt mirror for restricted networks (e.g. APT_MIRROR=mirrors.aliyun.com) RUN if [ -n "${APT_MIRROR}" ]; then \ @@ -15,6 +16,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # Install Python dependencies RUN pip install --no-cache-dir \ + ${PIP_INDEX_URL:+--index-url "$PIP_INDEX_URL"} \ fastapi \ "uvicorn[standard]" \ kubernetes diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 66df240ef..26cb3bc29 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,16 +1,57 @@ #!/usr/bin/env bash # -# deploy.sh - Build and start (or stop) DeerFlow production services +# deploy.sh - Build, start, or stop DeerFlow production services # -# Usage: -# deploy.sh [up] — build images and start containers (default) -# deploy.sh down — stop and remove containers +# Commands: +# deploy.sh [--MODE] — build + start (default: --standard) +# deploy.sh build — build all images (mode-agnostic) +# deploy.sh start [--MODE] — start from pre-built images (default: --standard) +# deploy.sh down — stop and remove containers +# +# Runtime modes: +# --standard (default) All services including LangGraph server. +# --gateway No LangGraph container; nginx routes /api/langgraph/* +# to the Gateway compat API instead. +# +# Sandbox mode (local / aio / provisioner) is auto-detected from config.yaml. +# +# Examples: +# deploy.sh # build + start in standard mode +# deploy.sh --gateway # build + start in gateway mode +# deploy.sh build # build all images +# deploy.sh start --gateway # start pre-built images in gateway mode +# deploy.sh down # stop and remove containers # # Must be run from the repo root directory. set -e -CMD="${1:-up}" +RUNTIME_MODE="standard" + +case "${1:-}" in + build|start|down) + CMD="$1" + if [ -n "${2:-}" ]; then + case "$2" in + --standard) RUNTIME_MODE="standard" ;; + --gateway) RUNTIME_MODE="gateway" ;; + *) echo "Unknown mode: $2"; echo "Usage: deploy.sh [build|start|down] [--standard|--gateway]"; exit 1 ;; + esac + fi + ;; + --standard|--gateway) + CMD="" + RUNTIME_MODE="${1#--}" + ;; + "") + CMD="" + ;; + *) + echo "Unknown argument: $1" + echo "Usage: deploy.sh [build|start|down] [--standard|--gateway]" + exit 1 + ;; +esac REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" @@ -150,6 +191,32 @@ if [ "$CMD" = "down" ]; then exit 0 fi +# ── build ──────────────────────────────────────────────────────────────────── +# Build produces mode-agnostic images. No --gateway or sandbox detection needed. + +if [ "$CMD" = "build" ]; then + echo "==========================================" + echo " DeerFlow — Building Images" + echo "==========================================" + echo "" + + # Docker socket is needed for compose to parse volume specs + if [ -z "$DEER_FLOW_DOCKER_SOCKET" ]; then + export DEER_FLOW_DOCKER_SOCKET="/var/run/docker.sock" + fi + + "${COMPOSE_CMD[@]}" build + + echo "" + echo "==========================================" + echo " ✓ Images built successfully" + echo "==========================================" + echo "" + echo " Next: deploy.sh start [--gateway]" + echo "" + exit 0 +fi + # ── Banner ──────────────────────────────────────────────────────────────────── echo "==========================================" @@ -157,19 +224,28 @@ echo " DeerFlow Production Deployment" echo "==========================================" echo "" -# ── Step 1: Detect sandbox mode ────────────────────────────────────────────── +# ── Detect runtime configuration ──────────────────────────────────────────── +# Only needed for start / up — determines which containers to launch. sandbox_mode="$(detect_sandbox_mode)" echo -e "${BLUE}Sandbox mode: $sandbox_mode${NC}" -if [ "$sandbox_mode" = "provisioner" ]; then - services="" - extra_args="--profile provisioner" -else - services="frontend gateway langgraph nginx" - extra_args="" -fi +echo -e "${BLUE}Runtime mode: $RUNTIME_MODE${NC}" +case "$RUNTIME_MODE" in + gateway) + export LANGGRAPH_UPSTREAM=gateway:8001 + export LANGGRAPH_REWRITE=/api/ + services="frontend gateway nginx" + ;; + standard) + services="frontend gateway langgraph nginx" + ;; +esac + +if [ "$sandbox_mode" = "provisioner" ]; then + services="$services provisioner" +fi # ── DEER_FLOW_DOCKER_SOCKET ─────────────────────────────────────────────────── @@ -189,22 +265,34 @@ fi echo "" -# ── Step 2: Build and start ─────────────────────────────────────────────────── +# ── Start / Up ─────────────────────────────────────────────────────────────── -echo "Building images and starting containers..." -echo "" - -# shellcheck disable=SC2086 -"${COMPOSE_CMD[@]}" $extra_args up --build -d --remove-orphans $services +if [ "$CMD" = "start" ]; then + echo "Starting containers (no rebuild)..." + echo "" + # shellcheck disable=SC2086 + "${COMPOSE_CMD[@]}" up -d --remove-orphans $services +else + # Default: build + start + echo "Building images and starting containers..." + echo "" + # shellcheck disable=SC2086 + "${COMPOSE_CMD[@]}" up --build -d --remove-orphans $services +fi echo "" echo "==========================================" -echo " DeerFlow is running!" +echo " DeerFlow is running! ($RUNTIME_MODE mode)" echo "==========================================" echo "" echo " 🌐 Application: http://localhost:${PORT:-2026}" echo " 📡 API Gateway: http://localhost:${PORT:-2026}/api/*" -echo " 🤖 LangGraph: http://localhost:${PORT:-2026}/api/langgraph/*" +if [ "$RUNTIME_MODE" = "gateway" ]; then + echo " 🤖 Runtime: Gateway embedded" + echo " API: /api/langgraph/* → Gateway (compat)" +else + echo " 🤖 LangGraph: http://localhost:${PORT:-2026}/api/langgraph/*" +fi echo "" echo " Manage:" echo " make down — stop and remove containers" diff --git a/scripts/docker.sh b/scripts/docker.sh index bc20d4177..0ef1896fe 100755 --- a/scripts/docker.sh +++ b/scripts/docker.sh @@ -148,9 +148,18 @@ init() { } # Start Docker development environment +# Usage: start [--gateway] start() { local sandbox_mode local services + local gateway_mode=false + + # Check for --gateway flag + for arg in "$@"; do + if [ "$arg" = "--gateway" ]; then + gateway_mode=true + fi + done echo "==========================================" echo " Starting DeerFlow Docker Development" @@ -159,12 +168,21 @@ start() { sandbox_mode="$(detect_sandbox_mode)" - if [ "$sandbox_mode" = "provisioner" ]; then - services="frontend gateway langgraph provisioner nginx" + if $gateway_mode; then + services="frontend gateway nginx" + if [ "$sandbox_mode" = "provisioner" ]; then + services="frontend gateway provisioner nginx" + fi else services="frontend gateway langgraph nginx" + if [ "$sandbox_mode" = "provisioner" ]; then + services="frontend gateway langgraph provisioner nginx" + fi fi + if $gateway_mode; then + echo -e "${BLUE}Runtime: Gateway mode (experimental) — no LangGraph container${NC}" + fi echo -e "${BLUE}Detected sandbox mode: $sandbox_mode${NC}" if [ "$sandbox_mode" = "provisioner" ]; then echo -e "${BLUE}Provisioner enabled (Kubernetes mode).${NC}" @@ -213,6 +231,12 @@ start() { fi fi + # Set nginx routing for gateway mode (envsubst in nginx container) + if $gateway_mode; then + export LANGGRAPH_UPSTREAM=gateway:8001 + export LANGGRAPH_REWRITE=/api/ + fi + echo "Building and starting containers..." cd "$DOCKER_DIR" && $COMPOSE_CMD up --build -d --remove-orphans $services echo "" @@ -222,7 +246,12 @@ start() { echo "" echo " 🌐 Application: http://localhost:2026" echo " 📡 API Gateway: http://localhost:2026/api/*" - echo " 🤖 LangGraph: http://localhost:2026/api/langgraph/*" + if $gateway_mode; then + echo " 🤖 Runtime: Gateway embedded" + echo " API: /api/langgraph/* → Gateway (compat)" + else + echo " 🤖 LangGraph: http://localhost:2026/api/langgraph/*" + fi echo "" echo " 📋 View logs: make docker-logs" echo " 🛑 Stop: make docker-stop" @@ -300,9 +329,10 @@ help() { echo "Usage: $0 [options]" echo "" echo "Commands:" - echo " init - Pull the sandbox image (speeds up first Pod startup)" - echo " start - Start Docker services (auto-detects sandbox mode from config.yaml)" - echo " restart - Restart all running Docker services" + echo " init - Pull the sandbox image (speeds up first Pod startup)" + echo " start - Start Docker services (auto-detects sandbox mode from config.yaml)" + echo " start --gateway - Start without LangGraph container (Gateway mode, experimental)" + echo " restart - Restart all running Docker services" echo " logs [option] - View Docker development logs" echo " --frontend View frontend logs only" echo " --gateway View gateway logs only" @@ -320,7 +350,8 @@ main() { init ;; start) - start + shift + start "$@" ;; restart) restart diff --git a/scripts/serve.sh b/scripts/serve.sh index fe1556a91..bd810e05e 100755 --- a/scripts/serve.sh +++ b/scripts/serve.sh @@ -1,15 +1,40 @@ #!/usr/bin/env bash # -# start.sh - Start all DeerFlow development services +# serve.sh — Unified DeerFlow service launcher +# +# Usage: +# ./scripts/serve.sh [--dev|--prod] [--gateway] [--daemon] [--stop|--restart] +# +# Modes: +# --dev Development mode with hot-reload (default) +# --prod Production mode, pre-built frontend, no hot-reload +# --gateway Gateway mode (experimental): skip LangGraph server, +# agent runtime embedded in Gateway API +# --daemon Run all services in background (nohup), exit after startup +# +# Actions: +# --skip-install Skip dependency installation (faster restart) +# --stop Stop all running services and exit +# --restart Stop all services, then start with the given mode flags +# +# Examples: +# ./scripts/serve.sh --dev # Standard dev (4 processes) +# ./scripts/serve.sh --dev --gateway # Gateway dev (3 processes) +# ./scripts/serve.sh --prod --gateway # Gateway prod (3 processes) +# ./scripts/serve.sh --dev --daemon # Standard dev, background +# ./scripts/serve.sh --dev --gateway --daemon # Gateway dev, background +# ./scripts/serve.sh --stop # Stop all services +# ./scripts/serve.sh --restart --dev --gateway # Restart in gateway mode # # Must be run from the repo root directory. set -e -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +REPO_ROOT="$(builtin cd "$(dirname "${BASH_SOURCE[0]}")/.." >/dev/null 2>&1 && pwd -P)" cd "$REPO_ROOT" -# ── Load environment variables from .env ────────────────────────────────────── +# ── Load .env ──────────────────────────────────────────────────────────────── + if [ -f "$REPO_ROOT/.env" ]; then set -a source "$REPO_ROOT/.env" @@ -19,14 +44,80 @@ fi # ── Argument parsing ───────────────────────────────────────────────────────── DEV_MODE=true +GATEWAY_MODE=false +DAEMON_MODE=false +SKIP_INSTALL=false +ACTION="start" # start | stop | restart + for arg in "$@"; do case "$arg" in - --dev) DEV_MODE=true ;; - --prod) DEV_MODE=false ;; - *) echo "Unknown argument: $arg"; echo "Usage: $0 [--dev|--prod]"; exit 1 ;; + --dev) DEV_MODE=true ;; + --prod) DEV_MODE=false ;; + --gateway) GATEWAY_MODE=true ;; + --daemon) DAEMON_MODE=true ;; + --skip-install) SKIP_INSTALL=true ;; + --stop) ACTION="stop" ;; + --restart) ACTION="restart" ;; + *) + echo "Unknown argument: $arg" + echo "Usage: $0 [--dev|--prod] [--gateway] [--daemon] [--skip-install] [--stop|--restart]" + exit 1 + ;; esac done +# ── Stop helper ────────────────────────────────────────────────────────────── + +stop_all() { + echo "Stopping all services..." + pkill -f "langgraph dev" 2>/dev/null || true + pkill -f "uvicorn app.gateway.app:app" 2>/dev/null || true + pkill -f "next dev" 2>/dev/null || true + pkill -f "next start" 2>/dev/null || true + pkill -f "next-server" 2>/dev/null || true + nginx -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" -s quit 2>/dev/null || true + sleep 1 + pkill -9 nginx 2>/dev/null || true + ./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true + echo "✓ All services stopped" +} + +# ── Action routing ─────────────────────────────────────────────────────────── + +if [ "$ACTION" = "stop" ]; then + stop_all + exit 0 +fi + +ALREADY_STOPPED=false +if [ "$ACTION" = "restart" ]; then + stop_all + sleep 1 + ALREADY_STOPPED=true +fi + +# ── Derive runtime flags ──────────────────────────────────────────────────── + +if $GATEWAY_MODE; then + export SKIP_LANGGRAPH_SERVER=1 +fi + +# Mode label for banner +if $DEV_MODE && $GATEWAY_MODE; then + MODE_LABEL="DEV + GATEWAY (experimental)" +elif $DEV_MODE; then + MODE_LABEL="DEV (hot-reload enabled)" +elif $GATEWAY_MODE; then + MODE_LABEL="PROD + GATEWAY (experimental)" +else + MODE_LABEL="PROD (optimized)" +fi + +if $DAEMON_MODE; then + MODE_LABEL="$MODE_LABEL [daemon]" +fi + +# Frontend command if $DEV_MODE; then FRONTEND_CMD="pnpm run dev" else @@ -35,46 +126,26 @@ else elif command -v python >/dev/null 2>&1; then PYTHON_BIN="python" else - echo "Python is required to generate BETTER_AUTH_SECRET, but neither python3 nor python was found." + echo "Python is required to generate BETTER_AUTH_SECRET." exit 1 fi FRONTEND_CMD="env BETTER_AUTH_SECRET=$($PYTHON_BIN -c 'import secrets; print(secrets.token_hex(16))') pnpm run preview" fi -# ── Stop existing services ──────────────────────────────────────────────────── - -echo "Stopping existing services if any..." -pkill -f "langgraph dev" 2>/dev/null || true -pkill -f "uvicorn app.gateway.app:app" 2>/dev/null || true -pkill -f "next dev" 2>/dev/null || true -pkill -f "next-server" 2>/dev/null || true -nginx -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" -s quit 2>/dev/null || true -sleep 1 -pkill -9 nginx 2>/dev/null || true -killall -9 nginx 2>/dev/null || true -./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true -sleep 1 - -# ── Banner ──────────────────────────────────────────────────────────────────── - -echo "" -echo "==========================================" -echo " Starting DeerFlow Development Server" -echo "==========================================" -echo "" -if $DEV_MODE; then - echo " Mode: DEV (hot-reload enabled)" - echo " Tip: run \`make start\` in production mode" +# Extra flags for uvicorn/langgraph +LANGGRAPH_EXTRA_FLAGS="--no-reload" +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/'" else - echo " Mode: PROD (hot-reload disabled)" - echo " Tip: run \`make dev\` to start in development mode" + GATEWAY_EXTRA_FLAGS="" +fi + +# ── Stop existing services (skip if restart already did it) ────────────────── + +if ! $ALREADY_STOPPED; then + stop_all + sleep 1 fi -echo "" -echo "Services starting up..." -echo " → Backend: LangGraph + Gateway" -echo " → Frontend: Next.js" -echo " → Nginx: Reverse Proxy" -echo "" # ── Config check ───────────────────────────────────────────────────────────── @@ -84,64 +155,108 @@ if ! { \ [ -f config.yaml ]; \ }; then echo "✗ No DeerFlow config file found." - echo " Checked these locations:" - echo " - $DEER_FLOW_CONFIG_PATH (when DEER_FLOW_CONFIG_PATH is set)" - echo " - backend/config.yaml" - echo " - ./config.yaml" - echo "" - echo " Run 'make config' from the repo root to generate ./config.yaml, then set required model API keys in .env or your config file." + echo " Run 'make config' to generate config.yaml." exit 1 fi -# ── Auto-upgrade config ────────────────────────────────────────────────── - "$REPO_ROOT/scripts/config-upgrade.sh" -# ── Cleanup trap ───────────────────────────────────────────────────────────── +# ── Install dependencies ──────────────────────────────────────────────────── + +if ! $SKIP_INSTALL; then + echo "Syncing dependencies..." + (cd backend && uv sync --quiet) || { echo "✗ Backend dependency install failed"; exit 1; } + (cd frontend && pnpm install --silent) || { echo "✗ Frontend dependency install failed"; exit 1; } + echo "✓ Dependencies synced" +else + echo "⏩ Skipping dependency install (--skip-install)" +fi + +# ── Sync frontend .env.local ───────────────────────────────────────────────── +# Next.js .env.local takes precedence over process env vars. +# The script manages the NEXT_PUBLIC_LANGGRAPH_BASE_URL line to ensure +# the frontend routes match the active backend mode. + +FRONTEND_ENV_LOCAL="$REPO_ROOT/frontend/.env.local" +ENV_KEY="NEXT_PUBLIC_LANGGRAPH_BASE_URL" + +sync_frontend_env() { + if $GATEWAY_MODE; then + # Point frontend to Gateway's compat API + if [ -f "$FRONTEND_ENV_LOCAL" ] && grep -q "^${ENV_KEY}=" "$FRONTEND_ENV_LOCAL"; then + sed -i.bak "s|^${ENV_KEY}=.*|${ENV_KEY}=/api/langgraph-compat|" "$FRONTEND_ENV_LOCAL" && rm -f "${FRONTEND_ENV_LOCAL}.bak" + else + echo "${ENV_KEY}=/api/langgraph-compat" >> "$FRONTEND_ENV_LOCAL" + fi + else + # Remove override — frontend falls back to /api/langgraph (standard) + if [ -f "$FRONTEND_ENV_LOCAL" ] && grep -q "^${ENV_KEY}=" "$FRONTEND_ENV_LOCAL"; then + sed -i.bak "/^${ENV_KEY}=/d" "$FRONTEND_ENV_LOCAL" && rm -f "${FRONTEND_ENV_LOCAL}.bak" + fi + fi +} + +sync_frontend_env + +# ── Banner ─────────────────────────────────────────────────────────────────── + +echo "" +echo "==========================================" +echo " Starting DeerFlow" +echo "==========================================" +echo "" +echo " Mode: $MODE_LABEL" +echo "" +echo " Services:" +if ! $GATEWAY_MODE; then + echo " LangGraph → localhost:2024 (agent runtime)" +fi +echo " Gateway → localhost:8001 (REST API$(if $GATEWAY_MODE; then echo " + agent runtime"; fi))" +echo " Frontend → localhost:3000 (Next.js)" +echo " Nginx → localhost:2026 (reverse proxy)" +echo "" + +# ── Cleanup handler ────────────────────────────────────────────────────────── cleanup() { trap - INT TERM echo "" - echo "Shutting down services..." - if [ "${SKIP_LANGGRAPH_SERVER:-0}" != "1" ]; then - pkill -f "langgraph dev" 2>/dev/null || true - fi - pkill -f "uvicorn app.gateway.app:app" 2>/dev/null || true - pkill -f "next dev" 2>/dev/null || true - pkill -f "next start" 2>/dev/null || true - pkill -f "next-server" 2>/dev/null || true - # Kill nginx using the captured PID first (most reliable), - # then fall back to pkill/killall for any stray nginx workers. - if [ -n "${NGINX_PID:-}" ] && kill -0 "$NGINX_PID" 2>/dev/null; then - kill -TERM "$NGINX_PID" 2>/dev/null || true - sleep 1 - kill -9 "$NGINX_PID" 2>/dev/null || true - fi - pkill -9 nginx 2>/dev/null || true - killall -9 nginx 2>/dev/null || true - echo "Cleaning up sandbox containers..." - ./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true - echo "✓ All services stopped" + stop_all exit 0 } + trap cleanup INT TERM -# ── Start services ──────────────────────────────────────────────────────────── +# ── Helper: start a service ────────────────────────────────────────────────── + +# run_service NAME COMMAND PORT TIMEOUT +# In daemon mode, wraps with nohup. Waits for port to be ready. +run_service() { + local name="$1" cmd="$2" port="$3" timeout="$4" + + echo "Starting $name..." + if $DAEMON_MODE; then + nohup sh -c "$cmd" > /dev/null 2>&1 & + else + sh -c "$cmd" & + fi + + ./scripts/wait-for-port.sh "$port" "$timeout" "$name" || { + local logfile="logs/$(echo "$name" | tr '[:upper:]' '[:lower:]' | tr ' ' '-').log" + echo "✗ $name failed to start." + [ -f "$logfile" ] && tail -20 "$logfile" + cleanup + } + echo "✓ $name started on localhost:$port" +} + +# ── Start services ─────────────────────────────────────────────────────────── mkdir -p logs mkdir -p temp/client_body_temp temp/proxy_temp temp/fastcgi_temp temp/uwsgi_temp temp/scgi_temp -if $DEV_MODE; then - LANGGRAPH_EXTRA_FLAGS="--no-reload" - GATEWAY_EXTRA_FLAGS="--reload --reload-include='*.yaml' --reload-include='.env' --reload-exclude='*.pyc' --reload-exclude='__pycache__' --reload-exclude='sandbox/' --reload-exclude='.deer-flow/'" -else - LANGGRAPH_EXTRA_FLAGS="--no-reload" - GATEWAY_EXTRA_FLAGS="" -fi - -if [ "${SKIP_LANGGRAPH_SERVER:-0}" != "1" ]; then - echo "Starting LangGraph server..." - # Read log_level from config.yaml, fallback to env var, then to "info" +# 1. LangGraph (skip in gateway mode) +if ! $GATEWAY_MODE; then CONFIG_LOG_LEVEL=$(grep -m1 '^log_level:' config.yaml 2>/dev/null | awk '{print $2}' | tr -d ' ') LANGGRAPH_LOG_LEVEL="${LANGGRAPH_LOG_LEVEL:-${CONFIG_LOG_LEVEL:-info}}" LANGGRAPH_JOBS_PER_WORKER="${LANGGRAPH_JOBS_PER_WORKER:-10}" @@ -150,85 +265,54 @@ if [ "${SKIP_LANGGRAPH_SERVER:-0}" != "1" ]; then if [ "$LANGGRAPH_ALLOW_BLOCKING" = "1" ]; then LANGGRAPH_ALLOW_BLOCKING_FLAG="--allow-blocking" fi - (cd backend && NO_COLOR=1 uv run langgraph dev --no-browser $LANGGRAPH_ALLOW_BLOCKING_FLAG --n-jobs-per-worker "$LANGGRAPH_JOBS_PER_WORKER" --server-log-level $LANGGRAPH_LOG_LEVEL $LANGGRAPH_EXTRA_FLAGS > ../logs/langgraph.log 2>&1) & - ./scripts/wait-for-port.sh 2024 60 "LangGraph" || { - echo " See logs/langgraph.log for details" - tail -20 logs/langgraph.log - if grep -qE "config_version|outdated|Environment variable .* not found|KeyError|ValidationError|config\.yaml" logs/langgraph.log 2>/dev/null; then - echo "" - echo " Hint: This may be a configuration issue. Try running 'make config-upgrade' to update your config.yaml." - fi - cleanup - } - echo "✓ LangGraph server started on localhost:2024" + run_service "LangGraph" \ + "cd backend && NO_COLOR=1 uv run langgraph dev --no-browser $LANGGRAPH_ALLOW_BLOCKING_FLAG --n-jobs-per-worker $LANGGRAPH_JOBS_PER_WORKER --server-log-level $LANGGRAPH_LOG_LEVEL $LANGGRAPH_EXTRA_FLAGS > ../logs/langgraph.log 2>&1" \ + 2024 60 else - echo "⏩ Skipping LangGraph server (SKIP_LANGGRAPH_SERVER=1)" - echo " Use /api/langgraph-compat/* via Gateway instead" + echo "⏩ Skipping LangGraph (Gateway mode — runtime embedded in Gateway)" fi -echo "Starting Gateway API..." -(cd backend && PYTHONPATH=. uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 $GATEWAY_EXTRA_FLAGS > ../logs/gateway.log 2>&1) & -./scripts/wait-for-port.sh 8001 30 "Gateway API" || { - echo "✗ Gateway API failed to start. Last log output:" - tail -60 logs/gateway.log - echo "" - echo "Likely configuration errors:" - grep -E "Failed to load configuration|Environment variable .* not found|config\.yaml.*not found" logs/gateway.log | tail -5 || true - echo "" - echo " Hint: Try running 'make config-upgrade' to update your config.yaml with the latest fields." - cleanup -} -echo "✓ Gateway API started on localhost:8001" +# 2. Gateway API +run_service "Gateway" \ + "cd backend && PYTHONPATH=. uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 $GATEWAY_EXTRA_FLAGS > ../logs/gateway.log 2>&1" \ + 8001 30 -echo "Starting Frontend..." -(cd frontend && $FRONTEND_CMD > ../logs/frontend.log 2>&1) & -./scripts/wait-for-port.sh 3000 120 "Frontend" || { - echo " See logs/frontend.log for details" - tail -20 logs/frontend.log - cleanup -} -echo "✓ Frontend started on localhost:3000" +# 3. Frontend +run_service "Frontend" \ + "cd frontend && $FRONTEND_CMD > ../logs/frontend.log 2>&1" \ + 3000 120 -echo "Starting Nginx reverse proxy..." -nginx -g 'daemon off;' -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" > logs/nginx.log 2>&1 & -NGINX_PID=$! -./scripts/wait-for-port.sh 2026 10 "Nginx" || { - echo " See logs/nginx.log for details" - tail -10 logs/nginx.log - cleanup -} -echo "✓ Nginx started on localhost:2026" +# 4. Nginx +run_service "Nginx" \ + "nginx -g 'daemon off;' -c '$REPO_ROOT/docker/nginx/nginx.local.conf' -p '$REPO_ROOT' > logs/nginx.log 2>&1" \ + 2026 10 -# ── Ready ───────────────────────────────────────────────────────────────────── +# ── Ready ──────────────────────────────────────────────────────────────────── echo "" echo "==========================================" -if $DEV_MODE; then - echo " ✓ DeerFlow development server is running!" -else - echo " ✓ DeerFlow production server is running!" -fi +echo " ✓ DeerFlow is running! [$MODE_LABEL]" echo "==========================================" echo "" -echo " 🌐 Application: http://localhost:2026" -echo " 📡 API Gateway: http://localhost:2026/api/*" -if [ "${SKIP_LANGGRAPH_SERVER:-0}" = "1" ]; then - echo " 🤖 LangGraph: skipped (SKIP_LANGGRAPH_SERVER=1)" +echo " 🌐 http://localhost:2026" +echo "" +if $GATEWAY_MODE; then + echo " Routing: Frontend → Nginx → Gateway (embedded runtime)" + echo " API: /api/langgraph-compat/* → Gateway agent runtime" else - echo " 🤖 LangGraph: http://localhost:2026/api/langgraph/* (served by langgraph dev)" -fi -echo " 🧪 LangGraph Compat (experimental): http://localhost:2026/api/langgraph-compat/* (served by Gateway)" -if [ "${SKIP_LANGGRAPH_SERVER:-0}" = "1" ]; then - echo "" - echo " 💡 Set NEXT_PUBLIC_LANGGRAPH_BASE_URL=/api/langgraph-compat in frontend/.env.local" + echo " Routing: Frontend → Nginx → LangGraph + Gateway" + echo " API: /api/langgraph/* → LangGraph server (2024)" fi +echo " /api/* → Gateway REST API (8001)" echo "" -echo " 📋 Logs:" -echo " - LangGraph: logs/langgraph.log" -echo " - Gateway: logs/gateway.log" -echo " - Frontend: logs/frontend.log" -echo " - Nginx: logs/nginx.log" +echo " 📋 Logs: logs/{langgraph,gateway,frontend,nginx}.log" echo "" -echo "Press Ctrl+C to stop all services" -wait +if $DAEMON_MODE; then + echo " 🛑 Stop: make stop" + # Detach — trap is no longer needed + trap - INT TERM +else + echo " Press Ctrl+C to stop all services" + wait +fi diff --git a/scripts/start-daemon.sh b/scripts/start-daemon.sh index cce8c65bb..8822b73a0 100755 --- a/scripts/start-daemon.sh +++ b/scripts/start-daemon.sh @@ -1,146 +1,9 @@ #!/usr/bin/env bash # -# start-daemon.sh - Start all DeerFlow development services in daemon mode +# start-daemon.sh — Start DeerFlow in daemon (background) mode # -# This script starts DeerFlow services in the background without keeping -# the terminal connection. Logs are written to separate files. -# -# Must be run from the repo root directory. - -set -e +# Thin wrapper around serve.sh --daemon. +# Kept for backward compatibility. REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -cd "$REPO_ROOT" - -# ── Stop existing services ──────────────────────────────────────────────────── - -echo "Stopping existing services if any..." -pkill -f "langgraph dev" 2>/dev/null || true -pkill -f "uvicorn app.gateway.app:app" 2>/dev/null || true -pkill -f "next dev" 2>/dev/null || true -nginx -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" -s quit 2>/dev/null || true -sleep 1 -pkill -9 nginx 2>/dev/null || true -./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true -sleep 1 - -# ── Banner ──────────────────────────────────────────────────────────────────── - -echo "" -echo "==========================================" -echo " Starting DeerFlow in Daemon Mode" -echo "==========================================" -echo "" - -# ── Config check ───────────────────────────────────────────────────────────── - -if ! { \ - [ -n "$DEER_FLOW_CONFIG_PATH" ] && [ -f "$DEER_FLOW_CONFIG_PATH" ] || \ - [ -f backend/config.yaml ] || \ - [ -f config.yaml ]; \ - }; then - echo "✗ No DeerFlow config file found." - echo " Checked these locations:" - echo " - $DEER_FLOW_CONFIG_PATH (when DEER_FLOW_CONFIG_PATH is set)" - echo " - backend/config.yaml" - echo " - ./config.yaml" - echo "" - echo " Run 'make config' from the repo root to generate ./config.yaml, then set required model API keys in .env or your config file." - exit 1 -fi - -# ── Auto-upgrade config ────────────────────────────────────────────────── - -"$REPO_ROOT/scripts/config-upgrade.sh" - -# ── Cleanup on failure ─────────────────────────────────────────────────────── - -cleanup_on_failure() { - echo "Failed to start services, cleaning up..." - pkill -f "langgraph dev" 2>/dev/null || true - pkill -f "uvicorn app.gateway.app:app" 2>/dev/null || true - pkill -f "next dev" 2>/dev/null || true - nginx -c "$REPO_ROOT/docker/nginx/nginx.local.conf" -p "$REPO_ROOT" -s quit 2>/dev/null || true - sleep 1 - pkill -9 nginx 2>/dev/null || true - echo "✓ Cleanup complete" -} - -trap cleanup_on_failure INT TERM - -# ── Start services ──────────────────────────────────────────────────────────── - -mkdir -p logs -mkdir -p temp/client_body_temp temp/proxy_temp temp/fastcgi_temp temp/uwsgi_temp temp/scgi_temp - -echo "Starting LangGraph server..." -LANGGRAPH_JOBS_PER_WORKER="${LANGGRAPH_JOBS_PER_WORKER:-10}" -LANGGRAPH_ALLOW_BLOCKING="${LANGGRAPH_ALLOW_BLOCKING:-0}" -LANGGRAPH_ALLOW_BLOCKING_FLAG="" -if [ "$LANGGRAPH_ALLOW_BLOCKING" = "1" ]; then - LANGGRAPH_ALLOW_BLOCKING_FLAG="--allow-blocking" -fi -nohup sh -c "cd backend && NO_COLOR=1 uv run langgraph dev --no-browser ${LANGGRAPH_ALLOW_BLOCKING_FLAG} --no-reload --n-jobs-per-worker ${LANGGRAPH_JOBS_PER_WORKER} > ../logs/langgraph.log 2>&1" & -./scripts/wait-for-port.sh 2024 60 "LangGraph" || { - echo "✗ LangGraph failed to start. Last log output:" - tail -60 logs/langgraph.log - if grep -qE "config_version|outdated|Environment variable .* not found|KeyError|ValidationError|config\.yaml" logs/langgraph.log 2>/dev/null; then - echo "" - echo " Hint: This may be a configuration issue. Try running 'make config-upgrade' to update your config.yaml." - fi - cleanup_on_failure - exit 1 -} -echo "✓ LangGraph server started on localhost:2024" - -echo "Starting Gateway API..." -nohup sh -c 'cd backend && PYTHONPATH=. uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 > ../logs/gateway.log 2>&1' & -./scripts/wait-for-port.sh 8001 30 "Gateway API" || { - echo "✗ Gateway API failed to start. Last log output:" - tail -60 logs/gateway.log - echo "" - echo " Hint: Try running 'make config-upgrade' to update your config.yaml with the latest fields." - cleanup_on_failure - exit 1 -} -echo "✓ Gateway API started on localhost:8001" - -echo "Starting Frontend..." -nohup sh -c 'cd frontend && pnpm run dev > ../logs/frontend.log 2>&1' & -./scripts/wait-for-port.sh 3000 120 "Frontend" || { - echo "✗ Frontend failed to start. Last log output:" - tail -60 logs/frontend.log - cleanup_on_failure - exit 1 -} -echo "✓ Frontend started on localhost:3000" - -echo "Starting Nginx reverse proxy..." -nohup sh -c 'nginx -g "daemon off;" -c "$1/docker/nginx/nginx.local.conf" -p "$1" > logs/nginx.log 2>&1' _ "$REPO_ROOT" & -./scripts/wait-for-port.sh 2026 10 "Nginx" || { - echo "✗ Nginx failed to start. Last log output:" - tail -60 logs/nginx.log - cleanup_on_failure - exit 1 -} -echo "✓ Nginx started on localhost:2026" - -# ── Ready ───────────────────────────────────────────────────────────────────── - -echo "" -echo "==========================================" -echo " DeerFlow is running in daemon mode!" -echo "==========================================" -echo "" -echo " 🌐 Application: http://localhost:2026" -echo " 📡 API Gateway: http://localhost:2026/api/*" -echo " 🤖 LangGraph: http://localhost:2026/api/langgraph/*" -echo "" -echo " 📋 Logs:" -echo " - LangGraph: logs/langgraph.log" -echo " - Gateway: logs/gateway.log" -echo " - Frontend: logs/frontend.log" -echo " - Nginx: logs/nginx.log" -echo "" -echo " 🛑 Stop daemon: make stop" -echo "" +exec "$REPO_ROOT/scripts/serve.sh" --dev --daemon "$@"