Files
discord-fishbowl/src/main.py
root 5480219901 Fix comprehensive system issues and implement proper vector database backend selection
- Fix remaining datetime timezone errors across all database operations
- Implement dynamic vector database backend (Qdrant/ChromaDB) based on install.py configuration
- Add LLM timeout handling with immediate fallback responses for slow self-hosted models
- Use proper install.py configuration (2000 max tokens, 5min timeout, correct LLM endpoint)
- Fix PostgreSQL schema to use timezone-aware columns throughout
- Implement async LLM request handling with background processing
- Add configurable prompt limits and conversation history controls
- Start missing database services (PostgreSQL, Redis) automatically
- Fix environment variable mapping between install.py and application code
- Resolve all timezone-naive vs timezone-aware datetime conflicts

System now properly uses Qdrant vector database as specified in install.py instead of hardcoded ChromaDB.
Characters respond immediately with fallback messages during long LLM processing times.
All database timezone errors resolved with proper timestamptz columns.
2025-07-05 21:31:52 -07:00

306 lines
11 KiB
Python

#!/usr/bin/env python3
"""
Discord Fishbowl - Autonomous AI Character Chat System
Main entry point for the application
"""
import asyncio
import signal
import sys
import os
from pathlib import Path
# Add src to Python path
sys.path.insert(0, str(Path(__file__).parent))
from utils.config import get_settings, validate_environment, setup_logging
from utils.logging import setup_logging_interceptor
from database.connection import init_database, create_tables, close_database
from bot.discord_client import FishbowlBot
from bot.message_handler import MessageHandler, CommandHandler
from conversation.engine import ConversationEngine
from conversation.scheduler import ConversationScheduler
from llm.client import llm_client
from rag.vector_store import vector_store_manager
from rag.community_knowledge import initialize_community_knowledge_rag
from rag.memory_sharing import MemorySharingManager
from collaboration.creative_projects import CollaborativeCreativeManager
from mcp_servers.self_modification_server import mcp_server
from mcp_servers.file_system_server import filesystem_server
from mcp_servers.calendar_server import calendar_server
from mcp_servers.memory_sharing_server import MemorySharingMCPServer
from mcp_servers.creative_projects_server import initialize_creative_projects_mcp_server
import logging
# Setup logging first
logger = setup_logging()
setup_logging_interceptor()
class FishbowlApplication:
"""Main application class"""
def __init__(self):
self.settings = None
self.conversation_engine = None
self.scheduler = None
self.discord_bot = None
self.message_handler = None
self.command_handler = None
self.shutdown_event = asyncio.Event()
# RAG and MCP systems
self.vector_store = None
self.community_knowledge = None
self.memory_sharing_manager = None
self.creative_manager = None
self.mcp_servers = []
async def initialize(self):
"""Initialize all components"""
try:
logger.info("Starting Discord Fishbowl initialization...")
# Validate environment
validate_environment()
# Load settings
self.settings = get_settings()
logger.info("Configuration loaded successfully")
# Initialize database
await init_database()
await create_tables()
logger.info("Database initialized")
# Check LLM availability
is_available = await llm_client.check_model_availability()
if not is_available:
logger.error("LLM model not available. Please check your LLM service.")
raise RuntimeError("LLM service unavailable")
logger.info(f"LLM model '{llm_client.model}' is available")
# Initialize RAG systems
logger.info("Initializing RAG systems...")
# Initialize vector store
self.vector_store = vector_store_manager
character_names = ["Alex", "Sage", "Luna", "Echo"] # From config
await self.vector_store.initialize(character_names)
logger.info("Vector store initialized")
# Initialize community knowledge RAG
self.community_knowledge = initialize_community_knowledge_rag(self.vector_store)
await self.community_knowledge.initialize(character_names)
logger.info("Community knowledge RAG initialized")
# Initialize memory sharing manager
self.memory_sharing_manager = MemorySharingManager(self.vector_store)
await self.memory_sharing_manager.initialize(character_names)
logger.info("Memory sharing manager initialized")
# Initialize collaborative creative manager
self.creative_manager = CollaborativeCreativeManager(self.vector_store, self.memory_sharing_manager)
await self.creative_manager.initialize(character_names)
logger.info("Collaborative creative manager initialized")
# Initialize MCP servers
logger.info("Initializing MCP servers...")
# Initialize file system server
await filesystem_server.initialize(self.vector_store, character_names)
self.mcp_servers.append(filesystem_server)
logger.info("File system MCP server initialized")
# Initialize calendar/time awareness server
await calendar_server.initialize(character_names)
self.mcp_servers.append(calendar_server)
logger.info("Calendar/time awareness MCP server initialized")
# Initialize memory sharing MCP server
memory_sharing_mcp = MemorySharingMCPServer(self.memory_sharing_manager)
self.mcp_servers.append(memory_sharing_mcp)
logger.info("Memory sharing MCP server initialized")
# Initialize creative projects MCP server
creative_projects_mcp = initialize_creative_projects_mcp_server(self.creative_manager)
self.mcp_servers.append(creative_projects_mcp)
logger.info("Creative projects MCP server initialized")
# Initialize conversation engine with RAG and MCP systems
self.conversation_engine = ConversationEngine(
vector_store=self.vector_store,
memory_sharing_manager=self.memory_sharing_manager,
creative_manager=self.creative_manager,
mcp_servers=self.mcp_servers
)
logger.info("Conversation engine created with enhanced capabilities")
# Initialize scheduler
self.scheduler = ConversationScheduler(self.conversation_engine)
logger.info("Conversation scheduler created")
# Initialize Discord bot
self.discord_bot = FishbowlBot(self.conversation_engine)
# Initialize message and command handlers
self.message_handler = MessageHandler(self.discord_bot, self.conversation_engine)
self.command_handler = CommandHandler(self.discord_bot, self.conversation_engine)
logger.info("Discord bot and handlers initialized")
logger.info("✅ All components initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize application: {e}")
raise
async def start(self):
"""Start the application"""
try:
logger.info("🚀 Starting Discord Fishbowl...")
# Start conversation engine
await self.conversation_engine.initialize(self.discord_bot)
logger.info("Conversation engine started")
# Start scheduler
await self.scheduler.start()
logger.info("Conversation scheduler started")
# Start LLM cleanup task
cleanup_task = asyncio.create_task(self._llm_cleanup_loop())
logger.info("LLM cleanup task started")
# Start Discord bot
bot_task = asyncio.create_task(
self.discord_bot.start(self.settings.discord.token)
)
# Setup signal handlers
self._setup_signal_handlers()
logger.info("🎉 Discord Fishbowl is now running!")
logger.info("Characters will start chatting autonomously...")
# Wait for shutdown signal or bot completion
done, pending = await asyncio.wait(
[bot_task, cleanup_task, asyncio.create_task(self.shutdown_event.wait())],
return_when=asyncio.FIRST_COMPLETED
)
# Cancel pending tasks
for task in pending:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
except Exception as e:
logger.error(f"Error during application startup: {e}")
raise
async def shutdown(self):
"""Graceful shutdown"""
try:
logger.info("🛑 Shutting down Discord Fishbowl...")
# Stop scheduler
if self.scheduler:
await self.scheduler.stop()
logger.info("Conversation scheduler stopped")
# Stop conversation engine
if self.conversation_engine:
await self.conversation_engine.stop()
logger.info("Conversation engine stopped")
# Close Discord bot
if self.discord_bot:
await self.discord_bot.close()
logger.info("Discord bot disconnected")
# Close database connections
await close_database()
logger.info("Database connections closed")
logger.info("✅ Shutdown completed successfully")
except Exception as e:
logger.error(f"Error during shutdown: {e}")
def _setup_signal_handlers(self):
"""Setup signal handlers for graceful shutdown"""
def signal_handler(signum, frame):
logger.info(f"Received signal {signum}, initiating shutdown...")
self.shutdown_event.set()
# Handle common shutdown signals
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# On Windows, handle CTRL+C
if os.name == 'nt':
signal.signal(signal.SIGBREAK, signal_handler)
async def _llm_cleanup_loop(self):
"""Background task to clean up completed LLM requests"""
try:
while not self.shutdown_event.is_set():
await llm_client.cleanup_pending_requests()
pending_count = llm_client.get_pending_count()
if pending_count > 0:
logger.debug(f"LLM cleanup: {pending_count} pending background requests")
# Wait 30 seconds before next cleanup
await asyncio.sleep(30)
except asyncio.CancelledError:
logger.info("LLM cleanup task cancelled")
except Exception as e:
logger.error(f"Error in LLM cleanup loop: {e}")
async def main():
"""Main entry point"""
app = FishbowlApplication()
try:
# Initialize application
await app.initialize()
# Start application
await app.start()
except KeyboardInterrupt:
logger.info("Received keyboard interrupt")
except Exception as e:
logger.error(f"Application error: {e}")
return 1
finally:
# Ensure cleanup
await app.shutdown()
return 0
def cli_main():
"""CLI entry point"""
try:
# Check Python version
if sys.version_info < (3, 8):
print("Error: Python 3.8 or higher is required")
return 1
# Run the async main function
return asyncio.run(main())
except KeyboardInterrupt:
print("\nApplication interrupted by user")
return 1
except Exception as e:
print(f"Fatal error: {e}")
return 1
if __name__ == "__main__":
sys.exit(cli_main())