feat: Implement comprehensive non-hierarchical logging system

- Created new logging infrastructure with per-component filtering
- Added 6 log levels: DEBUG, INFO, API, WARNING, ERROR, CRITICAL
- Implemented non-hierarchical level control (any combination can be enabled)
- Migrated 917 print() statements across 31 files to structured logging
- Created web UI (system.html) for runtime configuration with dark theme
- Added global level controls to enable/disable levels across all components
- Added timestamp format control (off/time/date/datetime options)
- Implemented log rotation (10MB per file, 5 backups)
- Added API endpoints for dynamic log configuration
- Configured HTTP request logging with filtering via api.requests component
- Intercepted APScheduler logs with proper formatting
- Fixed persistence paths to use /app/memory for Docker volume compatibility
- Fixed checkbox display bug in web UI (enabled_levels now properly shown)
- Changed System Settings button to open in same tab instead of new window

Components: bot, api, api.requests, autonomous, persona, vision, llm,
conversation, mood, dm, scheduled, gpu, media, server, commands,
sentiment, core, apscheduler

All settings persist across container restarts via JSON config.
This commit is contained in:
2026-01-10 20:46:19 +02:00
parent ce00f9bd95
commit 32c2a7b930
34 changed files with 2766 additions and 936 deletions

View File

@@ -12,6 +12,9 @@ from typing import Dict, List, Optional
from collections import deque
import discord
from .autonomous_persistence import save_autonomous_context, load_autonomous_context, apply_context_to_signals
from utils.logger import get_logger
logger = get_logger('autonomous')
@dataclass
class ContextSignals:
@@ -238,13 +241,13 @@ class AutonomousEngine:
time_since_startup = time.time() - self.bot_startup_time
if time_since_startup < 120: # 2 minutes
if debug:
print(f"[V2 Debug] Startup cooldown active ({time_since_startup:.0f}s / 120s)")
logger.debug(f"[V2 Debug] Startup cooldown active ({time_since_startup:.0f}s / 120s)")
return None
# Never act when asleep
if ctx.current_mood == "asleep":
if debug:
print(f"💤 [V2 Debug] Mood is 'asleep' - no action taken")
logger.debug(f"[V2 Debug] Mood is 'asleep' - no action taken")
return None
# Get mood personality
@@ -254,14 +257,14 @@ class AutonomousEngine:
self._update_activity_metrics(guild_id)
if debug:
print(f"\n🔍 [V2 Debug] Decision Check for Guild {guild_id}")
print(f" Triggered by message: {triggered_by_message}")
print(f" Mood: {ctx.current_mood} (energy={profile['energy']:.2f}, sociability={profile['sociability']:.2f}, impulsiveness={profile['impulsiveness']:.2f})")
print(f" Momentum: {ctx.conversation_momentum:.2f}")
print(f" Messages (5min/1hr): {ctx.messages_last_5min}/{ctx.messages_last_hour}")
print(f" Messages since appearance: {ctx.messages_since_last_appearance}")
print(f" Time since last action: {ctx.time_since_last_action:.0f}s")
print(f" Active activities: {len(ctx.users_started_activity)}")
logger.debug(f"\n[V2 Debug] Decision Check for Guild {guild_id}")
logger.debug(f" Triggered by message: {triggered_by_message}")
logger.debug(f" Mood: {ctx.current_mood} (energy={profile['energy']:.2f}, sociability={profile['sociability']:.2f}, impulsiveness={profile['impulsiveness']:.2f})")
logger.debug(f" Momentum: {ctx.conversation_momentum:.2f}")
logger.debug(f" Messages (5min/1hr): {ctx.messages_last_5min}/{ctx.messages_last_hour}")
logger.debug(f" Messages since appearance: {ctx.messages_since_last_appearance}")
logger.debug(f" Time since last action: {ctx.time_since_last_action:.0f}s")
logger.debug(f" Active activities: {len(ctx.users_started_activity)}")
# --- Decision Logic ---
@@ -272,7 +275,7 @@ class AutonomousEngine:
# 1. CONVERSATION JOIN (high priority when momentum is high)
if self._should_join_conversation(ctx, profile, debug):
if debug:
print(f"[V2 Debug] DECISION: join_conversation")
logger.debug(f"[V2 Debug] DECISION: join_conversation")
return "join_conversation"
# 2. USER ENGAGEMENT (someone interesting appeared)
@@ -280,17 +283,17 @@ class AutonomousEngine:
if triggered_by_message:
# Convert to join_conversation when message-triggered
if debug:
print(f"[V2 Debug] DECISION: join_conversation (engage_user converted due to message trigger)")
logger.debug(f"[V2 Debug] DECISION: join_conversation (engage_user converted due to message trigger)")
return "join_conversation"
if debug:
print(f"[V2 Debug] DECISION: engage_user")
logger.debug(f"[V2 Debug] DECISION: engage_user")
return "engage_user"
# 3. FOMO RESPONSE (lots of activity without her)
# When FOMO triggers, join the conversation instead of saying something random
if self._should_respond_to_fomo(ctx, profile, debug):
if debug:
print(f"[V2 Debug] DECISION: join_conversation (FOMO)")
logger.debug(f"[V2 Debug] DECISION: join_conversation (FOMO)")
return "join_conversation" # Jump in and respond to what's being said
# 4. BORED/LONELY (quiet for too long, depending on mood)
@@ -299,29 +302,29 @@ class AutonomousEngine:
if self._should_break_silence(ctx, profile, debug):
if triggered_by_message:
if debug:
print(f"[V2 Debug] DECISION: join_conversation (break silence, but message just sent)")
logger.debug(f"[V2 Debug] DECISION: join_conversation (break silence, but message just sent)")
return "join_conversation" # Respond to the message instead of random general statement
else:
if debug:
print(f"[V2 Debug] DECISION: general (break silence)")
logger.debug(f"[V2 Debug] DECISION: general (break silence)")
return "general"
# 5. SHARE TWEET (low activity, wants to share something)
# Skip this entirely when triggered by message - would be inappropriate to ignore user's message
if not triggered_by_message and self._should_share_content(ctx, profile, debug):
if debug:
print(f"[V2 Debug] DECISION: share_tweet")
logger.debug(f"[V2 Debug] DECISION: share_tweet")
return "share_tweet"
# 6. CHANGE PROFILE PICTURE (very rare, once per day)
# Skip this entirely when triggered by message
if not triggered_by_message and self._should_change_profile_picture(ctx, profile, debug):
if debug:
print(f"[V2 Debug] DECISION: change_profile_picture")
logger.debug(f"[V2 Debug] DECISION: change_profile_picture")
return "change_profile_picture"
if debug:
print(f"[V2 Debug] DECISION: None (no conditions met)")
logger.debug(f"[V2 Debug] DECISION: None (no conditions met)")
return None
@@ -341,10 +344,10 @@ class AutonomousEngine:
result = all(conditions.values())
if debug:
print(f" [Join Conv] momentum={ctx.conversation_momentum:.2f} > {mood_adjusted:.2f}? {conditions['momentum_check']}")
print(f" [Join Conv] messages={ctx.messages_since_last_appearance} >= 5? {conditions['messages_check']}")
print(f" [Join Conv] cooldown={ctx.time_since_last_action:.0f}s > 300s? {conditions['cooldown_check']}")
print(f" [Join Conv] impulsive roll? {conditions['impulsiveness_roll']} | Result: {result}")
logger.debug(f" [Join Conv] momentum={ctx.conversation_momentum:.2f} > {mood_adjusted:.2f}? {conditions['momentum_check']}")
logger.debug(f" [Join Conv] messages={ctx.messages_since_last_appearance} >= 5? {conditions['messages_check']}")
logger.debug(f" [Join Conv] cooldown={ctx.time_since_last_action:.0f}s > 300s? {conditions['cooldown_check']}")
logger.debug(f" [Join Conv] impulsive roll? {conditions['impulsiveness_roll']} | Result: {result}")
return result
@@ -361,8 +364,8 @@ class AutonomousEngine:
if debug and has_activities:
activities = [name for name, ts in ctx.users_started_activity]
print(f" [Engage] activities={activities}, cooldown={ctx.time_since_last_action:.0f}s > 1800s? {cooldown_ok}")
print(f" [Engage] roll={roll:.2f} < {threshold:.2f}? {roll_ok} | Result: {result}")
logger.debug(f" [Engage] activities={activities}, cooldown={ctx.time_since_last_action:.0f}s > 1800s? {cooldown_ok}")
logger.debug(f" [Engage] roll={roll:.2f} < {threshold:.2f}? {roll_ok} | Result: {result}")
return result
@@ -378,9 +381,9 @@ class AutonomousEngine:
result = msgs_check and momentum_check and cooldown_check
if debug:
print(f" [FOMO] messages={ctx.messages_since_last_appearance} > {fomo_threshold:.0f}? {msgs_check}")
print(f" [FOMO] momentum={ctx.conversation_momentum:.2f} > 0.3? {momentum_check}")
print(f" [FOMO] cooldown={ctx.time_since_last_action:.0f}s > 900s? {cooldown_check} | Result: {result}")
logger.debug(f" [FOMO] messages={ctx.messages_since_last_appearance} > {fomo_threshold:.0f}? {msgs_check}")
logger.debug(f" [FOMO] momentum={ctx.conversation_momentum:.2f} > 0.3? {momentum_check}")
logger.debug(f" [FOMO] cooldown={ctx.time_since_last_action:.0f}s > 900s? {cooldown_check} | Result: {result}")
return result
@@ -397,9 +400,9 @@ class AutonomousEngine:
result = quiet_check and silence_check and energy_ok
if debug:
print(f" [Silence] msgs_last_hour={ctx.messages_last_hour} < 5? {quiet_check}")
print(f" [Silence] time={ctx.time_since_last_action:.0f}s > {min_silence:.0f}s? {silence_check}")
print(f" [Silence] energy roll={energy_roll:.2f} < {profile['energy']:.2f}? {energy_ok} | Result: {result}")
logger.debug(f" [Silence] msgs_last_hour={ctx.messages_last_hour} < 5? {quiet_check}")
logger.debug(f" [Silence] time={ctx.time_since_last_action:.0f}s > {min_silence:.0f}s? {silence_check}")
logger.debug(f" [Silence] energy roll={energy_roll:.2f} < {profile['energy']:.2f}? {energy_ok} | Result: {result}")
return result
@@ -416,10 +419,10 @@ class AutonomousEngine:
result = quiet_check and cooldown_check and energy_ok and mood_ok
if debug:
print(f" [Share] msgs_last_hour={ctx.messages_last_hour} < 10? {quiet_check}")
print(f" [Share] cooldown={ctx.time_since_last_action:.0f}s > 3600s? {cooldown_check}")
print(f" [Share] energy roll={energy_roll:.2f} < {energy_threshold:.2f}? {energy_ok}")
print(f" [Share] mood '{ctx.current_mood}' appropriate? {mood_ok} | Result: {result}")
logger.debug(f" [Share] msgs_last_hour={ctx.messages_last_hour} < 10? {quiet_check}")
logger.debug(f" [Share] cooldown={ctx.time_since_last_action:.0f}s > 3600s? {cooldown_check}")
logger.debug(f" [Share] energy roll={energy_roll:.2f} < {energy_threshold:.2f}? {energy_ok}")
logger.debug(f" [Share] mood '{ctx.current_mood}' appropriate? {mood_ok} | Result: {result}")
return result
@@ -447,11 +450,11 @@ class AutonomousEngine:
if hours_since_change < 20: # At least 20 hours between changes
if debug:
print(f" [PFP] Last change {hours_since_change:.1f}h ago, waiting...")
logger.debug(f" [PFP] Last change {hours_since_change:.1f}h ago, waiting...")
return False
except Exception as e:
if debug:
print(f" [PFP] Error checking last change: {e}")
logger.debug(f" [PFP] Error checking last change: {e}")
# Only consider changing during certain hours (10 AM - 10 PM)
hour = ctx.hour_of_day
@@ -472,11 +475,11 @@ class AutonomousEngine:
result = time_check and quiet_check and cooldown_check and roll_ok
if debug:
print(f" [PFP] hour={hour}, time_ok={time_check}")
print(f" [PFP] msgs_last_hour={ctx.messages_last_hour} < 5? {quiet_check}")
print(f" [PFP] cooldown={ctx.time_since_last_action:.0f}s > 5400s? {cooldown_check}")
print(f" [PFP] mood_boost={mood_boost}, roll={roll:.4f} < {base_chance:.4f}? {roll_ok}")
print(f" [PFP] Result: {result}")
logger.debug(f" [PFP] hour={hour}, time_ok={time_check}")
logger.debug(f" [PFP] msgs_last_hour={ctx.messages_last_hour} < 5? {quiet_check}")
logger.debug(f" [PFP] cooldown={ctx.time_since_last_action:.0f}s > 5400s? {cooldown_check}")
logger.debug(f" [PFP] mood_boost={mood_boost}, roll={roll:.4f} < {base_chance:.4f}? {roll_ok}")
logger.debug(f" [PFP] Result: {result}")
return result