1 line
253 KiB
JSON
1 line
253 KiB
JSON
{"repo": "befamousvideo/betty", "files": {"LEAN/Betty/betty-n8n/docker-compose.yml": {"c752ce706cf1702f80774980639568e0ce607a36": {"t": 1785746993.208478, "n": 21905, "h": "d5e3bb63aeb98c66", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-300}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_OPERATOR_URL: http://betty-operator:8000\n BETTY_BRAIN_ACTIONS_URL: http://betty-actions:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n BETTY_OWNER_EMAIL: ${BETTY_OWNER_EMAIL:-vincentthinks@gmail.com}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:-}\n BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL: ${BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL:-#betty-station}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_MEMORY_URL: http://betty-memory:8000\n BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS: ${BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS:-15}\n BETTY_OWNER_SLACK_USER_ID: ${BETTY_OWNER_SLACK_USER_ID:-}\n BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS: ${BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS:-60}\n BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS: ${BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-whisper:\n build:\n context: ./services/whisper\n image: betty/whisper-service:local\n container_name: betty-whisper\n environment:\n WHISPER_MODEL: ${BETTY_WHISPER_MODEL:-Systran/faster-distil-whisper-large-v3}\n WHISPER_DEVICE: ${BETTY_WHISPER_DEVICE:-cuda}\n WHISPER_COMPUTE_TYPE: ${BETTY_WHISPER_COMPUTE_TYPE:-float16}\n WHISPER_BATCH_SIZE: ${BETTY_WHISPER_BATCH_SIZE:-16}\n WHISPER_BEAM_SIZE: ${BETTY_WHISPER_BEAM_SIZE:-1}\n WHISPER_VAD_FILTER: ${BETTY_WHISPER_VAD_FILTER:-true}\n WHISPER_DEFAULT_LANGUAGE: ${BETTY_WHISPER_DEFAULT_LANGUAGE:-en}\n WHISPER_DOWNLOAD_ROOT: /data\n volumes:\n - betty_whisper_data:/data\n deploy:\n resources:\n reservations:\n devices:\n - driver: nvidia\n count: 1\n capabilities: [gpu]\n healthcheck:\n test: [\"CMD\", \"python3\", \"-c\", \"import urllib.request,sys,json;d=json.load(urllib.request.urlopen('http://localhost:8000/health',timeout=5));sys.exit(0 if d.get('ready') else 1)\"]\n interval: 30s\n timeout: 10s\n retries: 5\n start_period: 180s\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_BRAIN_URL: http://betty-brain:8000\n BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS: ${BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED: ${BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED:-true}\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_OPERATOR_WHISPER_REMOTE_URL: ${BETTY_OPERATOR_WHISPER_REMOTE_URL:-http://betty-whisper:8000}\n BETTY_OPERATOR_WHISPER_REMOTE_TIMEOUT_SECONDS: ${BETTY_OPERATOR_WHISPER_REMOTE_TIMEOUT_SECONDS:-90}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:13b}\n # Slice 8: photo/scan vision. DGX direct (bypasses ollama-wrapper since wrapper\n # talks to local betty-ollama which has no LLaVA). Set to ollama-wrapper URL\n # if DGX is unreachable and you've pulled llava:13b locally.\n BETTY_VISION_OLLAMA_URL: ${BETTY_VISION_OLLAMA_URL:-http://192.168.68.79:11434}\n BETTY_VISION_TIMEOUT_SECONDS: ${BETTY_VISION_TIMEOUT_SECONDS:-90}\n # Cloud vision tier (Photo+ button → Haiku 4.5 vision via Anthropic API).\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}\n BETTY_VISION_CLOUD_MODEL: ${BETTY_VISION_CLOUD_MODEL:-claude-haiku-4-5}\n BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD:-1.0}\n BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD:-5.0}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n betty_whisper_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "e5cd4cb99bfa583b5fbebb7ce0a64438c7a5525d": {"t": 1785769582.7823634, "n": 20492, "h": "c4c967930f893c80", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-300}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_OPERATOR_URL: http://betty-operator:8000\n BETTY_BRAIN_ACTIONS_URL: http://betty-actions:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n BETTY_OWNER_EMAIL: ${BETTY_OWNER_EMAIL:-vincentthinks@gmail.com}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:-}\n BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL: ${BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL:-#betty-station}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_MEMORY_URL: http://betty-memory:8000\n BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS: ${BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS:-15}\n BETTY_OWNER_SLACK_USER_ID: ${BETTY_OWNER_SLACK_USER_ID:-}\n BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS: ${BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS:-60}\n BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS: ${BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_BRAIN_URL: http://betty-brain:8000\n BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS: ${BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED: ${BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED:-true}\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:13b}\n # Slice 8: photo/scan vision. DGX direct (bypasses ollama-wrapper since wrapper\n # talks to local betty-ollama which has no LLaVA). Set to ollama-wrapper URL\n # if DGX is unreachable and you've pulled llava:13b locally.\n BETTY_VISION_OLLAMA_URL: ${BETTY_VISION_OLLAMA_URL:-http://192.168.68.79:11434}\n BETTY_VISION_TIMEOUT_SECONDS: ${BETTY_VISION_TIMEOUT_SECONDS:-90}\n # Cloud vision tier (Photo+ button → Haiku 4.5 vision via Anthropic API).\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}\n BETTY_VISION_CLOUD_MODEL: ${BETTY_VISION_CLOUD_MODEL:-claude-haiku-4-5}\n BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD:-1.0}\n BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD:-5.0}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "54db37acbfa0f7b96ff8ad55f6ada555a61b3714": {"t": 1785769584.22096, "n": 20435, "h": "f1eb463f23a6c2f8", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-300}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_OPERATOR_URL: http://betty-operator:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n BETTY_OWNER_EMAIL: ${BETTY_OWNER_EMAIL:-vincentthinks@gmail.com}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:-}\n BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL: ${BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL:-#betty-station}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_MEMORY_URL: http://betty-memory:8000\n BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS: ${BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS:-15}\n BETTY_OWNER_SLACK_USER_ID: ${BETTY_OWNER_SLACK_USER_ID:-}\n BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS: ${BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS:-60}\n BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS: ${BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_BRAIN_URL: http://betty-brain:8000\n BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS: ${BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED: ${BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED:-true}\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:13b}\n # Slice 8: photo/scan vision. DGX direct (bypasses ollama-wrapper since wrapper\n # talks to local betty-ollama which has no LLaVA). Set to ollama-wrapper URL\n # if DGX is unreachable and you've pulled llava:13b locally.\n BETTY_VISION_OLLAMA_URL: ${BETTY_VISION_OLLAMA_URL:-http://192.168.68.79:11434}\n BETTY_VISION_TIMEOUT_SECONDS: ${BETTY_VISION_TIMEOUT_SECONDS:-90}\n # Cloud vision tier (Photo+ button → Haiku 4.5 vision via Anthropic API).\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}\n BETTY_VISION_CLOUD_MODEL: ${BETTY_VISION_CLOUD_MODEL:-claude-haiku-4-5}\n BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD:-1.0}\n BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD:-5.0}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "25c2831549e96e148900a597d7f4d3873b52b2a3": {"t": 1785769585.585058, "n": 20435, "h": "28efddcffc5281ee", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_OPERATOR_URL: http://betty-operator:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n BETTY_OWNER_EMAIL: ${BETTY_OWNER_EMAIL:-vincentthinks@gmail.com}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:-}\n BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL: ${BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL:-#betty-station}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_MEMORY_URL: http://betty-memory:8000\n BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS: ${BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS:-15}\n BETTY_OWNER_SLACK_USER_ID: ${BETTY_OWNER_SLACK_USER_ID:-}\n BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS: ${BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS:-60}\n BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS: ${BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_BRAIN_URL: http://betty-brain:8000\n BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS: ${BETTY_OPERATOR_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED: ${BETTY_OPERATOR_BRAIN_DEFAULT_ENABLED:-true}\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:13b}\n # Slice 8: photo/scan vision. DGX direct (bypasses ollama-wrapper since wrapper\n # talks to local betty-ollama which has no LLaVA). Set to ollama-wrapper URL\n # if DGX is unreachable and you've pulled llava:13b locally.\n BETTY_VISION_OLLAMA_URL: ${BETTY_VISION_OLLAMA_URL:-http://192.168.68.79:11434}\n BETTY_VISION_TIMEOUT_SECONDS: ${BETTY_VISION_TIMEOUT_SECONDS:-90}\n # Cloud vision tier (Photo+ button → Haiku 4.5 vision via Anthropic API).\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}\n BETTY_VISION_CLOUD_MODEL: ${BETTY_VISION_CLOUD_MODEL:-claude-haiku-4-5}\n BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD:-1.0}\n BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD:-5.0}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "486301d090aa5073d733b53d96608a638d99812b": {"t": 1785769589.1776931, "n": 20071, "h": "e5908ac6a6bf4618", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:-}\n BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL: ${BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL:-#betty-station}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_MEMORY_URL: http://betty-memory:8000\n BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS: ${BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS:-15}\n BETTY_OWNER_SLACK_USER_ID: ${BETTY_OWNER_SLACK_USER_ID:-}\n BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS: ${BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS:-60}\n BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS: ${BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:13b}\n # Slice 8: photo/scan vision. DGX direct (bypasses ollama-wrapper since wrapper\n # talks to local betty-ollama which has no LLaVA). Set to ollama-wrapper URL\n # if DGX is unreachable and you've pulled llava:13b locally.\n BETTY_VISION_OLLAMA_URL: ${BETTY_VISION_OLLAMA_URL:-http://192.168.68.79:11434}\n BETTY_VISION_TIMEOUT_SECONDS: ${BETTY_VISION_TIMEOUT_SECONDS:-90}\n # Cloud vision tier (Photo+ button → Haiku 4.5 vision via Anthropic API).\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}\n BETTY_VISION_CLOUD_MODEL: ${BETTY_VISION_CLOUD_MODEL:-claude-haiku-4-5}\n BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_INPUT_COST_PER_1M_USD:-1.0}\n BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD: ${BETTY_VISION_CLOUD_OUTPUT_COST_PER_1M_USD:-5.0}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "6ffea95553020c9b0114a3f1a9c0a89d877be2d9": {"t": 1785769591.724989, "n": 19275, "h": "46446bf603617b95", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:-}\n BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL: ${BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL:-#betty-station}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_MEMORY_URL: http://betty-memory:8000\n BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS: ${BETTY_DAEMON_MEMORY_TIMEOUT_SECONDS:-15}\n BETTY_OWNER_SLACK_USER_ID: ${BETTY_OWNER_SLACK_USER_ID:-}\n BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS: ${BETTY_DAEMON_REMINDER_POLL_INTERVAL_SECONDS:-60}\n BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS: ${BETTY_DAEMON_REMINDER_LOOKAHEAD_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:34b}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "1e566827119fdf4cba567432ddb0346fb7d10017": {"t": 1785769593.6904247, "n": 18818, "h": "c21bc78fedefd3c2", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:-}\n BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL: ${BETTY_SLACK_DEFAULT_DELEGATION_CHANNEL:-#betty-station}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:34b}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "5dc4686ab15e8a05dfc3b6a9a09b993864c5e590": {"t": 1785769594.987383, "n": 18658, "h": "7666f9328f5ae696", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_LOOKUP_URL: http://betty-lookup:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT: ${BETTY_BRAIN_LOOKUP_HTTP_TIMEOUT:-60}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:34b}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "9fc86cf915b71aa3c07a262f8dfa0e4191a22815": {"t": 1785769596.221914, "n": 18470, "h": "87890a1ebdaaa77b", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n BETTY_BRAIN_MAX_AGENT_ITERATIONS: ${BETTY_BRAIN_MAX_AGENT_ITERATIONS:-8}\n BETTY_BRAIN_MEMORY_URL: http://betty-memory:8000\n BETTY_BRAIN_TOOL_HTTP_TIMEOUT: ${BETTY_BRAIN_TOOL_HTTP_TIMEOUT:-30}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:34b}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "19b7bec86d7fcb25bb64ef1ca60a903e1ff243b9": {"t": 1785769597.5997763, "n": 18207, "h": "05374b7722d80bcb", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-daemon:\n build:\n context: ./services/betty-slack-daemon\n image: betty/betty-slack-daemon:local\n container_name: betty-daemon\n depends_on:\n betty-brain:\n condition: service_healthy\n environment:\n BETTY_SLACK_APP_TOKEN: ${BETTY_SLACK_APP_TOKEN:?set BETTY_SLACK_APP_TOKEN in .env after creating Betty's Slack app}\n BETTY_SLACK_BOT_TOKEN: ${BETTY_SLACK_BOT_TOKEN:?set BETTY_SLACK_BOT_TOKEN in .env after creating Betty's Slack app}\n BETTY_BRAIN_URL: http://betty-brain:8000\n BETTY_BRAIN_TIMEOUT_SECONDS: ${BETTY_BRAIN_TIMEOUT_SECONDS:-60}\n BETTY_DAEMON_DATA_DIR: /data\n BETTY_DAEMON_THREAD_KEEP_MSGS: ${BETTY_DAEMON_THREAD_KEEP_MSGS:-20}\n volumes:\n - ./data/betty-daemon:/data\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:34b}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "a4274351ad7c1dec5a7893663fcc341df54c8f13": {"t": 1785769599.9573977, "n": 17346, "h": "af6066b410428112", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-brain:\n build:\n context: ./services/brain\n image: betty/brain-service:local\n container_name: betty-brain\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_BRAIN_POSTGRES_HOST: postgres\n BETTY_BRAIN_POSTGRES_PORT: 5432\n BETTY_BRAIN_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_BRAIN_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_BRAIN_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_BRAIN_MODEL: ${BETTY_BRAIN_MODEL:-claude-haiku-4-5}\n BETTY_BRAIN_MAX_TOKENS: ${BETTY_BRAIN_MAX_TOKENS:-2000}\n ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY in .env}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-1}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-30}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Rancho Santa Margarita, CA}\n ports:\n - \"127.0.0.1:${BETTY_BRAIN_HOST_PORT:-18140}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:34b}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "e0496557da5335b6aa70e7720fc059c60980d89b": {"t": 1785769601.3827682, "n": 15870, "h": "adab18bf02a713fc", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty-v1-facts}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.45}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-actions:\n build:\n context: ./services/actions\n image: betty/actions-service:local\n container_name: betty-actions\n depends_on:\n postgres:\n condition: service_healthy\n environment:\n BETTY_ACTIONS_POSTGRES_HOST: postgres\n BETTY_ACTIONS_POSTGRES_PORT: 5432\n BETTY_ACTIONS_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_ACTIONS_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_ACTIONS_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_ACTIONS_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n ports:\n - \"127.0.0.1:${BETTY_ACTIONS_HOST_PORT:-18110}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-lookup:\n build:\n context: ./services/lookup\n image: betty/lookup-service:local\n container_name: betty-lookup\n depends_on:\n postgres:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n BETTY_LOOKUP_POSTGRES_HOST: postgres\n BETTY_LOOKUP_POSTGRES_PORT: 5432\n BETTY_LOOKUP_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_LOOKUP_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_LOOKUP_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_LOOKUP_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BRAVE_ENABLED: ${BRAVE_ENABLED:-false}\n SEARXNG_ENABLED: ${SEARXNG_ENABLED:-true}\n SEARXNG_BASE_URL: ${SEARXNG_BASE_URL:-http://host.docker.internal:8080}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BRAVE_SEARCH_API_KEY: ${BRAVE_SEARCH_API_KEY:-}\n BETTY_LOOKUP_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_LOOKUP_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_LOOKUP_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_LOOKUP_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_LOOKUP_MODEL: ${BETTY_LOOKUP_MODEL:-}\n BETTY_LOOKUP_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n ports:\n - \"127.0.0.1:${BETTY_LOOKUP_HOST_PORT:-18130}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-operator:\n build:\n context: ./services/operator\n image: betty/operator-service:local\n container_name: betty-operator\n depends_on:\n postgres:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n environment:\n BETTY_OPERATOR_POSTGRES_HOST: postgres\n BETTY_OPERATOR_POSTGRES_PORT: 5432\n BETTY_OPERATOR_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_OPERATOR_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_OPERATOR_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_OPERATOR_MEMORY_URL: http://betty-memory:8000\n BETTY_OPERATOR_ACTIONS_URL: http://betty-actions:8000\n BETTY_OPERATOR_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_WRAPPER_URL: http://ollama-wrapper:8000/v1/chat/completions\n BETTY_OPERATOR_WRAPPER_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n BETTY_OPERATOR_WRAPPER_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n BETTY_OPERATOR_WRAPPER_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n BETTY_OPERATOR_WRAPPER_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n BETTY_OPERATOR_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n BETTY_PUBLIC_BASE_URL: ${BETTY_PUBLIC_BASE_URL:-https://YOUR_PUBLIC_URL}\n BETTY_OWNER_PHONE: ${BETTY_OWNER_PHONE:-}\n BETTY_OWNER_NAME: ${BETTY_OWNER_NAME:-Vincent}\n BETTY_HOME_CITY: ${BETTY_HOME_CITY:-Corona Del Mar, CA}\n BETTY_OPERATOR_STT_PROVIDER: ${BETTY_OPERATOR_STT_PROVIDER:-whisper_local}\n BETTY_OPERATOR_WHISPER_MODEL: ${BETTY_OPERATOR_WHISPER_MODEL:-base.en}\n BETTY_OPERATOR_WHISPER_DEVICE: ${BETTY_OPERATOR_WHISPER_DEVICE:-cpu}\n BETTY_OPERATOR_WHISPER_COMPUTE_TYPE: ${BETTY_OPERATOR_WHISPER_COMPUTE_TYPE:-int8}\n BETTY_OPERATOR_WHISPER_DATA_DIR: ${BETTY_OPERATOR_WHISPER_DATA_DIR:-/app/data/whisper}\n BETTY_MODEL_MODE: ${BETTY_MODEL_MODE:-default}\n BETTY_DEFAULT_CHAT_PROVIDER: ${BETTY_DEFAULT_CHAT_PROVIDER:-auto}\n BETTY_DEFAULT_CHAT_MODEL: ${BETTY_DEFAULT_CHAT_MODEL:-deepseek-chat}\n BETTY_DEFAULT_LOCAL_CHAT_MODEL: ${BETTY_DEFAULT_LOCAL_CHAT_MODEL:-llama3.1:8b}\n BETTY_MODEL_DOLPHIN: ${BETTY_MODEL_DOLPHIN:-dolphin-llama3:70b}\n BETTY_MODEL_MEDICAL: ${BETTY_MODEL_MEDICAL:-medllama2}\n BETTY_MODEL_MUSIC: ${BETTY_MODEL_MUSIC:-llamusic/llamusic}\n BETTY_MODEL_IMAGE: ${BETTY_MODEL_IMAGE:-llava:34b}\n BETTY_MODEL_WRITE: ${BETTY_MODEL_WRITE:-HammerAI/mythomax-l2}\n BETTY_MODEL_CODE: ${BETTY_MODEL_CODE:-qwen2.5-coder:32b}\n BETTY_MODEL_RESEARCH: ${BETTY_MODEL_RESEARCH:-nemotron}\n BETTY_MODEL_FAST: ${BETTY_MODEL_FAST:-gemma2:27b}\n DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}\n DEEPSEEK_BASE_URL: ${DEEPSEEK_BASE_URL:-https://api.deepseek.com}\n BETTY_DEEPSEEK_MODEL: ${BETTY_DEEPSEEK_MODEL:-deepseek-chat}\n BETTY_DEEPSEEK_REASONER_MODEL: ${BETTY_DEEPSEEK_REASONER_MODEL:-deepseek-reasoner}\n BETTY_DEEPSEEK_TIMEOUT_SECONDS: ${BETTY_DEEPSEEK_TIMEOUT_SECONDS:-120}\n BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD:-0.27}\n BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD:-1.10}\n BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD:-0.55}\n BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD: ${BETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD:-2.19}\n KOKORO_ENABLED: ${KOKORO_ENABLED:-true}\n KOKORO_DEFAULT_VOICE: ${KOKORO_DEFAULT_VOICE:-af_bella}\n BETTY_OPERATOR_KOKORO_SPEED: ${BETTY_OPERATOR_KOKORO_SPEED:-1.0}\n BETTY_OPERATOR_KOKORO_DATA_DIR: ${BETTY_OPERATOR_KOKORO_DATA_DIR:-/app/data/kokoro}\n ELEVENLABS_ENABLED: ${ELEVENLABS_ENABLED:-true}\n TWILIO_ENABLED: ${TWILIO_ENABLED:-true}\n VAPI_ENABLED: ${VAPI_ENABLED:-true}\n BETTY_COST_DAILY_CAP_USD: ${BETTY_COST_DAILY_CAP_USD:-5}\n BETTY_COST_MONTHLY_CAP_USD: ${BETTY_COST_MONTHLY_CAP_USD:-50}\n BETTY_COST_WARNING_RATIO: ${BETTY_COST_WARNING_RATIO:-0.8}\n BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD: ${BETTY_ELEVENLABS_COST_PER_1K_CHARS_USD:-0.30}\n BETTY_TWILIO_SMS_COST_USD: ${BETTY_TWILIO_SMS_COST_USD:-0.008}\n BETTY_VAPI_COST_PER_MINUTE_USD: ${BETTY_VAPI_COST_PER_MINUTE_USD:-0.075}\n ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-}\n ELEVENLABS_VOICE_ID: ${ELEVENLABS_VOICE_ID:-4tRn1lSkEn13EVTuqb0g}\n ELEVENLABS_MODEL_ID: ${ELEVENLABS_MODEL_ID:-eleven_turbo_v2_5}\n PIPER_VOICE_MODEL: ${PIPER_VOICE_MODEL:-en_US-lessac-medium}\n BETTY_OPERATOR_PIPER_DATA_DIR: ${BETTY_OPERATOR_PIPER_DATA_DIR:-/app/data/piper}\n GOOGLE_CALENDAR_CLIENT_ID: ${GOOGLE_CALENDAR_CLIENT_ID:-}\n GOOGLE_CALENDAR_CLIENT_SECRET: ${GOOGLE_CALENDAR_CLIENT_SECRET:-}\n GOOGLE_CALENDAR_OAUTH_REDIRECT_URI: ${GOOGLE_CALENDAR_OAUTH_REDIRECT_URI:-}\n BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS: ${BETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS:-60}\n BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT: ${BETTY_OPERATOR_ELEVENLABS_RATE_LIMIT:-20}\n BETTY_OPERATOR_TWILIO_RATE_LIMIT: ${BETTY_OPERATOR_TWILIO_RATE_LIMIT:-20}\n VAPI_API_KEY: ${VAPI_API_KEY:-}\n VAPI_ASSISTANT_ID: ${VAPI_ASSISTANT_ID:-}\n VAPI_WEBHOOK_SECRET: ${VAPI_WEBHOOK_SECRET:-}\n TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}\n TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}\n TWILIO_PHONE_NUMBER: ${TWILIO_PHONE_NUMBER:-+19492391845}\n ports:\n - \"0.0.0.0:${BETTY_OPERATOR_HOST_PORT:-18120}:8000\"\n volumes:\n - betty_operator_data:/app/data\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n betty-actions:\n condition: service_healthy\n betty-lookup:\n condition: service_healthy\n betty-operator:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n BETTY_ACTIONS_URL: http://betty-actions:8000\n BETTY_LOOKUP_URL: http://betty-lookup:8000\n BETTY_OPERATOR_URL: http://betty-operator:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n betty_operator_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "4ca93b88ab8a4ac1a7b18aecb09c97d784c7f5b4": {"t": 1785769602.9500952, "n": 6032, "h": "f4262cc1b6d7851d", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n chroma:\n image: chromadb/chroma:0.5.23\n container_name: betty-chroma\n volumes:\n - chroma_data:/chroma/chroma\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/v1/heartbeat', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n betty-memory:\n build:\n context: ./services/memory\n image: betty/memory-service:local\n container_name: betty-memory\n depends_on:\n postgres:\n condition: service_healthy\n chroma:\n condition: service_healthy\n ollama:\n condition: service_healthy\n environment:\n BETTY_MEMORY_POSTGRES_HOST: postgres\n BETTY_MEMORY_POSTGRES_PORT: 5432\n BETTY_MEMORY_POSTGRES_DB: ${POSTGRES_DB:-n8n}\n BETTY_MEMORY_POSTGRES_USER: ${POSTGRES_USER:-n8n}\n BETTY_MEMORY_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n BETTY_MEMORY_CHROMA_HOST: chroma\n BETTY_MEMORY_CHROMA_PORT: 8000\n BETTY_MEMORY_COLLECTION: ${BETTY_MEMORY_COLLECTION:-betty_memory}\n BETTY_MEMORY_OLLAMA_URL: http://ollama:11434\n BETTY_MEMORY_EMBED_MODEL: ${BETTY_MEMORY_EMBED_MODEL:-nomic-embed-text}\n BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD: ${BETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD:-0.35}\n BETTY_MEMORY_LOG_LEVEL: ${BETTY_MEMORY_LOG_LEVEL:-info}\n BETTY_MEMORY_DEFAULT_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n ports:\n - \"127.0.0.1:${BETTY_MEMORY_HOST_PORT:-18100}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 15s\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n betty-memory:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n BETTY_MEMORY_URL: http://betty-memory:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n chroma_data:\n ollama_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}, "1efc591a22219ba6fa309398394275336d18ebc8": {"t": 1785769604.3385262, "n": 3801, "h": "72431acf15dd46d5", "c": "name: betty-stack\n\nservices:\n postgres:\n image: postgres:16-alpine\n container_name: betty-postgres\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-n8n}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n POSTGRES_DB: ${POSTGRES_DB:-n8n}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}\"]\n interval: 10s\n timeout: 5s\n retries: 10\n start_period: 15s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama:\n image: ollama/ollama:latest\n container_name: betty-ollama\n ports:\n - \"127.0.0.1:${OLLAMA_HOST_PORT:-11435}:11434\"\n volumes:\n - ollama_data:/root/.ollama\n healthcheck:\n test: [\"CMD-SHELL\", \"ollama list >/dev/null 2>&1 || exit 1\"]\n interval: 20s\n timeout: 10s\n retries: 6\n start_period: 30s\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\n ollama-wrapper:\n build:\n context: ./ollama-wrapper\n image: betty/ollama-wrapper:local\n container_name: betty-ollama-wrapper\n depends_on:\n ollama:\n condition: service_healthy\n environment:\n OLLAMA_URL: http://ollama:11434\n API_USERNAME: ${WRAPPER_API_USERNAME:?set WRAPPER_API_USERNAME in .env}\n API_PASSWORD: ${WRAPPER_API_PASSWORD:?set WRAPPER_API_PASSWORD in .env}\n API_BEARER_TOKEN: ${WRAPPER_API_BEARER_TOKEN:-}\n WRAPPER_REQUEST_TIMEOUT_SECONDS: ${WRAPPER_REQUEST_TIMEOUT_SECONDS:-120}\n LOG_LEVEL: ${WRAPPER_LOG_LEVEL:-info}\n ports:\n - \"127.0.0.1:${WRAPPER_HOST_PORT:-18000}:8000\"\n healthcheck:\n test:\n [\n \"CMD\",\n \"python\",\n \"-c\",\n \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()\",\n ]\n interval: 15s\n timeout: 5s\n retries: 8\n start_period: 10s\n read_only: true\n tmpfs:\n - /tmp\n security_opt:\n - no-new-privileges:true\n restart: unless-stopped\n networks:\n - betty-net\n\n n8n:\n image: n8nio/n8n:latest\n container_name: betty-n8n\n depends_on:\n postgres:\n condition: service_healthy\n ollama:\n condition: service_healthy\n ollama-wrapper:\n condition: service_healthy\n environment:\n N8N_HOST: ${N8N_HOST:-localhost}\n N8N_PORT: 5678\n N8N_PROTOCOL: ${N8N_PROTOCOL:-http}\n WEBHOOK_URL: ${WEBHOOK_URL:-http://localhost:15678/}\n GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-America/Los_Angeles}\n N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY:?set N8N_ENCRYPTION_KEY in .env}\n N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: \"true\"\n N8N_DIAGNOSTICS_ENABLED: \"false\"\n N8N_PERSONALIZATION_ENABLED: \"false\"\n N8N_SECURE_COOKIE: \"false\"\n N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE:-true}\n N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER:?set N8N_BASIC_AUTH_USER in .env}\n N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD:?set N8N_BASIC_AUTH_PASSWORD in .env}\n DB_TYPE: postgresdb\n DB_POSTGRESDB_HOST: postgres\n DB_POSTGRESDB_PORT: 5432\n DB_POSTGRESDB_DATABASE: ${POSTGRES_DB:-n8n}\n DB_POSTGRESDB_USER: ${POSTGRES_USER:-n8n}\n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}\n OLLAMA_WRAPPER_URL: http://ollama-wrapper:8000\n ports:\n - \"127.0.0.1:${N8N_HOST_PORT:-15678}:5678\"\n volumes:\n - n8n_data:/home/node/.n8n\n restart: unless-stopped\n security_opt:\n - no-new-privileges:true\n networks:\n - betty-net\n\nvolumes:\n postgres_data:\n ollama_data:\n n8n_data:\n\nnetworks:\n betty-net:\n driver: bridge\n"}}, "LEAN/Betty/betty-n8n/.env.example": {"c752ce706cf1702f80774980639568e0ce607a36": {"t": 1785747143.2407775, "n": 3520, "h": "3ae9b649cfa74026", "c": "# Copy this file to .env and replace placeholders (or run ./scripts/generate-secrets.sh)\n\n# Core timezone + public URL settings\nGENERIC_TIMEZONE=America/Los_Angeles\nN8N_HOST=localhost\nN8N_PROTOCOL=http\nN8N_HOST_PORT=15678\nOLLAMA_HOST_PORT=11435\nWRAPPER_HOST_PORT=18000\nWEBHOOK_URL=http://localhost:15678/\n\n# Postgres\nPOSTGRES_USER=n8n\nPOSTGRES_DB=n8n\nPOSTGRES_PASSWORD=replace_with_strong_password\n\n# n8n security\nN8N_ENCRYPTION_KEY=replace_with_32plus_char_key\nN8N_BASIC_AUTH_ACTIVE=true\nN8N_BASIC_AUTH_USER=betty_admin\nN8N_BASIC_AUTH_PASSWORD=replace_with_strong_password\n\n# Ollama wrapper auth\nWRAPPER_API_USERNAME=betty\nWRAPPER_API_PASSWORD=replace_with_strong_password\n# Optional: enable Bearer token auth for clients that cannot send HTTP Basic\nWRAPPER_API_BEARER_TOKEN=\nWRAPPER_REQUEST_TIMEOUT_SECONDS=120\nWRAPPER_LOG_LEVEL=info\nBETTY_MEMORY_HOST_PORT=18100\nBETTY_MEMORY_COLLECTION=betty-v1-facts\nBETTY_MEMORY_EMBED_MODEL=nomic-embed-text\nBETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD=0.45\nBETTY_MEMORY_LOG_LEVEL=info\nBETTY_ACTIONS_HOST_PORT=18110\nBETTY_OPERATOR_HOST_PORT=18120\nBETTY_LOOKUP_HOST_PORT=18130\nBETTY_OPERATOR_STT_PROVIDER=whisper_local\nBETTY_OPERATOR_WHISPER_MODEL=base.en\nBETTY_OPERATOR_WHISPER_DEVICE=cpu\nBETTY_OPERATOR_WHISPER_COMPUTE_TYPE=int8\nBETTY_OPERATOR_WHISPER_DATA_DIR=/app/data/whisper\nBETTY_MODEL_MODE=default\nBETTY_DEFAULT_CHAT_PROVIDER=auto\nBETTY_DEFAULT_CHAT_MODEL=deepseek-chat\nBETTY_DEFAULT_LOCAL_CHAT_MODEL=llama3.1:8b\nBETTY_MODEL_DOLPHIN=dolphin-llama3:70b\nBETTY_MODEL_MEDICAL=medllama2\nBETTY_MODEL_MUSIC=llamusic/llamusic\nBETTY_MODEL_IMAGE=llava:34b\nBETTY_MODEL_WRITE=HammerAI/mythomax-l2\nBETTY_MODEL_CODE=qwen2.5-coder:32b\nBETTY_MODEL_RESEARCH=nemotron\nBETTY_MODEL_FAST=gemma2:27b\nKOKORO_ENABLED=true\nKOKORO_DEFAULT_VOICE=af_bella\nBETTY_OPERATOR_KOKORO_SPEED=1.0\nBETTY_OPERATOR_KOKORO_DATA_DIR=/app/data/kokoro\n# Set to your Tailscale Funnel HTTPS URL once wired (e.g. https://ai26.tail1234.ts.net)\nBETTY_PUBLIC_BASE_URL=https://YOUR_TAILSCALE_FUNNEL_URL\nGOOGLE_CALENDAR_OAUTH_REDIRECT_URI=\nBETTY_OWNER_PHONE=\nBETTY_OWNER_NAME=Vincent\nBETTY_HOME_CITY=Corona Del Mar, CA\nBETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS=60\nBETTY_OPERATOR_ELEVENLABS_RATE_LIMIT=20\nBETTY_OPERATOR_TWILIO_RATE_LIMIT=20\nBETTY_OPERATOR_PIPER_DATA_DIR=/app/data/piper\nELEVENLABS_ENABLED=false\nTWILIO_ENABLED=false\nVAPI_ENABLED=false\nBRAVE_ENABLED=false\nSEARXNG_ENABLED=true\nSEARXNG_BASE_URL=http://host.docker.internal:8080\nBETTY_COST_DAILY_CAP_USD=5\nBETTY_COST_MONTHLY_CAP_USD=50\nBETTY_COST_WARNING_RATIO=0.8\nBETTY_ELEVENLABS_COST_PER_1K_CHARS_USD=0.30\nBETTY_TWILIO_SMS_COST_USD=0.008\nBETTY_VAPI_COST_PER_MINUTE_USD=0.075\nBETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD=0.27\nBETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD=1.10\nBETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD=0.55\nBETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD=2.19\n\n# Optional external integrations (fill when needed)\nELEVENLABS_API_KEY=\nELEVENLABS_VOICE_ID=4tRn1lSkEn13EVTuqb0g\nELEVENLABS_MODEL_ID=eleven_turbo_v2_5\nBRAVE_SEARCH_API_KEY=\nBETTY_LOOKUP_MODEL=\nVAPI_API_KEY=\nVAPI_ASSISTANT_ID=\nVAPI_WEBHOOK_SECRET=replace_with_vapi_webhook_secret\nTWILIO_ACCOUNT_SID=\nTWILIO_AUTH_TOKEN=\n# Betty dedicated Twilio number in E.164 format.\nTWILIO_PHONE_NUMBER=+19492391845\nPIPER_VOICE_MODEL=en_US-lessac-medium\nNGROK_AUTHTOKEN=\nGOOGLE_CALENDAR_CLIENT_ID=\nGOOGLE_CALENDAR_CLIENT_SECRET=\nDEEPSEEK_API_KEY=\nDEEPSEEK_BASE_URL=https://api.deepseek.com\nBETTY_DEEPSEEK_MODEL=deepseek-chat\nBETTY_DEEPSEEK_REASONER_MODEL=deepseek-reasoner\nBETTY_DEEPSEEK_TIMEOUT_SECONDS=120\n"}, "e0496557da5335b6aa70e7720fc059c60980d89b": {"t": 1785769585.7764528, "n": 3520, "h": "3ae9b649cfa74026", "c": "# Copy this file to .env and replace placeholders (or run ./scripts/generate-secrets.sh)\n\n# Core timezone + public URL settings\nGENERIC_TIMEZONE=America/Los_Angeles\nN8N_HOST=localhost\nN8N_PROTOCOL=http\nN8N_HOST_PORT=15678\nOLLAMA_HOST_PORT=11435\nWRAPPER_HOST_PORT=18000\nWEBHOOK_URL=http://localhost:15678/\n\n# Postgres\nPOSTGRES_USER=n8n\nPOSTGRES_DB=n8n\nPOSTGRES_PASSWORD=replace_with_strong_password\n\n# n8n security\nN8N_ENCRYPTION_KEY=replace_with_32plus_char_key\nN8N_BASIC_AUTH_ACTIVE=true\nN8N_BASIC_AUTH_USER=betty_admin\nN8N_BASIC_AUTH_PASSWORD=replace_with_strong_password\n\n# Ollama wrapper auth\nWRAPPER_API_USERNAME=betty\nWRAPPER_API_PASSWORD=replace_with_strong_password\n# Optional: enable Bearer token auth for clients that cannot send HTTP Basic\nWRAPPER_API_BEARER_TOKEN=\nWRAPPER_REQUEST_TIMEOUT_SECONDS=120\nWRAPPER_LOG_LEVEL=info\nBETTY_MEMORY_HOST_PORT=18100\nBETTY_MEMORY_COLLECTION=betty-v1-facts\nBETTY_MEMORY_EMBED_MODEL=nomic-embed-text\nBETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD=0.45\nBETTY_MEMORY_LOG_LEVEL=info\nBETTY_ACTIONS_HOST_PORT=18110\nBETTY_OPERATOR_HOST_PORT=18120\nBETTY_LOOKUP_HOST_PORT=18130\nBETTY_OPERATOR_STT_PROVIDER=whisper_local\nBETTY_OPERATOR_WHISPER_MODEL=base.en\nBETTY_OPERATOR_WHISPER_DEVICE=cpu\nBETTY_OPERATOR_WHISPER_COMPUTE_TYPE=int8\nBETTY_OPERATOR_WHISPER_DATA_DIR=/app/data/whisper\nBETTY_MODEL_MODE=default\nBETTY_DEFAULT_CHAT_PROVIDER=auto\nBETTY_DEFAULT_CHAT_MODEL=deepseek-chat\nBETTY_DEFAULT_LOCAL_CHAT_MODEL=llama3.1:8b\nBETTY_MODEL_DOLPHIN=dolphin-llama3:70b\nBETTY_MODEL_MEDICAL=medllama2\nBETTY_MODEL_MUSIC=llamusic/llamusic\nBETTY_MODEL_IMAGE=llava:34b\nBETTY_MODEL_WRITE=HammerAI/mythomax-l2\nBETTY_MODEL_CODE=qwen2.5-coder:32b\nBETTY_MODEL_RESEARCH=nemotron\nBETTY_MODEL_FAST=gemma2:27b\nKOKORO_ENABLED=true\nKOKORO_DEFAULT_VOICE=af_bella\nBETTY_OPERATOR_KOKORO_SPEED=1.0\nBETTY_OPERATOR_KOKORO_DATA_DIR=/app/data/kokoro\n# Set to your Tailscale Funnel HTTPS URL once wired (e.g. https://ai26.tail1234.ts.net)\nBETTY_PUBLIC_BASE_URL=https://YOUR_TAILSCALE_FUNNEL_URL\nGOOGLE_CALENDAR_OAUTH_REDIRECT_URI=\nBETTY_OWNER_PHONE=\nBETTY_OWNER_NAME=Vincent\nBETTY_HOME_CITY=Corona Del Mar, CA\nBETTY_OPERATOR_RATE_LIMIT_WINDOW_SECONDS=60\nBETTY_OPERATOR_ELEVENLABS_RATE_LIMIT=20\nBETTY_OPERATOR_TWILIO_RATE_LIMIT=20\nBETTY_OPERATOR_PIPER_DATA_DIR=/app/data/piper\nELEVENLABS_ENABLED=false\nTWILIO_ENABLED=false\nVAPI_ENABLED=false\nBRAVE_ENABLED=false\nSEARXNG_ENABLED=true\nSEARXNG_BASE_URL=http://host.docker.internal:8080\nBETTY_COST_DAILY_CAP_USD=5\nBETTY_COST_MONTHLY_CAP_USD=50\nBETTY_COST_WARNING_RATIO=0.8\nBETTY_ELEVENLABS_COST_PER_1K_CHARS_USD=0.30\nBETTY_TWILIO_SMS_COST_USD=0.008\nBETTY_VAPI_COST_PER_MINUTE_USD=0.075\nBETTY_DEEPSEEK_CHAT_INPUT_COST_PER_1M_USD=0.27\nBETTY_DEEPSEEK_CHAT_OUTPUT_COST_PER_1M_USD=1.10\nBETTY_DEEPSEEK_REASONER_INPUT_COST_PER_1M_USD=0.55\nBETTY_DEEPSEEK_REASONER_OUTPUT_COST_PER_1M_USD=2.19\n\n# Optional external integrations (fill when needed)\nELEVENLABS_API_KEY=\nELEVENLABS_VOICE_ID=4tRn1lSkEn13EVTuqb0g\nELEVENLABS_MODEL_ID=eleven_turbo_v2_5\nBRAVE_SEARCH_API_KEY=\nBETTY_LOOKUP_MODEL=\nVAPI_API_KEY=\nVAPI_ASSISTANT_ID=\nVAPI_WEBHOOK_SECRET=replace_with_vapi_webhook_secret\nTWILIO_ACCOUNT_SID=\nTWILIO_AUTH_TOKEN=\n# Betty dedicated Twilio number in E.164 format.\nTWILIO_PHONE_NUMBER=+19492391845\nPIPER_VOICE_MODEL=en_US-lessac-medium\nNGROK_AUTHTOKEN=\nGOOGLE_CALENDAR_CLIENT_ID=\nGOOGLE_CALENDAR_CLIENT_SECRET=\nDEEPSEEK_API_KEY=\nDEEPSEEK_BASE_URL=https://api.deepseek.com\nBETTY_DEEPSEEK_MODEL=deepseek-chat\nBETTY_DEEPSEEK_REASONER_MODEL=deepseek-reasoner\nBETTY_DEEPSEEK_TIMEOUT_SECONDS=120\n"}, "4ca93b88ab8a4ac1a7b18aecb09c97d784c7f5b4": {"t": 1785769588.903257, "n": 1144, "h": "99ef82342e6b55a9", "c": "# Copy this file to .env and replace placeholders (or run ./scripts/generate-secrets.sh)\n\n# Core timezone + public URL settings\nGENERIC_TIMEZONE=America/Los_Angeles\nN8N_HOST=localhost\nN8N_PROTOCOL=http\nN8N_HOST_PORT=15678\nOLLAMA_HOST_PORT=11435\nWRAPPER_HOST_PORT=18000\nWEBHOOK_URL=http://localhost:15678/\n\n# Postgres\nPOSTGRES_USER=n8n\nPOSTGRES_DB=n8n\nPOSTGRES_PASSWORD=replace_with_strong_password\n\n# n8n security\nN8N_ENCRYPTION_KEY=replace_with_32plus_char_key\nN8N_BASIC_AUTH_ACTIVE=true\nN8N_BASIC_AUTH_USER=betty_admin\nN8N_BASIC_AUTH_PASSWORD=replace_with_strong_password\n\n# Ollama wrapper auth\nWRAPPER_API_USERNAME=betty\nWRAPPER_API_PASSWORD=replace_with_strong_password\n# Optional: enable Bearer token auth for clients that cannot send HTTP Basic\nWRAPPER_API_BEARER_TOKEN=\nWRAPPER_REQUEST_TIMEOUT_SECONDS=120\nWRAPPER_LOG_LEVEL=info\nBETTY_MEMORY_HOST_PORT=18100\nBETTY_MEMORY_COLLECTION=betty_memory\nBETTY_MEMORY_EMBED_MODEL=nomic-embed-text\nBETTY_MEMORY_SEMANTIC_DISTANCE_THRESHOLD=0.35\nBETTY_MEMORY_LOG_LEVEL=info\n\n# Optional external integrations (fill when needed)\nELEVENLABS_API_KEY=\nBRAVE_SEARCH_API_KEY=\nVAPI_API_KEY=\nNGROK_AUTHTOKEN=\n"}, "1efc591a22219ba6fa309398394275336d18ebc8": {"t": 1785769590.2812586, "n": 962, "h": "6cea4b310e5eb823", "c": "# Copy this file to .env and replace placeholders (or run ./scripts/generate-secrets.sh)\n\n# Core timezone + public URL settings\nGENERIC_TIMEZONE=America/Los_Angeles\nN8N_HOST=localhost\nN8N_PROTOCOL=http\nN8N_HOST_PORT=15678\nOLLAMA_HOST_PORT=11435\nWRAPPER_HOST_PORT=18000\nWEBHOOK_URL=http://localhost:15678/\n\n# Postgres\nPOSTGRES_USER=n8n\nPOSTGRES_DB=n8n\nPOSTGRES_PASSWORD=replace_with_strong_password\n\n# n8n security\nN8N_ENCRYPTION_KEY=replace_with_32plus_char_key\nN8N_BASIC_AUTH_ACTIVE=true\nN8N_BASIC_AUTH_USER=betty_admin\nN8N_BASIC_AUTH_PASSWORD=replace_with_strong_password\n\n# Ollama wrapper auth\nWRAPPER_API_USERNAME=betty\nWRAPPER_API_PASSWORD=replace_with_strong_password\n# Optional: enable Bearer token auth for clients that cannot send HTTP Basic\nWRAPPER_API_BEARER_TOKEN=\nWRAPPER_REQUEST_TIMEOUT_SECONDS=120\nWRAPPER_LOG_LEVEL=info\n\n# Optional external integrations (fill when needed)\nELEVENLABS_API_KEY=\nBRAVE_SEARCH_API_KEY=\nVAPI_API_KEY=\nNGROK_AUTHTOKEN=\n"}}}} |