fix: 3 critical autonomous engine & mood system bugs

1. Momentum cliff at 10 messages (P0): The conversation momentum formula
   had a discontinuity where the 10th message caused momentum to DROP from
   0.9 to 0.5. Replaced with a smooth log1p curve that monotonically
   increases (0→0→0.20→0.32→...→0.70→0.89→1.0 at 30 msgs).

2. Neutral keywords overriding all moods (P0): detect_mood_shift() checked
   neutral early with generic keywords (okay, sure, hmm) that matched
   almost any response, constantly resetting mood to neutral. Now: all
   specific moods are scored by match count first (best-match wins),
   neutral is only checked as fallback and requires 2+ keyword matches.

3. Uncancellable delayed_wakeup tasks (P0): Fire-and-forget sleep tasks
   could stack and overwrite mood state after manual wake-up. Added a
   centralized wakeup task registry in ServerManager with automatic
   cancellation on manual wake or new sleep cycle.
This commit is contained in:
2026-02-20 15:37:57 +02:00
parent 2f0d430c35
commit 422366df4c
4 changed files with 97 additions and 46 deletions

View File

@@ -63,10 +63,6 @@ def detect_mood_shift(response_text, server_context=None):
"asleep": [
"good night", "goodnight", "sweet dreams", "going to bed", "I will go to bed", "zzz~", "sleep tight"
],
"neutral": [
"okay", "sure", "alright", "i see", "understood", "hmm",
"sounds good", "makes sense", "alrighty", "fine", "got it"
],
"bubbly": [
"so excited", "feeling bubbly", "super cheerful", "yay!", "", "nya~",
"kyaa~", "heehee", "bouncy", "so much fun", "i'm glowing!", "nee~", "teehee", "I'm so happy"
@@ -116,25 +112,41 @@ def detect_mood_shift(response_text, server_context=None):
]
}
# First pass: find ALL matching moods with their match counts (excluding neutral)
response_lower = response_text.lower()
mood_matches = {}
for mood, phrases in mood_keywords.items():
# Check against server mood if provided, otherwise skip
if mood == "asleep":
# asleep requires sleepy prerequisite
if server_context:
# For server context, check against server's current mood
current_mood = server_context.get('current_mood_name', 'neutral')
if current_mood != "sleepy":
logger.debug(f"Mood 'asleep' skipped - server mood isn't 'sleepy', it's '{current_mood}'")
continue
else:
# For DM context, check against DM mood
if globals.DM_MOOD != "sleepy":
logger.debug(f"Mood 'asleep' skipped - DM mood isn't 'sleepy', it's '{globals.DM_MOOD}'")
continue
for phrase in phrases:
if phrase.lower() in response_text.lower():
logger.info(f"Mood keyword triggered: {phrase}")
return mood
match_count = sum(1 for phrase in phrases if phrase.lower() in response_lower)
if match_count > 0:
mood_matches[mood] = match_count
if mood_matches:
# Return the mood with the most keyword matches (strongest signal)
best_mood = max(mood_matches, key=mood_matches.get)
logger.info(f"Mood shift detected: {best_mood} ({mood_matches[best_mood]} keyword matches, all matches: {mood_matches})")
return best_mood
# Neutral is checked separately and only triggers if NOTHING else matched
# Requires 2+ neutral keywords to avoid false positives from casual "okay" / "sure"
neutral_phrases = [
"okay", "sure", "alright", "i see", "understood", "hmm",
"sounds good", "makes sense", "alrighty", "fine", "got it"
]
neutral_count = sum(1 for phrase in neutral_phrases if phrase.lower() in response_lower)
if neutral_count >= 2:
logger.info(f"Mood shift detected: neutral ({neutral_count} neutral keywords)")
return "neutral"
return None
async def rotate_dm_mood():
@@ -287,27 +299,10 @@ async def rotate_server_mood(guild_id: int):
except Exception as mood_notify_error:
logger.error(f"Failed to notify autonomous engine of mood change: {mood_notify_error}")
# If transitioning to asleep, set up auto-wake
# If transitioning to asleep, set up auto-wake via centralized registry
if new_mood_name == "asleep":
server_manager.set_server_sleep_state(guild_id, True)
# Schedule wake-up after 1 hour
async def delayed_wakeup():
await asyncio.sleep(3600) # 1 hour
server_manager.set_server_sleep_state(guild_id, False)
server_manager.set_server_mood(guild_id, "neutral")
# V2: Notify autonomous engine of mood change
try:
from utils.autonomous import on_mood_change
on_mood_change(guild_id, "neutral")
except Exception as mood_notify_error:
logger.error(f"Failed to notify autonomous engine of wake-up mood change: {mood_notify_error}")
await update_server_nickname(guild_id)
logger.info(f"Server {guild_id} woke up from auto-sleep (mood rotation)")
globals.client.loop.create_task(delayed_wakeup())
logger.info(f"Scheduled auto-wake for server {guild_id} in 1 hour")
server_manager.schedule_wakeup_task(guild_id, delay_seconds=3600)
# Update nickname for this specific server
await update_server_nickname(guild_id)