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.
Services Reference
This document provides detailed API documentation for all services in the Daemon Boyfriend bot.
Table of Contents
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
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.
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.
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.
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
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.
await db.init()
close() -> None
Close database connection.
await db.close()
create_tables() -> None
Create all tables from schema.sql.
await db.create_tables()
session() -> AsyncGenerator[AsyncSession, None]
Get a database session with automatic commit/rollback.
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
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.
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.
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.
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.
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.
# 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.
count = await user_service.delete_user_facts(user)
print(f"Deactivated {count} facts")
set_preference(user, key, value) -> UserPreference
Set a user preference.
await user_service.set_preference(user, "theme", "dark")
get_preference(user, key) -> str | None
Get a user preference value.
theme = await user_service.get_preference(user, "theme")
get_user_context(user) -> str
Build a context string about a user for the AI.
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.
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
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.
conversation_manager.add_message(
user_id=123456789,
role="user",
content="Hello!"
)
get_history(user_id) -> list[dict]
Get conversation history for a user.
history = conversation_manager.get_history(123456789)
# Returns: [{"role": "user", "content": "Hello!"}, ...]
clear_history(user_id) -> None
Clear conversation history for a user.
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.
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.
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.
messages = await pcm.get_recent_messages(
session=session,
conversation=conversation,
limit=20
)
SearXNGService
File: services/searxng.py
Web search integration using SearXNG.
Initialization
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.
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
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
from loyal_companion.services.providers import (
AIProvider, # Abstract base class
Message, # Chat message
AIResponse, # Response from provider
ImageAttachment, # Image attachment
)
Message Class
@dataclass
class Message:
role: str # "user", "assistant", "system"
content: str # Message content
images: list[ImageAttachment] = [] # Optional image attachments
AIResponse Class
@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:
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
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 for detailed coverage:
| Service | Documentation |
|---|---|
| MoodService | mood-system.md |
| RelationshipService | relationship-system.md |
| FactExtractionService | fact-extraction.md |
| OpinionService | opinion-system.md |
| CommunicationStyleService | Living AI README |
| ProactiveService | Living AI README |
| AssociationService | Living AI README |
| SelfAwarenessService | Living AI README |
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() │
└───────────────┘