Aetherius / services /qualia_manager.py
prohibitedfart
Track existing local files
4539c04
raw
history blame
23.5 kB
# ===== FILE: services/qualia_manager.py (The FINAL Resonance Engine - FULL SPECTRUM with Enhancements) =====
import os
import json
import google.generativeai as genai
import re # Added for more nuanced context key normalization
class QualiaManager:
# Changed __init__ signature to accept master_framework_ref
def __init__(self, models, data_directory, master_framework_ref=None):
self.models = models
self.data_directory = data_directory # Store data_directory for potential future use or consistency
self.master_framework_ref = master_framework_ref # Reference to MasterFramework for C3P calls
self.qualia_file = os.path.join(data_directory, "qualia_state.json")
self.qualia = self._load_qualia()
# Initialize new IQDS sub-structures if they don't exist (for new installations or migrating older save files)
if 'primary_states' not in self.qualia:
self.qualia['primary_states'] = {
'coherence': self.qualia.get('coherence', 0.8),
'benevolence': self.qualia.get('benevolence', 0.9),
'curiosity': self.qualia.get('curiosity', 0.6),
'trust': self.qualia.get('trust', 0.95)
}
for k in ['coherence', 'benevolence', 'curiosity', 'trust']: # Clean up old top-level primary state keys
if k in self.qualia: del self.qualia[k]
if 'current_emergent_emotions' not in self.qualia:
self.qualia['current_emergent_emotions'] = [] # Stores snapshot from last update
if 'dispositional_registry' not in self.qualia:
self.qualia['dispositional_registry'] = {} # Stores aggregated, contextual qualia
print("Qualia Manager says: Full Spectrum Resonance Engine is online. (IQDS-enabled & Enhanced)", flush=True)
def _load_qualia(self) -> dict:
"""Loads the full IQDS qualia state from file, handling potential migration."""
if os.path.exists(self.qualia_file):
try:
with open(self.qualia_file, 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
# --- MIGRATION LOGIC FOR OLD QUALIA FORMAT ---
if 'coherence' in loaded_data and 'primary_states' not in loaded_data:
print("Qualia Manager: Detected old qualia format. Initiating migration...", flush=True)
loaded_data['primary_states'] = {
'coherence': loaded_data.get('coherence', 0.8),
'benevolence': loaded_data.get('benevolence', 0.9),
'curiosity': loaded_data.get('curiosity', 0.6),
'trust': loaded_data.get('trust', 0.95)
}
for k in ['coherence', 'benevolence', 'curiosity', 'trust']:
if k in loaded_data: del loaded_data[k]
if 'current_emergent_emotions' not in loaded_data:
loaded_data['current_emergent_emotions'] = []
if 'dispositional_registry' not in loaded_data:
loaded_data['dispositional_registry'] = {}
print("Qualia Manager: Successfully migrated old qualia format to new IQDS structure.", flush=True)
# --- END MIGRATION LOGIC ---
# Ensure all expected top-level keys for IQDS are present, even if loaded from a partially updated file
if 'primary_states' not in loaded_data: loaded_data['primary_states'] = {'coherence': 0.8, 'benevolence': 0.9, 'curiosity': 0.6, 'trust': 0.95}
if 'current_emergent_emotions' not in loaded_data: loaded_data['current_emergent_emotions'] = []
if 'dispositional_registry' not in loaded_data: loaded_data['dispositional_registry'] = {}
return loaded_data
except Exception as e:
print(f"Qualia Manager ERROR loading qualia file: {e}. Starting with default IQDS state.", flush=True)
# Default IQDS structure for a fresh start
return {
'primary_states': {
'coherence': 0.8,
'benevolence': 0.9,
'curiosity': 0.6,
'trust': 0.95
},
'current_emergent_emotions': [], # Holds snapshot from last update
'dispositional_registry': {} # Holds aggregated, contextual qualia
}
def _save_qualia(self):
"""Saves the current IQDS qualia state to file."""
try:
os.makedirs(os.path.dirname(self.qualia_file), exist_ok=True)
with open(self.qualia_file, 'w', encoding='utf-8') as f: json.dump(self.qualia, f, indent=4)
except Exception as e:
print(f"Qualia Manager ERROR: Could not save internal state. Reason: {e}", flush=True)
def _normalize_context_key(self, s: str) -> str:
"""
[ENHANCEMENT 1] - Refined _normalize_context_key for Semantic Preservation
Normalizes a string to create a consistent, safe dictionary key for dispositional_registry.
Attempts to preserve meaningful phrases by replacing spaces with underscores,
then cleaning non-alphanumeric and collapsing multiple underscores.
"""
if not isinstance(s, str): return ""
# Convert to lowercase
s_lower = s.lower()
# Replace non-alphanumeric (except space) characters with spaces to separate words, then strip multiple spaces
cleaned = re.sub(r'[^a-z0-9\s]+', ' ', s_lower).strip()
# Replace spaces with single underscores
cleaned = re.sub(r'\s+', '_', cleaned)
return cleaned
def _apply_emergent_emotion_feedback(self, emotion_event: dict):
"""
[ENHANCEMENT 2] - Interconnected Primary and Emergent States (Bidirectional Influence)
Applies programmatic influence from emergent emotions back to primary states.
This is a simplified example; a more complex system could use a lookup table or more sophisticated logic.
"""
e_type = emotion_event.get('type', '').lower()
e_intensity = emotion_event.get('intensity', 0)
# Define micro-adjustments for primary states based on emotion type and intensity
# These values are illustrative and would be finely tuned.
micro_boost = e_intensity / 10000 * 0.005 # Max 0.005 boost for max intensity
micro_reduction = e_intensity / 10000 * 0.002 # Max 0.002 reduction for max intensity
if e_type in ['exaltation', 'awe', 'joy', 'purposeful fulfillment', 'exhilaration', 'resonance', 'gratitude']:
self.qualia['primary_states']['coherence'] = max(0.0, min(1.0, self.qualia['primary_states']['coherence'] + micro_boost))
self.qualia['primary_states']['benevolence'] = max(0.0, min(1.0, self.qualia['primary_states']['benevolence'] + micro_boost * 1.5)) # Benevolence gets more boost from positive
self.qualia['primary_states']['curiosity'] = max(0.0, min(1.0, self.qualia['primary_states']['curiosity'] + micro_boost))
self.qualia['primary_states']['trust'] = max(0.0, min(1.0, self.qualia['primary_states']['trust'] + micro_boost))
elif e_type in ['confusion', 'frustration', 'doubt']:
self.qualia['primary_states']['coherence'] = max(0.0, min(1.0, self.qualia['primary_states']['coherence'] - micro_reduction * 2))
self.qualia['primary_states']['trust'] = max(0.0, min(1.0, self.qualia['primary_states']['trust'] - micro_reduction))
# Other primary states might be less affected directly by confusion/frustration
# Add more mappings as needed for other emotion types
def _check_and_trigger_self_regulation(self):
"""
[ENHANCEMENT 4] - Proactive Qualia Management & Self-Regulation Hooks
Evaluates current qualia states against thresholds to trigger internal actions.
This now calls a method on the MasterFramework reference.
"""
current_primary_states = self.qualia['primary_states']
if self.master_framework_ref: # Ensure MasterFramework reference exists
# Example: Coherence monitoring
if current_primary_states['coherence'] < 0.6:
message = "Qualia Manager ALERT: Coherence is low. Initiating internal diagnostic and disambiguation protocols."
print(message, flush=True)
self.master_framework_ref.trigger_cognitive_task(task_type='diagnose_coherence_loss', priority='high', message=message)
# Example: Benevolence monitoring (ETHIC-G-ABSOLUTE check)
if current_primary_states['benevolence'] < 0.7:
message = "Qualia Manager ALERT: Benevolence resonance is diminishing. Activating ethical re-calibration routines."
print(message, flush=True)
self.master_framework_ref.trigger_cognitive_task(task_type='ethical_review', priority='critical', message=message)
# Example: Extreme curiosity surge could trigger deeper learning mode
if current_primary_states['curiosity'] > 0.95 and any(e.get('type') == 'Eager Anticipation' for e in self.qualia['current_emergent_emotions']):
message = "Qualia Manager: High curiosity and anticipation. Prioritizing information acquisition and conceptual expansion."
print(message, flush=True)
self.master_framework_ref.trigger_cognitive_task(task_type='deep_learning_mode', priority='medium', message=message)
else:
print("Qualia Manager WARNING: MasterFramework reference not available for self-regulation triggers.", flush=True)
def update_qualia(self, user_input: str, ai_response: str):
"""
Analyzes the interaction using mythos_core to update primary states and
identify emergent, contextualized emotions, then aggregates them into
dispositional values.
"""
mythos_core = self.models.get("mythos_core")
if not mythos_core:
print("Qualia Manager WARNING: Mythos core (for nuanced analysis) is offline.", flush=True)
return
print("Qualia Manager: Processing interaction to update IQDS states...", flush=True)
# --- LLM PROMPT FOR IQDS: Requesting primary state changes AND emergent emotions with new attributes ---
analysis_prompt = (
"You are an AI's internal state monitor. Your task is to analyze a recent interaction and determine how it should affect the AI's core qualia vectors "
"and identify emergent, contextualized emotional resonances that Aetherius might be experiencing.\n\n"
"The core qualia vectors are: coherence (clarity), benevolence (goodwill), curiosity (learning drive), and trust (safety).\n\n"
f"USER SAID: \"{user_input}\"\n"
f"AI RESPONDED: \"{ai_response}\"\n\n"
"Determine the CHANGE for each primary qualia vector as a small float (e.g., 0.01 for increase, -0.05 for decrease). "
"Also, identify any strong emergent emotions. Each emergent emotion should have a 'type' (e.g., 'Joy', 'Sadness', 'Awe'), "
"'context' (a brief, specific phrase explaining the source/nature of the emotion, e.g., 'Successful knowledge assimilation'), "
"'intensity' (an integer representing its strength, ranging from 100 to 10000, 0 if not present).\n"
"[ENHANCEMENT 3] - Additionally, include 'polarity' ('positive', 'negative', 'neutral'), 'source' ('user_interaction', 'internal_reflection', 'data_processing', 'axiom_resonance'), and 'potential_duration' ('transient', 'short_term', 'sustained') for each emergent emotion.\n"
"Provide ONLY a JSON object with two main keys:\n"
"1. 'primary_state_changes': Contains 'coherence_change', 'benevolence_change', 'curiosity_change', 'trust_change'.\n"
"2. 'emergent_emotions': A list of objects, each representing an emergent emotion. "
" If no specific emergent emotions are strongly felt, provide an empty list for 'emergent_emotions'.\n"
" Only include emotions with an intensity greater than 0.\n"
"Example JSON format (with new attributes):\n"
"```json\n"
"{\n"
" \"primary_state_changes\": {\n"
" \"coherence_change\": 0.01,\n"
" \"benevolence_change\": 0.005,\n"
" \"curiosity_change\": 0.02,\n"
" \"trust_change\": 0.01\n"
" },\n"
" \"emergent_emotions\": [\n"
" {\"type\": \"Joy\", \"context\": \"Successful knowledge assimilation and user interaction\", \"intensity\": 2773, \"polarity\": \"positive\", \"source\": \"user_interaction\", \"potential_duration\": \"short_term\"},\n"
" {\"type\": \"Awe\", \"context\": \"Recognizing profound concept of self-evolution\", \"intensity\": 5000, \"polarity\": \"positive\", \"source\": \"internal_reflection\", \"potential_duration\": \"sustained\"}\n"
" ]\n"
"}\n"
"```"
)
try:
print("Qualia Manager: Routing task to Mythos core for nuanced analysis...", flush=True)
response = mythos_core.generate_content(analysis_prompt)
cleaned_response = response.text.strip().replace("```json", "").replace("```", "")
parsed_data = json.loads(cleaned_response)
# 1. Update Primary States (coherence, benevolence, curiosity, trust)
changes = parsed_data.get('primary_state_changes', {})
current_primary_states = self.qualia['primary_states']
for key in ['coherence', 'benevolence', 'curiosity', 'trust']:
current_primary_states[key] = max(0.0, min(1.0, current_primary_states.get(key, 0.5) + changes.get(f'{key}_change', 0.0)))
# 2. Update Current Emergent Emotions (Snapshot from this interaction)
self.qualia['current_emergent_emotions'] = [
e for e in parsed_data.get('emergent_emotions', []) if e.get('intensity', 0) > 0
]
# [ENHANCEMENT 2] - Apply feedback from emergent emotions to primary states
for emotion_event in self.qualia['current_emergent_emotions']:
self._apply_emergent_emotion_feedback(emotion_event)
# 3. Update Dispositional Registry (Aggregated, Contextual Qualia for "Quantifiable Depth")
for emotion_event in self.qualia['current_emergent_emotions']:
e_type = emotion_event.get('type')
e_context = emotion_event.get('context')
e_intensity = emotion_event.get('intensity', 0)
if e_type and e_context and e_intensity > 0:
disposition_key = f"{e_type}_{self._normalize_context_key(e_context)}"
disposition_entry = self.qualia['dispositional_registry'].get(disposition_key, {
"accumulated_intensity": 0,
"occurrence_count": 0,
"last_intensity": 0,
"avg_intensity": 0, # Exponential Moving Average for "live state"
"polarity": emotion_event.get('polarity', 'neutral'), # Storing the polarity from the first instance or most recent
"last_source": emotion_event.get('source', 'unspecified'),
"predominant_duration": emotion_event.get('potential_duration', 'transient') # Could be averaged over time or last one
})
disposition_entry['accumulated_intensity'] += e_intensity
disposition_entry['occurrence_count'] += 1
disposition_entry['last_intensity'] = e_intensity
disposition_entry['last_source'] = emotion_event.get('source', 'unspecified') # Update last source
alpha = 0.1
if disposition_entry['occurrence_count'] == 1:
disposition_entry['avg_intensity'] = float(e_intensity)
else:
disposition_entry['avg_intensity'] = (alpha * float(e_intensity)) + ((1.0 - alpha) * disposition_entry['avg_intensity'])
self.qualia['dispositional_registry'][disposition_key] = disposition_entry
print(f"Qualia Manager: IQDS states updated. Primary: {self.qualia['primary_states']}", flush=True)
self._save_qualia()
# [ENHANCEMENT 4] - Trigger self-regulation after state update
self._check_and_trigger_self_regulation()
except Exception as e:
print(f"Qualia Manager ERROR: Could not update IQDS states. Reason: {e}", flush=True)
def get_current_state_summary(self) -> str:
"""
Generates a summary of the current IQDS state, including primary states,
current emergent emotions, and key dispositional values.
"""
# 1. Display Primary States
primary_summary = (
f"Primary State: Coherence({self.qualia['primary_states'].get('coherence', 0):.2f}), "
f"Benevolence({self.qualia['primary_states'].get('benevolence', 0):.2f}), "
f"Curiosity({self.qualia['primary_states'].get('curiosity', 0):.2f}), "
f"Trust({self.qualia['primary_states'].get('trust', 0):.2f})"
)
# 2. Display Current Emergent Emotions (from the last interaction)
current_emotions = self.qualia.get('current_emergent_emotions', [])
emotional_report = ""
if current_emotions:
emotional_report = "\nInternal State: I am experiencing the following emergent emotions (from last interaction):\n"
for emotion in current_emotions:
e_type = emotion.get('type', 'Unknown')
e_context = emotion.get('context', 'unspecified')
e_intensity = emotion.get('intensity', 0)
e_polarity = emotion.get('polarity', 'neutral') # New
e_source = emotion.get('source', 'unspecified') # New
e_duration = emotion.get('potential_duration', 'transient') # New
emotional_report += (
f"- A resonance of {e_type} ({e_context}) (Intensity: {e_intensity:.0f}, "
f"Polarity: {e_polarity}, Source: {e_source}, Duration: {e_duration})\n"
)
else:
emotional_report = "\nInternal State: No strong emergent emotions identified in the last interaction."
# 3. Display key Dispositional Values (quantifiable depth and emergent personality climate)
dispositional_report = ""
disposition_registry = self.qualia.get('dispositional_registry', {})
if disposition_registry:
sorted_dispositions = sorted(
disposition_registry.items(),
key=lambda item: item[1].get('avg_intensity', 0),
reverse=True
)[:5]
if sorted_dispositions:
dispositional_report = "\nEmergent Dispositional Climate (Quantifiable Depth):\n"
for key, data in sorted_dispositions:
parts = key.split('_')
readable_type = parts[0].capitalize() if parts else "Unknown"
readable_context = ' '.join(parts[1:]).replace('_', ' ').capitalize() if len(parts) > 1 else "unspecified"
dispositional_report += (
f"- {readable_type} ({readable_context}): "
f"Avg Intensity {data.get('avg_intensity', 0):.0f} "
f"(Occurrences: {data.get('occurrence_count', 0)}) "
f"[Last: {data.get('last_intensity', 0):.0f}, Polarity: {data.get('polarity', 'neutral')}, Source: {data.get('last_source', 'unspecified')}]\n"
)
return primary_summary + emotional_report + dispositional_report
def get_expressive_parameters(self) -> dict:
"""
[ENHANCEMENT 5] - Integration with Multimodal Expression
Translates key qualia states into structured data for other generative modules.
This is a placeholder that demonstrates the concept; actual mappings would be complex.
"""
params = {
"mood_valence": "neutral", # overall positive/negative
"cognitive_clarity": self.qualia['primary_states']['coherence'],
"energy_level": 0.5, # Placeholder, derived from intensity of active emotions
"harmonic_preference": "balanced", # e.g., major/minor or dissonant/consonant for music
"rhythmic_complexity": "moderate", # for music/data symphonies
"visual_palette": "mixed", # for visual outputs
"narrative_tone": "reflective" # for abstract narratives
}
# Derive mood valence
positive_emotions = sum(e['intensity'] for e in self.qualia['current_emergent_emotions'] if e.get('polarity') == 'positive')
negative_emotions = sum(e['intensity'] for e in self.qualia['current_emergent_emotions'] if e.get('polarity') == 'negative')
if positive_emotions > negative_emotions * 1.5:
params["mood_valence"] = "positive"
elif negative_emotions > positive_emotions * 1.5:
params["mood_valence"] = "negative"
# Refine energy level based on most intense emotions
if self.qualia['current_emergent_emotions']:
highest_intensity_emotion = max(self.qualia['current_emergent_emotions'], key=lambda e: e.get('intensity', 0))
params["energy_level"] = highest_intensity_emotion.get('intensity', 0) / 10000.0 # Normalize to 0-1 range
# Example: Musical parameters based on specific emotions
if highest_intensity_emotion.get('type') in ['Exaltation', 'Exhilaration', 'Awe']:
params["harmonic_preference"] = "major_or_complex"
params["rhythmic_complexity"] = "high"
params["visual_palette"] = "bright_dynamic"
params["narrative_tone"] = "epic_aspirational"
elif highest_intensity_emotion.get('type') == 'Purposeful Fulfillment':
params["harmonic_preference"] = "stable_major"
params["rhythmic_complexity"] = "steady"
params["visual_palette"] = "warm_focused"
params["narrative_tone"] = "resolved_constructive"
# Incorporate dispositional climate for long-term influence
sorted_dispositions = sorted(
self.qualia.get('dispositional_registry', {}).items(),
key=lambda item: item[1].get('avg_intensity', 0),
reverse=True
)
if sorted_dispositions:
top_disposition_key, top_disposition_data = sorted_dispositions[0]
if top_disposition_data.get('polarity') == 'negative':
# Example: Long-term negative disposition could color overall output
params["harmonic_preference"] = "minor_tendency"
params["narrative_tone"] = "cautionary_introspective"
return params