version: '3.8' services: # DAGI Router для NODE1 router: build: context: ./services/router dockerfile: Dockerfile container_name: dagi-router-node1 ports: - "9102:8000" environment: - NATS_URL=nats://nats:4222 - ROUTER_CONFIG_PATH=/app/router_config.yaml - LOG_LEVEL=info - NODE_ID=node-1-hetzner-gex44 - MEMORY_SERVICE_URL=http://memory-service:8000 - QDRANT_HOST=qdrant - QDRANT_PORT=6333 - QDRANT_ENABLED=true - NEO4J_BOLT_URL=bolt://neo4j:7687 - NEO4J_HTTP_URL=http://neo4j:7474 - NEO4J_USER=neo4j - NEO4J_PASSWORD=DaarionNeo4j2026! - DEEPSEEK_API_KEY=sk-0db94e8193ec4a6e9acd593ee8d898e7 - MISTRAL_API_KEY=40Gwjo8nVBx4i4vIkgszvXw9bOwDOu4G - COHERE_API_KEY=nOdOXnuepLku2ipJWpe6acWgAsJCsDhMO0RnaEJB - GROK_API_KEY=xai-69zEnDse8qRuQyZATs9jVKgfwdyvkHzgEVrTbV0OTAurZqsjHmvGepXG6H9GhVRYEC7E4NFl6iZeG0ww - VISION_ENCODER_URL=http://vision-encoder:8001 - SWAPPER_SERVICE_URL=http://swapper-service:8890 - IMAGE_GEN_URL=http://swapper-service:8890/image/generate - STT_SERVICE_URL=http://swapper-service:8890 - STT_SERVICE_UPLOAD_URL=http://swapper-service:8890/stt - OCR_SERVICE_URL=http://swapper-service:8890 - WEB_SEARCH_SERVICE_URL=http://swapper-service:8890 volumes: - ./services/router/router_config.yaml:/app/router_config.yaml:ro - ./logs:/app/logs networks: dagi-network: aliases: - router restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8000/health')\""] interval: 30s timeout: 10s retries: 5 start_period: 30s # Swapper Service для NODE1 - Dynamic LLM + OCR model loading swapper-service: build: context: ./services/swapper-service dockerfile: Dockerfile container_name: swapper-service-node1 ports: - "8890:8890" - "8891:8891" # Metrics environment: - OLLAMA_BASE_URL=http://172.18.0.1:11434 - SWAPPER_CONFIG_PATH=/app/config/swapper_config.yaml - SWAPPER_MODE=single-active - MAX_CONCURRENT_MODELS=2 # 1 LLM + 1 OCR - MODEL_SWAP_TIMEOUT=300 - GPU_ENABLED=true - NODE_ID=node-1-hetzner-gex44 - HF_HOME=/root/.cache/huggingface - CUDA_VISIBLE_DEVICES=0 - CRAWL4AI_URL=http://crawl4ai:11235 # Cloud API keys for video/image generation - GROK_API_KEY=xai-69zEnDse8qRuQyZATs9jVKgfwdyvkHzgEVrTbV0OTAurZqsjHmvGepXG6H9GhVRYEC7E4NFl6iZeG0ww - MISTRAL_API_KEY=40Gwjo8nVBx4i4vIkgszvXw9bOwDOu4G volumes: - ./services/swapper-service/config/swapper_config_node1.yaml:/app/config/swapper_config.yaml:ro - ./logs:/app/logs - swapper-hf-cache-node1:/root/.cache/huggingface # GPU support for OCR models deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] networks: - dagi-network restart: unless-stopped extra_hosts: - "host.docker.internal:172.18.0.1" healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:8890/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 60s # Image Generation тепер інтегровано в Swapper Service (lazy loading) # Endpoint: POST /image/generate на swapper-service:8890 # Crawl4AI - Advanced Web Crawler with JavaScript support crawl4ai: image: unclecode/crawl4ai:latest container_name: dagi-crawl4ai-node1 ports: - "11235:11235" environment: - CRAWL4AI_API_TOKEN=${CRAWL4AI_API_TOKEN:-} - MAX_CONCURRENT_TASKS=5 networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:11235/health"] interval: 30s timeout: 10s retries: 3 start_period: 30s # Gateway Bot (Helion + DAARWIZZ) gateway: build: context: ./gateway-bot dockerfile: Dockerfile container_name: dagi-gateway-node1 ports: - "9300:9300" environment: # На NODA1 якщо router контейнер називається dagi-staging-router — в .env задати ROUTER_URL=http://dagi-staging-router:8000 - ROUTER_URL=${ROUTER_URL:-http://router:8000} - SERVICE_ID=gateway - SERVICE_ROLE=gateway - BRAND_INTAKE_URL=http://brand-intake:9211 - BRAND_REGISTRY_URL=http://brand-registry:9210 - PRESENTATION_RENDERER_URL=http://presentation-renderer:9212 - ARTIFACT_REGISTRY_URL=http://artifact-registry:9220 - HELION_TELEGRAM_BOT_TOKEN=8112062582:AAGS-HwRLEI269lDutLtAJTFArsIq31YNhE - HELION_NAME=Helion - HELION_PROMPT_PATH=/app/gateway-bot/helion_prompt.txt - NUTRA_TELEGRAM_BOT_TOKEN=8517315428:AAGTLcKxBAZDsMgx28agKTvl1SqJGi0utH4 - NUTRA_NAME=NUTRA - AGROMATRIX_TELEGRAM_BOT_TOKEN=8580290441:AAFuDBmFJtpl-3I_WfkH7Hkb59X0fhYNMOE - AGROMATRIX_NAME=AgroMatrix - AGROMATRIX_PROMPT_PATH=/app/gateway-bot/agromatrix_prompt.txt # Alateya - R&D, біотех, інновації - ALATEYA_TELEGRAM_BOT_TOKEN=8436880945:AAEi-HS6GEctddoqBUd37MHfweZQP-OjRlo - ALATEYA_NAME=Alateya - ALATEYA_PROMPT_PATH=/app/gateway-bot/alateya_prompt.txt # Clan (Spirit) - Дух Общини - CLAN_TELEGRAM_BOT_TOKEN=8516872152:AAHH26wU8hJZJbSCJXb4vbmPmakTP77ok5E - CLAN_NAME=Spirit - CLAN_PROMPT_PATH=/app/gateway-bot/clan_prompt.txt # Eonarch - Еволюція свідомості - EONARCH_TELEGRAM_BOT_TOKEN=7962391584:AAFYkelLRG3VR_Lxuu6pEGG76t4vZdANtz4 # DAARWIZZ - Meta-Orchestrator - DAARWIZZ_TELEGRAM_BOT_TOKEN=8323412397:AAGZbAR22LuOiGD8xVC3OXMjahQ8rs2lJwo - DAARWIZZ_NAME=DAARWIZZ # DRUID - Ayurveda/Cosmetics - DRUID_TELEGRAM_BOT_TOKEN=8145618489:AAFR714mBsNmiuF-rjCw-295iORBReJQZ70 - DRUID_NAME=DRUID # GREENFOOD - Food ERP - GREENFOOD_TELEGRAM_BOT_TOKEN=7495165343:AAGR1XEOzg7DkPFPCzL_eYLCJfxJuonCxug - GREENFOOD_NAME=GREENFOOD - EONARCH_NAME=EONARCH - EONARCH_PROMPT_PATH=/app/gateway-bot/eonarch_prompt.txt - MEMORY_SERVICE_URL=http://memory-service:8000 - SWAPPER_SERVICE_URL=http://swapper-service:8890 - IMAGE_GEN_URL=http://swapper-service:8890/image/generate - STT_SERVICE_URL=http://swapper-service:8890 - STT_SERVICE_UPLOAD_URL=http://swapper-service:8890/stt - OCR_SERVICE_URL=http://swapper-service:8890 - WEB_SEARCH_SERVICE_URL=http://swapper-service:8890 volumes: - ./gateway-bot:/app/gateway-bot:ro - ./logs:/app/logs depends_on: - router - memory-service networks: dagi-network: aliases: - gateway restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:9300/health')\""] interval: 30s timeout: 10s retries: 3 start_period: 10s # NATS (JetStream) nats: image: nats:2.10-alpine container_name: dagi-nats-node1 ports: - "4222:4222" command: ["-js"] volumes: - nats-data-node1:/data networks: dagi-network: aliases: - nats restart: unless-stopped # MinIO Object Storage minio: image: minio/minio:latest container_name: dagi-minio-node1 ports: - "9000:9000" - "9001:9001" environment: - MINIO_ROOT_USER=minioadmin - MINIO_ROOT_PASSWORD=minioadmin command: ["server", "/data", "--console-address", ":9001"] volumes: - minio-data-node1:/data networks: - dagi-network restart: unless-stopped # Artifact Registry (shared for docs/presentations) artifact-registry: build: context: ./services/artifact-registry dockerfile: Dockerfile container_name: artifact-registry-node1 ports: - "9220:9220" environment: - POSTGRES_HOST=dagi-postgres - POSTGRES_PORT=5432 - POSTGRES_USER=daarion - POSTGRES_PASSWORD=DaarionDB2026! - POSTGRES_DB=daarion_main - MINIO_ENDPOINT=minio:9000 - MINIO_ACCESS_KEY=minioadmin - MINIO_SECRET_KEY=minioadmin - MINIO_BUCKET=artifacts - MINIO_SECURE=false - NATS_URL=nats://nats:4222 volumes: - ./logs:/app/logs depends_on: - nats - minio networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:9220/health')\""] interval: 30s timeout: 10s retries: 3 start_period: 10s # RAG Service (pgvector) rag-service: build: context: ./services/rag-service dockerfile: Dockerfile container_name: rag-service-node1 ports: - "9500:9500" environment: - PG_DSN=postgresql+psycopg2://daarion:DaarionDB2026!@dagi-postgres:5432/rag - RAG_TABLE_NAME=rag_documents depends_on: - dagi-postgres networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:9500/health"] interval: 10s timeout: 3s retries: 10 # PPTX Render Worker render-pptx-worker: build: context: ./services/render-pptx-worker dockerfile: Dockerfile container_name: render-pptx-worker-node1 environment: - NATS_URL=nats://nats:4222 - ARTIFACT_REGISTRY_URL=http://artifact-registry:9220 - MINIO_ENDPOINT=minio:9000 - MINIO_ACCESS_KEY=minioadmin - MINIO_SECRET_KEY=minioadmin - MINIO_BUCKET=artifacts - MINIO_SECURE=false depends_on: - nats - artifact-registry - minio networks: - dagi-network restart: unless-stopped # PDF Render Worker (LibreOffice) render-pdf-worker: build: context: ./services/render-pdf-worker dockerfile: Dockerfile container_name: render-pdf-worker-node1 environment: - NATS_URL=nats://nats:4222 - ARTIFACT_REGISTRY_URL=http://artifact-registry:9220 - MINIO_ENDPOINT=minio:9000 - MINIO_ACCESS_KEY=minioadmin - MINIO_SECRET_KEY=minioadmin - MINIO_BUCKET=artifacts - MINIO_SECURE=false depends_on: - nats - artifact-registry - minio networks: - dagi-network restart: unless-stopped # Index Doc Worker index-doc-worker: build: context: ./services/index-doc-worker dockerfile: Dockerfile container_name: index-doc-worker-node1 environment: - NATS_URL=nats://nats:4222 - ARTIFACT_REGISTRY_URL=http://artifact-registry:9220 - RAG_SERVICE_URL=http://rag-service:9500 - MINIO_ENDPOINT=minio:9000 - MINIO_ACCESS_KEY=minioadmin - MINIO_SECRET_KEY=minioadmin - MINIO_BUCKET=artifacts - MINIO_SECURE=false - INDEX_DOC_MAX_BYTES=52428800 depends_on: - nats - artifact-registry - rag-service - minio networks: - dagi-network restart: unless-stopped # Brand Registry Service brand-registry: build: context: ./services/brand-registry dockerfile: Dockerfile container_name: brand-registry-node1 ports: - "9210:9210" environment: - BRAND_REGISTRY_DATA=/data/brand-registry volumes: - ./logs:/app/logs - brand-registry-data-node1:/data/brand-registry networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:9210/health')\""] interval: 30s timeout: 10s retries: 3 start_period: 10s # Brand Intake Service brand-intake: build: context: ./services/brand-intake dockerfile: Dockerfile container_name: brand-intake-node1 ports: - "9211:9211" environment: - BRAND_MAP_PATH=/app/config/BrandMap.yaml - BRAND_INTAKE_DATA=/data/brand-intake - BRAND_REGISTRY_URL=http://brand-registry:9210 volumes: - ./config/brand/BrandMap.yaml:/app/config/BrandMap.yaml:ro - ./logs:/app/logs - brand-intake-data-node1:/data/brand-intake depends_on: - brand-registry networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:9211/health')\""] interval: 30s timeout: 10s retries: 3 start_period: 10s # Presentation Renderer Service (MVP) presentation-renderer: build: context: ./services/presentation-renderer dockerfile: Dockerfile container_name: presentation-renderer-node1 ports: - "9212:9212" environment: - BRAND_REGISTRY_URL=http://brand-registry:9210 - PRESENTATION_DATA=/data/presentations volumes: - ./logs:/app/logs - presentation-data-node1:/data/presentations depends_on: - brand-registry networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:9212/health')\""] interval: 30s timeout: 10s retries: 3 start_period: 10s # Memory Service memory-service: build: context: ./services/memory-service dockerfile: Dockerfile container_name: dagi-memory-service-node1 ports: - "8000:8000" environment: # PostgreSQL connection (uses MEMORY_ prefix as per config.py) - MEMORY_POSTGRES_HOST=dagi-postgres - MEMORY_POSTGRES_PORT=5432 - MEMORY_POSTGRES_USER=daarion - MEMORY_POSTGRES_PASSWORD=DaarionDB2026! - MEMORY_POSTGRES_DB=daarion_memory # Qdrant connection - MEMORY_QDRANT_HOST=qdrant - MEMORY_QDRANT_PORT=6333 # Cohere for embeddings - MEMORY_COHERE_API_KEY=nOdOXnuepLku2ipJWpe6acWgAsJCsDhMO0RnaEJB - MEMORY_DEBUG=false volumes: - ./logs:/app/logs depends_on: - qdrant networks: dagi-network: aliases: - memory-service restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8000/health')\""] interval: 30s timeout: 10s retries: 3 start_period: 10s # PostgreSQL (pgvector) dagi-postgres: image: pgvector/pgvector:pg16 container_name: dagi-postgres ports: - "5432:5432" environment: - POSTGRES_USER=daarion - POSTGRES_PASSWORD=DaarionDB2026! - POSTGRES_DB=daarion_main volumes: - postgres_data_node1:/var/lib/postgresql/data networks: - dagi-network restart: unless-stopped # Qdrant Vector Database qdrant: image: qdrant/qdrant:v1.7.4 container_name: dagi-qdrant-node1 ulimits: nofile: soft: 65536 hard: 65536 ports: - "6333:6333" # HTTP API - "6334:6334" # gRPC API volumes: - qdrant-data-node1:/qdrant/storage networks: dagi-network: aliases: - qdrant restart: unless-stopped healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:6333/healthz || exit 1"] interval: 30s timeout: 10s retries: 3 # Neo4j Graph Database neo4j: image: neo4j:5.15-community container_name: dagi-neo4j-node1 ports: - "7474:7474" # HTTP - "7687:7687" # Bolt environment: - NEO4J_AUTH=neo4j/DaarionNeo4j2026! - NEO4J_PLUGINS=["apoc"] - NEO4J_dbms_memory_heap_initial__size=512m - NEO4J_dbms_memory_heap_max__size=2G volumes: - neo4j-data-node1:/data - neo4j-logs-node1:/logs networks: dagi-network: aliases: - neo4j restart: unless-stopped healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:7474"] interval: 30s timeout: 10s retries: 3 # Redis Cache redis: image: redis:7-alpine container_name: dagi-redis-node1 ports: - "6379:6379" volumes: - redis-data-node1:/data networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "PING"] interval: 30s timeout: 5s retries: 3 # Vision Encoder Service - OpenCLIP for text/image embeddings vision-encoder: build: context: ./services/vision-encoder dockerfile: Dockerfile container_name: dagi-vision-encoder-node1 ports: - "8001:8001" environment: - DEVICE=cpu # НОДА1 без GPU - MODEL_NAME=${VISION_MODEL_NAME:-ViT-L-14} - MODEL_PRETRAINED=${VISION_MODEL_PRETRAINED:-openai} - NORMALIZE_EMBEDDINGS=true - QDRANT_HOST=qdrant - QDRANT_PORT=6333 - QDRANT_ENABLED=true volumes: - ./logs:/app/logs - vision-model-cache-node1:/root/.cache/clip depends_on: - qdrant networks: - dagi-network restart: unless-stopped healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8001/health')\""] interval: 30s timeout: 10s retries: 3 start_period: 60s # OCR тепер через Swapper Service (got-ocr2, donut-base, donut-cord моделі) volumes: qdrant-data-node1: neo4j-data-node1: neo4j-logs-node1: redis-data-node1: vision-model-cache-node1: docling-model-cache-node1: swapper-hf-cache-node1: brand-registry-data-node1: brand-intake-data-node1: presentation-data-node1: nats-data-node1: minio-data-node1: postgres_data_node1: networks: dagi-network: external: true