2026-01-09 00:03:59 +02:00
# utils/persona_dialogue.py
"""
Persona Dialogue System for Miku .
Enables natural back - and - forth conversations between Hatsune Miku and Evil Miku .
Unlike bipolar_mode . py ( which handles arguments ) , this module handles :
- Detecting when the opposite persona should interject
- Managing natural dialogue flow with self - signaling continuation
- Tracking tension that can escalate into arguments
- Seamless handoff to the argument system when tension is high
This system is designed to be lightweight on LLM calls :
- Initial trigger uses fast heuristics + sentiment analysis
- Each dialogue turn uses ONE LLM call that generates response AND decides continuation
- Only escalates to argument system when tension threshold is reached
"""
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.
2026-01-10 20:46:19 +02:00
import discord
import asyncio
import time
import globals
from utils . logger import get_logger
2026-02-18 12:01:08 +02:00
from utils . task_tracker import create_tracked_task
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.
2026-01-10 20:46:19 +02:00
logger = get_logger ( ' persona ' )
2026-01-09 00:03:59 +02:00
import os
import json
2026-04-30 15:07:55 +03:00
import re
2026-01-09 00:03:59 +02:00
# ============================================================================
# CONSTANTS
# ============================================================================
DIALOGUE_STATE_FILE = " memory/persona_dialogue_state.json "
# Dialogue settings
MAX_TURNS = 20 # Maximum turns before forced end
DIALOGUE_TIMEOUT = 900 # 15 minutes max dialogue duration
ARGUMENT_TENSION_THRESHOLD = 0.75 # Tension level that triggers argument escalation
# Initial trigger settings
2026-04-30 11:45:13 +03:00
INTERJECTION_COOLDOWN_HARD = 180 # 3 minutes hard block PER CHANNEL
INTERJECTION_COOLDOWN_SOFT = 900 # 15 minutes for full recovery PER CHANNEL
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.
2026-01-10 20:46:19 +02:00
INTERJECTION_THRESHOLD = 0.5 # Score needed to trigger interjection
2026-01-09 00:03:59 +02:00
2026-04-30 11:45:13 +03:00
# Conversation streak: if score is close but below threshold N times in a row,
# force a dialogue trigger (catches extended conversations building toward something)
STREAK_THRESHOLD = 3 # Number of near-miss messages before force trigger
STREAK_MIN_SCORE = 0.3 # Minimum score to count as a "near miss"
2026-01-09 00:03:59 +02:00
# ============================================================================
# INTERJECTION SCORER (Initial Trigger Decision)
# ============================================================================
class InterjectionScorer :
"""
Decides if the opposite persona should interject based on message content .
2026-04-30 15:07:55 +03:00
Uses fast heuristics — no LLM calls , no heavy ML dependencies .
2026-01-09 00:03:59 +02:00
"""
_instance = None
2026-04-30 15:07:55 +03:00
# Simple sentiment word lists (no PyTorch/transformers needed)
_POSITIVE_WORDS = { " happy " , " love " , " wonderful " , " amazing " , " great " , " beautiful " , " sweet " , " kind " , " hope " , " dream " , " excited " , " best " , " grateful " , " blessed " , " joy " , " perfect " , " adorable " , " precious " , " delightful " , " fantastic " }
_NEGATIVE_WORDS = { " hate " , " terrible " , " awful " , " horrible " , " disgusting " , " pathetic " , " worthless " , " stupid " , " idiot " , " sad " , " angry " , " upset " , " miserable " , " worst " , " ugly " , " boring " , " annoying " , " frustrated " , " cruel " , " mean " }
2026-01-09 00:03:59 +02:00
def __new__ ( cls ) :
if cls . _instance is None :
cls . _instance = super ( ) . __new__ ( cls )
2026-04-30 11:45:13 +03:00
cls . _instance . _cooldowns = { } # Per-channel cooldown timestamps
cls . _instance . _streaks = { } # Per-channel near-miss streaks
2026-01-09 00:03:59 +02:00
return cls . _instance
2026-04-30 15:07:55 +03:00
def _get_sentiment ( self , text : str ) - > tuple :
""" Lightweight heuristic sentiment analysis — returns (label, score).
No ML dependencies . Uses word counting + intensity markers .
Returns :
tuple : ( ' POSITIVE ' or ' NEGATIVE ' , confidence 0.0 - 1.0 )
"""
text_lower = text . lower ( )
words = set ( re . findall ( r ' \ b \ w+ \ b ' , text_lower ) )
pos_count = len ( words & self . _POSITIVE_WORDS )
neg_count = len ( words & self . _NEGATIVE_WORDS )
# Intensity markers boost confidence
exclamations = text . count ( ' ! ' )
caps_ratio = sum ( 1 for c in text if c . isupper ( ) ) / max ( len ( text ) , 1 )
intensity_boost = min ( ( exclamations * 0.1 ) + ( caps_ratio * 0.3 ) , 0.4 )
if neg_count > pos_count :
confidence = min ( 0.5 + ( neg_count * 0.15 ) + intensity_boost , 1.0 )
return ( ' NEGATIVE ' , confidence )
elif pos_count > neg_count :
confidence = min ( 0.5 + ( pos_count * 0.15 ) + intensity_boost , 1.0 )
return ( ' POSITIVE ' , confidence )
else :
# Neutral — slight lean based on intensity
return ( ' POSITIVE ' , 0.5 )
2026-01-09 00:03:59 +02:00
async def should_interject ( self , message : discord . Message , current_persona : str ) - > tuple :
"""
Determine if the opposite persona should interject .
Args :
message : The Discord message to analyze
current_persona : Who just spoke ( " miku " or " evil " )
Returns :
Tuple of ( should_interject : bool , reason : str , score : float )
"""
# Quick rejections
if not self . _passes_basic_filter ( message ) :
return False , " basic_filter_failed " , 0.0
2026-04-30 11:45:13 +03:00
# Check per-channel cooldown
channel_id = message . channel . id
cooldown_mult = self . _check_cooldown ( channel_id )
2026-01-09 00:03:59 +02:00
if cooldown_mult == 0.0 :
return False , " cooldown_active " , 0.0
opposite_persona = " evil " if current_persona == " miku " else " miku "
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Interjection] Analyzing content: ' { message . content [ : 100 ] } ... ' " )
logger . debug ( f " [Interjection] Current persona: { current_persona } , Opposite: { opposite_persona } " )
2026-01-09 00:03:59 +02:00
# Calculate score from various factors
score = 0.0
reasons = [ ]
# Factor 1: Direct addressing (automatic trigger)
if self . _mentions_opposite ( message . content , opposite_persona ) :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " [Interjection] Direct mention of { opposite_persona } detected! " )
2026-01-09 00:03:59 +02:00
return True , " directly_addressed " , 1.0
# Factor 2: Topic relevance
topic_score = self . _check_topic_relevance ( message . content , opposite_persona )
if topic_score > 0 :
score + = topic_score * 0.3
reasons . append ( f " topic: { topic_score : .2f } " )
# Factor 3: Emotional intensity
emotion_score = self . _check_emotional_intensity ( message . content )
if emotion_score > 0.6 :
score + = emotion_score * 0.25
reasons . append ( f " emotion: { emotion_score : .2f } " )
# Factor 4: Personality clash
clash_score = self . _detect_personality_clash ( message . content , opposite_persona )
if clash_score > 0 :
score + = clash_score * 0.25
reasons . append ( f " clash: { clash_score : .2f } " )
# Factor 5: Mood multiplier
mood_mult = self . _get_mood_multiplier ( opposite_persona )
score * = mood_mult
if mood_mult != 1.0 :
reasons . append ( f " mood_mult: { mood_mult : .2f } " )
# Factor 6: Context bonus
context_bonus = self . _check_conversation_context ( message )
score + = context_bonus * 0.2
if context_bonus > 0 :
reasons . append ( f " context: { context_bonus : .2f } " )
# Apply cooldown multiplier
score * = cooldown_mult
2026-04-30 11:45:13 +03:00
# Check conversation streak (near-misses that build toward a trigger)
streak_triggered = self . _check_streak ( channel_id , score )
2026-01-09 00:03:59 +02:00
# Decision
2026-04-30 11:45:13 +03:00
should_interject = score > = INTERJECTION_THRESHOLD or streak_triggered
2026-01-09 00:03:59 +02:00
reason_str = " | " . join ( reasons ) if reasons else " no_triggers "
2026-04-30 11:45:13 +03:00
if streak_triggered and not should_interject :
reason_str = " streak_force_trigger "
logger . info ( f " [Interjection] Streak force trigger in channel { channel_id } (score: { score : .2f } ) " )
2026-01-09 00:03:59 +02:00
if should_interject :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " { opposite_persona . upper ( ) } WILL INTERJECT (score: { score : .2f } ) " )
logger . info ( f " Reasons: { reason_str } " )
2026-01-09 00:03:59 +02:00
return should_interject , reason_str , score
def _passes_basic_filter ( self , message : discord . Message ) - > bool :
""" Fast rejection criteria """
# System messages
if message . type != discord . MessageType . default :
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Basic Filter] System message type: { message . type } " )
2026-01-09 00:03:59 +02:00
return False
# Bipolar mode must be enabled
if not globals . BIPOLAR_MODE :
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Basic Filter] Bipolar mode not enabled " )
2026-01-09 00:03:59 +02:00
return False
# Allow bot's own messages (we're checking them for interjections!)
# Also allow webhook messages (persona messages)
# Only reject OTHER bots' messages
if message . author . bot and not message . webhook_id :
# Check if it's our own bot
if message . author . id != globals . client . user . id :
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Basic Filter] Other bot message (not our bot) " )
2026-01-09 00:03:59 +02:00
return False
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Basic Filter] Passed (bot= { message . author . bot } , webhook= { message . webhook_id } , our_bot= { message . author . id == globals . client . user . id if message . author . bot else ' N/A ' } ) " )
2026-01-09 00:03:59 +02:00
return True
def _mentions_opposite ( self , content : str , opposite_persona : str ) - > bool :
""" Check if message directly addresses the opposite persona """
content_lower = content . lower ( )
if opposite_persona == " evil " :
patterns = [ " evil miku " , " dark miku " , " evil version " , " bad miku " , " evil you " ]
else :
patterns = [ " normal miku " , " regular miku " , " good miku " , " real miku " , " nice miku " , " other miku " , " original miku " ]
return any ( pattern in content_lower for pattern in patterns )
def _check_topic_relevance ( self , content : str , opposite_persona : str ) - > float :
""" Check if topics would interest the opposite persona """
content_lower = content . lower ( )
if opposite_persona == " evil " :
# Things Evil Miku can't resist commenting on
TRIGGER_TOPICS = {
2026-04-30 11:45:13 +03:00
" optimism " : [ " happiness " , " joy " , " love " , " kindness " , " hope " , " dreams " , " wonderful " , " amazing " , " blessed " , " grateful " ] ,
" morality " : [ " good " , " should " , " must " , " right thing " , " deserve " , " fair " , " justice " , " the right " , " better person " ] ,
" weakness " : [ " scared " , " nervous " , " worried " , " unsure " , " help me " , " don ' t know " , " confused " , " lost " , " lonely " , " alone " ] ,
" innocence " : [ " innocent " , " pure " , " sweet " , " cute " , " wholesome " , " precious " , " adorable " ] ,
" enthusiasm " : [ " best day " , " so excited " , " can ' t wait " , " so happy " , " i love this " , " this is great " ] ,
" vulnerability " : [ " i think " , " i feel " , " maybe " , " sometimes i wonder " , " i wish " , " i ' m trying " ] ,
2026-01-09 00:03:59 +02:00
}
else :
# Things Miku can't ignore
TRIGGER_TOPICS = {
2026-04-30 11:45:13 +03:00
" negativity " : [ " hate " , " terrible " , " awful " , " worst " , " horrible " , " disgusting " , " pathetic " , " ugly " , " boring " , " annoying " ] ,
" cruelty " : [ " deserve pain " , " suffer " , " worthless " , " stupid " , " idiot " , " fool " , " moron " , " loser " , " nobody " ] ,
" hopelessness " : [ " no point " , " meaningless " , " nobody cares " , " why bother " , " give up " , " what ' s the point " , " don ' t care " , " doesn ' t matter " , " who cares " ] ,
" evil_gloating " : [ " foolish " , " naive " , " weak " , " inferior " , " pathetic " , " beneath me " , " waste of space " ] ,
" provocation " : [ " fight me " , " prove it " , " make me " , " i dare you " , " try me " , " you can ' t " , " you won ' t " ] ,
" dismissal " : [ " whatever " , " shut up " , " go away " , " leave me alone " , " not worth " , " don ' t bother " ] ,
2026-01-09 00:03:59 +02:00
}
total_matches = 0
for category , keywords in TRIGGER_TOPICS . items ( ) :
matches = sum ( 1 for keyword in keywords if keyword in content_lower )
total_matches + = matches
2026-04-30 11:45:13 +03:00
return min ( total_matches / 2.0 , 1.0 ) # Lower divisor = higher base scores
2026-01-09 00:03:59 +02:00
def _check_emotional_intensity ( self , content : str ) - > float :
2026-04-30 15:07:55 +03:00
""" Check emotional intensity using lightweight heuristic sentiment """
label , confidence = self . _get_sentiment ( content )
2026-01-09 00:03:59 +02:00
2026-04-30 15:07:55 +03:00
# Punctuation intensity
exclamations = content . count ( ' ! ' )
questions = content . count ( ' ? ' )
caps_ratio = sum ( 1 for c in content if c . isupper ( ) ) / max ( len ( content ) , 1 )
intensity_markers = ( exclamations * 0.15 ) + ( questions * 0.1 ) + ( caps_ratio * 0.3 )
# Negative content = higher emotional intensity for triggering purposes
if label == ' NEGATIVE ' :
return min ( confidence * 0.7 + intensity_markers , 1.0 )
else :
return min ( confidence * 0.4 + intensity_markers , 1.0 )
2026-01-09 00:03:59 +02:00
def _detect_personality_clash ( self , content : str , opposite_persona : str ) - > float :
""" Detect statements that clash with the opposite persona ' s values """
content_lower = content . lower ( )
if opposite_persona == " evil " :
# User being too positive/naive = Evil Miku wants to "correct" them
positive_statements = [
" i believe in " , " i love " , " everything will be " , " so happy " ,
" the best " , " amazing " , " perfect " , " wonderful life " , " so grateful "
]
return 0.8 if any ( stmt in content_lower for stmt in positive_statements ) else 0.0
else :
# User being cruel/negative = Miku wants to help/defend
negative_statements = [
" i hate " , " everyone sucks " , " life is meaningless " , " don ' t care " ,
" deserve to suffer " , " nobody matters " , " worthless " , " all terrible "
]
return 0.8 if any ( stmt in content_lower for stmt in negative_statements ) else 0.0
def _get_mood_multiplier ( self , opposite_persona : str ) - > float :
""" Current mood affects likelihood of interjection """
if opposite_persona == " evil " :
MOOD_MULTIPLIERS = {
" aggressive " : 1.5 ,
2026-03-04 00:45:23 +02:00
" manic " : 1.4 ,
" jealous " : 1.3 ,
2026-01-09 00:03:59 +02:00
" cunning " : 1.0 ,
" sarcastic " : 1.1 ,
2026-03-04 00:45:23 +02:00
" playful_cruel " : 1.2 ,
" contemptuous " : 0.7 ,
2026-01-09 00:03:59 +02:00
" evil_neutral " : 0.8 ,
2026-03-04 00:45:23 +02:00
" bored " : 0.5 ,
" melancholic " : 0.6 ,
2026-01-09 00:03:59 +02:00
}
return MOOD_MULTIPLIERS . get ( globals . EVIL_DM_MOOD , 1.0 )
else :
MOOD_MULTIPLIERS = {
" bubbly " : 1.4 ,
" excited " : 1.3 ,
" curious " : 1.2 ,
" neutral " : 1.0 ,
" irritated " : 0.9 ,
" melancholy " : 0.7 ,
" asleep " : 0.1 ,
}
return MOOD_MULTIPLIERS . get ( globals . DM_MOOD , 1.0 )
def _check_conversation_context ( self , message : discord . Message ) - > float :
""" Check if this is part of an active conversation """
score = 0.0
# Part of a reply chain
if hasattr ( message , ' reference ' ) and message . reference :
score + = 0.5
# Could add more context checks here
score + = 0.2 # Base activity bonus
return min ( score , 1.0 )
2026-04-30 11:45:13 +03:00
def _check_cooldown ( self , channel_id : int ) - > float :
""" Check per-channel cooldown and return multiplier (0.0 = blocked, 1.0 = full) """
2026-01-09 00:03:59 +02:00
current_time = time . time ( )
2026-04-30 11:45:13 +03:00
last_time = self . _cooldowns . get ( channel_id , 0 )
time_since_last = current_time - last_time
2026-01-09 00:03:59 +02:00
if time_since_last < INTERJECTION_COOLDOWN_HARD :
return 0.0
elif time_since_last < INTERJECTION_COOLDOWN_SOFT :
return ( time_since_last - INTERJECTION_COOLDOWN_HARD ) / ( INTERJECTION_COOLDOWN_SOFT - INTERJECTION_COOLDOWN_HARD )
else :
return 1.0
2026-04-30 11:45:13 +03:00
def _update_cooldown ( self , channel_id : int ) :
""" Mark a dialogue as having started in this channel """
self . _cooldowns [ channel_id ] = time . time ( )
def _check_streak ( self , channel_id : int , score : float ) - > bool :
""" Track near-miss interjection scores. After STREAK_THRESHOLD consecutive
near - misses , force a trigger to catch extended conversations building tension . """
if score > = INTERJECTION_THRESHOLD :
# Above threshold — reset streak (actual trigger handles it)
self . _streaks [ channel_id ] = 0
return False
if score < STREAK_MIN_SCORE :
# Too low — reset streak
self . _streaks [ channel_id ] = 0
return False
# Near miss — increment streak
current = self . _streaks . get ( channel_id , 0 ) + 1
self . _streaks [ channel_id ] = current
logger . debug ( f " [Streak] Channel { channel_id } : { current } / { STREAK_THRESHOLD } near-misses (score: { score : .2f } ) " )
if current > = STREAK_THRESHOLD :
self . _streaks [ channel_id ] = 0 # Reset after force trigger
return True
return False
2026-01-09 00:03:59 +02:00
# ============================================================================
# PERSONA DIALOGUE MANAGER
# ============================================================================
class PersonaDialogue :
"""
Manages natural back - and - forth conversations between Miku and Evil Miku .
Each turn :
1. Generate response + continuation signal ( single LLM call )
2. Calculate tension delta from response
3. If tension > = threshold , escalate to argument
4. Otherwise , continue or end based on signal
"""
_instance = None
def __new__ ( cls ) :
if cls . _instance is None :
cls . _instance = super ( ) . __new__ ( cls )
cls . _instance . active_dialogues = { }
return cls . _instance
# ========================================================================
# DIALOGUE STATE MANAGEMENT
# ========================================================================
def is_dialogue_active ( self , channel_id : int ) - > bool :
""" Check if a dialogue is active in a channel """
return channel_id in self . active_dialogues
def get_dialogue_state ( self , channel_id : int ) - > dict :
""" Get dialogue state for a channel """
return self . active_dialogues . get ( channel_id , None )
def start_dialogue ( self , channel_id : int ) - > dict :
""" Start a new dialogue in a channel """
state = {
" turn_count " : 0 ,
" started_at " : time . time ( ) ,
" tension " : 0.0 ,
" tension_history " : [ ] ,
" last_speaker " : None ,
}
self . active_dialogues [ channel_id ] = state
2026-04-30 11:45:13 +03:00
# Update per-channel cooldown via the scorer
scorer = get_interjection_scorer ( )
scorer . _update_cooldown ( channel_id )
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Started persona dialogue in channel { channel_id } " )
2026-01-09 00:03:59 +02:00
return state
def end_dialogue ( self , channel_id : int ) :
""" End a dialogue in a channel """
if channel_id in self . active_dialogues :
state = self . active_dialogues [ channel_id ]
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Ended persona dialogue in channel { channel_id } " )
logger . info ( f " Turns: { state [ ' turn_count ' ] } , Final tension: { state [ ' tension ' ] : .2f } " )
2026-01-09 00:03:59 +02:00
del self . active_dialogues [ channel_id ]
# ========================================================================
# TENSION CALCULATION
# ========================================================================
def calculate_tension_delta ( self , response_text : str , current_tension : float ) - > float :
"""
Analyze a response and determine how much tension it adds / removes .
Returns delta to add to current tension score .
"""
2026-04-30 11:45:13 +03:00
# Natural tension decay — conversations cool off over time
base_delta = - 0.03
2026-01-09 00:03:59 +02:00
2026-04-30 15:07:55 +03:00
# Lightweight heuristic sentiment — no ML dependencies
try :
scorer = InterjectionScorer ( )
label , sentiment_score = scorer . _get_sentiment ( response_text )
is_negative = label == ' NEGATIVE '
if is_negative :
base_delta = sentiment_score * 0.15
else :
base_delta = - sentiment_score * 0.08 # Stronger cooling for positive
except Exception as e :
logger . error ( f " Sentiment analysis error in tension calc: { e } " )
2026-01-09 00:03:59 +02:00
text_lower = response_text . lower ( )
2026-04-30 11:45:13 +03:00
# Escalation patterns (reduced weight: 0.05 per match)
2026-01-09 00:03:59 +02:00
escalation_patterns = {
" insult " : [ " idiot " , " stupid " , " pathetic " , " fool " , " naive " , " worthless " , " disgusting " , " moron " ] ,
" dismissive " : [ " whatever " , " don ' t care " , " waste of time " , " not worth " , " beneath me " , " boring " ] ,
" confrontational " : [ " wrong " , " you always " , " you never " , " how dare " , " shut up " , " stop " ] ,
" mockery " : [ " oh please " , " how cute " , " adorable that you think " , " laughable " , " hilarious " ] ,
" challenge " : [ " prove it " , " fight me " , " make me " , " i dare you " , " try me " ] ,
}
2026-04-30 11:45:13 +03:00
# De-escalation patterns (increased weight: -0.08 per match)
2026-01-09 00:03:59 +02:00
deescalation_patterns = {
" concession " : [ " you ' re right " , " fair point " , " i suppose " , " maybe you have " , " good point " ] ,
2026-04-30 11:45:13 +03:00
" softening " : [ " i understand " , " let ' s calm " , " didn ' t mean " , " sorry " , " apologize " , " i hear you " ] ,
" deflection " : [ " anyway " , " moving on " , " whatever you say " , " agree to disagree " , " let ' s just " , " maybe we should " ] ,
2026-01-09 00:03:59 +02:00
}
# Check escalation
for category , patterns in escalation_patterns . items ( ) :
matches = sum ( 1 for p in patterns if p in text_lower )
if matches > 0 :
2026-04-30 11:45:13 +03:00
base_delta + = matches * 0.05 # Reduced from 0.08
2026-01-09 00:03:59 +02:00
# Check de-escalation
for category , patterns in deescalation_patterns . items ( ) :
matches = sum ( 1 for p in patterns if p in text_lower )
if matches > 0 :
2026-04-30 11:45:13 +03:00
base_delta - = matches * 0.08 # Increased from 0.06
2026-01-09 00:03:59 +02:00
2026-04-30 11:45:13 +03:00
# Intensity multipliers (reduced)
2026-01-09 00:03:59 +02:00
exclamation_count = response_text . count ( ' ! ' )
caps_ratio = sum ( 1 for c in response_text if c . isupper ( ) ) / max ( len ( response_text ) , 1 )
if exclamation_count > 2 or caps_ratio > 0.3 :
2026-04-30 11:45:13 +03:00
base_delta * = 1.2 # Reduced from 1.3
2026-01-09 00:03:59 +02:00
2026-04-30 11:45:13 +03:00
# Momentum factor (reduced)
2026-01-09 00:03:59 +02:00
if current_tension > 0.5 :
2026-04-30 11:45:13 +03:00
base_delta * = 1.1 # Reduced from 1.2
# Spike cooldown: if last turn had a big spike, halve this delta
# (prevents runaway tension spirals from a single heated exchange)
if hasattr ( self , ' _last_tension_delta ' ) and abs ( self . _last_tension_delta ) > 0.15 :
base_delta * = 0.5
logger . debug ( f " [Tension] Spike cooldown active — delta halved to { base_delta : +.3f } " )
self . _last_tension_delta = base_delta
2026-01-09 00:03:59 +02:00
return base_delta
# ========================================================================
# RESPONSE GENERATION
# ========================================================================
async def generate_response_with_continuation (
self ,
channel : discord . TextChannel ,
responding_persona : str ,
context : str ,
) - > tuple :
"""
Generate response AND continuation signal in a single LLM call .
Returns :
Tuple of ( response_text , should_continue , confidence )
"""
from utils . llm import query_llama
opposite = " Hatsune Miku " if responding_persona == " evil " else " Evil Miku "
# Get system prompt for persona
system_prompt = self . _get_persona_system_prompt ( responding_persona )
# Build the combined prompt
prompt = f """ { system_prompt }
Recent conversation :
{ context }
Respond naturally as yourself . Keep your response conversational and in - character .
- - -
2026-04-30 15:07:55 +03:00
After your response , evaluate whether { opposite } would want to keep talking .
2026-01-09 00:03:59 +02:00
The conversation should CONTINUE if ANY of these are true :
2026-04-30 15:07:55 +03:00
- You asked them a direct question ( almost always YES — they need to answer )
- You shared something they ' d naturally react to or build on
- The topic feels unfinished — there ' s more to explore
- You left an opening for them to share their perspective
2026-01-09 00:03:59 +02:00
The conversation might END if ALL of these are true :
- No questions were asked
2026-04-30 15:07:55 +03:00
- You made a clear closing statement or changed the subject definitively
- The exchange feels naturally complete
- Both sides have said their piece and there ' s nothing left hanging
2026-01-09 00:03:59 +02:00
2026-04-30 15:07:55 +03:00
IMPORTANT : This is a CONVERSATION , not a debate . Let it flow naturally . If you asked a question , the answer is almost always YES — they need to respond !
2026-01-09 00:03:59 +02:00
On a new line after your response , write :
[ CONTINUE : YES or NO ] [ CONFIDENCE : HIGH , MEDIUM , or LOW ] """
# Use appropriate model
model = globals . EVIL_TEXT_MODEL if responding_persona == " evil " else globals . TEXT_MODEL
2026-03-04 00:45:23 +02:00
# Use force_evil_context to avoid race condition with globals.EVIL_MODE
raw_response = await query_llama (
user_prompt = prompt ,
user_id = f " persona_dialogue_ { channel . id } " ,
guild_id = channel . guild . id if hasattr ( channel , ' guild ' ) and channel . guild else None ,
response_type = " autonomous_general " ,
model = model ,
force_evil_context = ( responding_persona == " evil " )
)
2026-01-09 00:03:59 +02:00
if not raw_response or raw_response . startswith ( " Error " ) :
return None , False , " LOW "
# Parse response and signal
response_text , should_continue , confidence = self . _parse_response ( raw_response )
return response_text , should_continue , confidence
def _parse_response ( self , raw_response : str ) - > tuple :
""" Extract response text and continuation signal """
lines = raw_response . strip ( ) . split ( ' \n ' )
should_continue = False
confidence = " MEDIUM "
response_lines = [ ]
for line in lines :
line_upper = line . upper ( )
if " [CONTINUE: " in line_upper :
should_continue = " YES " in line_upper
if " HIGH " in line_upper :
confidence = " HIGH "
elif " LOW " in line_upper :
confidence = " LOW "
else :
confidence = " MEDIUM "
else :
response_lines . append ( line )
response_text = ' \n ' . join ( response_lines ) . strip ( )
2026-03-04 00:45:23 +02:00
# Clean up any stray [CONTINUE: markers that leaked into response lines
# Only strip the structural markers, NOT common words like YES/NO/HIGH etc.
import re
response_text = re . sub ( r ' \ [CONTINUE: \ s*(YES|NO) \ ] \ s* \ [CONFIDENCE: \ s*(HIGH|MEDIUM|LOW) \ ] ' , ' ' , response_text )
response_text = re . sub ( r ' \ [CONTINUE: \ s*(YES|NO) \ ] ' , ' ' , response_text )
response_text = re . sub ( r ' \ [CONFIDENCE: \ s*(HIGH|MEDIUM|LOW) \ ] ' , ' ' , response_text )
2026-01-09 00:03:59 +02:00
response_text = response_text . strip ( )
# Override: If the response contains a question mark, always continue
if ' ? ' in response_text :
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Parse Override] Question detected, forcing continue=YES " )
2026-01-09 00:03:59 +02:00
should_continue = True
if confidence == " LOW " :
confidence = " MEDIUM "
return response_text , should_continue , confidence
def _get_persona_system_prompt ( self , persona : str ) - > str :
2026-04-30 15:07:55 +03:00
""" Get system prompt for a persona — full personality + conversation-mode guidance """
2026-01-09 00:03:59 +02:00
if persona == " evil " :
from utils . evil_mode import get_evil_system_prompt
2026-04-30 15:07:55 +03:00
full_prompt = get_evil_system_prompt ( )
# Overlay conversation-mode guidance on top of the full personality
return full_prompt + """
2026-01-09 00:03:59 +02:00
2026-04-30 15:07:55 +03:00
- - -
⚠ ️ CONVERSATION MODE : You are talking with Hatsune Miku , your " good " counterpart .
This is a CONVERSATION , not an argument . You can be snarky , dark , and sarcastic — that ' s who you are — but you ' re not here to fight . You ' re here to TALK. Be playful-cruel, not vicious. Show curiosity. Reveal cracks in the darkness. Do NOT launch into full attack mode or try to " win " — this isn ' t a competition . """
else :
from utils . context_manager import get_miku_system_prompt_compact
full_prompt = get_miku_system_prompt_compact ( )
# Overlay conversation-mode guidance on top of the full personality
return full_prompt + """
2026-01-09 00:03:59 +02:00
2026-04-30 15:07:55 +03:00
- - -
⚠ ️ CONVERSATION MODE : You are talking with Evil Miku , your dark alter ego .
This is a CONVERSATION , not an argument . Be yourself — kind , bubbly , optimistic — but you ' re not here to fight or defend your existence. Ask genuine questions. Share your feelings without attacking hers. Find common ground. Be curious, not defensive. Do NOT lecture her about being " good " or try to " fix " her. Just TALK. ✨💙 " " "
2026-01-09 00:03:59 +02:00
# ========================================================================
# DIALOGUE TURN HANDLING
# ========================================================================
async def handle_dialogue_turn (
self ,
channel : discord . TextChannel ,
responding_persona : str ,
trigger_reason : str = None
) :
"""
Handle one turn of dialogue , tracking tension for potential argument escalation .
"""
channel_id = channel . id
# Get or create dialogue state
state = self . active_dialogues . get ( channel_id )
if not state :
state = self . start_dialogue ( channel_id )
# Safety limits
if state [ " turn_count " ] > = MAX_TURNS :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Dialogue reached { MAX_TURNS } turns, ending " )
2026-01-09 00:03:59 +02:00
self . end_dialogue ( channel_id )
return
if time . time ( ) - state [ " started_at " ] > DIALOGUE_TIMEOUT :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Dialogue timeout (15 min), ending " )
2026-01-09 00:03:59 +02:00
self . end_dialogue ( channel_id )
return
# Build context from recent messages
context = await self . _build_conversation_context ( channel )
# Generate response with continuation signal
response_text , should_continue , confidence = await self . generate_response_with_continuation (
channel = channel ,
responding_persona = responding_persona ,
context = context ,
)
if not response_text :
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.
2026-01-10 20:46:19 +02:00
logger . error ( f " Failed to generate response for { responding_persona } " )
2026-01-09 00:03:59 +02:00
self . end_dialogue ( channel_id )
return
# Calculate tension change
tension_delta = self . calculate_tension_delta ( response_text , state [ " tension " ] )
state [ " tension " ] = max ( 0.0 , min ( 1.0 , state [ " tension " ] + tension_delta ) )
state [ " tension_history " ] . append ( {
" turn " : state [ " turn_count " ] ,
" speaker " : responding_persona ,
" delta " : tension_delta ,
" total " : state [ " tension " ] ,
} )
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " Tension: { state [ ' tension ' ] : .2f } (delta: { tension_delta : +.2f } ) " )
2026-01-09 00:03:59 +02:00
# Check if we should escalate to argument
if state [ " tension " ] > = ARGUMENT_TENSION_THRESHOLD :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " TENSION THRESHOLD REACHED ( { state [ ' tension ' ] : .2f } ) - ESCALATING TO ARGUMENT " )
2026-01-09 00:03:59 +02:00
# Send the response that pushed us over
await self . _send_as_persona ( channel , responding_persona , response_text )
# Transition to argument system
await self . _escalate_to_argument ( channel , responding_persona , response_text )
return
# Send response
await self . _send_as_persona ( channel , responding_persona , response_text )
# Update state
state [ " turn_count " ] + = 1
state [ " last_speaker " ] = responding_persona
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " Turn { state [ ' turn_count ' ] } : { responding_persona } | Continue: { should_continue } ( { confidence } ) | Tension: { state [ ' tension ' ] : .2f } " )
2026-01-09 00:03:59 +02:00
# Decide what happens next
opposite = " evil " if responding_persona == " miku " else " miku "
if should_continue and confidence in [ " HIGH " , " MEDIUM " ] :
2026-02-18 12:01:08 +02:00
create_tracked_task ( self . _next_turn ( channel , opposite ) , task_name = " persona_next_turn " )
2026-01-09 00:03:59 +02:00
elif should_continue and confidence == " LOW " :
2026-02-18 12:01:08 +02:00
create_tracked_task ( self . _next_turn ( channel , opposite ) , task_name = " persona_next_turn " )
2026-01-09 00:03:59 +02:00
elif not should_continue and confidence == " LOW " :
# Offer opposite persona the last word
2026-02-18 12:01:08 +02:00
create_tracked_task (
self . _offer_last_word ( channel , opposite , context + f " \n { responding_persona } : { response_text } " ) ,
task_name = " persona_last_word "
2026-01-09 00:03:59 +02:00
)
else :
# Clear signal to end
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Dialogue ended naturally after { state [ ' turn_count ' ] } turns (tension: { state [ ' tension ' ] : .2f } ) " )
2026-01-09 00:03:59 +02:00
self . end_dialogue ( channel_id )
async def _next_turn ( self , channel : discord . TextChannel , persona : str ) :
""" Queue the next turn """
# Check if dialogue was interrupted
if await self . _was_interrupted ( channel ) :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Dialogue interrupted by other activity " )
2026-01-09 00:03:59 +02:00
self . end_dialogue ( channel . id )
return
await self . handle_dialogue_turn ( channel , persona )
async def _offer_last_word ( self , channel : discord . TextChannel , persona : str , context : str ) :
"""
When speaker said NO with LOW confidence , ask opposite if they want to respond .
"""
from utils . llm import query_llama
channel_id = channel . id
state = self . active_dialogues . get ( channel_id )
if not state :
return
if await self . _was_interrupted ( channel ) :
self . end_dialogue ( channel_id )
return
system_prompt = self . _get_persona_system_prompt ( persona )
prompt = f """ { system_prompt }
Recent exchange :
{ context }
The conversation seems to be wrapping up , but wasn ' t explicitly ended.
Do you have anything to add ? If so , respond naturally .
If you ' re fine letting it end here, write only: [DONE]
Don ' t force a response if you have nothing meaningful to contribute. " " "
model = globals . EVIL_TEXT_MODEL if persona == " evil " else globals . TEXT_MODEL
2026-03-04 00:45:23 +02:00
# Use force_evil_context to avoid race condition with globals.EVIL_MODE
response = await query_llama (
user_prompt = prompt ,
user_id = f " persona_dialogue_ { channel_id } " ,
guild_id = channel . guild . id if hasattr ( channel , ' guild ' ) and channel . guild else None ,
response_type = " autonomous_general " ,
model = model ,
force_evil_context = ( persona == " evil " )
)
2026-01-09 00:03:59 +02:00
if not response :
self . end_dialogue ( channel_id )
return
if " [DONE] " in response . upper ( ) :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " { persona } chose not to respond, dialogue ended (tension: { state [ ' tension ' ] : .2f } ) " )
2026-01-09 00:03:59 +02:00
self . end_dialogue ( channel_id )
else :
clean_response = response . replace ( " [DONE] " , " " ) . strip ( )
# Calculate tension
tension_delta = self . calculate_tension_delta ( clean_response , state [ " tension " ] )
state [ " tension " ] = max ( 0.0 , min ( 1.0 , state [ " tension " ] + tension_delta ) )
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " Last word tension: { state [ ' tension ' ] : .2f } (delta: { tension_delta : +.2f } ) " )
2026-01-09 00:03:59 +02:00
# Check for argument escalation
if state [ " tension " ] > = ARGUMENT_TENSION_THRESHOLD :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " TENSION THRESHOLD REACHED on last word - ESCALATING TO ARGUMENT " )
2026-01-09 00:03:59 +02:00
await self . _send_as_persona ( channel , persona , clean_response )
await self . _escalate_to_argument ( channel , persona , clean_response )
return
# Normal flow
await self . _send_as_persona ( channel , persona , clean_response )
state [ " turn_count " ] + = 1
# Check if this looks like a closing statement
opposite = " evil " if persona == " miku " else " miku "
await self . _check_if_final ( channel , persona , clean_response , opposite )
async def _check_if_final ( self , channel : discord . TextChannel , speaker : str , response : str , opposite : str ) :
""" Check if a response looks like a closing statement """
state = self . active_dialogues . get ( channel . id )
if not state :
return
# Simple heuristics for closing statements
closing_indicators = [
response . rstrip ( ) . endswith ( ' . ' ) , # Statement, not question
' ? ' not in response , # No questions asked
len ( response ) < 100 , # Short responses often close things
]
if all ( closing_indicators ) :
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Dialogue ended after last word, { state [ ' turn_count ' ] } turns total " )
2026-01-09 00:03:59 +02:00
self . end_dialogue ( channel . id )
else :
2026-02-18 12:01:08 +02:00
create_tracked_task ( self . _next_turn ( channel , opposite ) , task_name = " persona_next_turn " )
2026-01-09 00:03:59 +02:00
# ========================================================================
# ARGUMENT ESCALATION
# ========================================================================
async def _escalate_to_argument ( self , channel : discord . TextChannel , last_speaker : str , triggering_message : str ) :
"""
Transition from dialogue to full bipolar argument .
"""
from utils . bipolar_mode import is_argument_in_progress , run_argument
# Clean up dialogue state
self . end_dialogue ( channel . id )
# Don't start if an argument is already going
if is_argument_in_progress ( channel . id ) :
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.
2026-01-10 20:46:19 +02:00
logger . warning ( f " Argument already in progress, skipping escalation " )
2026-01-09 00:03:59 +02:00
return
# Build context for the argument
escalation_context = f """ This argument erupted from a conversation that got heated.
The last thing said was : " {triggering_message} "
This pushed things over the edge into a full argument . """
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Escalating to argument in # { channel . name } " )
2026-01-09 00:03:59 +02:00
# Use the existing argument system
# Pass the triggering message so the opposite persona responds to it
await run_argument (
channel = channel ,
client = globals . client ,
trigger_context = escalation_context ,
)
# ========================================================================
# HELPER METHODS
# ========================================================================
async def _was_interrupted ( self , channel : discord . TextChannel ) - > bool :
""" Check if someone else sent a message during the dialogue """
state = self . active_dialogues . get ( channel . id )
if not state :
return True
try :
async for msg in channel . history ( limit = 1 ) :
# If latest message is NOT from our webhooks, we were interrupted
if not msg . webhook_id :
# Check if it's from the bot itself (could be normal response)
if msg . author . id != globals . client . user . id :
return True
except Exception as e :
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.
2026-01-10 20:46:19 +02:00
logger . warning ( f " Error checking for interruption: { e } " )
2026-01-09 00:03:59 +02:00
return False
async def _build_conversation_context ( self , channel : discord . TextChannel , limit : int = 15 ) - > str :
""" Get recent messages for context """
messages = [ ]
try :
async for msg in channel . history ( limit = limit ) :
speaker = self . _identify_speaker ( msg )
messages . append ( f " { speaker } : { msg . content } " )
messages . reverse ( )
except Exception as e :
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.
2026-01-10 20:46:19 +02:00
logger . warning ( f " Error building conversation context: { e } " )
2026-01-09 00:03:59 +02:00
return ' \n ' . join ( messages )
def _identify_speaker ( self , message : discord . Message ) - > str :
""" Identify who sent a message """
if message . webhook_id :
name_lower = ( message . author . name or " " ) . lower ( )
if " evil " in name_lower :
return " Evil Miku "
return " Hatsune Miku "
elif message . author . id == globals . client . user . id :
# Bot's own messages - check mode at time of message
if globals . EVIL_MODE :
return " Evil Miku "
return " Hatsune Miku "
return message . author . display_name
async def _send_as_persona ( self , channel : discord . TextChannel , persona : str , content : str ) :
""" Send message via webhook """
from utils . bipolar_mode import (
get_or_create_webhooks_for_channel ,
get_miku_display_name ,
2026-02-25 13:20:18 +02:00
get_evil_miku_display_name ,
get_persona_avatar_urls
2026-01-09 00:03:59 +02:00
)
webhooks = await get_or_create_webhooks_for_channel ( channel )
if not webhooks :
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.
2026-01-10 20:46:19 +02:00
logger . warning ( f " Could not get webhooks for # { channel . name } " )
2026-01-09 00:03:59 +02:00
return
webhook = webhooks [ " evil_miku " ] if persona == " evil " else webhooks [ " miku " ]
display_name = get_evil_miku_display_name ( ) if persona == " evil " else get_miku_display_name ( )
2026-02-25 13:20:18 +02:00
avatar_urls = get_persona_avatar_urls ( )
avatar_url = avatar_urls . get ( " evil_miku " ) if persona == " evil " else avatar_urls . get ( " miku " )
2026-01-09 00:03:59 +02:00
try :
2026-02-25 13:20:18 +02:00
await webhook . send ( content = content , username = display_name , avatar_url = avatar_url )
2026-01-09 00:03:59 +02:00
except Exception as e :
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.
2026-01-10 20:46:19 +02:00
logger . error ( f " Error sending as { persona } : { e } " )
2026-01-09 00:03:59 +02:00
# ============================================================================
# CONVENIENCE FUNCTIONS
# ============================================================================
# Singleton instances
_scorer = None
_dialogue_manager = None
def get_interjection_scorer ( ) - > InterjectionScorer :
""" Get the singleton InterjectionScorer instance """
global _scorer
if _scorer is None :
_scorer = InterjectionScorer ( )
return _scorer
def get_dialogue_manager ( ) - > PersonaDialogue :
""" Get the singleton PersonaDialogue instance """
global _dialogue_manager
if _dialogue_manager is None :
_dialogue_manager = PersonaDialogue ( )
return _dialogue_manager
async def check_for_interjection ( message : discord . Message , current_persona : str ) - > bool :
"""
Check if the opposite persona should interject based on a message .
If they should , starts a dialogue automatically .
Args :
message : The Discord message that was just sent
current_persona : Who sent the message ( " miku " or " evil " )
Returns :
True if an interjection was triggered , False otherwise
"""
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Persona Dialogue] Checking interjection for message from { current_persona } " )
2026-01-09 00:03:59 +02:00
scorer = get_interjection_scorer ( )
dialogue_manager = get_dialogue_manager ( )
# Don't trigger if dialogue already active
if dialogue_manager . is_dialogue_active ( message . channel . id ) :
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Persona Dialogue] Dialogue already active in channel { message . channel . id } " )
2026-01-09 00:03:59 +02:00
return False
# Check if we should interject
should_interject , reason , score = await scorer . should_interject ( message , current_persona )
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.
2026-01-10 20:46:19 +02:00
logger . debug ( f " [Persona Dialogue] Interjection check: should_interject= { should_interject } , reason= { reason } , score= { score : .2f } " )
2026-01-09 00:03:59 +02:00
if should_interject :
opposite_persona = " evil " if current_persona == " miku " else " miku "
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.
2026-01-10 20:46:19 +02:00
logger . info ( f " Triggering { opposite_persona } interjection (reason: { reason } , score: { score : .2f } ) " )
2026-01-09 00:03:59 +02:00
# Start dialogue with the opposite persona responding first
dialogue_manager . start_dialogue ( message . channel . id )
2026-02-18 12:01:08 +02:00
create_tracked_task (
dialogue_manager . handle_dialogue_turn ( message . channel , opposite_persona , trigger_reason = reason ) ,
task_name = " persona_dialogue_turn "
2026-01-09 00:03:59 +02:00
)
return True
return False
def is_persona_dialogue_active ( channel_id : int ) - > bool :
""" Check if a persona dialogue is currently active in a channel """
dialogue_manager = get_dialogue_manager ( )
return dialogue_manager . is_dialogue_active ( channel_id )