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
    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

  # 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:
      - 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
      - DRUID_TELEGRAM_BOT_TOKEN=8145618489:AAFR714mBsNmiuF-rjCw-295iORBReJQZ70
      - DRUID_NAME=Druid
      - DRUID_PROMPT_PATH=/app/gateway-bot/druid_prompt.txt
      - DAARWIZZ_TELEGRAM_BOT_TOKEN=8323412397:AAGZbAR22LuOiGD8xVC3OXMjahQ8rs2lJwo
      - DAARWIZZ_NAME=DAARWIZZ
      - DAARWIZZ_PROMPT_PATH=/app/gateway-bot/daarwizz_prompt.txt
      - GREENFOOD_TELEGRAM_BOT_TOKEN=7495165343:AAGR1XEOzg7DkPFPCzL_eYLCJfxJuonCxug
      - GREENFOOD_NAME=GREENFOOD
      - GREENFOOD_PROMPT_PATH=/app/gateway-bot/greenfood_prompt.txt
      - AGROMATRIX_TELEGRAM_BOT_TOKEN=8580290441:AAFuDBmFJtpl-3I_WfkH7Hkb59X0fhYNMOE
      - AGROMATRIX_NAME=AgroMatrix
      - AGROMATRIX_PROMPT_PATH=/app/gateway-bot/agromatrix_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
    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=dagi-qdrant-node1
      - 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
    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
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "true"]
      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
    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 моделі)


# Explicit volume names to prevent prefix mismatch
# This ensures volumes are always named consistently regardless of COMPOSE_PROJECT_NAME
volumes:
  qdrant-data-node1:
    name: qdrant-data-node1
    driver: local
  neo4j-data-node1:
    name: neo4j-data-node1
    driver: local
  neo4j-logs-node1:
    name: neo4j-logs-node1
    driver: local
  redis-data-node1:
    name: redis-data-node1
    driver: local
  vision-model-cache-node1:
    name: vision-model-cache-node1
    driver: local
  docling-model-cache-node1:
    name: docling-model-cache-node1
    driver: local
  swapper-hf-cache-node1:
    name: swapper-hf-cache-node1
    driver: local
  brand-registry-data-node1:
    name: brand-registry-data-node1
    driver: local
  brand-intake-data-node1:
    name: brand-intake-data-node1
    driver: local
  presentation-data-node1:
    name: presentation-data-node1
    driver: local
  nats-data-node1:
    name: nats-data-node1
    driver: local
  minio-data-node1:
    name: minio-data-node1
    driver: local
  postgres_data_node1:
    name: postgres-data-node1
    driver: local

networks:
  dagi-network:
    external: true

