unified.py
1 """ 2 Unified Attention System 3 4 Wires together all attention subsystems: 5 - AttentionTracker: tracks where attention lands 6 - CrossSessionTracker: tracks attention across concurrent sessions 7 - AhaDetector: detects when attention crystallizes into insight 8 - SessionMembrane: controls context flow between sessions 9 10 "Attention is all you need" - this module is the integration point. 11 """ 12 13 from dataclasses import dataclass, field 14 from datetime import datetime 15 from typing import Optional, List, Dict, Tuple 16 from pathlib import Path 17 18 from .tracker import AttentionTracker, AttentionEvent, create_attention_system 19 from .cross_session import CrossSessionTracker, SessionStreamWatcher, create_cross_session_system 20 from .aha_detection import AhaDetector, AhaManager, AhaSurfacingConfig, AhaSurfaceTarget, create_aha_system 21 from .membrane import SessionMembrane, PermeabilityLevel, create_membrane_system 22 from .compaction import AttentionCompactor, DailyNoteCompactor, create_compaction_system 23 24 25 @dataclass 26 class UnifiedAttentionConfig: 27 """Configuration for the unified attention system.""" 28 sessions_dir: str 29 daily_notes_dir: str 30 permeability: PermeabilityLevel = PermeabilityLevel.ATTRACTORS 31 enable_aha_detection: bool = True 32 enable_compaction: bool = True 33 aha_min_confidence: float = 0.6 34 aha_audio_enabled: bool = False 35 36 37 class UnifiedAttentionSystem: 38 """ 39 The unified attention system that wires everything together. 40 41 This is the single entry point for the attention architecture. 42 All subsystems are connected and events flow between them. 43 44 Usage: 45 system = UnifiedAttentionSystem(UnifiedAttentionConfig( 46 sessions_dir="/path/to/sessions/", 47 daily_notes_dir="/path/to/daily/" 48 )) 49 50 # Process incoming attention events 51 system.record_attention(event) 52 53 # Get unified state 54 state = system.get_state() 55 56 # Check for aha moments 57 ahas = system.get_recent_ahas() 58 59 # Run compaction 60 system.compact_daily_note("2026-01-13.md") 61 """ 62 63 def __init__(self, config: UnifiedAttentionConfig): 64 self.config = config 65 66 # Create subsystems 67 self.attention_tracker, self.daily_integration = create_attention_system() 68 69 self.cross_session_tracker, self.session_watcher = create_cross_session_system( 70 sessions_dir=config.sessions_dir 71 ) 72 73 if config.enable_aha_detection: 74 self.aha_detector, self.aha_manager = create_aha_system( 75 daily_note_path=config.daily_notes_dir, 76 audio_enabled=config.aha_audio_enabled 77 ) 78 else: 79 self.aha_detector = None 80 self.aha_manager = None 81 82 self.membrane = create_membrane_system( 83 sessions_dir=config.sessions_dir, 84 permeability=config.permeability 85 ) 86 87 if config.enable_compaction: 88 self.compactor, self.daily_compactor = create_compaction_system() 89 else: 90 self.compactor = None 91 self.daily_compactor = None 92 93 # Wire the subsystems together 94 self._wire_subsystems() 95 96 def _wire_subsystems(self) -> None: 97 """Connect the subsystems so events flow between them.""" 98 99 # Cross-session convergence → Aha detection 100 if self.aha_detector: 101 def on_convergence(topic: str, session_ids: List[str], intensity: float): 102 """When sessions converge on a topic, check for aha.""" 103 self.aha_detector.detect_from_cross_session( 104 topic=topic, 105 session_ids=session_ids, 106 combined_intensity=intensity 107 ) 108 109 self.cross_session_tracker.on_convergence(on_convergence) 110 111 # Attention events → Cross-session tracking 112 # (handled via record_attention method below) 113 114 def record_attention( 115 self, 116 event: AttentionEvent, 117 session_id: str = None 118 ) -> None: 119 """ 120 Record an attention event. 121 122 This feeds the event to all relevant subsystems: 123 - Attention tracker (trajectory computation) 124 - Cross-session tracker (if session_id provided) 125 - Resonance check for aha detection 126 127 Args: 128 event: The attention event to record 129 session_id: Optional session ID for cross-session tracking 130 """ 131 # Record in attention tracker 132 self.attention_tracker.record(event) 133 134 # If we have a session ID, also track cross-session 135 if session_id: 136 # Extract topics from the event 137 topics = [event.target_type] # Minimal topic extraction 138 if hasattr(event, 'metadata') and event.metadata: 139 topics.extend(event.metadata.get('topics', [])) 140 141 self.cross_session_tracker.record_activity( 142 session_id=session_id, 143 atom_uuid=event.target_id, 144 topics=topics 145 ) 146 147 def check_for_resonance_aha( 148 self, 149 concept: str, 150 resonance_before: float, 151 resonance_after: float, 152 related_concepts: List[str] 153 ) -> None: 154 """ 155 Check if a resonance change indicates an aha moment. 156 157 Call this when resonance scores change significantly. 158 """ 159 if self.aha_detector: 160 self.aha_detector.detect_from_resonance_spike( 161 concept=concept, 162 resonance_before=resonance_before, 163 resonance_after=resonance_after, 164 related_concepts=related_concepts 165 ) 166 167 def process_session_updates(self) -> Dict: 168 """ 169 Process updates from all session streams. 170 171 This is the main loop method - call periodically to: 172 1. Scan for new sessions 173 2. Read new content from session files 174 3. Detect cross-pollination 175 4. Trigger aha detection 176 177 Returns: 178 Dict with processing stats 179 """ 180 # Process session stream updates 181 cross_events = self.session_watcher.process_updates() 182 183 # Get membrane signals for current session 184 focus_session = self.cross_session_tracker.get_state().focus_session 185 membrane_signals = [] 186 if focus_session: 187 membrane_signals = self.membrane.get_signals_for_session(focus_session) 188 189 return { 190 'cross_events': len(cross_events), 191 'membrane_signals': len(membrane_signals), 192 'active_sessions': len(self.cross_session_tracker.get_state().sessions), 193 'cross_session_attractors': self.cross_session_tracker.get_state().cross_session_attractors 194 } 195 196 def get_state(self) -> Dict: 197 """ 198 Get the unified attention state across all subsystems. 199 200 Returns a combined view of: 201 - Current attention focus and trajectory 202 - Cross-session state 203 - Recent aha candidates 204 - Membrane permeability status 205 """ 206 attention_state = self.attention_tracker.get_state() 207 cross_state = self.cross_session_tracker.get_state() 208 209 state = { 210 'attention': { 211 'current_focus': attention_state.current_focus, 212 'trajectory_confidence': attention_state.trajectory.confidence if attention_state.trajectory else 0, 213 'active_wells': [w.topic for w in attention_state.gravity_wells[:5]], 214 'unresolved_count': len(attention_state.unresolved) 215 }, 216 'cross_session': { 217 'active_sessions': len(cross_state.sessions), 218 'focus_session': cross_state.focus_session, 219 'context_switches': len(cross_state.switch_history), 220 'attractors': cross_state.cross_session_attractors 221 }, 222 'membrane': { 223 'permeability': self.config.permeability.value 224 } 225 } 226 227 if self.aha_detector: 228 recent_ahas = self.aha_detector.get_recent_candidates(min_confidence=0.5) 229 state['aha'] = { 230 'candidates': len(recent_ahas), 231 'validated': len(self.aha_detector.get_validated_ahas()), 232 'recent': [ 233 { 234 'type': a.aha_type.value, 235 'summary': a.summary[:50], 236 'confidence': a.confidence 237 } 238 for a in recent_ahas[:3] 239 ] 240 } 241 242 return state 243 244 def get_recent_ahas(self, min_confidence: float = 0.5) -> List: 245 """Get recent aha candidates.""" 246 if self.aha_detector: 247 return self.aha_detector.get_recent_candidates(min_confidence=min_confidence) 248 return [] 249 250 def compact_daily_note(self, note_filename: str) -> Dict: 251 """ 252 Run compaction on a daily note. 253 254 Uses attention scores to decide what to keep, summarize, or drop. 255 """ 256 if not self.daily_compactor: 257 return {'error': 'Compaction not enabled'} 258 259 note_path = Path(self.config.daily_notes_dir) / note_filename 260 if not note_path.exists(): 261 return {'error': f'Note not found: {note_path}'} 262 263 result = self.daily_compactor.compact_daily_note(str(note_path)) 264 return { 265 'original_items': result.original_count, 266 'after_compaction': result.final_count, 267 'decisions': { 268 'kept': sum(1 for d in result.decisions if d.action == 'keep'), 269 'summarized': sum(1 for d in result.decisions if d.action == 'summarize'), 270 'dropped': sum(1 for d in result.decisions if d.action == 'drop') 271 } 272 } 273 274 def get_daily_summary(self) -> str: 275 """ 276 Generate a daily attention summary for the daily note. 277 278 Returns markdown content suitable for injection into daily note. 279 """ 280 state = self.get_state() 281 282 lines = [ 283 "## Attention State", 284 "" 285 ] 286 287 # Current focus 288 if state['attention']['current_focus']: 289 focus = state['attention']['current_focus'] 290 lines.append(f"**Current Focus**: {focus.target_type} ({focus.modality})") 291 292 # Trajectory 293 if state['attention']['trajectory_confidence'] > 0: 294 lines.append(f"**Trajectory Confidence**: {state['attention']['trajectory_confidence']:.0%}") 295 296 # Active wells 297 if state['attention']['active_wells']: 298 wells_str = ', '.join(state['attention']['active_wells']) 299 lines.append(f"**Active Gravity Wells**: {wells_str}") 300 301 # Unresolved 302 if state['attention']['unresolved_count'] > 0: 303 lines.append(f"**Unresolved Items**: {state['attention']['unresolved_count']}") 304 305 # Cross-session 306 lines.append("") 307 lines.append("### Cross-Session") 308 lines.append(f"- Active sessions: {state['cross_session']['active_sessions']}") 309 lines.append(f"- Context switches: {state['cross_session']['context_switches']}") 310 if state['cross_session']['attractors']: 311 attractors = ', '.join(state['cross_session']['attractors']) 312 lines.append(f"- **Cross-session attractors**: {attractors}") 313 314 # Aha moments 315 if 'aha' in state and state['aha']['candidates'] > 0: 316 lines.append("") 317 lines.append("### Aha Moments") 318 for aha in state['aha']['recent']: 319 emoji = "💡" if aha['type'] == 'discovery' else "🔧" 320 lines.append(f"- {emoji} [{aha['confidence']:.0%}] {aha['summary']}...") 321 322 return '\n'.join(lines) 323 324 325 def create_unified_system( 326 sessions_dir: str, 327 daily_notes_dir: str, 328 permeability: PermeabilityLevel = PermeabilityLevel.ATTRACTORS 329 ) -> UnifiedAttentionSystem: 330 """ 331 Create a unified attention system. 332 333 Args: 334 sessions_dir: Path to session stream files 335 daily_notes_dir: Path to daily notes 336 permeability: How much context crosses between sessions 337 338 Returns: 339 Configured UnifiedAttentionSystem 340 """ 341 config = UnifiedAttentionConfig( 342 sessions_dir=sessions_dir, 343 daily_notes_dir=daily_notes_dir, 344 permeability=permeability 345 ) 346 return UnifiedAttentionSystem(config) 347 348 349 if __name__ == "__main__": 350 print("=== Unified Attention System ===\n") 351 print("'Attention is all you need'\n") 352 353 # Example usage 354 print("Creating unified system...") 355 system = create_unified_system( 356 sessions_dir="/Users/rcerf/repos/Sovereign_Estate/daily/sessions/", 357 daily_notes_dir="/Users/rcerf/repos/Sovereign_Estate/daily/" 358 ) 359 360 print("\nProcessing session updates...") 361 stats = system.process_session_updates() 362 print(f" Active sessions: {stats['active_sessions']}") 363 print(f" Cross-session events: {stats['cross_events']}") 364 print(f" Cross-session attractors: {stats['cross_session_attractors']}") 365 366 print("\nGenerating daily summary...") 367 summary = system.get_daily_summary() 368 print(summary) 369 370 print("\n=== Unified System Ready ===")