x-app-env: &app-env APP_ENV: ${APP_ENV:-dev} APP_LOG_LEVEL: ${APP_LOG_LEVEL:-INFO} APP_SERVICE_NAME: ${COMPOSE_APP_SERVICE_NAME:-agent-console} AGENT_CONSOLE_ENV_FILE: ${COMPOSE_AGENT_CONSOLE_ENV_FILE:-/data/config/.env} OTEL_EXPORTER_OTLP_ENDPOINT: ${COMPOSE_OTEL_EXPORTER_OTLP_ENDPOINT:-http://otel-collector:4317} OTEL_EXPORTER_OTLP_INSECURE: ${COMPOSE_OTEL_EXPORTER_OTLP_INSECURE:-true} OTEL_TRACES_SAMPLER_ARG: ${COMPOSE_OTEL_TRACES_SAMPLER_ARG:-1.0} DB_DSN: ${COMPOSE_DB_DSN:-postgresql+asyncpg://cs:compose_dev_postgres_password@postgres:5432/cs} REDIS_URL: ${COMPOSE_REDIS_URL:-redis://redis:6379/0} QDRANT_URL: ${COMPOSE_QDRANT_URL:-http://qdrant:6333} QDRANT_API_KEY: ${COMPOSE_QDRANT_API_KEY:-} LLM_PROVIDER: ${LLM_PROVIDER:-fake} OPENAI_API_KEY: ${COMPOSE_OPENAI_API_KEY:-} OPENAI_BASE_URL: ${COMPOSE_OPENAI_BASE_URL:-https://api.openai.com/v1} XAI_API_KEY: ${XAI_API_KEY:-} GROK_MODELS_BASE_URL: ${GROK_MODELS_BASE_URL:-} OPENAI_API_MODE: ${COMPOSE_OPENAI_API_MODE:-responses} OPENAI_RESPONSES_STREAM_FIRST_EVENT_TIMEOUT_SECONDS: ${COMPOSE_OPENAI_RESPONSES_STREAM_FIRST_EVENT_TIMEOUT_SECONDS:-45} OPENAI_RESPONSES_STREAM_IDLE_TIMEOUT_SECONDS: ${COMPOSE_OPENAI_RESPONSES_STREAM_IDLE_TIMEOUT_SECONDS:-120} OPENAI_RESPONSES_STREAM_MAX_DURATION_SECONDS: ${COMPOSE_OPENAI_RESPONSES_STREAM_MAX_DURATION_SECONDS:-300} OPENAI_WEB_SEARCH_ENABLED: ${COMPOSE_OPENAI_WEB_SEARCH_ENABLED:-false} OPENAI_WEB_SEARCH_TOOL: ${COMPOSE_OPENAI_WEB_SEARCH_TOOL:-web_search} OPENAI_WEB_SEARCH_LIVE_ENABLED: ${COMPOSE_OPENAI_WEB_SEARCH_LIVE_ENABLED:-true} LLM_EMBED_PROVIDER: ${COMPOSE_LLM_EMBED_PROVIDER:-fake} KNOWLEDGE_FEATURES_ENABLED: ${COMPOSE_KNOWLEDGE_FEATURES_ENABLED:-false} CUSTOMER_SERVICE_PROMPT_ENABLED: ${COMPOSE_CUSTOMER_SERVICE_PROMPT_ENABLED:-true} LLM_MODEL_TIER1: ${COMPOSE_LLM_MODEL_TIER1:-gpt-5.5} LLM_MODEL_TIER2: ${COMPOSE_LLM_MODEL_TIER2:-gpt-5.5} LLM_MODEL_TIER3: ${COMPOSE_LLM_MODEL_TIER3:-gpt-5.5} LOCAL_AGENT_BASE_URL: ${LOCAL_AGENT_BASE_URL:-} LOCAL_AGENT_TOKEN: ${LOCAL_AGENT_TOKEN:-} LOCAL_AGENT_OVERFLOW_ENABLED: ${LOCAL_AGENT_OVERFLOW_ENABLED:-true} LOCAL_AGENT_OVERFLOW_MIN_CHARS: ${LOCAL_AGENT_OVERFLOW_MIN_CHARS:-24000} LOCAL_AGENT_OVERFLOW_BACKEND: ${LOCAL_AGENT_OVERFLOW_BACKEND:-auto} WXBOT_REPORT_LLM_BACKEND: ${WXBOT_REPORT_LLM_BACKEND:-http} WXBOT_SELF_REVIEW_LLM_BACKEND: ${WXBOT_SELF_REVIEW_LLM_BACKEND:-} PERSONA_EXTRACT_LLM_BACKEND: ${PERSONA_EXTRACT_LLM_BACKEND:-http} SPEAKER_PORTRAIT_LLM_BACKEND: ${SPEAKER_PORTRAIT_LLM_BACKEND:-grok} SPEAKER_PORTRAIT_TIMEOUT_SECONDS: ${SPEAKER_PORTRAIT_TIMEOUT_SECONDS:-600} SPEAKER_PORTRAIT_MAX_CHARS: ${SPEAKER_PORTRAIT_MAX_CHARS:-80000} SPEAKER_PORTRAIT_WORKER_ROLES: ${SPEAKER_PORTRAIT_WORKER_ROLES:-scheduler} SPEAKER_PORTRAIT_FULL_MAX_CHARS: ${SPEAKER_PORTRAIT_FULL_MAX_CHARS:-160000} SPEAKER_PORTRAIT_HOT_UPDATE_ENABLED: ${SPEAKER_PORTRAIT_HOT_UPDATE_ENABLED:-true} SPEAKER_PORTRAIT_HOT_UPDATE_MIN_MESSAGES: ${SPEAKER_PORTRAIT_HOT_UPDATE_MIN_MESSAGES:-40} SPEAKER_PORTRAIT_HOT_UPDATE_MIN_SECONDS: ${SPEAKER_PORTRAIT_HOT_UPDATE_MIN_SECONDS:-3600} SPEAKER_PORTRAIT_DATA_DIR: ${SPEAKER_PORTRAIT_DATA_DIR:-/data/portraits} SPEAKER_PORTRAIT_HOST_DIR: ${SPEAKER_PORTRAIT_HOST_DIR:-/opt/agent-console-portraits} SPEAKER_PORTRAIT_INLINE_MAX_MESSAGES: ${SPEAKER_PORTRAIT_INLINE_MAX_MESSAGES:-400} SPEAKER_PORTRAIT_MAX_TURNS: ${SPEAKER_PORTRAIT_MAX_TURNS:-40} LLM_EMBED_MODEL: ${COMPOSE_LLM_EMBED_MODEL:-voyage-3} PERSONA_EXTRACT_STAGE_TIMEOUT_SECONDS: ${COMPOSE_PERSONA_EXTRACT_STAGE_TIMEOUT_SECONDS:-120} PERSONA_EXTRACT_STAGE_MAX_RETRIES: ${COMPOSE_PERSONA_EXTRACT_STAGE_MAX_RETRIES:-0} PERSONA_EXTRACT_JOB_MAX_ATTEMPTS: ${COMPOSE_PERSONA_EXTRACT_JOB_MAX_ATTEMPTS:-3} PERSONA_EXTRACT_JOB_RETRY_BACKOFF_SECONDS: ${COMPOSE_PERSONA_EXTRACT_JOB_RETRY_BACKOFF_SECONDS:-30} PERSONA_EXTRACT_JOB_LEASE_SECONDS: ${COMPOSE_PERSONA_EXTRACT_JOB_LEASE_SECONDS:-180} PERSONA_EXTRACT_JOB_HEARTBEAT_SECONDS: ${COMPOSE_PERSONA_EXTRACT_JOB_HEARTBEAT_SECONDS:-30} PERSONA_EXTRACT_JOB_POLL_INTERVAL_SECONDS: ${COMPOSE_PERSONA_EXTRACT_JOB_POLL_INTERVAL_SECONDS:-2} PERSONA_EXTRACT_WORKER_ROLES: ${COMPOSE_PERSONA_EXTRACT_WORKER_ROLES:-scheduler} PERSONA_EXTRACT_CHUNK_MAX_TOKENS: ${COMPOSE_PERSONA_EXTRACT_CHUNK_MAX_TOKENS:-8000} PERSONA_EXTRACT_CHUNK_MAX_MESSAGES: ${COMPOSE_PERSONA_EXTRACT_CHUNK_MAX_MESSAGES:-400} PERSONA_EXTRACT_CHUNK_CONCURRENCY: ${COMPOSE_PERSONA_EXTRACT_CHUNK_CONCURRENCY:-3} PERSONA_EXTRACT_ONLINE_MAX_MESSAGES: ${COMPOSE_PERSONA_EXTRACT_ONLINE_MAX_MESSAGES:-10000} PERSONA_EXTRACT_OFFLINE_EXPORT_DIR: ${COMPOSE_PERSONA_EXTRACT_OFFLINE_EXPORT_DIR:-/data/config/persona-exports} PERSONA_EXTRACT_OFFLINE_EXPORT_TIMEOUT_SECONDS: ${COMPOSE_PERSONA_EXTRACT_OFFLINE_EXPORT_TIMEOUT_SECONDS:-600} PERSONA_EXTRACT_OFFLINE_EXPORT_MAX_BYTES: ${COMPOSE_PERSONA_EXTRACT_OFFLINE_EXPORT_MAX_BYTES:-268435456} PERSONA_EXTRACT_OFFLINE_RETENTION_SECONDS: ${COMPOSE_PERSONA_EXTRACT_OFFLINE_RETENTION_SECONDS:-604800} RAG_VECTOR_RELEVANCE_THRESHOLD: ${COMPOSE_RAG_VECTOR_RELEVANCE_THRESHOLD:-0.2} RAG_KEYWORD_OVERLAP_THRESHOLD: ${COMPOSE_RAG_KEYWORD_OVERLAP_THRESHOLD:-0.12} RAG_RERANK_ENABLED: ${COMPOSE_RAG_RERANK_ENABLED:-true} RAG_CITATION_VALIDATION_ENABLED: ${COMPOSE_RAG_CITATION_VALIDATION_ENABLED:-true} RAG_CITATION_REPAIR_ENABLED: ${COMPOSE_RAG_CITATION_REPAIR_ENABLED:-true} RAG_CITATION_SUPPORT_THRESHOLD: ${COMPOSE_RAG_CITATION_SUPPORT_THRESHOLD:-0.08} TENANT_DEFAULT_DAILY_TOKENS: ${COMPOSE_TENANT_DEFAULT_DAILY_TOKENS:-1000000} ORCHESTRATOR_HANDLE_TIMEOUT_SECONDS: ${COMPOSE_ORCHESTRATOR_HANDLE_TIMEOUT_SECONDS:-150} ORCHESTRATOR_CAPABILITY_DISPATCH_TIMEOUT_SECONDS: ${COMPOSE_ORCHESTRATOR_CAPABILITY_DISPATCH_TIMEOUT_SECONDS:-135} SESSION_LOCK_LOST_MAX_RETRIES: ${COMPOSE_SESSION_LOCK_LOST_MAX_RETRIES:-1} SESSION_LOCK_RETRY_BACKOFF_SECONDS: ${COMPOSE_SESSION_LOCK_RETRY_BACKOFF_SECONDS:-0.05} ORCHESTRATOR_FLOW_RUNTIME_ENABLED: ${COMPOSE_ORCHESTRATOR_FLOW_RUNTIME_ENABLED:-false} ORCHESTRATOR_FLOW_RUNTIME_NAME: ${COMPOSE_ORCHESTRATOR_FLOW_RUNTIME_NAME:-default_compatible_flow} ORCHESTRATOR_FLOW_RUNTIME_ALLOWED_NAMES: ${COMPOSE_ORCHESTRATOR_FLOW_RUNTIME_ALLOWED_NAMES:-default_compatible_flow} ORCHESTRATOR_FLOW_RUNTIME_ALLOW_TARGET_FLOWS: ${COMPOSE_ORCHESTRATOR_FLOW_RUNTIME_ALLOW_TARGET_FLOWS:-false} ORCHESTRATOR_FLOW_RUNTIME_ALLOW_COMPATIBLE_FALLBACK: ${COMPOSE_ORCHESTRATOR_FLOW_RUNTIME_ALLOW_COMPATIBLE_FALLBACK:-false} ORCHESTRATOR_FLOW_EFFECT_COMMIT_BACKEND: ${COMPOSE_ORCHESTRATOR_FLOW_EFFECT_COMMIT_BACKEND:-none} ORCHESTRATOR_FLOW_EFFECT_HANDLERS_ENABLED: ${COMPOSE_ORCHESTRATOR_FLOW_EFFECT_HANDLERS_ENABLED:-false} ORCHESTRATOR_FLOW_EFFECT_LOG_BACKEND: ${COMPOSE_ORCHESTRATOR_FLOW_EFFECT_LOG_BACKEND:-none} ORCHESTRATOR_FLOW_EFFECT_LOG_FAILURE_POLICY: ${COMPOSE_ORCHESTRATOR_FLOW_EFFECT_LOG_FAILURE_POLICY:-fail_closed} ORCHESTRATOR_FLOW_SHADOW_ENABLED: ${COMPOSE_ORCHESTRATOR_FLOW_SHADOW_ENABLED:-false} ORCHESTRATOR_FLOW_SHADOW_NAME: ${COMPOSE_ORCHESTRATOR_FLOW_SHADOW_NAME:-default_compatible_flow} ORCHESTRATOR_FLOW_SHADOW_MODE: ${COMPOSE_ORCHESTRATOR_FLOW_SHADOW_MODE:-noop} ORCHESTRATOR_FLOW_SHADOW_CORE_PREVIEW_ENABLED: ${COMPOSE_ORCHESTRATOR_FLOW_SHADOW_CORE_PREVIEW_ENABLED:-false} ORCHESTRATOR_FLOW_SHADOW_PLUGIN_DRY_RUN_ENABLED: ${COMPOSE_ORCHESTRATOR_FLOW_SHADOW_PLUGIN_DRY_RUN_ENABLED:-false} ORCHESTRATOR_FLOW_SHADOW_EFFECT_DRY_RUN_ENABLED: ${COMPOSE_ORCHESTRATOR_FLOW_SHADOW_EFFECT_DRY_RUN_ENABLED:-false} MEMORY_RETRIEVAL_ENABLED: ${COMPOSE_MEMORY_RETRIEVAL_ENABLED:-true} MEMORY_HYBRID_RETRIEVAL_ENABLED: ${COMPOSE_MEMORY_HYBRID_RETRIEVAL_ENABLED:-false} MEMORY_LLM_EXTRACTION_ENABLED: ${COMPOSE_MEMORY_LLM_EXTRACTION_ENABLED:-false} MEMORY_LLM_EXTRACTION_TIMEOUT_SECONDS: ${COMPOSE_MEMORY_LLM_EXTRACTION_TIMEOUT_SECONDS:-1.0} MEMORY_LLM_EXTRACTION_MAX_ACTIONS: ${COMPOSE_MEMORY_LLM_EXTRACTION_MAX_ACTIONS:-4} MEMORY_LLM_EXTRACTION_MIN_CONFIDENCE: ${COMPOSE_MEMORY_LLM_EXTRACTION_MIN_CONFIDENCE:-0.75} MEMORY_ACCEPTANCE_AUTO_ACCEPT_MIN: ${COMPOSE_MEMORY_ACCEPTANCE_AUTO_ACCEPT_MIN:-0.78} MEMORY_ACCEPTANCE_REJECT_BELOW: ${COMPOSE_MEMORY_ACCEPTANCE_REJECT_BELOW:-0.35} MEMORY_LLM_EXTRACTION_JOB_ENABLED: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_ENABLED:-true} MEMORY_LLM_EXTRACTION_JOB_DRAIN_ENABLED: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_DRAIN_ENABLED:-false} MEMORY_LLM_EXTRACTION_JOB_SCOPE_ALLOWLIST: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_SCOPE_ALLOWLIST:-} MEMORY_LLM_EXTRACTION_JOB_DRAIN_BATCH_SIZE: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_DRAIN_BATCH_SIZE:-5} MEMORY_LLM_EXTRACTION_JOB_DRAIN_MAX_CLAIMS: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_DRAIN_MAX_CLAIMS:-0} MEMORY_LLM_EXTRACTION_JOB_MAX_ATTEMPTS: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_MAX_ATTEMPTS:-3} MEMORY_LLM_EXTRACTION_JOB_BACKOFF_SECONDS: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_BACKOFF_SECONDS:-30.0} MEMORY_LLM_EXTRACTION_JOB_TIMEOUT_SECONDS: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_TIMEOUT_SECONDS:-5.0} MEMORY_LLM_EXTRACTION_JOB_LOCK_TTL_SECONDS: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_LOCK_TTL_SECONDS:-60.0} MEMORY_LLM_EXTRACTION_JOB_DRAIN_INTERVAL_SECONDS: ${COMPOSE_MEMORY_LLM_EXTRACTION_JOB_DRAIN_INTERVAL_SECONDS:-5.0} MEMORY_GROUP_IDENTITY_MEMORY_ENABLED: ${COMPOSE_MEMORY_GROUP_IDENTITY_MEMORY_ENABLED:-false} MEMORY_RETRIEVAL_TOP_K: ${COMPOSE_MEMORY_RETRIEVAL_TOP_K:-6} MEMORY_RETRIEVAL_BUDGET_CHARS: ${COMPOSE_MEMORY_RETRIEVAL_BUDGET_CHARS:-1600} MEMORY_VECTOR_INDEX_ENABLED: ${COMPOSE_MEMORY_VECTOR_INDEX_ENABLED:-false} MEMORY_VECTOR_COLLECTION: ${COMPOSE_MEMORY_VECTOR_COLLECTION:-agent_console_memory_items} MEMORY_VECTOR_SIZE: ${COMPOSE_MEMORY_VECTOR_SIZE:-64} MEMORY_VECTOR_EMBED_MODEL: ${COMPOSE_MEMORY_VECTOR_EMBED_MODEL:-} MEMORY_VECTOR_TIMEOUT_SECONDS: ${COMPOSE_MEMORY_VECTOR_TIMEOUT_SECONDS:-2.0} MEMORY_VECTOR_TOP_K: ${COMPOSE_MEMORY_VECTOR_TOP_K:-12} MEMORY_GRAPH_VECTOR_TOP_K: ${COMPOSE_MEMORY_GRAPH_VECTOR_TOP_K:-12} MEMORY_VECTOR_INDEX_STRICT_STARTUP_CHECK: ${COMPOSE_MEMORY_VECTOR_INDEX_STRICT_STARTUP_CHECK:-false} MEMORY_GRAPH_RETRIEVAL_ENABLED: ${COMPOSE_MEMORY_GRAPH_RETRIEVAL_ENABLED:-false} MEMORY_GRAPH_RETRIEVAL_FACT_TOP_K: ${COMPOSE_MEMORY_GRAPH_RETRIEVAL_FACT_TOP_K:-3} MEMORY_GRAPH_RETRIEVAL_EPISODE_TOP_K: ${COMPOSE_MEMORY_GRAPH_RETRIEVAL_EPISODE_TOP_K:-2} MEMORY_GRAPH_RETRIEVAL_BUDGET_CHARS: ${COMPOSE_MEMORY_GRAPH_RETRIEVAL_BUDGET_CHARS:-600} MEMORY_GRAPH_LLM_EXTRACTION_ENABLED: ${COMPOSE_MEMORY_GRAPH_LLM_EXTRACTION_ENABLED:-false} MEMORY_GRAPH_LLM_EXTRACTION_TIMEOUT_SECONDS: ${COMPOSE_MEMORY_GRAPH_LLM_EXTRACTION_TIMEOUT_SECONDS:-1.0} MEMORY_GRAPH_LLM_EXTRACTION_MAX_ACTIONS: ${COMPOSE_MEMORY_GRAPH_LLM_EXTRACTION_MAX_ACTIONS:-16} MEMORY_GRAPH_LLM_EXTRACTION_MAX_ENTITIES: ${COMPOSE_MEMORY_GRAPH_LLM_EXTRACTION_MAX_ENTITIES:-8} MEMORY_GRAPH_LLM_EXTRACTION_MAX_FACTS: ${COMPOSE_MEMORY_GRAPH_LLM_EXTRACTION_MAX_FACTS:-4} MEMORY_GRAPH_LLM_EXTRACTION_MAX_EPISODES: ${COMPOSE_MEMORY_GRAPH_LLM_EXTRACTION_MAX_EPISODES:-2} MEMORY_GRAPH_LLM_EXTRACTION_MIN_CONFIDENCE: ${COMPOSE_MEMORY_GRAPH_LLM_EXTRACTION_MIN_CONFIDENCE:-0.8} MEMORY_GOVERNANCE_AUTO_CLEANUP_ENABLED: ${COMPOSE_MEMORY_GOVERNANCE_AUTO_CLEANUP_ENABLED:-true} MEMORY_GOVERNANCE_INTERVAL_SECONDS: ${COMPOSE_MEMORY_GOVERNANCE_INTERVAL_SECONDS:-86400} MEMORY_NEEDS_REVIEW_RETENTION_DAYS: ${COMPOSE_MEMORY_NEEDS_REVIEW_RETENTION_DAYS:-30} MEMORY_REJECTED_RETENTION_DAYS: ${COMPOSE_MEMORY_REJECTED_RETENTION_DAYS:-7} MEMORY_AUTO_EXPIRE_DAYS: ${COMPOSE_MEMORY_AUTO_EXPIRE_DAYS:-180} MEMORY_GOVERNANCE_BATCH_SIZE: ${COMPOSE_MEMORY_GOVERNANCE_BATCH_SIZE:-500} AGENT_MAX_TOOL_ROUNDS: ${COMPOSE_AGENT_MAX_TOOL_ROUNDS:-5} AGENT_MAX_TOOL_CALLS_PER_ROUND: ${COMPOSE_AGENT_MAX_TOOL_CALLS_PER_ROUND:-4} AGENT_REQUIRED_WEB_SEARCH_TIMEOUT_SECONDS: ${COMPOSE_AGENT_REQUIRED_WEB_SEARCH_TIMEOUT_SECONDS:-90} AGENT_REQUIRED_WEB_SEARCH_MAX_OUTPUT_TOKENS: ${COMPOSE_AGENT_REQUIRED_WEB_SEARCH_MAX_OUTPUT_TOKENS:-2400} AGENT_REQUIRED_WEB_SEARCH_MAX_ATTEMPTS: ${COMPOSE_AGENT_REQUIRED_WEB_SEARCH_MAX_ATTEMPTS:-2} ANTHROPIC_API_KEY: ${COMPOSE_ANTHROPIC_API_KEY:-} OUTBOUND_WEBHOOK_URL: ${COMPOSE_OUTBOUND_WEBHOOK_URL:-http://host.docker.internal:9999/deliver} OUTBOUND_HMAC_SECRET: ${COMPOSE_OUTBOUND_HMAC_SECRET:-compose_dev_outbound_secret} OUTBOUND_MAX_RETRIES: ${COMPOSE_OUTBOUND_MAX_RETRIES:-5} OUTBOUND_TIMEOUT_SECONDS: ${COMPOSE_OUTBOUND_TIMEOUT_SECONDS:-10} OUTBOUND_TRANSPORT_MAX_ATTEMPTS: ${COMPOSE_OUTBOUND_TRANSPORT_MAX_ATTEMPTS:-3} OUTBOUND_TRANSPORT_RETRY_BASE_SECONDS: ${COMPOSE_OUTBOUND_TRANSPORT_RETRY_BASE_SECONDS:-0.2} OUTBOUND_TRANSPORT_RETRY_MAX_SECONDS: ${COMPOSE_OUTBOUND_TRANSPORT_RETRY_MAX_SECONDS:-2} OUTBOX_RELAY_POLL_INTERVAL_SECONDS: ${COMPOSE_OUTBOX_RELAY_POLL_INTERVAL_SECONDS:-1} OUTBOX_RELAY_BATCH_SIZE: ${COMPOSE_OUTBOX_RELAY_BATCH_SIZE:-50} OUTBOX_RELAY_LEASE_SECONDS: ${COMPOSE_OUTBOX_RELAY_LEASE_SECONDS:-30} OUTBOX_RELAY_PUBLISH_TIMEOUT_SECONDS: ${COMPOSE_OUTBOX_RELAY_PUBLISH_TIMEOUT_SECONDS:-10} OUTBOX_RELAY_MAX_ATTEMPTS: ${COMPOSE_OUTBOX_RELAY_MAX_ATTEMPTS:-12} EFFECT_INTENT_RELAY_POLL_INTERVAL_SECONDS: ${COMPOSE_EFFECT_INTENT_RELAY_POLL_INTERVAL_SECONDS:-0.5} EFFECT_INTENT_RELAY_BATCH_SIZE: ${COMPOSE_EFFECT_INTENT_RELAY_BATCH_SIZE:-32} EFFECT_INTENT_RELAY_LEASE_SECONDS: ${COMPOSE_EFFECT_INTENT_RELAY_LEASE_SECONDS:-30} EFFECT_INTENT_RELAY_HANDLER_TIMEOUT_SECONDS: ${COMPOSE_EFFECT_INTENT_RELAY_HANDLER_TIMEOUT_SECONDS:-20} EFFECT_INTENT_RELAY_MAX_ATTEMPTS: ${COMPOSE_EFFECT_INTENT_RELAY_MAX_ATTEMPTS:-12} INBOX_PROCESSING_LEASE_SECONDS: ${COMPOSE_INBOX_PROCESSING_LEASE_SECONDS:-180} TENANT_INBOUND_SECRETS: ${COMPOSE_TENANT_INBOUND_SECRETS:-{}} TENANT_OUTBOUND_WEBHOOK_URLS: ${COMPOSE_TENANT_OUTBOUND_WEBHOOK_URLS:-{}} TENANT_OUTBOUND_HMAC_SECRETS: ${COMPOSE_TENANT_OUTBOUND_HMAC_SECRETS:-{}} ADMIN_BEARER_TOKEN: ${COMPOSE_ADMIN_BEARER_TOKEN:-} ADMIN_SESSION_SIGNING_SECRET: ${COMPOSE_ADMIN_SESSION_SIGNING_SECRET:-} MEDIA_ID_SIGNING_SECRET: ${COMPOSE_MEDIA_ID_SIGNING_SECRET:-} ADMIN_SESSION_TTL_SECONDS: ${COMPOSE_ADMIN_SESSION_TTL_SECONDS:-3600} ADMIN_SESSION_COOKIE_NAME: ${COMPOSE_ADMIN_SESSION_COOKIE_NAME:-agent_console_admin_session} ADMIN_SESSION_COOKIE_SECURE: ${COMPOSE_ADMIN_SESSION_COOKIE_SECURE:-false} ADMIN_ALLOW_BEARER_FALLBACK: ${COMPOSE_ADMIN_ALLOW_BEARER_FALLBACK:-true} TENANT_DEMO_SECRET: ${COMPOSE_TENANT_DEMO_SECRET:-compose_dev_tenant_secret} FRONTEND_CORS_ORIGINS: ${COMPOSE_FRONTEND_CORS_ORIGINS:-http://127.0.0.1:4173,http://localhost:4173,http://127.0.0.1:5173,http://localhost:5173} WXBOT_DAILY_REPORT_FOOTER: ${COMPOSE_WXBOT_DAILY_REPORT_FOOTER:-} WXBOT_REPORT_MAX_CHARS_PER_CHUNK: ${COMPOSE_WXBOT_REPORT_MAX_CHARS_PER_CHUNK:-12000} TIBO_RESET_ENABLED: ${COMPOSE_TIBO_RESET_ENABLED:-false} TIBO_RESET_API_URL: ${COMPOSE_TIBO_RESET_API_URL:-} TIBO_RESET_POLL_INTERVAL_SECONDS: ${COMPOSE_TIBO_RESET_POLL_INTERVAL_SECONDS:-300} TIBO_RESET_REQUEST_TIMEOUT_SECONDS: ${COMPOSE_TIBO_RESET_REQUEST_TIMEOUT_SECONDS:-15} TIBO_RESET_TIMEZONE: ${COMPOSE_TIBO_RESET_TIMEZONE:-UTC} DRAW_API_URL: ${COMPOSE_DRAW_API_URL:-} DRAW_API_EDIT_URL: ${COMPOSE_DRAW_API_EDIT_URL:-} DRAW_API_KEY: ${COMPOSE_DRAW_API_KEY:-} DRAW_API_MODEL: ${COMPOSE_DRAW_API_MODEL:-} DRAW_API_PROVIDER: ${COMPOSE_DRAW_API_PROVIDER:-} DRAW_API_TIMEOUT_SECONDS: ${COMPOSE_DRAW_API_TIMEOUT_SECONDS:-600} DRAW_API_KEY_HEADER: ${COMPOSE_DRAW_API_KEY_HEADER:-Authorization} DRAW_API_KEY_PREFIX: ${COMPOSE_DRAW_API_KEY_PREFIX:-Bearer } DRAW_API_PROMPT_FIELD: ${COMPOSE_DRAW_API_PROMPT_FIELD:-prompt} DRAW_API_MODEL_FIELD: ${COMPOSE_DRAW_API_MODEL_FIELD:-model} DRAW_API_RESPONSE_FORMAT: ${COMPOSE_DRAW_API_RESPONSE_FORMAT:-} DRAW_API_EXTRA_BODY: ${COMPOSE_DRAW_API_EXTRA_BODY:-} DRAW_FALLBACK_API_URL: ${COMPOSE_DRAW_FALLBACK_API_URL:-} DRAW_FALLBACK_API_EDIT_URL: ${COMPOSE_DRAW_FALLBACK_API_EDIT_URL:-} DRAW_FALLBACK_API_KEY: ${COMPOSE_DRAW_FALLBACK_API_KEY:-} DRAW_FALLBACK_API_MODEL: ${COMPOSE_DRAW_FALLBACK_API_MODEL:-} DRAW_FALLBACK_API_PROVIDER: ${COMPOSE_DRAW_FALLBACK_API_PROVIDER:-} DRAW_FALLBACK_API_TIMEOUT_SECONDS: ${COMPOSE_DRAW_FALLBACK_API_TIMEOUT_SECONDS:-600} DRAW_FALLBACK_API_KEY_HEADER: ${COMPOSE_DRAW_FALLBACK_API_KEY_HEADER:-Authorization} DRAW_FALLBACK_API_KEY_PREFIX: ${COMPOSE_DRAW_FALLBACK_API_KEY_PREFIX:-Bearer } DRAW_FALLBACK_API_PROMPT_FIELD: ${COMPOSE_DRAW_FALLBACK_API_PROMPT_FIELD:-prompt} DRAW_FALLBACK_API_MODEL_FIELD: ${COMPOSE_DRAW_FALLBACK_API_MODEL_FIELD:-model} DRAW_FALLBACK_API_RESPONSE_FORMAT: ${COMPOSE_DRAW_FALLBACK_API_RESPONSE_FORMAT:-} DRAW_FALLBACK_API_EXTRA_BODY: ${COMPOSE_DRAW_FALLBACK_API_EXTRA_BODY:-} DRAW_STORAGE_DIR: ${COMPOSE_DRAW_STORAGE_DIR:-/data/draw} VIDEO_API_URL: ${COMPOSE_VIDEO_API_URL:-} VIDEO_API_KEY: ${COMPOSE_VIDEO_API_KEY:-} VIDEO_API_MODEL: ${COMPOSE_VIDEO_API_MODEL:-grok-imagine-video-1.5-preview} VIDEO_API_TIMEOUT_SECONDS: ${COMPOSE_VIDEO_API_TIMEOUT_SECONDS:-600} VIDEO_API_POLL_INTERVAL_SECONDS: ${COMPOSE_VIDEO_API_POLL_INTERVAL_SECONDS:-5} VIDEO_API_POLL_TIMEOUT_SECONDS: ${COMPOSE_VIDEO_API_POLL_TIMEOUT_SECONDS:-1800} VIDEO_API_KEY_HEADER: ${COMPOSE_VIDEO_API_KEY_HEADER:-Authorization} VIDEO_API_KEY_PREFIX: ${COMPOSE_VIDEO_API_KEY_PREFIX:-Bearer } VIDEO_API_EXTRA_BODY: ${COMPOSE_VIDEO_API_EXTRA_BODY:-} VIDEO_STORAGE_DIR: ${COMPOSE_VIDEO_STORAGE_DIR:-/data/video} MODERATION_WEBHOOK_ALLOWED_HOSTS: ${COMPOSE_MODERATION_WEBHOOK_ALLOWED_HOSTS:-qyapi.weixin.qq.com} AMAP_API_KEY: ${COMPOSE_AMAP_API_KEY:-} AMAP_API_TIMEOUT_SECONDS: ${COMPOSE_AMAP_API_TIMEOUT_SECONDS:-30} AMAP_STORAGE_DIR: ${COMPOSE_AMAP_STORAGE_DIR:-/data/amap} AMAP_SEARCH_CREDIT_COST: ${COMPOSE_AMAP_SEARCH_CREDIT_COST:-2} AMAP_MAP_CREDIT_COST: ${COMPOSE_AMAP_MAP_CREDIT_COST:-8} AMAP_ROUTE_MAP_CREDIT_COST: ${COMPOSE_AMAP_ROUTE_MAP_CREDIT_COST:-12} BUS_CONSUME_BATCH_SIZE: ${BUS_CONSUME_BATCH_SIZE:-16} BUS_CONSUME_BLOCK_MS: ${BUS_CONSUME_BLOCK_MS:-5000} BUS_MAX_ATTEMPTS: ${COMPOSE_BUS_MAX_ATTEMPTS:-5} BUS_RETRY_BASE_SECONDS: ${COMPOSE_BUS_RETRY_BASE_SECONDS:-1} BUS_RETRY_MAX_SECONDS: ${COMPOSE_BUS_RETRY_MAX_SECONDS:-30} BUS_PENDING_CLAIM_IDLE_MS: ${COMPOSE_BUS_PENDING_CLAIM_IDLE_MS:-300000} WORKER_SHUTDOWN_TIMEOUT_SECONDS: ${WORKER_SHUTDOWN_TIMEOUT_SECONDS:-15} WORKER_HEARTBEAT_INTERVAL_SECONDS: ${WORKER_HEARTBEAT_INTERVAL_SECONDS:-5} WORKER_HEARTBEAT_TTL_SECONDS: ${WORKER_HEARTBEAT_TTL_SECONDS:-20} READINESS_REQUIRED_WORKER_ROLES: ${COMPOSE_READINESS_REQUIRED_WORKER_ROLES:-inbound,outbound,scheduler} # Core roles need non-secret connector routing metadata for admin reads and # connection-scoped runtime status. The SDK credential remains bridge-only. WXBOT_SDK_URL: ${COMPOSE_WXBOT_SDK_URL:-http://host.docker.internal:5080} WXBOT_FILE_DOWNLOAD_MAX_BYTES: ${COMPOSE_WXBOT_FILE_DOWNLOAD_MAX_BYTES:-2147483648} WXBOT_OUTBOUND_FILE_DIR: ${WXBOT_OUTBOUND_FILE_DIR:-/data/wxbot-outbound} WXBOT_OUTBOUND_FILE_MAX_BYTES: ${COMPOSE_WXBOT_OUTBOUND_FILE_MAX_BYTES:-10485760} WXBOT_OUTBOUND_FILE_RETENTION_SECONDS: ${COMPOSE_WXBOT_OUTBOUND_FILE_RETENTION_SECONDS:-604800} WXBOT_OUTBOUND_FILE_CLEANUP_GRACE_SECONDS: ${COMPOSE_WXBOT_OUTBOUND_FILE_CLEANUP_GRACE_SECONDS:-300} CHANNEL_CONNECTION_ID: ${COMPOSE_CHANNEL_CONNECTION_ID:-} SCHEDULER_LEASE_KEY: ${COMPOSE_SCHEDULER_LEASE_KEY:-agent-console:scheduler:leader} SCHEDULER_LEASE_TTL_SECONDS: ${COMPOSE_SCHEDULER_LEASE_TTL_SECONDS:-30} SCHEDULER_LEASE_ACQUIRE_TIMEOUT_SECONDS: ${COMPOSE_SCHEDULER_LEASE_ACQUIRE_TIMEOUT_SECONDS:-30} SCHEDULER_LEASE_POLL_INTERVAL_SECONDS: ${COMPOSE_SCHEDULER_LEASE_POLL_INTERVAL_SECONDS:-1} # The WeChat SDK bridge is an optional adapter process. Keep its credential # mapping out of the core service anchor; only non-secret routing metadata is # shared with control-plane roles. x-wxbot-adapter-env: &wxbot-adapter-env APP_ENV: ${APP_ENV:-dev} APP_LOG_LEVEL: ${APP_LOG_LEVEL:-INFO} APP_SERVICE_NAME: ${COMPOSE_APP_SERVICE_NAME:-agent-console} OTEL_EXPORTER_OTLP_ENDPOINT: ${COMPOSE_OTEL_EXPORTER_OTLP_ENDPOINT:-http://otel-collector:4317} OTEL_EXPORTER_OTLP_INSECURE: ${COMPOSE_OTEL_EXPORTER_OTLP_INSECURE:-true} OTEL_TRACES_SAMPLER_ARG: ${COMPOSE_OTEL_TRACES_SAMPLER_ARG:-1.0} DB_DSN: ${COMPOSE_DB_DSN:-postgresql+asyncpg://cs:compose_dev_postgres_password@postgres:5432/cs} REDIS_URL: ${COMPOSE_REDIS_URL:-redis://redis:6379/0} DRAW_STORAGE_DIR: ${COMPOSE_DRAW_STORAGE_DIR:-/data/draw} BUS_CONSUME_BATCH_SIZE: ${BUS_CONSUME_BATCH_SIZE:-16} BUS_CONSUME_BLOCK_MS: ${BUS_CONSUME_BLOCK_MS:-5000} BUS_MAX_ATTEMPTS: ${COMPOSE_BUS_MAX_ATTEMPTS:-5} BUS_RETRY_BASE_SECONDS: ${COMPOSE_BUS_RETRY_BASE_SECONDS:-1} BUS_RETRY_MAX_SECONDS: ${COMPOSE_BUS_RETRY_MAX_SECONDS:-30} BUS_PENDING_CLAIM_IDLE_MS: ${COMPOSE_BUS_PENDING_CLAIM_IDLE_MS:-300000} WORKER_SHUTDOWN_TIMEOUT_SECONDS: ${WORKER_SHUTDOWN_TIMEOUT_SECONDS:-15} WORKER_HEARTBEAT_INTERVAL_SECONDS: ${WORKER_HEARTBEAT_INTERVAL_SECONDS:-5} WORKER_HEARTBEAT_TTL_SECONDS: ${WORKER_HEARTBEAT_TTL_SECONDS:-20} WXBOT_API_TOKEN: ${COMPOSE_WXBOT_API_TOKEN:-} WXBOT_SDK_URL: ${COMPOSE_WXBOT_SDK_URL:-http://host.docker.internal:5080} WXBOT_DEFAULT_TENANT_ID: ${COMPOSE_WXBOT_DEFAULT_TENANT_ID:-default} WXBOT_BRIDGE_POLL_INTERVAL: ${COMPOSE_WXBOT_BRIDGE_POLL_INTERVAL:-3} WXBOT_BRIDGE_SEND_INTERVAL: ${COMPOSE_WXBOT_BRIDGE_SEND_INTERVAL:-2} WXBOT_GROUP_REPLY_COOLDOWN_SECONDS: ${COMPOSE_WXBOT_GROUP_REPLY_COOLDOWN_SECONDS:-1} WXBOT_GROUP_REPLY_COALESCE_WINDOW_MS: ${COMPOSE_WXBOT_GROUP_REPLY_COALESCE_WINDOW_MS:-250} WXBOT_GROUP_REPLY_ADAPTIVE_COOLDOWN_ENABLED: ${COMPOSE_WXBOT_GROUP_REPLY_ADAPTIVE_COOLDOWN_ENABLED:-true} WXBOT_GROUP_REPLY_BURST_WINDOW_SECONDS: ${COMPOSE_WXBOT_GROUP_REPLY_BURST_WINDOW_SECONDS:-10} WXBOT_GROUP_REPLY_ADAPTIVE_COOLDOWN_MAX_SECONDS: ${COMPOSE_WXBOT_GROUP_REPLY_ADAPTIVE_COOLDOWN_MAX_SECONDS:-8} WXBOT_PREVIEW_WAIT_SECONDS: ${COMPOSE_WXBOT_PREVIEW_WAIT_SECONDS:-30} WXBOT_PREVIEW_POLL_INTERVAL_SECONDS: ${COMPOSE_WXBOT_PREVIEW_POLL_INTERVAL_SECONDS:-0.7} WXBOT_MEDIA_BASE_URL: ${COMPOSE_WXBOT_MEDIA_BASE_URL:-} WXBOT_OUTBOUND_FILE_DIR: ${WXBOT_OUTBOUND_FILE_DIR:-/data/wxbot-outbound} WXBOT_OUTBOUND_FILE_MAX_BYTES: ${COMPOSE_WXBOT_OUTBOUND_FILE_MAX_BYTES:-10485760} WXBOT_OUTBOUND_FILE_RETENTION_SECONDS: ${COMPOSE_WXBOT_OUTBOUND_FILE_RETENTION_SECONDS:-604800} WXBOT_OUTBOUND_FILE_CLEANUP_GRACE_SECONDS: ${COMPOSE_WXBOT_OUTBOUND_FILE_CLEANUP_GRACE_SECONDS:-300} CHANNEL_CONNECTION_ID: ${COMPOSE_CHANNEL_CONNECTION_ID:-} CHANNEL_ALLOWED_SDK_ORIGINS: ${COMPOSE_CHANNEL_ALLOWED_SDK_ORIGINS:-} services: postgres: image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777 environment: POSTGRES_USER: ${COMPOSE_POSTGRES_USER:-cs} POSTGRES_PASSWORD: ${COMPOSE_POSTGRES_PASSWORD:-compose_dev_postgres_password} POSTGRES_DB: ${COMPOSE_POSTGRES_DB:-cs} expose: ["5432"] volumes: ["pgdata:/var/lib/postgresql/data"] healthcheck: test: ["CMD-SHELL", "pg_isready -U ${COMPOSE_POSTGRES_USER:-cs} -d ${COMPOSE_POSTGRES_DB:-cs}"] interval: 3s timeout: 3s retries: 20 redis: image: redis:7-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99 command: ["redis-server", "--appendonly", "yes"] expose: ["6379"] volumes: ["redisdata:/data"] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 3s timeout: 2s retries: 20 qdrant: profiles: ["knowledge"] image: qdrant/qdrant:v1.12.1@sha256:d774e7bb65744454984c6021637a0da89271f30df15e48601a9fafc926d26b1f expose: ["6333", "6334"] volumes: ["qdrantdata:/qdrant/storage"] otel-collector: image: otel/opentelemetry-collector-contrib:0.108.0@sha256:923eb1cfae32fe09676cfd74762b2b237349f2273888529594f6c6ffe1fb3d7e command: ["--config=/etc/otel/config.yaml"] volumes: - ./config/otel-collector.yaml:/etc/otel/config.yaml:ro expose: ["4317", "4318"] migrate: profiles: ["app"] build: . environment: *app-env depends_on: postgres: condition: service_healthy command: ["alembic", "upgrade", "head"] restart: "no" api: profiles: ["app"] build: . environment: <<: *app-env APP_PROCESS_ROLE: api depends_on: postgres: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully ports: ["${COMPOSE_BIND_ADDRESS:-127.0.0.1}:${COMPOSE_API_PORT:-8000}:8000"] volumes: - configdata:/data/config - drawdata:/data/draw - videodata:/data/video - amapdata:/data/amap - wxbotoutbound:${WXBOT_OUTBOUND_FILE_DIR:-/data/wxbot-outbound} extra_hosts: - "host.docker.internal:host-gateway" command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] healthcheck: test: - CMD - python - -c - >- import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/readyz', timeout=3).read() interval: 15s timeout: 5s retries: 4 start_period: 90s inbound-worker: profiles: ["app"] build: . environment: <<: *app-env # Empty falls back to container hostname + PID, which remains unique # when this service is horizontally scaled. APP_PROCESS_ROLE: inbound WORKER_INSTANCE_ID: ${INBOUND_WORKER_INSTANCE_ID:-} WORKER_METRICS_HOST: 0.0.0.0 WORKER_METRICS_PORT: "9100" depends_on: postgres: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully extra_hosts: - "host.docker.internal:host-gateway" volumes: - configdata:/data/config - drawdata:/data/draw - videodata:/data/video - amapdata:/data/amap - wxbotoutbound:${WXBOT_OUTBOUND_FILE_DIR:-/data/wxbot-outbound} command: ["python", "-m", "app.workers.inbound_worker"] healthcheck: test: ["CMD", "python", "-m", "app.workers.healthcheck", "--role", "inbound"] interval: 15s timeout: 5s retries: 4 start_period: 90s outbound-worker: profiles: ["app"] build: . environment: <<: *app-env APP_PROCESS_ROLE: outbound WORKER_INSTANCE_ID: ${OUTBOUND_WORKER_INSTANCE_ID:-} WORKER_METRICS_HOST: 0.0.0.0 WORKER_METRICS_PORT: "9100" depends_on: postgres: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully extra_hosts: - "host.docker.internal:host-gateway" volumes: - configdata:/data/config - drawdata:/data/draw command: ["python", "-m", "app.workers.outbound_worker"] healthcheck: test: ["CMD", "python", "-m", "app.workers.healthcheck", "--role", "outbound"] interval: 15s timeout: 5s retries: 4 start_period: 90s scheduler: profiles: ["app"] build: . environment: <<: *app-env APP_PROCESS_ROLE: scheduler WORKER_INSTANCE_ID: ${SCHEDULER_INSTANCE_ID:-} WORKER_METRICS_HOST: 0.0.0.0 WORKER_METRICS_PORT: "9100" depends_on: postgres: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully extra_hosts: - "host.docker.internal:host-gateway" volumes: - configdata:/data/config - drawdata:/data/draw - videodata:/data/video - amapdata:/data/amap - wxbotoutbound:${WXBOT_OUTBOUND_FILE_DIR:-/data/wxbot-outbound} command: ["python", "-m", "app.workers.scheduler_worker"] healthcheck: test: ["CMD", "python", "-m", "app.workers.healthcheck", "--role", "scheduler"] interval: 15s timeout: 5s retries: 4 start_period: 90s wxbot-bridge-worker: profiles: ["wxbot"] build: . environment: <<: *wxbot-adapter-env APP_PROCESS_ROLE: wxbot_bridge WORKER_INSTANCE_ID: ${WXBOT_BRIDGE_WORKER_INSTANCE_ID:-} WORKER_METRICS_HOST: 0.0.0.0 WORKER_METRICS_PORT: "9100" depends_on: postgres: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully extra_hosts: - "host.docker.internal:host-gateway" volumes: - drawdata:/data/draw - videodata:/data/video command: ["python", "-m", "app.workers.wxbot_bridge_worker"] healthcheck: test: - CMD - python - -m - app.workers.healthcheck - --role - wxbot_bridge interval: 15s timeout: 5s retries: 4 start_period: 90s frontend: profiles: ["app"] build: context: ./frontend depends_on: api: condition: service_started ports: ["${COMPOSE_BIND_ADDRESS:-127.0.0.1}:${COMPOSE_FRONTEND_PORT:-4173}:8080"] volumes: pgdata: {} redisdata: {} qdrantdata: {} configdata: {} drawdata: {} videodata: {} amapdata: {} wxbotoutbound: {}