{"repo": "abhinavjha0239/llmlog", "files": {"docker-compose.yml": {"70abdfc6407bfa7a8958a05b78f3ca62238374cc": {"t": 1785745240.515468, "n": 4336, "h": "dd17172bf5c497fc", "c": "# =============================================================================\n# One-command local stack: docker compose up --build\n#\n# chat http://localhost:8000 (UI + chat API)\n# ingestion http://localhost:8100 (log intake + metrics API)\n# worker (no port — drains the stream into Postgres)\n# postgres :5432 redis :6379\n#\n# Zero API keys required: the default provider is 'mock'. Drop real keys into\n# a .env file (see .env.example) to light up real providers.\n#\n# Scale the worker to show horizontal consumer-group processing:\n# docker compose up --build --scale worker=3\n# =============================================================================\n\nservices:\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: app\n POSTGRES_PASSWORD: app\n POSTGRES_DB: llmlogs\n # Host port is configurable so the stack coexists with a local Postgres.\n ports: [\"${POSTGRES_HOST_PORT:-5432}:5432\"]\n volumes:\n - pgdata:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U app -d llmlogs\"]\n interval: 3s\n timeout: 3s\n retries: 20\n\n redis:\n image: redis:7-alpine\n # Disable persistence — telemetry buffer, not a system of record.\n command: [\"redis-server\", \"--save\", \"\", \"--appendonly\", \"no\"]\n ports: [\"6379:6379\"]\n healthcheck:\n test: [\"CMD\", \"redis-cli\", \"ping\"]\n interval: 3s\n timeout: 3s\n retries: 20\n\n ingestion:\n build:\n context: .\n dockerfile: services/ingestion/Dockerfile\n environment:\n DATABASE_URL: postgresql://app:app@postgres:5432/llmlogs\n REDIS_URL: redis://redis:6379/0\n LOG_STREAM_MAX_BACKLOG: \"100000\"\n ports: [\"8100:8100\"]\n depends_on:\n postgres: { condition: service_healthy }\n redis: { condition: service_healthy }\n healthcheck:\n test: [\"CMD\", \"python3\", \"-c\", \"import urllib.request; urllib.request.urlopen('http://localhost:8100/healthz', timeout=3)\"]\n interval: 5s\n timeout: 4s\n retries: 12\n\n worker:\n build:\n context: .\n dockerfile: services/worker/Dockerfile\n environment:\n DATABASE_URL: postgresql://app:app@postgres:5432/llmlogs\n REDIS_URL: redis://redis:6379/0\n # PII redaction of log previews happens here. Flip to redact full chat\n # message bodies before storage too (lossy) by setting REDACT_MESSAGES.\n REDACT_MESSAGES: \"false\"\n depends_on:\n postgres: { condition: service_healthy }\n redis: { condition: service_healthy }\n # No ports. Safe to run multiple replicas (same consumer group).\n restart: unless-stopped\n\n chat:\n build:\n context: .\n dockerfile: services/chat/Dockerfile\n environment:\n DATABASE_URL: postgresql://app:app@postgres:5432/llmlogs\n REDIS_URL: redis://redis:6379/0\n LOG_ENDPOINT: http://ingestion:8100/v1/logs\n DEFAULT_PROVIDER: ${DEFAULT_PROVIDER:-mock}\n CONTEXT_MAX_MESSAGES: \"20\"\n # Provider keys — all optional; absent providers just show \"no key\" in UI.\n OPENAI_API_KEY: ${OPENAI_API_KEY:-}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n XAI_API_KEY: ${XAI_API_KEY:-}\n GEMINI_API_KEY: ${GEMINI_API_KEY:-}\n # Model overrides (optional).\n OPENAI_MODEL: ${OPENAI_MODEL:-gpt-4.1-mini}\n ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-4-6}\n DEEPSEEK_MODEL: ${DEEPSEEK_MODEL:-deepseek-v4-flash}\n XAI_MODEL: ${XAI_MODEL:-grok-4.3}\n GEMINI_MODEL: ${GEMINI_MODEL:-gemini-3.5-flash}\n # Base-URL overrides (optional) — point a provider at any compatible\n # host, e.g. OPENAI_BASE_URL at an Azure OpenAI /openai/v1 endpoint.\n OPENAI_BASE_URL: ${OPENAI_BASE_URL:-}\n ANTHROPIC_BASE_URL: ${ANTHROPIC_BASE_URL:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-}\n XAI_BASE_URL: ${XAI_BASE_URL:-}\n GEMINI_BASE_URL: ${GEMINI_BASE_URL:-}\n ports: [\"8000:8000\"]\n depends_on:\n postgres: { condition: service_healthy }\n redis: { condition: service_healthy }\n ingestion: { condition: service_started }\n healthcheck:\n test: [\"CMD\", \"python3\", \"-c\", \"import urllib.request; urllib.request.urlopen('http://localhost:8000/healthz', timeout=3)\"]\n interval: 5s\n timeout: 4s\n retries: 12\n\nvolumes:\n pgdata:\n"}}}}