# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build & Run Commands ```bash # Install dependencies pip install -r requirements.txt # Run the bot (requires .env with DISCORD_TOKEN and AI provider key) python -m daemon_boyfriend # Run with Docker (includes PostgreSQL) docker-compose up -d # Run database migrations alembic upgrade head # Syntax check all Python files python -m py_compile src/daemon_boyfriend/**/*.py ``` ## Architecture This is a Discord bot that responds to @mentions with AI-generated responses (multi-provider support). ### Provider Pattern The AI system uses a provider abstraction pattern: - `services/providers/base.py` defines `AIProvider` abstract class with `generate()` method - `services/providers/openai.py`, `openrouter.py`, `anthropic.py`, `gemini.py` implement the interface - `services/ai_service.py` is the factory that creates the correct provider based on `AI_PROVIDER` env var - OpenRouter uses OpenAI's client with a different base URL - Gemini uses the `google-genai` SDK ### Cog System Discord functionality is in `cogs/`: - `ai_chat.py` - `@mention` handler (responds when bot is mentioned) - `memory.py` - Memory management commands (`!setname`, `!remember`, etc.) - `status.py` - Bot health and status commands Cogs are auto-loaded by `bot.py` from the `cogs/` directory. ### Database & Memory System The bot uses PostgreSQL for persistent memory (optional, falls back to in-memory): - `models/` - SQLAlchemy models (User, UserFact, Conversation, Message, Guild, GuildMember) - `services/database.py` - Connection pool and async session management - `services/user_service.py` - User CRUD, custom names, facts management - `services/persistent_conversation.py` - Database-backed conversation history - `alembic/` - Database migrations Key features: - Custom names: Set preferred names for users so the bot knows "who is who" - User facts: Bot remembers things about users (hobbies, preferences, etc.) - Persistent conversations: Chat history survives restarts - Conversation timeout: New conversation starts after 60 minutes of inactivity ### Configuration All config flows through `config.py` using pydantic-settings. The `settings` singleton is created at module load, so env vars must be set before importing. ### Web Search The bot can search the web for current information via SearXNG: - `services/searxng.py` provides `SearXNGService` for web queries - `ai_chat.py` uses a two-step approach: first asks AI if search is needed, then provides results as context - Search is triggered automatically when the AI determines the query needs current information - Configured via `SEARXNG_URL`, `SEARXNG_ENABLED`, and `SEARXNG_MAX_RESULTS` env vars ### Key Design Decisions - `PersistentConversationManager` stores conversations in PostgreSQL when `DATABASE_URL` is set - `ConversationManager` is the in-memory fallback when database is not configured - Long AI responses are split via `split_message()` in `ai_chat.py` to respect Discord's 2000 char limit - The bot responds only to @mentions via `on_message` listener - Web search uses AI to decide when to search, avoiding unnecessary API calls for general knowledge questions - User context (custom name + known facts) is included in AI prompts for personalized responses ## Environment Variables Required: `DISCORD_TOKEN`, plus one of `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, `ANTHROPIC_API_KEY`, or `GEMINI_API_KEY` depending on `AI_PROVIDER` setting. Optional: - `DATABASE_URL` - PostgreSQL connection string (e.g., `postgresql+asyncpg://user:pass@host:5432/db`) - `POSTGRES_PASSWORD` - Used by docker-compose for the PostgreSQL container - `SEARXNG_URL` - SearXNG instance URL for web search capability ## Memory Commands User commands: - `!setname ` - Set your preferred name - `!clearname` - Reset to Discord display name - `!remember ` - Tell the bot something about you - `!whatdoyouknow` - See what the bot remembers about you - `!forgetme` - Clear all facts about you Admin commands: - `!setusername @user ` - Set name for another user - `!teachbot @user ` - Add a fact about a user