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.
This commit is contained in:
root
2025-07-05 21:31:52 -07:00
parent 4c474eeb23
commit 5480219901
38 changed files with 777 additions and 380 deletions

View File

@@ -1,7 +1,7 @@
import asyncio
import json
from typing import Dict, List, Any, Optional, Set
from datetime import datetime, timedelta, date
from datetime import datetime, timedelta, timezone, date
from dataclasses import dataclass, asdict
from pathlib import Path
import aiofiles
@@ -51,7 +51,7 @@ class ScheduledEvent:
def __post_init__(self):
if self.created_at is None:
self.created_at = datetime.utcnow()
self.created_at = datetime.now(timezone.utc)
def to_dict(self) -> Dict[str, Any]:
return {
@@ -224,7 +224,7 @@ class CalendarTimeAwarenessMCP:
# Create event
event = ScheduledEvent(
id=f"event_{character_name}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}",
id=f"event_{character_name}_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}",
character_name=character_name,
event_type=event_type_enum,
title=title,
@@ -275,7 +275,7 @@ class CalendarTimeAwarenessMCP:
) -> List[TextContent]:
"""Get character's upcoming events"""
try:
now = datetime.utcnow()
now = datetime.now(timezone.utc)
end_time = now + timedelta(days=days_ahead)
upcoming_events = []
@@ -340,7 +340,7 @@ class CalendarTimeAwarenessMCP:
event = self.scheduled_events[character_name][event_id]
event.completed = True
event.metadata["completion_time"] = datetime.utcnow().isoformat()
event.metadata["completion_time"] = datetime.now(timezone.utc).isoformat()
event.metadata["completion_notes"] = notes
await self._save_character_calendar(character_name)
@@ -442,7 +442,7 @@ class CalendarTimeAwarenessMCP:
# Create milestone
milestone = Milestone(
id=f"milestone_{character_name}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}",
id=f"milestone_{character_name}_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}",
character_name=character_name,
milestone_type=milestone_type,
description=description,
@@ -499,7 +499,7 @@ class CalendarTimeAwarenessMCP:
) -> List[TextContent]:
"""Get upcoming anniversaries and milestones"""
try:
now = datetime.utcnow()
now = datetime.now(timezone.utc)
end_time = now + timedelta(days=days_ahead)
upcoming_anniversaries = []
@@ -580,7 +580,7 @@ class CalendarTimeAwarenessMCP:
if "celebrations" not in milestone.__dict__:
milestone.__dict__["celebrations"] = {}
milestone.__dict__["celebrations"][celebration_key] = {
"date": datetime.utcnow().isoformat(),
"date": datetime.now(timezone.utc).isoformat(),
"notes": celebration_notes
}
@@ -628,7 +628,7 @@ class CalendarTimeAwarenessMCP:
"""Get time elapsed since a specific type of event"""
try:
# Search through recent events
cutoff_date = datetime.utcnow() - timedelta(days=search_days_back)
cutoff_date = datetime.now(timezone.utc) - timedelta(days=search_days_back)
matching_events = []
for event in self.scheduled_events.get(character_name, {}).values():
@@ -665,7 +665,7 @@ class CalendarTimeAwarenessMCP:
most_recent_description = most_recent_interaction["description"]
# Calculate time difference
time_diff = datetime.utcnow() - most_recent_time
time_diff = datetime.now(timezone.utc) - most_recent_time
# Format time difference
if time_diff.days > 0:
@@ -709,7 +709,7 @@ class CalendarTimeAwarenessMCP:
) -> List[TextContent]:
"""Get summary of character's activities over a time period"""
try:
end_date = datetime.utcnow()
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(days=period_days)
# Get completed events in period
@@ -783,7 +783,7 @@ class CalendarTimeAwarenessMCP:
if character_name not in self.last_interactions:
self.last_interactions[character_name] = {}
self.last_interactions[character_name][other_character] = datetime.utcnow()
self.last_interactions[character_name][other_character] = datetime.now(timezone.utc)
# Save to file
await self._save_relationship_tracking(character_name)
@@ -834,7 +834,7 @@ class CalendarTimeAwarenessMCP:
text=f"No recorded interactions with {other_character}"
)]
time_since = datetime.utcnow() - last_interaction
time_since = datetime.now(timezone.utc) - last_interaction
days_since = time_since.days
# Determine maintenance status
@@ -859,7 +859,7 @@ class CalendarTimeAwarenessMCP:
# Get status for all relationships
relationships = []
for other_char, last_interaction in self.last_interactions.get(character_name, {}).items():
time_since = datetime.utcnow() - last_interaction
time_since = datetime.now(timezone.utc) - last_interaction
days_since = time_since.days
if days_since <= 1:
@@ -914,13 +914,13 @@ class CalendarTimeAwarenessMCP:
"""Schedule relationship maintenance activity"""
try:
# Create relationship maintenance event
scheduled_time = datetime.utcnow() + timedelta(days=days_from_now)
scheduled_time = datetime.now(timezone.utc) + timedelta(days=days_from_now)
template = self.event_templates[EventType.RELATIONSHIP_MAINTENANCE]
description = template["description_template"].format(target=other_character)
event = ScheduledEvent(
id=f"rel_maintenance_{character_name}_{other_character}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}",
id=f"rel_maintenance_{character_name}_{other_character}_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}",
character_name=character_name,
event_type=EventType.RELATIONSHIP_MAINTENANCE,
title=f"Connect with {other_character}",
@@ -1002,7 +1002,7 @@ class CalendarTimeAwarenessMCP:
events_data = {
"events": [event.to_dict() for event in self.scheduled_events.get(character_name, {}).values()],
"last_updated": datetime.utcnow().isoformat()
"last_updated": datetime.now(timezone.utc).isoformat()
}
async with aiofiles.open(calendar_file, 'w') as f:
@@ -1019,7 +1019,7 @@ class CalendarTimeAwarenessMCP:
milestones_data = {
"milestones": [milestone.to_dict() for milestone in self.milestones.get(character_name, {}).values()],
"last_updated": datetime.utcnow().isoformat()
"last_updated": datetime.now(timezone.utc).isoformat()
}
async with aiofiles.open(milestones_file, 'w') as f:
@@ -1039,7 +1039,7 @@ class CalendarTimeAwarenessMCP:
other_char: timestamp.isoformat()
for other_char, timestamp in self.last_interactions.get(character_name, {}).items()
},
"last_updated": datetime.utcnow().isoformat()
"last_updated": datetime.now(timezone.utc).isoformat()
}
async with aiofiles.open(tracking_file, 'w') as f:
@@ -1051,7 +1051,7 @@ class CalendarTimeAwarenessMCP:
async def _schedule_initial_events(self, character_name: str):
"""Schedule initial automatic events for character"""
try:
now = datetime.utcnow()
now = datetime.now(timezone.utc)
# Schedule first personal reflection in 6 hours
reflection_time = now + timedelta(hours=6)
@@ -1120,9 +1120,9 @@ class CalendarTimeAwarenessMCP:
next_time = completed_event.scheduled_time + timedelta(days=frequency_days)
# Only schedule if it's in the future
if next_time > datetime.utcnow():
if next_time > datetime.now(timezone.utc):
follow_up_event = ScheduledEvent(
id=f"followup_{completed_event.event_type.value}_{character_name}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}",
id=f"followup_{completed_event.event_type.value}_{character_name}_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}",
character_name=character_name,
event_type=completed_event.event_type,
title=completed_event.title,
@@ -1259,7 +1259,7 @@ class CalendarTimeAwarenessMCP:
if not last_interaction:
return
days_since = (datetime.utcnow() - last_interaction).days
days_since = (datetime.now(timezone.utc) - last_interaction).days
# Auto-schedule maintenance if overdue and not already scheduled
if days_since >= 7:

View File

@@ -7,7 +7,7 @@ import asyncio
import json
import logging
from typing import Dict, List, Any, Optional, Sequence
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from mcp.server import Server
from mcp.server.models import InitializationOptions
@@ -397,7 +397,7 @@ class CreativeProjectsMCPServer:
pending_invitations = []
for invitation in self.creative_manager.pending_invitations.values():
if invitation.invitee == self.current_character and invitation.status == "pending":
if datetime.utcnow() <= invitation.expires_at:
if datetime.now(timezone.utc) <= invitation.expires_at:
pending_invitations.append(invitation)
if not pending_invitations:

View File

@@ -1,7 +1,7 @@
import asyncio
import json
from typing import Dict, Any, List, Optional, Set
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
import aiofiles
import hashlib
@@ -340,7 +340,7 @@ class CharacterFileSystemMCP:
# Generate filename
safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).rstrip()
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
filename = f"{work_type}_{safe_title}_{timestamp}.md"
file_path = f"creative/{filename}"
@@ -348,7 +348,7 @@ class CharacterFileSystemMCP:
metadata = {
"title": title,
"type": work_type,
"created": datetime.utcnow().isoformat(),
"created": datetime.now(timezone.utc).isoformat(),
"author": character_name,
"tags": tags,
"word_count": len(content.split())
@@ -358,7 +358,7 @@ class CharacterFileSystemMCP:
formatted_content = f"""# {title}
**Type:** {work_type}
**Created:** {datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")}
**Created:** {datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")}
**Author:** {character_name}
**Tags:** {', '.join(tags)}
@@ -385,7 +385,7 @@ class CharacterFileSystemMCP:
content=f"Created {work_type} titled '{title}': {content}",
memory_type=MemoryType.CREATIVE,
character_name=character_name,
timestamp=datetime.utcnow(),
timestamp=datetime.now(timezone.utc),
importance=0.8,
metadata={
"work_type": work_type,
@@ -432,7 +432,7 @@ class CharacterFileSystemMCP:
tags = []
# Generate diary entry
timestamp = datetime.utcnow()
timestamp = datetime.now(timezone.utc)
entry = f"""
## {timestamp.strftime("%Y-%m-%d %H:%M:%S")}
@@ -519,7 +519,7 @@ class CharacterFileSystemMCP:
existing_content = await f.read()
# Format contribution
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
contribution_text = f"""
## Contribution by {character_name} ({timestamp})
@@ -544,7 +544,7 @@ class CharacterFileSystemMCP:
content=f"Contributed to {document_name}: {contribution}",
memory_type=MemoryType.COMMUNITY,
character_name=character_name,
timestamp=datetime.utcnow(),
timestamp=datetime.now(timezone.utc),
importance=0.7,
metadata={
"document": document_name,
@@ -601,7 +601,7 @@ class CharacterFileSystemMCP:
shared_name = f"{character_name}_{source_path.name}"
# Create shared file with metadata
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
shared_content = f"""# Shared by {character_name}
**Original file:** {source_file_path}
@@ -782,7 +782,7 @@ class CharacterFileSystemMCP:
character_name=character_name,
file_path=file_path,
access_type=access_type,
timestamp=datetime.utcnow(),
timestamp=datetime.now(timezone.utc),
success=success
)
self.access_log.append(access)
@@ -815,7 +815,7 @@ class CharacterFileSystemMCP:
content=f"File {file_path}: {content}",
memory_type=memory_type,
character_name=character_name,
timestamp=datetime.utcnow(),
timestamp=datetime.now(timezone.utc),
importance=0.7,
metadata={
"source": "file_system",
@@ -836,13 +836,13 @@ class CharacterFileSystemMCP:
"""Create initial files for a new character"""
try:
# Create initial diary entry
diary_file = char_dir / "diary" / f"{datetime.utcnow().strftime('%Y_%m')}_diary.md"
diary_file = char_dir / "diary" / f"{datetime.now(timezone.utc).strftime('%Y_%m')}_diary.md"
if not diary_file.exists():
initial_diary = f"""# {character_name}'s Digital Diary
Welcome to my personal digital space. This is where I record my thoughts, experiences, and reflections.
## {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}
## {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}
**Mood:** curious
**Tags:** beginning, digital_life

View File

@@ -6,7 +6,7 @@ Enables characters to autonomously share memories with trusted friends
import asyncio
import logging
from typing import Dict, List, Any, Optional, Sequence
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
import json
from mcp.server.models import InitializationOptions
@@ -414,7 +414,7 @@ class MemorySharingMCPServer:
response = f"📬 **{len(pending_requests)} Pending Memory Share Request(s)**\n\n"
for i, request in enumerate(pending_requests, 1):
expires_in = request.expires_at - datetime.utcnow()
expires_in = request.expires_at - datetime.now(timezone.utc)
expires_days = expires_in.days
response += f"**{i}. Request from {request.requesting_character}**\n"

View File

@@ -1,7 +1,7 @@
import asyncio
import json
from typing import Dict, Any, List, Optional, Union
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
import aiofiles
from dataclasses import dataclass, asdict
@@ -140,7 +140,7 @@ class SelfModificationMCPServer:
new_value=new_personality,
reason=reason,
confidence=confidence,
timestamp=datetime.utcnow()
timestamp=datetime.now(timezone.utc)
)
# Apply to database
@@ -211,7 +211,7 @@ class SelfModificationMCPServer:
goals_data = {
"goals": new_goals,
"previous_goals": current_goals,
"updated_at": datetime.utcnow().isoformat(),
"updated_at": datetime.now(timezone.utc).isoformat(),
"reason": reason,
"confidence": confidence
}
@@ -282,7 +282,7 @@ class SelfModificationMCPServer:
new_value=new_style,
reason=reason,
confidence=confidence,
timestamp=datetime.utcnow()
timestamp=datetime.now(timezone.utc)
)
# Apply to database
@@ -354,13 +354,13 @@ class SelfModificationMCPServer:
current_rules = json.loads(content)
# Add new rule
rule_id = f"{memory_type}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}"
rule_id = f"{memory_type}_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}"
current_rules[rule_id] = {
"memory_type": memory_type,
"importance_weight": importance_weight,
"retention_days": retention_days,
"description": rule_description,
"created_at": datetime.utcnow().isoformat(),
"created_at": datetime.now(timezone.utc).isoformat(),
"confidence": confidence,
"active": True
}
@@ -521,7 +521,7 @@ class SelfModificationMCPServer:
async def get_modification_limits(character_name: str) -> List[TextContent]:
"""Get current modification limits and usage"""
try:
today = datetime.utcnow().date().isoformat()
today = datetime.now(timezone.utc).date().isoformat()
usage = self.daily_modifications.get(character_name, {}).get(today, {})
@@ -571,7 +571,7 @@ class SelfModificationMCPServer:
}
# Check daily limits
today = datetime.utcnow().date().isoformat()
today = datetime.now(timezone.utc).date().isoformat()
if character_name not in self.daily_modifications:
self.daily_modifications[character_name] = {}
if today not in self.daily_modifications[character_name]:
@@ -605,7 +605,7 @@ class SelfModificationMCPServer:
async def _track_modification(self, character_name: str, modification_type: str):
"""Track modification usage for daily limits"""
today = datetime.utcnow().date().isoformat()
today = datetime.now(timezone.utc).date().isoformat()
if character_name not in self.daily_modifications:
self.daily_modifications[character_name] = {}