Ctrl K
ComunidadGuías…

docker-compose.dev.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# ══════════════════════════════════════════════════════════════════════
#  Geny — Docker Compose (Development / Full)
#
#  소스 코드가 바인드 마운트되어 로컬 편집이 컨테이너에 즉시 반영됩니다.
#  로컬 오디오 엔진 (TTS / STT) 은 --profile audio-local 로 활성화할 수 있습니다.
#
#  - Backend : uvicorn --reload (watchfiles 기반 hot reload)
#  - Frontend: next dev (HMR / Fast Refresh)
#
#  Usage:
#    docker compose -f docker-compose.dev.yml up --build
#    docker compose -f docker-compose.dev.yml --profile audio-local up --build
#    docker compose -f docker-compose.dev.yml down
#    docker compose -f docker-compose.dev.yml logs -f
#
#  경량 버전 (TTS 없음):
#    docker compose -f docker-compose.dev-core.yml up --build
#
# ══════════════════════════════════════════════════════════════════════

services:

  # ── PostgreSQL Database ───────────────────────────────────────────
  postgres:
    image: postgres:16-alpine
    container_name: geny-postgres-dev
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-geny}
      POSTGRES_USER: ${POSTGRES_USER:-geny}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-geny123}
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
      TZ: ${TIMEZONE:-Asia/Seoul}
    volumes:
      - geny-pgdata-dev:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-geny} -d ${POSTGRES_DB:-geny}"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    networks:
      - geny-net-dev

  # ── Backend (FastAPI + LangGraph + Claude CLI) — Dev Mode ─────────
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: geny-backend-dev
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${BACKEND_PORT:-8000}:8000"
    env_file:
      - path: ./backend/.env
        required: false
    environment:
      - APP_HOST=0.0.0.0
      - APP_PORT=8000
      - DEBUG_MODE=true
      - GENY_AGENT_STORAGE_ROOT=/data/geny_agent_sessions
      # PostgreSQL
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - POSTGRES_DB=${POSTGRES_DB:-geny}
      - POSTGRES_USER=${POSTGRES_USER:-geny}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-geny123}
      - AUTO_MIGRATION=true
      - TIMEZONE=${TIMEZONE:-Asia/Seoul}
      # ── Persistent-storage paths (Phase 1, 2026-05-19) ─────────────
      # Mirrors docker-compose.prod.yml — each path is backed by a
      # named volume so its data survives ``docker compose up --build``.
      - ENVIRONMENT_STORAGE_PATH=${ENVIRONMENT_STORAGE_PATH:-/data/environments}
      - TRIGGER_PRESET_STORAGE_PATH=${TRIGGER_PRESET_STORAGE_PATH:-/data/trigger_presets}
      - GENY_TOOL_PRESETS_DIR=/data/tool_presets
      - GENY_CHAT_CONVERSATIONS_DIR=/data/chat_conversations
      - GENY_GAME_STATE_DB=/data/creature_state/geny_state.sqlite3
      - GENY_CONFIG_VARIABLES_DIR=/data/config_variables
      - GENY_MCP_CREDENTIALS=/data/mcp/credentials.json
      - GENY_VOICE_STUDIO_DATA_DIR=/data/voice_studio
    volumes:
      # ── Bind mount: 로컬 소스 ↔ 컨테이너 동기화 ──────────────────
      - ./backend:/app
      # Persistent agent storage root — see the matching block in
      # docker-compose.prod.yml. Without this every ``docker compose
      # up --build backend`` wipes user notes / audio captures.
      - geny-agent-sessions-dev:/data/geny_agent_sessions
      # ── Phase-1 persistence volumes (2026-05-19) ────────────────────
      # Mirrors docker-compose.prod.yml. Each env var declared above
      # resolves to a path that is backed by a named volume — without
      # these mounts ``docker compose up --build`` wipes the data.
      - geny-environments-dev:/data/environments
      - geny-trigger-presets-dev:/data/trigger_presets
      - geny-tool-presets-dev:/data/tool_presets
      - geny-chat-conversations-dev:/data/chat_conversations
      - geny-creature-state-dev:/data/creature_state
      - geny-config-variables-dev:/data/config_variables
      - geny-mcp-credentials-dev:/data/mcp
      - geny-voice-studio-dev:/data/voice_studio
      # Baked puppet zips dropped here by avatar-editor. Backend reads + moves to
      # installed/ subdir after install (rw — was :ro before C.3 added install).
      - geny-baked-exports-dev:/data/baked-imports
      # Phase G — host's ``~/.claude`` for Pro/Max OAuth credential
      # passthrough to the in-container ``claude`` CLI. RW for token
      # refresh.
      - ${CLAUDE_HOST_DIR:-geny-claude-creds-dev}:/root/.claude:rw
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:8000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s
    networks:
      - geny-net-dev

  # ── Frontend (Next.js) — Dev Mode ─────────────────────────────────
  frontend:
    build:
      # Root context: the page renders shared/chat, which lives beside
      # frontend/ and must be in the image.
      context: .
      dockerfile: frontend/Dockerfile.dev
    container_name: geny-frontend-dev
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${FRONTEND_PORT:-3000}:3000"
    environment:
      - API_URL=http://backend:8000
      - NEXT_PUBLIC_BACKEND_PORT=${BACKEND_PORT:-8000}
      - PORT=3000
      - HOSTNAME=0.0.0.0
    volumes:
      # ── Bind mount: 로컬 소스 ↔ 컨테이너 동기화 ──────────────────
      - ./frontend/src:/app/src
      - ./frontend/public:/app/public
      - ./frontend/next.config.ts:/app/next.config.ts
      - ./frontend/tsconfig.json:/app/tsconfig.json
      - ./frontend/postcss.config.mjs:/app/postcss.config.mjs
    depends_on:
      backend:
        condition: service_healthy
    networks:
      - geny-net-dev

  # ── OmniVoice (Optional · Dev Mode — server/ hot-reload) ─────────
  omnivoice:
    build:
      context: ./omnivoice
      dockerfile: Dockerfile
    image: geny-omnivoice:dev
    container_name: geny-omnivoice-dev
    profiles: ["audio-local"]
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${OMNIVOICE_PORT:-9881}:9881"
    environment:
      - OMNIVOICE_MODEL=${OMNIVOICE_MODEL:-k2-fsa/OmniVoice}
      - OMNIVOICE_DEVICE=${OMNIVOICE_DEVICE:-cuda:0}
      - OMNIVOICE_DTYPE=${OMNIVOICE_DTYPE:-float16}
      # 5070(Blackwell, sm_120) 기준. 공유 GPU 환경에서는 1~2 로 내릴 것.
      - OMNIVOICE_MAX_CONCURRENCY=${OMNIVOICE_MAX_CONCURRENCY:-1}
      - OMNIVOICE_DEFAULT_NUM_STEP=${OMNIVOICE_DEFAULT_NUM_STEP:-14}
      - OMNIVOICE_GPU_MEMORY_FRACTION=${OMNIVOICE_GPU_MEMORY_FRACTION:-0.65}
      - PYTORCH_CUDA_ALLOC_CONF=${PYTORCH_CUDA_ALLOC_CONF:-expandable_segments:True}
      - OMNIVOICE_AUTO_ASR=${OMNIVOICE_AUTO_ASR:-false}
      - OMNIVOICE_LOG_LEVEL=${OMNIVOICE_LOG_LEVEL:-debug}
      - HF_ENDPOINT=${HF_ENDPOINT:-https://huggingface.co}
    volumes:
      # ── Bind mount: server/ hot-reload 만 활성. omnivoice_core/ 는 빌드 시 고정.
      - ./omnivoice/server:/app/server
      - ./backend/static/voices:/voices:ro
      - geny-omnivoice-models-dev:/models
    command:
      - python
      - -m
      - uvicorn
      - server.main:app
      - --host
      - 0.0.0.0
      - --port
      - "9881"
      - --reload
      - --reload-dir
      - /app/server
    shm_size: 8G
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:9881/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 300s
    networks:
      - geny-net-dev

  # ── Avatar Editor (geny-avatar submodule, Next.js) — Dev Mode ─────
  # Same standalone build as the standalone-full compose (no hot-reload
  # of vendor/geny-avatar — it's a pinned submodule, not local source).
  # Container/volume names suffixed with -dev for parallel dev/prod runs.
  avatar-editor:
    build:
      context: ./vendor/geny-avatar
      dockerfile: Dockerfile
      args:
        # NEXT_PUBLIC_* must be passed at build time — see docker-compose.yml
        # comment for the full reasoning.
        NEXT_PUBLIC_BASE_PATH: ""
        NEXT_PUBLIC_GENY_HOST: "true"
    container_name: geny-avatar-editor-dev
    init: true
    restart: unless-stopped
    ports:
      - "127.0.0.1:${AVATAR_EDITOR_PORT:-3001}:3000"
    environment:
      - NEXT_PUBLIC_BASE_PATH=
      - NEXT_PUBLIC_GENY_HOST=true
      - GENY_BAKED_EXPORTS_DIR=/exports
      # AI provider keys for the editor's Generate panel.
      - GEMINI_API_KEY=${GEMINI_API_KEY:-}
      - OPENAI_API_KEY=${OPENAI_API_KEY:-}
      - REPLICATE_API_TOKEN=${REPLICATE_API_TOKEN:-}
      - PORT=3000
      - HOSTNAME=0.0.0.0
      - NODE_ENV=production
    volumes:
      - geny-baked-exports-dev:/exports
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:3000/ || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s
    networks:
      - geny-net-dev

  # ── Whisper STT (dev variant of the prod service) ───────────────
  # GPU split (RTX 5070, 12 GB):
  #   omnivoice  → 0.50 (≈ 6 GB)
  #   whisper    → 0.35 (≈ 4 GB)
  #   total       0.85   (~10 GB)  — 15% headroom for stability
  whisper-stt:
    build:
      context: ./whisper-stt
      dockerfile: Dockerfile
    image: geny-whisper-stt:dev
    container_name: geny-whisper-stt-dev
    profiles: ["audio-local"]
    init: true
    restart: unless-stopped
    expose:
      - "8001"
    environment:
      - WHISPER_MODEL=${WHISPER_MODEL:-openai/whisper-large-v3}
      - WHISPER_PORT=${WHISPER_PORT:-8001}
      - WHISPER_GPU_MEMORY_FRACTION=${WHISPER_GPU_MEMORY_FRACTION:-0.35}
      - WHISPER_DTYPE=${WHISPER_DTYPE:-auto}
      # Blackwell (sm_120) FA2 incompat — see prod variant comment.
      - WHISPER_ATTENTION_BACKEND=${WHISPER_ATTENTION_BACKEND:-TRITON_ATTN}
      - HUGGINGFACE_HUB_TOKEN=${HUGGINGFACE_HUB_TOKEN:-}
      - HF_ENDPOINT=${HF_ENDPOINT:-https://huggingface.co}
    volumes:
      - geny-whisper-cache-dev:/cache/huggingface
    shm_size: 4G
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8001/health"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 600s
    networks:
      - geny-net-dev

networks:
  geny-net-dev:
    driver: bridge

volumes:
  geny-pgdata-dev:
    driver: local
  geny-omnivoice-models-dev:
    driver: local
  geny-whisper-cache-dev:
    driver: local
  # Backend storage root — see matching block in docker-compose.prod.yml.
  geny-agent-sessions-dev:
    driver: local
  # Shared between avatar-editor (rw) and backend (ro) — dev variant.
  geny-baked-exports-dev:
    driver: local
  # Phase G — Claude Code OAuth credentials. See backend.volumes.
  geny-claude-creds-dev:
    driver: local
  # ── Phase-1 persistent-storage volumes (2026-05-19) ─────────────────
  # See docker-compose.prod.yml for the full rationale.
  geny-environments-dev:
    driver: local
  geny-trigger-presets-dev:
    driver: local
  geny-tool-presets-dev:
    driver: local
  geny-chat-conversations-dev:
    driver: local
  geny-creature-state-dev:
    driver: local
  geny-config-variables-dev:
    driver: local
  geny-mcp-credentials-dev:
  geny-voice-studio-dev:
    driver: local