services: web-admin: build: context: . dockerfile: Dockerfile.web image: assistant-web-admin:local ports: - "${WEB_ADMIN_HOST_PORT:-4173}:80" depends_on: assistant-api: condition: service_healthy restart: unless-stopped ops: profiles: ["ops"] build: context: . dockerfile: Dockerfile.ops image: assistant-ops:local environment: DATABASE_URL: ${DATABASE_URL:-postgresql://assistant:assistant@postgres:5432/assistant} volumes: - ./var/backups:/backups depends_on: postgres: condition: service_healthy migrate: build: context: . image: assistant-api:local working_dir: /app command: ["alembic", "-c", "/app/alembic.ini", "upgrade", "head"] environment: DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://assistant:assistant@postgres:5432/assistant} depends_on: postgres: condition: service_healthy restart: "no" runtime-init: build: context: . image: assistant-api:local user: "0:0" command: - /bin/sh - -c - >- mkdir -p /app/data/skills /app/data/prompts /app/data/skill-packages /app/data/artifacts /app/data/workspace/sessions /app/data/knowledge /app/data/sandbox /app/run && chown -R 10001:10001 /app/data /app/run volumes: - skills-data:/app/data/skills - prompts-data:/app/data/prompts - skill-packages-data:/app/data/skill-packages - artifacts-data:/app/data/artifacts - session-workspace-data:/app/data/workspace/sessions - knowledge-data:/app/data/knowledge - sandbox-data:/app/data/sandbox - beat-run:/app/run restart: "no" assistant-api: build: context: . image: assistant-api:local command: - uvicorn - app.main:app - --host - 0.0.0.0 - --port - "8000" environment: APP_ENV: ${APP_ENV:-local} LOG_LEVEL: ${LOG_LEVEL:-INFO} SERVICE_NAME: ${SERVICE_NAME:-assistant-api} LOCAL_API_AUTH_REQUIRED: ${LOCAL_API_AUTH_REQUIRED:-false} LOCAL_API_TOKEN: ${LOCAL_API_TOKEN:-} CREDENTIAL_MASTER_KEY: ${CREDENTIAL_MASTER_KEY:-} ADMIN_RECOVERY_ENABLED: ${ADMIN_RECOVERY_ENABLED:-false} ADMIN_RECOVERY_QUESTION: ${ADMIN_RECOVERY_QUESTION:-} ADMIN_RECOVERY_ANSWER_VERIFIER: ${ADMIN_RECOVERY_ANSWER_VERIFIER:-} ADMIN_RECOVERY_MAX_ATTEMPTS: ${ADMIN_RECOVERY_MAX_ATTEMPTS:-5} ADMIN_RECOVERY_COOLDOWN_MINUTES: ${ADMIN_RECOVERY_COOLDOWN_MINUTES:-15} MANAGED_SKILLS_ROOT: /app/data/skills MANAGED_PROMPTS_ROOT: /app/data/prompts SKILL_PACKAGES_ROOT: /app/data/skill-packages ARTIFACTS_ROOT: /app/data/artifacts SESSION_WORKSPACE_ROOT: /app/data/workspace/sessions KNOWLEDGE_ROOT: /app/data/knowledge DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://assistant:assistant@postgres:5432/assistant} REDIS_URL: ${REDIS_URL:-redis://redis:6379/0} SENTRY_DSN: ${SENTRY_DSN:-} LANGBOT_WEBHOOK_SECRET: ${LANGBOT_WEBHOOK_SECRET:-placeholder-langbot-webhook-secret} LANGBOT_API_BASE_URL: ${LANGBOT_API_BASE_URL:-https://langbot.invalid} LANGBOT_API_KEY: ${LANGBOT_API_KEY:-placeholder-langbot-api-key} LANGBOT_SEND_TIMEOUT_SECONDS: ${LANGBOT_SEND_TIMEOUT_SECONDS:-10} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-placeholder-deepseek-api-key} DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://deepseek.invalid/v1} DEEPSEEK_LIGHT_MODEL: ${DEEPSEEK_LIGHT_MODEL:-deepseek-light-placeholder} DEEPSEEK_STANDARD_MODEL: ${DEEPSEEK_STANDARD_MODEL:-deepseek-standard-placeholder} QWEN_API_KEY: ${QWEN_API_KEY:-} QWEN_BASE_URL: ${QWEN_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1} QWEN_RAG_MODEL: ${QWEN_RAG_MODEL:-qwen-plus} MODELS_TIMEOUT_SECONDS: ${MODELS_TIMEOUT_SECONDS:-30} MODELS_RETRY_ATTEMPTS: ${MODELS_RETRY_ATTEMPTS:-2} MODELS_NODES_JSON: ${MODELS_NODES_JSON:-[]} TAVILY_API_KEY: ${TAVILY_API_KEY:-placeholder-tavily-api-key} TAVILY_TIMEOUT_SECONDS: ${TAVILY_TIMEOUT_SECONDS:-10} TAVILY_MAX_RESULTS: ${TAVILY_MAX_RESULTS:-5} SEARCH_PROVIDER_ORDER: ${SEARCH_PROVIDER_ORDER:-tavily,serper,exa} SERPER_API_KEY: ${SERPER_API_KEY:-} SERPER_BASE_URL: ${SERPER_BASE_URL:-https://google.serper.dev/search} EXA_API_KEY: ${EXA_API_KEY:-} EXA_BASE_URL: ${EXA_BASE_URL:-https://api.exa.ai/search} SEARCH_FALLBACK_ON_EMPTY: ${SEARCH_FALLBACK_ON_EMPTY:-true} SEARCH_PROVIDER_TIMEOUT_SECONDS: ${SEARCH_PROVIDER_TIMEOUT_SECONDS:-} RUNNING_TASK_TIMEOUT_SECONDS: ${RUNNING_TASK_TIMEOUT_SECONDS:-300} PENDING_TASK_COMPENSATION_DELAY_SECONDS: ${PENDING_TASK_COMPENSATION_DELAY_SECONDS:-120} SCHEDULER_MAINTENANCE_INTERVAL_SECONDS: ${SCHEDULER_MAINTENANCE_INTERVAL_SECONDS:-300} BROWSER_ENABLED: ${BROWSER_ENABLED:-false} BROWSER_TIMEOUT_SECONDS: ${BROWSER_TIMEOUT_SECONDS:-20} BROWSER_MAX_TEXT_CHARS: ${BROWSER_MAX_TEXT_CHARS:-50000} SANDBOX_PROVIDER: ${SANDBOX_PROVIDER:-none} SHELL_EXEC_ENABLED: ${SHELL_EXEC_ENABLED:-false} SANDBOX_WORKSPACE_ROOT: /app/data/sandbox SANDBOX_DOCKER_IMAGE: ${SANDBOX_DOCKER_IMAGE:-} SANDBOX_DOCKER_ALLOWED_IMAGES: ${SANDBOX_DOCKER_ALLOWED_IMAGES:-} SANDBOX_TIMEOUT_SECONDS: ${SANDBOX_TIMEOUT_SECONDS:-30} SUBAGENT_ENABLED: ${SUBAGENT_ENABLED:-true} SUBAGENT_MAX_COUNT: ${SUBAGENT_MAX_COUNT:-3} SUBAGENT_CONCURRENCY: ${SUBAGENT_CONCURRENCY:-2} SUBAGENT_TIMEOUT_SECONDS: ${SUBAGENT_TIMEOUT_SECONDS:-30} MEM0_CONFIG_PATH: ${MEM0_CONFIG_PATH:-} MEM0_SEARCH_LIMIT: ${MEM0_SEARCH_LIMIT:-5} QUALITY_JUDGE_SAMPLE_RATE: ${QUALITY_JUDGE_SAMPLE_RATE:-0} QUALITY_JUDGE_POLICY_VERSION: ${QUALITY_JUDGE_POLICY_VERSION:-judge-v1} QUALITY_JUDGE_THRESHOLD: ${QUALITY_JUDGE_THRESHOLD:-0.6} WORKSPACE_CONTEXT_ROOT: ${WORKSPACE_CONTEXT_ROOT:-.} WORKSPACE_CONTEXT_ENABLED: ${WORKSPACE_CONTEXT_ENABLED:-true} WORKSPACE_CONTEXT_DENY_GLOBS: ${WORKSPACE_CONTEXT_DENY_GLOBS:-.env,.env.*,**/.env,**/.env.*,.git/**,**/.git/**,node_modules/**,**/node_modules/**,__pycache__/**,**/__pycache__/**,*.pem,**/*.pem,*.key,**/*.key,*.p12,**/*.p12,*.sqlite,**/*.sqlite,*.db,**/*.db} WORKSPACE_CONTEXT_MAX_FILE_BYTES: ${WORKSPACE_CONTEXT_MAX_FILE_BYTES:-200000} WORKSPACE_CONTEXT_MAX_RESULTS: ${WORKSPACE_CONTEXT_MAX_RESULTS:-50} READONLY_SHELL_ENABLED: ${READONLY_SHELL_ENABLED:-false} READONLY_SHELL_TIMEOUT_SECONDS: ${READONLY_SHELL_TIMEOUT_SECONDS:-10} READONLY_SHELL_MAX_OUTPUT_CHARS: ${READONLY_SHELL_MAX_OUTPUT_CHARS:-50000} DESKTOP_CAPTURE_ENABLED: ${DESKTOP_CAPTURE_ENABLED:-true} DESKTOP_CAPTURE_MODE: ${DESKTOP_CAPTURE_MODE:-controller_http} DESKTOP_CAPTURE_CONTROLLER_URL: ${DESKTOP_CAPTURE_CONTROLLER_URL:-http://host.docker.internal:8765/screenshot} DESKTOP_CAPTURE_DEFAULT_TARGET: ${DESKTOP_CAPTURE_DEFAULT_TARGET:-frontend} DESKTOP_CAPTURE_REQUIRE_CONFIRMATION_FOR_DESKTOP: ${DESKTOP_CAPTURE_REQUIRE_CONFIRMATION_FOR_DESKTOP:-true} DESKTOP_CAPTURE_ARTIFACT_TTL_SECONDS: ${DESKTOP_CAPTURE_ARTIFACT_TTL_SECONDS:-3600} LANGFUSE_PUBLIC_KEY: ${LANGFUSE_PUBLIC_KEY:-} LANGFUSE_SECRET_KEY: ${LANGFUSE_SECRET_KEY:-} LANGFUSE_BASE_URL: ${LANGFUSE_BASE_URL:-} extra_hosts: - "host.docker.internal:host-gateway" ports: - "127.0.0.1:${ASSISTANT_API_HOST_PORT:-18080}:8000" volumes: - skills-data:/app/data/skills - prompts-data:/app/data/prompts - skill-packages-data:/app/data/skill-packages - artifacts-data:/app/data/artifacts - session-workspace-data:/app/data/workspace/sessions - knowledge-data:/app/data/knowledge - sandbox-data:/app/data/sandbox depends_on: migrate: condition: service_completed_successfully redis: condition: service_healthy runtime-init: condition: service_completed_successfully healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3)"] interval: 10s timeout: 5s retries: 10 restart: unless-stopped celery-worker: build: context: . image: assistant-api:local command: - celery - -A - workers.worker:celery_app - worker - --loglevel=INFO - --concurrency=${CELERY_WORKER_CONCURRENCY:-1} environment: APP_ENV: ${APP_ENV:-local} LOG_LEVEL: ${LOG_LEVEL:-INFO} SERVICE_NAME: ${SERVICE_NAME:-assistant-api} CELERY_WORKER_CONCURRENCY: ${CELERY_WORKER_CONCURRENCY:-1} CREDENTIAL_MASTER_KEY: ${CREDENTIAL_MASTER_KEY:-} MANAGED_SKILLS_ROOT: /app/data/skills MANAGED_PROMPTS_ROOT: /app/data/prompts SKILL_PACKAGES_ROOT: /app/data/skill-packages ARTIFACTS_ROOT: /app/data/artifacts SESSION_WORKSPACE_ROOT: /app/data/workspace/sessions KNOWLEDGE_ROOT: /app/data/knowledge DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://assistant:assistant@postgres:5432/assistant} REDIS_URL: ${REDIS_URL:-redis://redis:6379/0} LANGBOT_API_BASE_URL: ${LANGBOT_API_BASE_URL:-https://langbot.invalid} LANGBOT_API_KEY: ${LANGBOT_API_KEY:-placeholder-langbot-api-key} LANGBOT_SEND_TIMEOUT_SECONDS: ${LANGBOT_SEND_TIMEOUT_SECONDS:-10} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-placeholder-deepseek-api-key} DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://deepseek.invalid/v1} DEEPSEEK_LIGHT_MODEL: ${DEEPSEEK_LIGHT_MODEL:-deepseek-light-placeholder} DEEPSEEK_STANDARD_MODEL: ${DEEPSEEK_STANDARD_MODEL:-deepseek-standard-placeholder} QWEN_API_KEY: ${QWEN_API_KEY:-} QWEN_BASE_URL: ${QWEN_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1} QWEN_RAG_MODEL: ${QWEN_RAG_MODEL:-qwen-plus} MODELS_TIMEOUT_SECONDS: ${MODELS_TIMEOUT_SECONDS:-30} MODELS_RETRY_ATTEMPTS: ${MODELS_RETRY_ATTEMPTS:-2} MODELS_NODES_JSON: ${MODELS_NODES_JSON:-[]} TAVILY_API_KEY: ${TAVILY_API_KEY:-placeholder-tavily-api-key} TAVILY_TIMEOUT_SECONDS: ${TAVILY_TIMEOUT_SECONDS:-10} TAVILY_MAX_RESULTS: ${TAVILY_MAX_RESULTS:-5} SEARCH_PROVIDER_ORDER: ${SEARCH_PROVIDER_ORDER:-tavily,serper,exa} SERPER_API_KEY: ${SERPER_API_KEY:-} SERPER_BASE_URL: ${SERPER_BASE_URL:-https://google.serper.dev/search} EXA_API_KEY: ${EXA_API_KEY:-} EXA_BASE_URL: ${EXA_BASE_URL:-https://api.exa.ai/search} SEARCH_FALLBACK_ON_EMPTY: ${SEARCH_FALLBACK_ON_EMPTY:-true} SEARCH_PROVIDER_TIMEOUT_SECONDS: ${SEARCH_PROVIDER_TIMEOUT_SECONDS:-} RUNNING_TASK_TIMEOUT_SECONDS: ${RUNNING_TASK_TIMEOUT_SECONDS:-300} PENDING_TASK_COMPENSATION_DELAY_SECONDS: ${PENDING_TASK_COMPENSATION_DELAY_SECONDS:-120} SCHEDULER_MAINTENANCE_INTERVAL_SECONDS: ${SCHEDULER_MAINTENANCE_INTERVAL_SECONDS:-300} BROWSER_ENABLED: ${BROWSER_ENABLED:-false} BROWSER_TIMEOUT_SECONDS: ${BROWSER_TIMEOUT_SECONDS:-20} BROWSER_MAX_TEXT_CHARS: ${BROWSER_MAX_TEXT_CHARS:-50000} SANDBOX_PROVIDER: ${SANDBOX_PROVIDER:-none} SHELL_EXEC_ENABLED: ${SHELL_EXEC_ENABLED:-false} SANDBOX_WORKSPACE_ROOT: /app/data/sandbox SANDBOX_DOCKER_IMAGE: ${SANDBOX_DOCKER_IMAGE:-} SANDBOX_DOCKER_ALLOWED_IMAGES: ${SANDBOX_DOCKER_ALLOWED_IMAGES:-} SANDBOX_TIMEOUT_SECONDS: ${SANDBOX_TIMEOUT_SECONDS:-30} SUBAGENT_ENABLED: ${SUBAGENT_ENABLED:-true} SUBAGENT_MAX_COUNT: ${SUBAGENT_MAX_COUNT:-3} SUBAGENT_CONCURRENCY: ${SUBAGENT_CONCURRENCY:-2} SUBAGENT_TIMEOUT_SECONDS: ${SUBAGENT_TIMEOUT_SECONDS:-30} MEM0_CONFIG_PATH: ${MEM0_CONFIG_PATH:-} MEM0_SEARCH_LIMIT: ${MEM0_SEARCH_LIMIT:-5} QUALITY_JUDGE_SAMPLE_RATE: ${QUALITY_JUDGE_SAMPLE_RATE:-0} QUALITY_JUDGE_POLICY_VERSION: ${QUALITY_JUDGE_POLICY_VERSION:-judge-v1} QUALITY_JUDGE_THRESHOLD: ${QUALITY_JUDGE_THRESHOLD:-0.6} WORKSPACE_CONTEXT_ROOT: ${WORKSPACE_CONTEXT_ROOT:-.} WORKSPACE_CONTEXT_ENABLED: ${WORKSPACE_CONTEXT_ENABLED:-true} WORKSPACE_CONTEXT_DENY_GLOBS: ${WORKSPACE_CONTEXT_DENY_GLOBS:-.env,.env.*,**/.env,**/.env.*,.git/**,**/.git/**,node_modules/**,**/node_modules/**,__pycache__/**,**/__pycache__/**,*.pem,**/*.pem,*.key,**/*.key,*.p12,**/*.p12,*.sqlite,**/*.sqlite,*.db,**/*.db} WORKSPACE_CONTEXT_MAX_FILE_BYTES: ${WORKSPACE_CONTEXT_MAX_FILE_BYTES:-200000} WORKSPACE_CONTEXT_MAX_RESULTS: ${WORKSPACE_CONTEXT_MAX_RESULTS:-50} READONLY_SHELL_ENABLED: ${READONLY_SHELL_ENABLED:-false} READONLY_SHELL_TIMEOUT_SECONDS: ${READONLY_SHELL_TIMEOUT_SECONDS:-10} READONLY_SHELL_MAX_OUTPUT_CHARS: ${READONLY_SHELL_MAX_OUTPUT_CHARS:-50000} DESKTOP_CAPTURE_ENABLED: ${DESKTOP_CAPTURE_ENABLED:-true} DESKTOP_CAPTURE_MODE: ${DESKTOP_CAPTURE_MODE:-controller_http} DESKTOP_CAPTURE_CONTROLLER_URL: ${DESKTOP_CAPTURE_CONTROLLER_URL:-http://host.docker.internal:8765/screenshot} DESKTOP_CAPTURE_DEFAULT_TARGET: ${DESKTOP_CAPTURE_DEFAULT_TARGET:-frontend} DESKTOP_CAPTURE_REQUIRE_CONFIRMATION_FOR_DESKTOP: ${DESKTOP_CAPTURE_REQUIRE_CONFIRMATION_FOR_DESKTOP:-true} DESKTOP_CAPTURE_ARTIFACT_TTL_SECONDS: ${DESKTOP_CAPTURE_ARTIFACT_TTL_SECONDS:-3600} LANGFUSE_PUBLIC_KEY: ${LANGFUSE_PUBLIC_KEY:-} LANGFUSE_SECRET_KEY: ${LANGFUSE_SECRET_KEY:-} LANGFUSE_BASE_URL: ${LANGFUSE_BASE_URL:-} extra_hosts: - "host.docker.internal:host-gateway" volumes: - skills-data:/app/data/skills - prompts-data:/app/data/prompts - skill-packages-data:/app/data/skill-packages - artifacts-data:/app/data/artifacts - session-workspace-data:/app/data/workspace/sessions - knowledge-data:/app/data/knowledge - sandbox-data:/app/data/sandbox depends_on: migrate: condition: service_completed_successfully redis: condition: service_healthy runtime-init: condition: service_completed_successfully healthcheck: test: ["CMD", "celery", "-A", "workers.worker:celery_app", "inspect", "ping", "--timeout", "5"] interval: 20s timeout: 10s retries: 5 restart: unless-stopped celery-beat: build: context: . image: assistant-api:local command: - celery - -A - workers.worker:celery_app - beat - --loglevel=INFO - --schedule=/app/run/celerybeat-schedule environment: APP_ENV: ${APP_ENV:-local} LOG_LEVEL: ${LOG_LEVEL:-INFO} SERVICE_NAME: ${SERVICE_NAME:-assistant-api} DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://assistant:assistant@postgres:5432/assistant} REDIS_URL: ${REDIS_URL:-redis://redis:6379/0} LANGBOT_API_KEY: ${LANGBOT_API_KEY:-placeholder-langbot-api-key} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-placeholder-deepseek-api-key} QWEN_API_KEY: ${QWEN_API_KEY:-} QWEN_BASE_URL: ${QWEN_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1} QWEN_RAG_MODEL: ${QWEN_RAG_MODEL:-qwen-plus} MODELS_TIMEOUT_SECONDS: ${MODELS_TIMEOUT_SECONDS:-30} MODELS_RETRY_ATTEMPTS: ${MODELS_RETRY_ATTEMPTS:-2} MODELS_NODES_JSON: ${MODELS_NODES_JSON:-[]} TAVILY_API_KEY: ${TAVILY_API_KEY:-placeholder-tavily-api-key} RUNNING_TASK_TIMEOUT_SECONDS: ${RUNNING_TASK_TIMEOUT_SECONDS:-300} PENDING_TASK_COMPENSATION_DELAY_SECONDS: ${PENDING_TASK_COMPENSATION_DELAY_SECONDS:-120} SCHEDULER_MAINTENANCE_INTERVAL_SECONDS: ${SCHEDULER_MAINTENANCE_INTERVAL_SECONDS:-300} depends_on: migrate: condition: service_completed_successfully redis: condition: service_healthy runtime-init: condition: service_completed_successfully healthcheck: test: ["CMD", "python", "-c", "import os; os.kill(1, 0)"] interval: 20s timeout: 5s retries: 5 restart: unless-stopped volumes: - beat-run:/app/run postgres: image: postgres:16 environment: POSTGRES_DB: ${POSTGRES_DB:-assistant} POSTGRES_USER: ${POSTGRES_USER:-assistant} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-assistant} volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", 'pg_isready -U "$${POSTGRES_USER}" -d "$${POSTGRES_DB}"'] interval: 5s timeout: 5s retries: 10 restart: unless-stopped redis: image: redis:7 healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 10 restart: unless-stopped volumes: postgres-data: skills-data: prompts-data: skill-packages-data: artifacts-data: session-workspace-data: knowledge-data: sandbox-data: beat-run: