Files
microdao-daarion/docker-compose.node1.yml
Apple a92c424845 P2: Global multi-node model selection + NCS on NODA1
Architecture for 150+ nodes:
- global_capabilities_client.py: NATS scatter-gather discovery using
  wildcard subject node.*.capabilities.get — zero static node lists.
  New nodes auto-register by deploying NCS and subscribing to NATS.
  Dead nodes expire from cache after 3x TTL automatically.

Multi-node model_select.py:
- ModelSelection now includes node, local, via_nats fields
- select_best_model prefers local candidates, then remote
- Prefer list resolution: local first, remote second
- All logged per request: node, runtime, model, local/remote

NODA1 compose:
- Added node-capabilities service (NCS) to docker-compose.node1.yml
- NATS subscription: node.noda1.capabilities.get
- Router env: NODE_CAPABILITIES_URL + ENABLE_GLOBAL_CAPS_NATS=true

NODA2 compose:
- Router env: ENABLE_GLOBAL_CAPS_NATS=true

Router main.py:
- Startup: initializes global_capabilities_client (NATS connect + first
  discovery). Falls back to local-only capabilities_client if unavailable.
- /infer: uses get_global_capabilities() for cross-node model pool
- Offload support: send_offload_request(node_id, type, payload) via NATS

Verified on NODA2:
- Global caps: 1 node, 14 models (NODA1 not yet deployed)
- Sofiia: cloud_grok → grok-4-1-fast-reasoning (OK)
- Helion: NCS → qwen3:14b local (OK)
- When NODA1 deploys NCS, its models appear automatically via NATS discovery

Made-with: Cursor
2026-02-27 02:26:12 -08:00

1125 lines
36 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
# Timeout policy: Gateway (180s) > Router (60s) > LLM (30s)
- ROUTER_TIMEOUT=180
- 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-CpoLMPgw91NP9AEdHPhIrvU4ZnhV1q1P8BJBKCpD5kTPFRXJmTOkgGNHwYdZpXMlRxBgHcgcSlIXccxh
- 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
- REDIS_URL=redis://redis:6379/0
- CREWAI_SERVICE_URL=http://dagi-staging-crewai-service:9010
- NATURE_ID_URL=http://plant-vision-node1:8085
- NATURE_ID_MIN_CONFIDENCE=0.65
- PLANTNET_API_KEY=${PLANTNET_API_KEY}
- ONEOK_CRM_BASE_URL=http://oneok-crm-adapter:8088
- ONEOK_CALC_BASE_URL=http://oneok-calc-adapter:8089
- ONEOK_DOCS_BASE_URL=http://oneok-docs-adapter:8090
- ONEOK_SCHEDULE_BASE_URL=http://oneok-schedule-adapter:8091
- ONEOK_ADAPTER_API_KEY=${ONEOK_ADAPTER_API_KEY}
- ROUTER_TOOL_MAX_ROUNDS=${ROUTER_TOOL_MAX_ROUNDS:-10}
- AGROMATRIX_REVIEW_AUTH_MODE=${AGROMATRIX_REVIEW_AUTH_MODE:-bearer}
- AGROMATRIX_REVIEW_BEARER_TOKENS=${AGROMATRIX_REVIEW_BEARER_TOKENS}
# ── Node Capabilities (multi-node model selection) ──
- NODE_CAPABILITIES_URL=http://node-capabilities:8099/capabilities
- ENABLE_GLOBAL_CAPS_NATS=true
volumes:
- ${DEPLOY_ROOT:-.}/services/router/router_config.yaml:/app/router_config.yaml:ro
- ${DEPLOY_ROOT:-.}/services/router/router-config.yml:/app/router-config.yml:ro
- ${DEPLOY_ROOT:-.}/config/crewai_agents.json:/config/crewai_agents.json:ro
- ${DEPLOY_ROOT:-.}/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-CpoLMPgw91NP9AEdHPhIrvU4ZnhV1q1P8BJBKCpD5kTPFRXJmTOkgGNHwYdZpXMlRxBgHcgcSlIXccxh
- MISTRAL_API_KEY=40Gwjo8nVBx4i4vIkgszvXw9bOwDOu4G
volumes:
- ${DEPLOY_ROOT:-.}/services/swapper-service/config/swapper_config_node1.yaml:/app/config/swapper_config.yaml:ro
- ${DEPLOY_ROOT:-.}/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
# Plant Vision wrapper (local nature-id CLI -> HTTP)
plant-vision-node1:
build:
context: ./services/plant-vision-node1
dockerfile: Dockerfile
container_name: plant-vision-node1
environment:
- NATURE_ID_CMD=${NATURE_ID_CMD:-python /opt/nature-id/nature_id.py -m plants -l -r 5 -s {image_path}}
- NATURE_ID_TIMEOUT=40
- DOWNLOAD_TIMEOUT=20
networks:
- dagi-network
volumes:
- ${DEPLOY_ROOT:-.}/third_party/nature-id:/opt/nature-id:ro
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8085/health')\""]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
# Crawl4AI - Advanced Web Crawler with JavaScript support
crawl4ai:
image: unclecode/crawl4ai@sha256:4d8b065bf185962733cb5f9701f4122d03383fa1ab6b5f6a9873f04fa0416a84
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=${ROUTER_URL:-http://dagi-staging-router:8000}
- GATEWAY_MAX_TOKENS_CONCISE=350
- GATEWAY_MAX_TOKENS_SENPAI_DEFAULT=700
- GATEWAY_MAX_TOKENS_DEFAULT=700
- GATEWAY_MAX_TOKENS_DETAILED=1200
- 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
# 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
- EONARCH_NAME=EONARCH
- EONARCH_PROMPT_PATH=/app/gateway-bot/eonarch_prompt.txt
- SENPAI_TELEGRAM_BOT_TOKEN=8510265026:AAGFrFBIIEihsLptZSxuKdmW2RoRPQDY9FE
- ONEOK_TELEGRAM_BOT_TOKEN=${ONEOK_TELEGRAM_BOT_TOKEN}
- SOUL_TELEGRAM_BOT_TOKEN=8041596416:AAHhpfCtY8paCm_9AD-4stJJg-Vw-CBf6Qk
- YAROMIR_TELEGRAM_BOT_TOKEN=8128180674:AAGNZdG3LwECI4z_803smsuRHsK3nPdjMLY
- SOFIIA_TELEGRAM_BOT_TOKEN=8589292566:AAEmPvS6nY9e-Y-TZm04CAHWlaFnWVxajE4
- SENPAI_NAME=SENPAI
- ONEOK_NAME=1OK
- SOUL_NAME=Athena
- YAROMIR_NAME=Yaromir
- SOFIIA_NAME=Sophia
- SENPAI_PROMPT_PATH=/app/gateway-bot/senpai_prompt.txt
- ONEOK_PROMPT_PATH=/app/gateway-bot/oneok_prompt.txt
- MEMORY_SERVICE_URL=http://memory-service:8000
# Timeout policy: Gateway (180s) > Router (60s) > LLM (30s)
- ROUTER_TIMEOUT=180
- 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
- REDIS_URL=redis://redis:6379/0
- CREWAI_SERVICE_URL=http://dagi-staging-crewai-service:9010
- AGROMATRIX_REVIEW_AUTH_MODE=${AGROMATRIX_REVIEW_AUTH_MODE:-bearer}
- AGROMATRIX_REVIEW_BEARER_TOKENS=${AGROMATRIX_REVIEW_BEARER_TOKENS}
# v4.3 FarmOS integration (fail-closed: якщо пусто — агент повідомить "не налаштований")
- FARMOS_BASE_URL=http://dagi-farmos-node1
- FARMOS_TOKEN=${FARMOS_TOKEN:-}
- FARMOS_USER=${FARMOS_USER:-}
- FARMOS_PASS=${FARMOS_PASS:-}
- FARMOS_CLIENT_ID=${FARMOS_CLIENT_ID:-farm}
env_file:
- .env.stepan.node1
volumes:
- ${DEPLOY_ROOT:-.}/gateway-bot:/app/gateway-bot:ro
- ${DEPLOY_ROOT:-.}/logs:/app/logs
depends_on:
- router
- memory-service
- redis
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
gateway-worker:
build:
context: ./gateway-bot
dockerfile: Dockerfile
container_name: dagi-gateway-worker-node1
command: ["python", "-m", "daarion_facade.worker"]
environment:
- ROUTER_BASE_URL=http://router:8000
- REDIS_URL=redis://redis:6379/0
- ROUTER_WORKER_TIMEOUT=60
volumes:
- ${DEPLOY_ROOT:-.}/gateway-bot:/app/gateway-bot:ro
- ${DEPLOY_ROOT:-.}/logs:/app/logs
depends_on:
- router
- redis
networks:
- dagi-network
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "print(\"ok\")"]
interval: 30s
timeout: 5s
retries: 3
gateway-reminder-worker:
build:
context: ./gateway-bot
dockerfile: Dockerfile
container_name: dagi-gateway-reminder-worker-node1
command: ["python", "-m", "daarion_facade.reminder_worker"]
environment:
- REDIS_URL=redis://redis:6379/0
- DAARION_REMINDER_POLL_SECONDS=${DAARION_REMINDER_POLL_SECONDS:-2}
- DAARION_REMINDER_TTL_SECONDS=${DAARION_REMINDER_TTL_SECONDS:-2592000}
- DAARION_REMINDER_DEFAULT_TZ=${DAARION_REMINDER_DEFAULT_TZ:-Europe/Kyiv}
- GLOBAL_RELAY_ALLOWED_USER_IDS=${GLOBAL_RELAY_ALLOWED_USER_IDS:-}
- MENTOR_PRIVATE_HANDLES=${MENTOR_PRIVATE_HANDLES:-ivantytar,archenvis,olegarch88}
- MENTOR_PRIVATE_NAMES=${MENTOR_PRIVATE_NAMES:-Іван Титар,Александр Вертій,Олег Ковальчук}
- MENTOR_DISCLOSURE_ALLOWED_USER_IDS=${MENTOR_DISCLOSURE_ALLOWED_USER_IDS:-}
- HELION_MENTOR_CHAT_IDS=${HELION_MENTOR_CHAT_IDS:-}
- HELION_RELAY_ALLOWED_USER_IDS=${HELION_RELAY_ALLOWED_USER_IDS:-}
- DAARWIZZ_TELEGRAM_BOT_TOKEN=${DAARWIZZ_TELEGRAM_BOT_TOKEN:-8323412397:AAGZbAR22LuOiGD8xVC3OXMjahQ8rs2lJwo}
- HELION_TELEGRAM_BOT_TOKEN=${HELION_TELEGRAM_BOT_TOKEN:-8112062582:AAGS-HwRLEI269lDutLtAJTFArsIq31YNhE}
- GREENFOOD_TELEGRAM_BOT_TOKEN=${GREENFOOD_TELEGRAM_BOT_TOKEN:-7495165343:AAGR1XEOzg7DkPFPCzL_eYLCJfxJuonCxug}
- AGROMATRIX_TELEGRAM_BOT_TOKEN=${AGROMATRIX_TELEGRAM_BOT_TOKEN:-8580290441:AAFuDBmFJtpl-3I_WfkH7Hkb59X0fhYNMOE}
- ALATEYA_TELEGRAM_BOT_TOKEN=${ALATEYA_TELEGRAM_BOT_TOKEN:-8436880945:AAEi-HS6GEctddoqBUd37MHfweZQP-OjRlo}
- NUTRA_TELEGRAM_BOT_TOKEN=${NUTRA_TELEGRAM_BOT_TOKEN:-8517315428:AAGTLcKxBAZDsMgx28agKTvl1SqJGi0utH4}
- DRUID_TELEGRAM_BOT_TOKEN=${DRUID_TELEGRAM_BOT_TOKEN:-8145618489:AAFR714mBsNmiuF-rjCw-295iORBReJQZ70}
- CLAN_TELEGRAM_BOT_TOKEN=${CLAN_TELEGRAM_BOT_TOKEN:-8516872152:AAHH26wU8hJZJbSCJXb4vbmPmakTP77ok5E}
- EONARCH_TELEGRAM_BOT_TOKEN=${EONARCH_TELEGRAM_BOT_TOKEN:-7962391584:AAFYkelLRG3VR_Lxuu6pEGG76t4vZdANtz4}
- SENPAI_TELEGRAM_BOT_TOKEN=${SENPAI_TELEGRAM_BOT_TOKEN:-8510265026:AAGFrFBIIEihsLptZSxuKdmW2RoRPQDY9FE}
- ONEOK_TELEGRAM_BOT_TOKEN=${ONEOK_TELEGRAM_BOT_TOKEN}
- SOUL_TELEGRAM_BOT_TOKEN=${SOUL_TELEGRAM_BOT_TOKEN:-8041596416:AAHhpfCtY8paCm_9AD-4stJJg-Vw-CBf6Qk}
- YAROMIR_TELEGRAM_BOT_TOKEN=${YAROMIR_TELEGRAM_BOT_TOKEN:-8128180674:AAGNZdG3LwECI4z_803smsuRHsK3nPdjMLY}
- SOFIIA_TELEGRAM_BOT_TOKEN=${SOFIIA_TELEGRAM_BOT_TOKEN:-8589292566:AAEmPvS6nY9e-Y-TZm04CAHWlaFnWVxajE4}
volumes:
- ${DEPLOY_ROOT:-.}/gateway-bot:/app/gateway-bot:ro
- ${DEPLOY_ROOT:-.}/logs:/app/logs
depends_on:
- redis
networks:
- dagi-network
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "print(\"ok\")"]
interval: 30s
timeout: 5s
retries: 3
metrics-poller-node1:
build:
context: ./gateway-bot
dockerfile: Dockerfile
container_name: dagi-metrics-poller-node1
command: ["python", "-m", "daarion_facade.metrics_poller"]
environment:
- REDIS_URL=redis://redis:6379/0
- MEMORY_SERVICE_URL=http://memory-service:8000
- DAARION_METRICS_POLL_INTERVAL_SECONDS=${DAARION_METRICS_POLL_INTERVAL_SECONDS:-10}
- DAARION_METRICS_TTL_SECONDS=${DAARION_METRICS_TTL_SECONDS:-60}
- DAARION_METRICS_HTTP_CONNECT_TIMEOUT_SECONDS=${DAARION_METRICS_HTTP_CONNECT_TIMEOUT_SECONDS:-2}
- DAARION_METRICS_HTTP_TOTAL_TIMEOUT_SECONDS=${DAARION_METRICS_HTTP_TOTAL_TIMEOUT_SECONDS:-5}
- DAARION_NODE_COUNT=${DAARION_NODE_COUNT:-1}
volumes:
- ${DEPLOY_ROOT:-.}/gateway-bot:/app/gateway-bot:ro
- ${DEPLOY_ROOT:-.}/logs:/app/logs
depends_on:
- redis
- memory-service
networks:
- dagi-network
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "print(\"ok\")"]
interval: 30s
timeout: 5s
retries: 3
# CLAN Consent Outbox Worker (Postgres event-store applier; no execute)
clan-consent-outbox-worker:
build:
context: ./services/clan-consent-adapter
dockerfile: Dockerfile
container_name: clan-consent-outbox-worker
command: ["python", "clan_consent_outbox_worker.py"]
environment:
- CLAN_PG_DSN=${CLAN_PG_DSN:-postgresql://daarion:DaarionDB2026!@dagi-postgres:5432/daarion_main}
- CLAN_OUTBOX_BATCH_SIZE=${CLAN_OUTBOX_BATCH_SIZE:-10}
- CLAN_OUTBOX_POLL_INTERVAL_SEC=${CLAN_OUTBOX_POLL_INTERVAL_SEC:-1.0}
- CLAN_OUTBOX_MAX_CAS_RETRIES=${CLAN_OUTBOX_MAX_CAS_RETRIES:-5}
- CLAN_CONSENT_APPLIER_ACTOR_ID=${CLAN_CONSENT_APPLIER_ACTOR_ID:-system:consent-applier}
volumes:
- ${DEPLOY_ROOT:-.}/logs:/app/logs
depends_on:
- dagi-postgres
networks:
- dagi-network
restart: unless-stopped
# 1OK - EspoCRM DB
oneok-espocrm-db:
image: mariadb:11
container_name: oneok-espocrm-db-node1
environment:
- MARIADB_ROOT_PASSWORD=${ONEOK_ESPO_DB_ROOT_PASSWORD:-change_me_root}
- MARIADB_DATABASE=${ONEOK_ESPO_DB_NAME:-oneok_espocrm}
- MARIADB_USER=${ONEOK_ESPO_DB_USER:-oneok}
- MARIADB_PASSWORD=${ONEOK_ESPO_DB_PASSWORD:-change_me_oneok}
volumes:
- oneok-espocrm-db-node1:/var/lib/mysql
networks:
- dagi-network
restart: unless-stopped
# 1OK - EspoCRM
oneok-espocrm:
image: espocrm/espocrm:latest
container_name: oneok-espocrm-node1
ports:
- "9080:80"
environment:
- ESPOCRM_DATABASE_HOST=oneok-espocrm-db
- ESPOCRM_DATABASE_NAME=${ONEOK_ESPO_DB_NAME:-oneok_espocrm}
- ESPOCRM_DATABASE_USER=${ONEOK_ESPO_DB_USER:-oneok}
- ESPOCRM_DATABASE_PASSWORD=${ONEOK_ESPO_DB_PASSWORD:-change_me_oneok}
- ESPOCRM_ADMIN_USERNAME=${ONEOK_ESPO_ADMIN_USER:-admin}
- ESPOCRM_ADMIN_PASSWORD=${ONEOK_ESPO_ADMIN_PASSWORD:-change_me_admin}
- ESPOCRM_SITE_URL=${ONEOK_ESPO_SITE_URL:-http://localhost:9080}
depends_on:
- oneok-espocrm-db
networks:
- dagi-network
restart: unless-stopped
# 1OK - Gotenberg PDF
oneok-gotenberg:
image: gotenberg/gotenberg:8
container_name: oneok-gotenberg-node1
command:
- gotenberg
- --api-timeout=30s
- --api-port=3000
- --chromium-disable-routes=true
ports:
- "3010:3000"
networks:
- dagi-network
restart: unless-stopped
# 1OK - CRM Adapter
oneok-crm-adapter:
build:
context: ./services/oneok-crm-adapter
dockerfile: Dockerfile
container_name: oneok-crm-adapter-node1
environment:
- ONEOK_ADAPTER_API_KEY=${ONEOK_ADAPTER_API_KEY}
- ONEOK_CRM_DB_PATH=/data/oneok_crm.sqlite
- ONEOK_ESPO_URL=http://oneok-espocrm
- ONEOK_ESPO_API_KEY=${ONEOK_ESPO_API_KEY}
volumes:
- oneok-crm-data-node1:/data
depends_on:
- oneok-espocrm
networks:
- dagi-network
restart: unless-stopped
# 1OK - Docs Adapter
oneok-docs-adapter:
build:
context: ./services/oneok-docs-adapter
dockerfile: Dockerfile
container_name: oneok-docs-adapter-node1
environment:
- ONEOK_ADAPTER_API_KEY=${ONEOK_ADAPTER_API_KEY}
- ONEOK_GOTENBERG_URL=http://oneok-gotenberg:3000
depends_on:
- oneok-gotenberg
networks:
- dagi-network
restart: unless-stopped
# 1OK - Calc Adapter
oneok-calc-adapter:
build:
context: ./services/oneok-calc-adapter
dockerfile: Dockerfile
container_name: oneok-calc-adapter-node1
environment:
- ONEOK_ADAPTER_API_KEY=${ONEOK_ADAPTER_API_KEY}
- ONEOK_BASE_RATE_PER_M2=${ONEOK_BASE_RATE_PER_M2:-3200}
- ONEOK_INSTALL_RATE_PER_M2=${ONEOK_INSTALL_RATE_PER_M2:-900}
- ONEOK_CURRENCY=${ONEOK_CURRENCY:-UAH}
networks:
- dagi-network
restart: unless-stopped
# 1OK - Schedule Adapter
oneok-schedule-adapter:
build:
context: ./services/oneok-schedule-adapter
dockerfile: Dockerfile
container_name: oneok-schedule-adapter-node1
environment:
- ONEOK_ADAPTER_API_KEY=${ONEOK_ADAPTER_API_KEY}
- ONEOK_SCHEDULE_TZ=Europe/Kyiv
networks:
- dagi-network
restart: unless-stopped
# Node Capabilities Service (model inventory for router)
node-capabilities:
build:
context: ./services/node-capabilities
dockerfile: Dockerfile
container_name: node-capabilities-node1
environment:
- NODE_ID=noda1
- OLLAMA_BASE_URL=http://host.docker.internal:11434
- SWAPPER_URL=http://swapper-service:8890
- CACHE_TTL_SEC=15
- ENABLE_NATS_CAPS=true
- NATS_URL=nats://nats:4222
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
- nats
networks:
dagi-network:
aliases:
- node-capabilities
restart: unless-stopped
# 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@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
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:
- ${DEPLOY_ROOT:-.}/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
stop_grace_period: 30s
# 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:
- ${DEPLOY_ROOT:-.}/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
- ${DEPLOY_ROOT:-.}/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:
- ${DEPLOY_ROOT:-.}/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:
- ${DEPLOY_ROOT:-.}/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:
- ${DEPLOY_ROOT:-.}/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
# E2E Agent Prober - monitors agent pipeline health
agent-e2e-prober:
build:
context: ./services/agent-e2e-prober
dockerfile: Dockerfile
container_name: agent-e2e-prober-node1
ports:
- "9108:9108"
environment:
- GATEWAY_URL=http://gateway:9300
- PROBE_INTERVAL=60
- PROBE_TIMEOUT=30
- METRICS_PORT=9108
- SEMANTIC_AGENTS=clan,sofiia,monitor,helion,agromatrix,senpai
networks:
- dagi-network
restart: unless-stopped
depends_on:
- gateway
# === Market Data Pipeline (added 2026-02-09) ===
market-data-service:
container_name: dagi-market-data-node1
restart: unless-stopped
build:
context: ./services/market-data-service
dockerfile: Dockerfile
environment:
- BINANCE_WS_URL=wss://stream.binance.com:9443/ws
- BYBIT_WS_URL=wss://stream.bybit.com/v5/public/spot
- ALPACA_DRY_RUN=true
- SQLITE_URL=sqlite+aiosqlite:////data/market_data.db
- JSONL_PATH=/data/events.jsonl
- HTTP_HOST=0.0.0.0
- HTTP_PORT=8891
- NATS_URL=nats://nats:4222
- NATS_ENABLED=true
- NATS_SUBJECT_PREFIX=md.events
- LOG_LEVEL=INFO
- LOG_SAMPLE_RATE=500
ports:
- "8893:8891"
volumes:
- market-data-node1:/data
networks:
- dagi-network
depends_on:
- nats
command: ["run", "--provider", "binance,bybit", "--symbols", "BTCUSDT,ETHUSDT"]
healthcheck:
test:
- CMD-SHELL
- python -c "import urllib.request; urllib.request.urlopen('http://localhost:8891/health')"
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
senpai-md-consumer:
container_name: dagi-senpai-md-consumer-node1
restart: unless-stopped
build:
context: ./services/senpai-md-consumer
dockerfile: Dockerfile
environment:
- NATS_URL=nats://nats:4222
- NATS_SUBJECT=md.events.>
- NATS_QUEUE_GROUP=senpai-md
- FEATURES_ENABLED=true
- FEATURES_PUB_RATE_HZ=10
- FEATURES_PUB_SUBJECT=senpai.features
- SIGNALS_PUB_SUBJECT=senpai.signals
- ALERTS_PUB_SUBJECT=senpai.alerts
- LOG_LEVEL=INFO
- HTTP_PORT=8892
ports:
- "8892:8892"
networks:
- dagi-network
depends_on:
nats:
condition: service_started
market-data-service:
condition: service_healthy
healthcheck:
test:
- CMD-SHELL
- python -c "import urllib.request; urllib.request.urlopen('http://localhost:8892/health')"
interval: 15s
timeout: 10s
retries: 5
start_period: 15s
binance-bot-monitor:
build:
context: ./services/binance-bot-monitor
dockerfile: Dockerfile
container_name: dagi-binance-bot-monitor-node1
restart: unless-stopped
environment:
- REDIS_URL=redis://redis:6379/0
- CRAWL4AI_URL=http://crawl4ai:11235
- SWAPPER_URL=http://swapper-service:8890
- BINANCE_CACHE_TTL=3600
- BINANCE_REFRESH_INTERVAL=1800
- BINANCE_API_KEY=${BINANCE_API_KEY:-}
- BINANCE_SECRET_KEY=${BINANCE_SECRET_KEY:-}
networks:
- dagi-network
# ── FarmOS (v4.3 integration) ────────────────────────────────────────────────
# PostgreSQL для farmOS (окрема БД, не чіпає dagi-postgres)
dagi-farmos-db-node1:
image: postgres:16-alpine
container_name: dagi-farmos-db-node1
restart: unless-stopped
environment:
- POSTGRES_DB=farmos
- POSTGRES_USER=farmos
- POSTGRES_PASSWORD=${FARMOS_DB_PASS}
volumes:
- farmos-db-data-node1:/var/lib/postgresql/data
networks:
- dagi-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U farmos -d farmos"]
interval: 10s
timeout: 5s
retries: 10
start_period: 15s
# farmOS Drupal application (4.x — актуальна стабільна, amd64 для x86_64 сервера)
dagi-farmos-node1:
image: farmos/farmos:4.x-amd64
container_name: dagi-farmos-node1
restart: unless-stopped
depends_on:
dagi-farmos-db-node1:
condition: service_healthy
environment:
- FARMOS_DB_HOST=dagi-farmos-db-node1
- FARMOS_DB_NAME=farmos
- FARMOS_DB_USER=farmos
- FARMOS_DB_PASSWORD=${FARMOS_DB_PASS}
- FARMOS_DB_DRIVER=pgsql
volumes:
- farmos-sites-node1:/opt/drupal/web/sites
networks:
- dagi-network
ports:
# Доступний тільки локально; для браузерного setup — SSH tunnel: ssh -L 8088:localhost:8088
- "127.0.0.1:8088:80"
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:80 -o /dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
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
market-data-node1:
name: market-data-node1
driver: local
oneok-espocrm-db-node1:
name: oneok-espocrm-db-node1
driver: local
oneok-crm-data-node1:
name: oneok-crm-data-node1
driver: local
# farmOS persistent volumes (v4.3)
farmos-db-data-node1:
name: farmos-db-data-node1
driver: local
farmos-sites-node1:
name: farmos-sites-node1
driver: local
networks:
dagi-network:
external: true