Files
loyal_companion/docs/architecture.md
latte dbd534d860 refactor: Transform daemon_boyfriend into Loyal Companion
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.
2026-01-14 18:08:35 +01:00

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