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.
616 lines
15 KiB
Markdown
616 lines
15 KiB
Markdown
# Services Reference
|
|
|
|
This document provides detailed API documentation for all services in the Daemon Boyfriend bot.
|
|
|
|
## Table of Contents
|
|
|
|
- [Core Services](#core-services)
|
|
- [AIService](#aiservice)
|
|
- [DatabaseService](#databaseservice)
|
|
- [UserService](#userservice)
|
|
- [ConversationManager](#conversationmanager)
|
|
- [PersistentConversationManager](#persistentconversationmanager)
|
|
- [SearXNGService](#searxngservice)
|
|
- [MonitoringService](#monitoringservice)
|
|
- [AI Providers](#ai-providers)
|
|
- [Living AI Services](#living-ai-services)
|
|
|
|
---
|
|
|
|
## Core Services
|
|
|
|
### AIService
|
|
|
|
**File:** `services/ai_service.py`
|
|
|
|
Factory and facade for AI providers. Manages provider creation, switching, and provides a unified interface for generating responses.
|
|
|
|
#### Initialization
|
|
|
|
```python
|
|
from loyal_companion.services.ai_service import AIService
|
|
from loyal_companion.config import settings
|
|
|
|
# Use default settings
|
|
ai_service = AIService()
|
|
|
|
# Or with custom settings
|
|
ai_service = AIService(config=custom_settings)
|
|
```
|
|
|
|
#### Properties
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `provider` | `AIProvider` | Current AI provider instance |
|
|
| `provider_name` | `str` | Name of current provider |
|
|
| `model` | `str` | Current model name |
|
|
|
|
#### Methods
|
|
|
|
##### `chat(messages, system_prompt) -> AIResponse`
|
|
|
|
Generate a chat response.
|
|
|
|
```python
|
|
from loyal_companion.services.providers import Message
|
|
|
|
response = await ai_service.chat(
|
|
messages=[
|
|
Message(role="user", content="Hello!"),
|
|
],
|
|
system_prompt="You are a helpful assistant."
|
|
)
|
|
|
|
print(response.content) # AI's response
|
|
print(response.model) # Model used
|
|
print(response.usage) # Token usage
|
|
```
|
|
|
|
##### `get_system_prompt() -> str`
|
|
|
|
Get the base system prompt for the bot.
|
|
|
|
```python
|
|
prompt = ai_service.get_system_prompt()
|
|
# Returns: "You are Daemon, a friendly and helpful Discord bot..."
|
|
```
|
|
|
|
##### `get_enhanced_system_prompt(...) -> str`
|
|
|
|
Build system prompt with all personality modifiers.
|
|
|
|
```python
|
|
enhanced_prompt = ai_service.get_enhanced_system_prompt(
|
|
mood=mood_state,
|
|
relationship=(relationship_level, relationship_record),
|
|
communication_style=user_style,
|
|
bot_opinions=relevant_opinions
|
|
)
|
|
```
|
|
|
|
**Parameters:**
|
|
|
|
| Parameter | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `mood` | `MoodState \| None` | Current mood state |
|
|
| `relationship` | `tuple[RelationshipLevel, UserRelationship] \| None` | Relationship info |
|
|
| `communication_style` | `UserCommunicationStyle \| None` | User's preferences |
|
|
| `bot_opinions` | `list[BotOpinion] \| None` | Relevant opinions |
|
|
|
|
---
|
|
|
|
### DatabaseService
|
|
|
|
**File:** `services/database.py`
|
|
|
|
Manages database connections and sessions.
|
|
|
|
#### Global Instance
|
|
|
|
```python
|
|
from loyal_companion.services.database import db, get_db
|
|
|
|
# Get global instance
|
|
db_service = get_db()
|
|
```
|
|
|
|
#### Properties
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `is_configured` | `bool` | Whether DATABASE_URL is set |
|
|
| `is_initialized` | `bool` | Whether connection is initialized |
|
|
|
|
#### Methods
|
|
|
|
##### `init() -> None`
|
|
|
|
Initialize database connection.
|
|
|
|
```python
|
|
await db.init()
|
|
```
|
|
|
|
##### `close() -> None`
|
|
|
|
Close database connection.
|
|
|
|
```python
|
|
await db.close()
|
|
```
|
|
|
|
##### `create_tables() -> None`
|
|
|
|
Create all tables from `schema.sql`.
|
|
|
|
```python
|
|
await db.create_tables()
|
|
```
|
|
|
|
##### `session() -> AsyncGenerator[AsyncSession, None]`
|
|
|
|
Get a database session with automatic commit/rollback.
|
|
|
|
```python
|
|
async with db.session() as session:
|
|
# Use session for database operations
|
|
user = await session.execute(select(User).where(...))
|
|
# Auto-commits on exit, auto-rollbacks on exception
|
|
```
|
|
|
|
---
|
|
|
|
### UserService
|
|
|
|
**File:** `services/user_service.py`
|
|
|
|
Service for user-related operations.
|
|
|
|
#### Initialization
|
|
|
|
```python
|
|
from loyal_companion.services.user_service import UserService
|
|
|
|
async with db.session() as session:
|
|
user_service = UserService(session)
|
|
```
|
|
|
|
#### Methods
|
|
|
|
##### `get_or_create_user(discord_id, username, display_name) -> User`
|
|
|
|
Get existing user or create new one.
|
|
|
|
```python
|
|
user = await user_service.get_or_create_user(
|
|
discord_id=123456789,
|
|
username="john_doe",
|
|
display_name="John"
|
|
)
|
|
```
|
|
|
|
##### `get_user_by_discord_id(discord_id) -> User | None`
|
|
|
|
Get a user by their Discord ID.
|
|
|
|
```python
|
|
user = await user_service.get_user_by_discord_id(123456789)
|
|
```
|
|
|
|
##### `set_custom_name(discord_id, custom_name) -> User | None`
|
|
|
|
Set a custom name for a user.
|
|
|
|
```python
|
|
user = await user_service.set_custom_name(123456789, "Johnny")
|
|
# Or clear custom name
|
|
user = await user_service.set_custom_name(123456789, None)
|
|
```
|
|
|
|
##### `add_fact(user, fact_type, fact_content, source, confidence) -> UserFact`
|
|
|
|
Add a fact about a user.
|
|
|
|
```python
|
|
fact = await user_service.add_fact(
|
|
user=user,
|
|
fact_type="hobby",
|
|
fact_content="enjoys playing chess",
|
|
source="conversation",
|
|
confidence=0.9
|
|
)
|
|
```
|
|
|
|
##### `get_user_facts(user, fact_type, active_only) -> list[UserFact]`
|
|
|
|
Get facts about a user.
|
|
|
|
```python
|
|
# All active facts
|
|
facts = await user_service.get_user_facts(user)
|
|
|
|
# Only hobby facts
|
|
hobby_facts = await user_service.get_user_facts(user, fact_type="hobby")
|
|
|
|
# Including inactive facts
|
|
all_facts = await user_service.get_user_facts(user, active_only=False)
|
|
```
|
|
|
|
##### `delete_user_facts(user) -> int`
|
|
|
|
Soft-delete all facts for a user.
|
|
|
|
```python
|
|
count = await user_service.delete_user_facts(user)
|
|
print(f"Deactivated {count} facts")
|
|
```
|
|
|
|
##### `set_preference(user, key, value) -> UserPreference`
|
|
|
|
Set a user preference.
|
|
|
|
```python
|
|
await user_service.set_preference(user, "theme", "dark")
|
|
```
|
|
|
|
##### `get_preference(user, key) -> str | None`
|
|
|
|
Get a user preference value.
|
|
|
|
```python
|
|
theme = await user_service.get_preference(user, "theme")
|
|
```
|
|
|
|
##### `get_user_context(user) -> str`
|
|
|
|
Build a context string about a user for the AI.
|
|
|
|
```python
|
|
context = await user_service.get_user_context(user)
|
|
# Returns:
|
|
# User's preferred name: John
|
|
# (You should address them as: Johnny)
|
|
#
|
|
# Known facts about this user:
|
|
# - [hobby] enjoys playing chess
|
|
# - [work] software developer
|
|
```
|
|
|
|
##### `get_user_with_facts(discord_id) -> User | None`
|
|
|
|
Get a user with their facts eagerly loaded.
|
|
|
|
```python
|
|
user = await user_service.get_user_with_facts(123456789)
|
|
print(user.facts) # Facts already loaded
|
|
```
|
|
|
|
---
|
|
|
|
### ConversationManager
|
|
|
|
**File:** `services/conversation.py`
|
|
|
|
In-memory conversation history manager (used when no database).
|
|
|
|
#### Initialization
|
|
|
|
```python
|
|
from loyal_companion.services.conversation import ConversationManager
|
|
|
|
conversation_manager = ConversationManager(max_history=50)
|
|
```
|
|
|
|
#### Methods
|
|
|
|
##### `add_message(user_id, role, content) -> None`
|
|
|
|
Add a message to a user's conversation history.
|
|
|
|
```python
|
|
conversation_manager.add_message(
|
|
user_id=123456789,
|
|
role="user",
|
|
content="Hello!"
|
|
)
|
|
```
|
|
|
|
##### `get_history(user_id) -> list[dict]`
|
|
|
|
Get conversation history for a user.
|
|
|
|
```python
|
|
history = conversation_manager.get_history(123456789)
|
|
# Returns: [{"role": "user", "content": "Hello!"}, ...]
|
|
```
|
|
|
|
##### `clear_history(user_id) -> None`
|
|
|
|
Clear conversation history for a user.
|
|
|
|
```python
|
|
conversation_manager.clear_history(123456789)
|
|
```
|
|
|
|
---
|
|
|
|
### PersistentConversationManager
|
|
|
|
**File:** `services/persistent_conversation.py`
|
|
|
|
Database-backed conversation manager.
|
|
|
|
#### Key Features
|
|
|
|
- Conversations persist across restarts
|
|
- Timeout-based conversation separation (60 min default)
|
|
- Per-guild conversation tracking
|
|
|
|
#### Methods
|
|
|
|
##### `get_or_create_conversation(session, user, guild_id) -> Conversation`
|
|
|
|
Get active conversation or create new one.
|
|
|
|
```python
|
|
conversation = await pcm.get_or_create_conversation(
|
|
session=session,
|
|
user=user,
|
|
guild_id=123456789
|
|
)
|
|
```
|
|
|
|
##### `add_message(session, conversation, role, content) -> Message`
|
|
|
|
Add a message to a conversation.
|
|
|
|
```python
|
|
message = await pcm.add_message(
|
|
session=session,
|
|
conversation=conversation,
|
|
role="user",
|
|
content="Hello!"
|
|
)
|
|
```
|
|
|
|
##### `get_recent_messages(session, conversation, limit) -> list[Message]`
|
|
|
|
Get recent messages from a conversation.
|
|
|
|
```python
|
|
messages = await pcm.get_recent_messages(
|
|
session=session,
|
|
conversation=conversation,
|
|
limit=20
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
### SearXNGService
|
|
|
|
**File:** `services/searxng.py`
|
|
|
|
Web search integration using SearXNG.
|
|
|
|
#### Initialization
|
|
|
|
```python
|
|
from loyal_companion.services.searxng import SearXNGService
|
|
|
|
searxng = SearXNGService()
|
|
```
|
|
|
|
#### Properties
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `is_configured` | `bool` | Whether SEARXNG_URL is set |
|
|
|
|
#### Methods
|
|
|
|
##### `search(query, num_results) -> list[SearchResult]`
|
|
|
|
Search the web for a query.
|
|
|
|
```python
|
|
if searxng.is_configured:
|
|
results = await searxng.search(
|
|
query="Python 3.12 new features",
|
|
num_results=5
|
|
)
|
|
|
|
for result in results:
|
|
print(f"{result.title}: {result.url}")
|
|
print(f" {result.snippet}")
|
|
```
|
|
|
|
---
|
|
|
|
### MonitoringService
|
|
|
|
**File:** `services/monitoring.py`
|
|
|
|
Health checks and metrics tracking.
|
|
|
|
#### Features
|
|
|
|
- Request counting
|
|
- Response time tracking
|
|
- Error rate monitoring
|
|
- Health status checks
|
|
|
|
#### Usage
|
|
|
|
```python
|
|
from loyal_companion.services.monitoring import MonitoringService
|
|
|
|
monitoring = MonitoringService()
|
|
|
|
# Record a request
|
|
monitoring.record_request()
|
|
|
|
# Record response time
|
|
monitoring.record_response_time(0.5) # 500ms
|
|
|
|
# Record an error
|
|
monitoring.record_error("API timeout")
|
|
|
|
# Get health status
|
|
status = monitoring.get_health_status()
|
|
print(status)
|
|
# {
|
|
# "status": "healthy",
|
|
# "uptime": 3600,
|
|
# "total_requests": 100,
|
|
# "avg_response_time": 0.3,
|
|
# "error_rate": 0.02,
|
|
# "recent_errors": [...]
|
|
# }
|
|
```
|
|
|
|
---
|
|
|
|
## AI Providers
|
|
|
|
**Location:** `services/providers/`
|
|
|
|
### Base Classes
|
|
|
|
```python
|
|
from loyal_companion.services.providers import (
|
|
AIProvider, # Abstract base class
|
|
Message, # Chat message
|
|
AIResponse, # Response from provider
|
|
ImageAttachment, # Image attachment
|
|
)
|
|
```
|
|
|
|
### Message Class
|
|
|
|
```python
|
|
@dataclass
|
|
class Message:
|
|
role: str # "user", "assistant", "system"
|
|
content: str # Message content
|
|
images: list[ImageAttachment] = [] # Optional image attachments
|
|
```
|
|
|
|
### AIResponse Class
|
|
|
|
```python
|
|
@dataclass
|
|
class AIResponse:
|
|
content: str # Generated response
|
|
model: str # Model used
|
|
usage: dict[str, int] # Token usage info
|
|
```
|
|
|
|
### Available Providers
|
|
|
|
| Provider | Class | Description |
|
|
|----------|-------|-------------|
|
|
| OpenAI | `OpenAIProvider` | GPT models via OpenAI API |
|
|
| OpenRouter | `OpenRouterProvider` | Multiple models via OpenRouter |
|
|
| Anthropic | `AnthropicProvider` | Claude models via Anthropic API |
|
|
| Gemini | `GeminiProvider` | Gemini models via Google API |
|
|
|
|
### Provider Interface
|
|
|
|
All providers implement:
|
|
|
|
```python
|
|
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
|
|
|
|
@property
|
|
@abstractmethod
|
|
def provider_name(self) -> str:
|
|
pass
|
|
```
|
|
|
|
### Example: Direct Provider Usage
|
|
|
|
```python
|
|
from loyal_companion.services.providers import OpenAIProvider, Message
|
|
|
|
provider = OpenAIProvider(
|
|
api_key="sk-...",
|
|
model="gpt-4"
|
|
)
|
|
|
|
response = await provider.generate(
|
|
messages=[Message(role="user", content="Hello!")],
|
|
system_prompt="You are helpful.",
|
|
max_tokens=500,
|
|
temperature=0.7
|
|
)
|
|
|
|
print(response.content)
|
|
```
|
|
|
|
---
|
|
|
|
## Living AI Services
|
|
|
|
See [Living AI Documentation](../living-ai/README.md) for detailed coverage:
|
|
|
|
| Service | Documentation |
|
|
|---------|--------------|
|
|
| MoodService | [mood-system.md](../living-ai/mood-system.md) |
|
|
| RelationshipService | [relationship-system.md](../living-ai/relationship-system.md) |
|
|
| FactExtractionService | [fact-extraction.md](../living-ai/fact-extraction.md) |
|
|
| OpinionService | [opinion-system.md](../living-ai/opinion-system.md) |
|
|
| CommunicationStyleService | [Living AI README](../living-ai/README.md#5-communication-style-system) |
|
|
| ProactiveService | [Living AI README](../living-ai/README.md#6-proactive-events-system) |
|
|
| AssociationService | [Living AI README](../living-ai/README.md#7-association-system) |
|
|
| SelfAwarenessService | [Living AI README](../living-ai/README.md#8-self-awareness-system) |
|
|
|
|
---
|
|
|
|
## Service Dependency Graph
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────────────┐
|
|
│ AIChatCog │
|
|
└────────────────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌───────────────────────┼───────────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
|
│ UserService │ │ AIService │ │ Conversation │
|
|
│ │ │ │ │ Manager │
|
|
│ - get_user │ │ - chat │ │ │
|
|
│ - get_context │ │ - get_prompt │ │ - get_history │
|
|
└───────────────┘ └───────────────┘ └───────────────┘
|
|
│ │ │
|
|
│ ┌────────┴────────┐ │
|
|
│ │ │ │
|
|
│ ▼ ▼ │
|
|
│ ┌───────────────┐ ┌───────────────┐ │
|
|
│ │ Provider │ │ Living AI │ │
|
|
│ │ (OpenAI, │ │ Services │ │
|
|
│ │ Anthropic) │ │ │ │
|
|
│ └───────────────┘ └───────────────┘ │
|
|
│ │
|
|
└──────────────────┬───────────────────────────┘
|
|
│
|
|
▼
|
|
┌───────────────┐
|
|
│ Database │
|
|
│ Service │
|
|
│ │
|
|
│ - session() │
|
|
└───────────────┘
|
|
```
|