Rebrand and personalize the bot as 'Bartender' - a companion for those who love deeply and feel intensely. Major changes: - Rename package: daemon_boyfriend -> loyal_companion - New default personality: Bartender - wise, steady, non-judgmental - Grief-aware system prompt (no toxic positivity, attachment-informed) - New relationship levels: New Face -> Close Friend progression - Bartender-style mood modifiers (steady presence) - New fact types: attachment_pattern, grief_context, coping_mechanism - Lower mood decay (0.05) for emotional stability - Higher fact extraction rate (0.4) - Bartender pays attention Updated all imports, configs, Docker files, and documentation.
432 lines
22 KiB
Markdown
432 lines
22 KiB
Markdown
# Architecture Overview
|
|
|
|
This document provides a comprehensive overview of the Daemon Boyfriend Discord bot architecture.
|
|
|
|
## Table of Contents
|
|
|
|
- [High-Level Architecture](#high-level-architecture)
|
|
- [Directory Structure](#directory-structure)
|
|
- [Core Components](#core-components)
|
|
- [Data Flow](#data-flow)
|
|
- [Design Patterns](#design-patterns)
|
|
- [Component Interaction Diagram](#component-interaction-diagram)
|
|
|
|
---
|
|
|
|
## High-Level Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ Discord API │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ Discord Bot (bot.py) │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ AIChatCog │ │ MemoryCog │ │ StatusCog │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌───────────────┼───────────────┐
|
|
▼ ▼ ▼
|
|
┌───────────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐
|
|
│ Core Services │ │ Living AI │ │ AI Providers │
|
|
│ ┌─────────────────┐ │ │ Services │ │ ┌─────────────────────┐ │
|
|
│ │ UserService │ │ │ ┌───────────┐ │ │ │ OpenAI Provider │ │
|
|
│ │ DatabaseService│ │ │ │ MoodSvc │ │ │ │ OpenRouter Provider│ │
|
|
│ │ ConversationMgr│ │ │ │ RelSvc │ │ │ │ Anthropic Provider │ │
|
|
│ │ SearXNGService │ │ │ │ FactSvc │ │ │ │ Gemini Provider │ │
|
|
│ └─────────────────┘ │ │ │ OpinionSvc│ │ │ └─────────────────────┘ │
|
|
└───────────────────────┘ │ │ StyleSvc │ │ └─────────────────────────────┘
|
|
│ │ ProactSvc │ │
|
|
│ │ AssocSvc │ │
|
|
│ │ SelfAware │ │
|
|
│ └───────────┘ │
|
|
└─────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ PostgreSQL Database │
|
|
│ ┌──────────────────────────────┐ ┌──────────────────────────────────────┐ │
|
|
│ │ Core Tables │ │ Living AI Tables │ │
|
|
│ │ users, guilds, conversations │ │ bot_states, user_relationships, │ │
|
|
│ │ messages, user_facts │ │ mood_history, scheduled_events │ │
|
|
│ └──────────────────────────────┘ └──────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
loyal-companion/
|
|
├── src/loyal_companion/
|
|
│ ├── __main__.py # Entry point
|
|
│ ├── bot.py # Discord bot setup & cog loading
|
|
│ ├── config.py # Pydantic settings configuration
|
|
│ │
|
|
│ ├── cogs/ # Discord command handlers
|
|
│ │ ├── ai_chat.py # @mention response handler
|
|
│ │ ├── memory.py # Memory management commands
|
|
│ │ └── status.py # Bot status & health commands
|
|
│ │
|
|
│ ├── models/ # SQLAlchemy ORM models
|
|
│ │ ├── base.py # Base model with PortableJSON
|
|
│ │ ├── user.py # User, UserFact, UserPreference
|
|
│ │ ├── conversation.py # Conversation, Message
|
|
│ │ ├── guild.py # Guild, GuildMember
|
|
│ │ └── living_ai.py # All Living AI models
|
|
│ │
|
|
│ └── services/ # Business logic layer
|
|
│ ├── ai_service.py # AI provider factory
|
|
│ ├── database.py # Database connection management
|
|
│ ├── user_service.py # User CRUD operations
|
|
│ ├── conversation.py # In-memory conversation manager
|
|
│ ├── persistent_conversation.py # DB-backed conversations
|
|
│ ├── searxng.py # Web search integration
|
|
│ ├── monitoring.py # Health & metrics tracking
|
|
│ │
|
|
│ ├── providers/ # AI provider implementations
|
|
│ │ ├── base.py # Abstract AIProvider
|
|
│ │ ├── openai.py
|
|
│ │ ├── openrouter.py
|
|
│ │ ├── anthropic.py
|
|
│ │ └── gemini.py
|
|
│ │
|
|
│ └── [Living AI Services]
|
|
│ ├── mood_service.py
|
|
│ ├── relationship_service.py
|
|
│ ├── fact_extraction_service.py
|
|
│ ├── opinion_service.py
|
|
│ ├── communication_style_service.py
|
|
│ ├── proactive_service.py
|
|
│ ├── association_service.py
|
|
│ └── self_awareness_service.py
|
|
│
|
|
├── alembic/ # Database migrations
|
|
├── tests/ # Test suite
|
|
├── schema.sql # Database schema definition
|
|
├── docker-compose.yml # Docker deployment
|
|
└── requirements.txt # Python dependencies
|
|
```
|
|
|
|
---
|
|
|
|
## Core Components
|
|
|
|
### 1. Discord Bot Layer
|
|
|
|
The bot uses [discord.py](https://discordpy.readthedocs.io/) with a cog-based architecture.
|
|
|
|
**Entry Point (`__main__.py`)**
|
|
- Creates the bot instance
|
|
- Initializes database connection
|
|
- Starts the Discord event loop
|
|
|
|
**Bot Setup (`bot.py`)**
|
|
- Configures Discord intents
|
|
- Auto-loads all cogs from `cogs/` directory
|
|
- Handles bot lifecycle events
|
|
|
|
**Cogs**
|
|
|
|
| Cog | Purpose | Trigger |
|
|
|-----|---------|---------|
|
|
| `AIChatCog` | Main conversation handler | `@mention` |
|
|
| `MemoryCog` | User memory management | `!setname`, `!remember`, etc. |
|
|
| `StatusCog` | Health & metrics | `!status` (admin) |
|
|
|
|
### 2. Service Layer
|
|
|
|
Services contain all business logic and are designed to be:
|
|
- **Stateless**: No instance state, all state in database
|
|
- **Async-first**: All I/O operations are async
|
|
- **Testable**: Accept database sessions as parameters
|
|
|
|
**Core Services**
|
|
|
|
| Service | Responsibility |
|
|
|---------|----------------|
|
|
| `AIService` | Factory for AI providers, prompt enhancement |
|
|
| `DatabaseService` | Connection pool, session management |
|
|
| `UserService` | User CRUD, facts, preferences |
|
|
| `ConversationManager` | In-memory conversation history |
|
|
| `PersistentConversationManager` | Database-backed conversations |
|
|
| `SearXNGService` | Web search for current information |
|
|
| `MonitoringService` | Health checks, metrics, error tracking |
|
|
|
|
**Living AI Services** (see [Living AI documentation](living-ai/README.md))
|
|
|
|
| Service | Responsibility |
|
|
|---------|----------------|
|
|
| `MoodService` | Emotional state management |
|
|
| `RelationshipService` | User relationship tracking |
|
|
| `FactExtractionService` | Autonomous fact learning |
|
|
| `OpinionService` | Topic sentiment tracking |
|
|
| `CommunicationStyleService` | User style preferences |
|
|
| `ProactiveService` | Scheduled events & follow-ups |
|
|
| `AssociationService` | Cross-user memory linking |
|
|
| `SelfAwarenessService` | Bot statistics & reflection |
|
|
|
|
### 3. AI Provider Layer
|
|
|
|
Uses the **Provider Pattern** to support multiple AI backends.
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────┐
|
|
│ AIService │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ get_provider() → returns appropriate AIProvider │ │
|
|
│ │ chat() → builds context and calls provider.generate │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
└────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────────────┼─────────────────┐
|
|
▼ ▼ ▼
|
|
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
│ OpenAI Provider │ │ Anthropic Provider│ │ Gemini Provider │
|
|
│ (+ OpenRouter) │ │ │ │ │
|
|
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
|
```
|
|
|
|
### 4. Data Layer
|
|
|
|
**Models** (`models/`)
|
|
- SQLAlchemy ORM with async support
|
|
- `PortableJSON` type for PostgreSQL/SQLite compatibility
|
|
- Timezone-aware UTC timestamps
|
|
|
|
**Database** (`services/database.py`)
|
|
- Async PostgreSQL via `asyncpg`
|
|
- Connection pooling
|
|
- Graceful fallback to in-memory when no database
|
|
|
|
---
|
|
|
|
## Data Flow
|
|
|
|
### Message Processing Flow
|
|
|
|
```
|
|
1. User sends @mention in Discord
|
|
│
|
|
▼
|
|
2. AIChatCog.on_message() receives event
|
|
│
|
|
▼
|
|
3. Get or create User via UserService
|
|
│
|
|
▼
|
|
4. Get or create Conversation
|
|
(PersistentConversationManager or ConversationManager)
|
|
│
|
|
▼
|
|
5. Build conversation history from messages
|
|
│
|
|
▼
|
|
6. [Optional] Check if web search needed
|
|
└─► SearXNGService.search() → add results to context
|
|
│
|
|
▼
|
|
7. Apply Living AI enhancements:
|
|
├─► MoodService.get_current_mood()
|
|
├─► RelationshipService.get_relationship()
|
|
├─► CommunicationStyleService.get_style()
|
|
└─► OpinionService.get_relevant_opinions()
|
|
│
|
|
▼
|
|
8. Build enhanced system prompt with all context
|
|
│
|
|
▼
|
|
9. AIService.chat() → Provider.generate()
|
|
│
|
|
▼
|
|
10. Post-response processing:
|
|
├─► MoodService.update_mood()
|
|
├─► RelationshipService.record_interaction()
|
|
├─► CommunicationStyleService.record_message()
|
|
├─► FactExtractionService.maybe_extract_facts()
|
|
└─► ProactiveService.check_for_events()
|
|
│
|
|
▼
|
|
11. Split response if > 2000 chars
|
|
│
|
|
▼
|
|
12. Send response(s) to Discord
|
|
```
|
|
|
|
### Database Fallback Flow
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Application Start │
|
|
└─────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────┐
|
|
│ DATABASE_URL set? │
|
|
└────────────────────────┘
|
|
│ │
|
|
Yes No
|
|
│ │
|
|
▼ ▼
|
|
┌───────────────────┐ ┌───────────────────────┐
|
|
│ PostgreSQL Mode │ │ In-Memory Mode │
|
|
│ - Full persistence│ │ - ConversationManager │
|
|
│ - Living AI state │ │ - No persistence │
|
|
│ - User facts │ │ - Basic functionality │
|
|
└───────────────────┘ └───────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Design Patterns
|
|
|
|
### 1. Provider Pattern (AI Services)
|
|
|
|
Abstract base class with multiple implementations.
|
|
|
|
```python
|
|
# base.py
|
|
class AIProvider(ABC):
|
|
@abstractmethod
|
|
async def generate(
|
|
self,
|
|
messages: list[Message],
|
|
system_prompt: str | None = None,
|
|
max_tokens: int = 1024,
|
|
temperature: float = 0.7,
|
|
) -> AIResponse:
|
|
pass
|
|
|
|
# openai.py
|
|
class OpenAIProvider(AIProvider):
|
|
async def generate(...) -> AIResponse:
|
|
# OpenAI-specific implementation
|
|
```
|
|
|
|
### 2. Factory Pattern (AIService)
|
|
|
|
Creates the appropriate provider based on configuration.
|
|
|
|
```python
|
|
class AIService:
|
|
def __init__(self):
|
|
self.provider = self._create_provider()
|
|
|
|
def _create_provider(self) -> AIProvider:
|
|
match settings.ai_provider:
|
|
case "openai": return OpenAIProvider()
|
|
case "anthropic": return AnthropicProvider()
|
|
# ...
|
|
```
|
|
|
|
### 3. Repository Pattern (Services)
|
|
|
|
Services encapsulate data access logic.
|
|
|
|
```python
|
|
class UserService:
|
|
@staticmethod
|
|
async def get_user(session: AsyncSession, discord_id: int) -> User | None:
|
|
result = await session.execute(
|
|
select(User).where(User.discord_id == discord_id)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
```
|
|
|
|
### 4. Cog Pattern (Discord.py)
|
|
|
|
Modular command grouping.
|
|
|
|
```python
|
|
class MemoryCog(commands.Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
@commands.command(name="setname")
|
|
async def set_name(self, ctx, *, name: str):
|
|
# Command implementation
|
|
```
|
|
|
|
### 5. Graceful Degradation
|
|
|
|
System works with reduced functionality when components unavailable.
|
|
|
|
```python
|
|
# Database fallback
|
|
if settings.database_url:
|
|
conversation_manager = PersistentConversationManager()
|
|
else:
|
|
conversation_manager = ConversationManager() # In-memory
|
|
|
|
# Feature toggles
|
|
if settings.living_ai_enabled and settings.mood_enabled:
|
|
mood = await MoodService.get_current_mood(session, guild_id)
|
|
else:
|
|
mood = None
|
|
```
|
|
|
|
---
|
|
|
|
## Component Interaction Diagram
|
|
|
|
```
|
|
┌──────────────────────────────────────────┐
|
|
│ User Message │
|
|
└──────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
│ AIChatCog │
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
│ │ UserService │ │ Conversation│ │ SearXNG │ │ AIService │ │
|
|
│ │ │ │ Manager │ │ Service │ │ │ │
|
|
│ │ get_user() │ │ get_history │ │ search() │ │ chat() ──────────┐ │ │
|
|
│ │ get_context │ │ add_message │ │ │ │ │ │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ └──────────────────│──┘ │
|
|
│ │ │
|
|
│ ┌────────────────────────────────────────────────────────────────────┼────┐ │
|
|
│ │ Living AI Services │ │ │
|
|
│ │ │ │ │
|
|
│ │ ┌───────────┐ ┌──────────────┐ ┌───────────────┐ ┌────────────┐ │ │ │
|
|
│ │ │ MoodSvc │ │ Relationship │ │ CommStyleSvc │ │ OpinionSvc │ │ │ │
|
|
│ │ │ │ │ Svc │ │ │ │ │ │ │ │
|
|
│ │ │get_mood() │ │get_relation()│ │ get_style() │ │get_opinions│ │ │ │
|
|
│ │ │update() │ │record() │ │ record() │ │ update() │ │ │ │
|
|
│ │ └───────────┘ └──────────────┘ └───────────────┘ └────────────┘ │ │ │
|
|
│ │ │ │ │
|
|
│ │ ┌───────────────┐ ┌────────────────┐ ┌───────────────────────┐ │ │ │
|
|
│ │ │ FactExtract │ │ ProactiveSvc │ │ SelfAwarenessSvc │ │ │ │
|
|
│ │ │ │ │ │ │ │ │ │ │
|
|
│ │ │ extract() │ │ detect_events()│ │ get_stats() │ │ │ │
|
|
│ │ └───────────────┘ └────────────────┘ └───────────────────────┘ │ │ │
|
|
│ └────────────────────────────────────────────────────────────────────│────┘ │
|
|
│ │ │
|
|
└────────────────────────────────────────────────────────────────────────│──────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────┐
|
|
│ AI Provider │
|
|
│ (OpenAI / Anthropic / Gemini / OpenRouter) │
|
|
└──────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────────┐
|
|
│ Response │
|
|
└──────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
- [Living AI System](living-ai/README.md) - Deep dive into the personality system
|
|
- [Services Reference](services/README.md) - Detailed API documentation
|
|
- [Database Schema](database.md) - Complete schema documentation
|
|
- [Configuration Reference](configuration.md) - All configuration options
|
|
- [Developer Guides](guides/README.md) - How to extend the system
|