1+ #!/usr/bin/env python3
2+ """
3+ OSA Action Hooks System
4+ Shows real-time learning and capability improvements
5+ """
6+
7+ import asyncio
8+ import time
9+ from typing import Dict , Any , List , Optional , Callable
10+ from datetime import datetime
11+ from dataclasses import dataclass
12+ from enum import Enum
13+
14+
15+ class ActionType (Enum ):
16+ """Types of actions OSA can perform"""
17+ SKILL_LEARNED = "🎓"
18+ KNOWLEDGE_ACQUIRED = "📚"
19+ PATTERN_RECOGNIZED = "🔄"
20+ OPTIMIZATION_APPLIED = "⚡"
21+ CONTEXT_EXPANDED = "🧠"
22+ SELF_IMPROVEMENT = "🔧"
23+ CAPABILITY_DISCOVERED = "✨"
24+ ERROR_RECOVERY = "🔨"
25+ CACHE_OPTIMIZATION = "💾"
26+ PERFORMANCE_TUNED = "🚀"
27+
28+
29+ @dataclass
30+ class ActionEvent :
31+ """Represents a single action event"""
32+ action_type : ActionType
33+ description : str
34+ timestamp : float
35+ confidence : float = 0.0
36+ details : Optional [Dict [str , Any ]] = None
37+ impact : Optional [str ] = None
38+
39+
40+ class ActionHooks :
41+ """Manages action hooks and notifications"""
42+
43+ def __init__ (self , verbose : bool = True ):
44+ self .verbose = verbose
45+ self .hooks : Dict [ActionType , List [Callable ]] = {}
46+ self .recent_actions : List [ActionEvent ] = []
47+ self .max_history = 100
48+ self .display_queue = asyncio .Queue () if asyncio .get_event_loop ().is_running () else None
49+
50+ # Register default display hook
51+ self .register_hook (None , self ._display_action )
52+
53+ def register_hook (self , action_type : Optional [ActionType ], callback : Callable ) -> None :
54+ """Register a callback for a specific action type (or all if None)"""
55+ if action_type is None :
56+ # Register for all action types
57+ for at in ActionType :
58+ if at not in self .hooks :
59+ self .hooks [at ] = []
60+ self .hooks [at ].append (callback )
61+ else :
62+ if action_type not in self .hooks :
63+ self .hooks [action_type ] = []
64+ self .hooks [action_type ].append (callback )
65+
66+ async def trigger_action (self , action_type : ActionType , description : str ,
67+ confidence : float = 0.0 , details : Optional [Dict ] = None ,
68+ impact : Optional [str ] = None ) -> None :
69+ """Trigger an action event"""
70+ event = ActionEvent (
71+ action_type = action_type ,
72+ description = description ,
73+ timestamp = time .time (),
74+ confidence = confidence ,
75+ details = details ,
76+ impact = impact
77+ )
78+
79+ # Store in history
80+ self .recent_actions .append (event )
81+ if len (self .recent_actions ) > self .max_history :
82+ self .recent_actions .pop (0 )
83+
84+ # Call registered hooks
85+ if action_type in self .hooks :
86+ for callback in self .hooks [action_type ]:
87+ if asyncio .iscoroutinefunction (callback ):
88+ await callback (event )
89+ else :
90+ callback (event )
91+
92+ def _display_action (self , event : ActionEvent ) -> None :
93+ """Default display hook for actions"""
94+ if not self .verbose :
95+ return
96+
97+ # Format the action notification
98+ icon = event .action_type .value
99+ timestamp = datetime .fromtimestamp (event .timestamp ).strftime ("%H:%M:%S" )
100+
101+ # Build the message
102+ message = f"\n { icon } { event .description } "
103+
104+ if event .confidence > 0 :
105+ conf_bar = self ._confidence_bar (event .confidence )
106+ message += f" { conf_bar } "
107+
108+ if event .impact :
109+ message += f"\n Impact: { event .impact } "
110+
111+ if event .details and self .verbose :
112+ for key , value in event .details .items ():
113+ message += f"\n { key } : { value } "
114+
115+ print (message )
116+
117+ def _confidence_bar (self , confidence : float ) -> str :
118+ """Create a visual confidence bar"""
119+ filled = int (confidence * 10 )
120+ empty = 10 - filled
121+ return f"[{ '█' * filled } { '░' * empty } ] { confidence :.0%} "
122+
123+ # Convenience methods for common actions
124+
125+ async def skill_learned (self , skill : str , source : str = "interaction" ) -> None :
126+ """Notify that a new skill was learned"""
127+ await self .trigger_action (
128+ ActionType .SKILL_LEARNED ,
129+ f"New skill learned: { skill } " ,
130+ confidence = 0.9 ,
131+ details = {"source" : source },
132+ impact = "Can now handle similar requests more effectively"
133+ )
134+
135+ async def knowledge_acquired (self , topic : str , facts : int = 1 ) -> None :
136+ """Notify that new knowledge was acquired"""
137+ await self .trigger_action (
138+ ActionType .KNOWLEDGE_ACQUIRED ,
139+ f"Knowledge acquired about: { topic } " ,
140+ confidence = 0.85 ,
141+ details = {"facts_learned" : facts },
142+ impact = f"Enhanced understanding of { topic } "
143+ )
144+
145+ async def pattern_recognized (self , pattern : str , occurrences : int = 1 ) -> None :
146+ """Notify that a pattern was recognized"""
147+ await self .trigger_action (
148+ ActionType .PATTERN_RECOGNIZED ,
149+ f"Pattern recognized: { pattern } " ,
150+ confidence = 0.8 ,
151+ details = {"occurrences" : occurrences },
152+ impact = "Improved response prediction"
153+ )
154+
155+ async def optimization_applied (self , optimization : str , speedup : float = 0.0 ) -> None :
156+ """Notify that an optimization was applied"""
157+ impact = f"{ speedup :.1f} x faster" if speedup > 0 else "Improved efficiency"
158+ await self .trigger_action (
159+ ActionType .OPTIMIZATION_APPLIED ,
160+ f"Optimization applied: { optimization } " ,
161+ confidence = 0.95 ,
162+ impact = impact
163+ )
164+
165+ async def context_expanded (self , context_type : str , tokens_added : int = 0 ) -> None :
166+ """Notify that context was expanded"""
167+ await self .trigger_action (
168+ ActionType .CONTEXT_EXPANDED ,
169+ f"Context expanded: { context_type } " ,
170+ confidence = 0.9 ,
171+ details = {"tokens_added" : tokens_added },
172+ impact = "Better understanding of conversation"
173+ )
174+
175+ async def capability_discovered (self , capability : str ) -> None :
176+ """Notify that a new capability was discovered"""
177+ await self .trigger_action (
178+ ActionType .CAPABILITY_DISCOVERED ,
179+ f"New capability discovered: { capability } " ,
180+ confidence = 0.85 ,
181+ impact = "Expanded range of possible actions"
182+ )
183+
184+ async def error_recovered (self , error_type : str , solution : str ) -> None :
185+ """Notify that an error was recovered from"""
186+ await self .trigger_action (
187+ ActionType .ERROR_RECOVERY ,
188+ f"Recovered from { error_type } " ,
189+ confidence = 0.8 ,
190+ details = {"solution" : solution },
191+ impact = "Improved resilience"
192+ )
193+
194+ async def performance_tuned (self , component : str , improvement : str ) -> None :
195+ """Notify that performance was tuned"""
196+ await self .trigger_action (
197+ ActionType .PERFORMANCE_TUNED ,
198+ f"Performance tuned: { component } " ,
199+ confidence = 0.9 ,
200+ details = {"improvement" : improvement },
201+ impact = "Faster response times"
202+ )
203+
204+ def get_recent_actions (self , limit : int = 10 ) -> List [ActionEvent ]:
205+ """Get recent action events"""
206+ return self .recent_actions [- limit :]
207+
208+ def get_statistics (self ) -> Dict [str , Any ]:
209+ """Get statistics about actions"""
210+ stats = {
211+ "total_actions" : len (self .recent_actions ),
212+ "actions_by_type" : {},
213+ "avg_confidence" : 0.0 ,
214+ "last_action" : None
215+ }
216+
217+ if self .recent_actions :
218+ # Count by type
219+ for event in self .recent_actions :
220+ type_name = event .action_type .name
221+ stats ["actions_by_type" ][type_name ] = stats ["actions_by_type" ].get (type_name , 0 ) + 1
222+
223+ # Average confidence
224+ confidences = [e .confidence for e in self .recent_actions if e .confidence > 0 ]
225+ if confidences :
226+ stats ["avg_confidence" ] = sum (confidences ) / len (confidences )
227+
228+ # Last action
229+ last = self .recent_actions [- 1 ]
230+ stats ["last_action" ] = {
231+ "type" : last .action_type .name ,
232+ "description" : last .description ,
233+ "time_ago" : time .time () - last .timestamp
234+ }
235+
236+ return stats
237+
238+
239+ class ThinkingStatus :
240+ """Shows OSA's current thinking/planning status"""
241+
242+ def __init__ (self ):
243+ self .current_thoughts : List [str ] = []
244+ self .planning_stack : List [str ] = []
245+ self .decision_tree : List [Dict [str , Any ]] = []
246+ self .is_thinking = False
247+ self .thinking_start = None
248+
249+ def start_thinking (self , topic : str ) -> None :
250+ """Start a thinking session"""
251+ self .is_thinking = True
252+ self .thinking_start = time .time ()
253+ self .current_thoughts = [topic ]
254+ self ._display_thinking ()
255+
256+ def add_thought (self , thought : str ) -> None :
257+ """Add a thought to current thinking"""
258+ if self .is_thinking :
259+ self .current_thoughts .append (thought )
260+ self ._display_thinking ()
261+
262+ def add_plan (self , plan : str ) -> None :
263+ """Add to planning stack"""
264+ self .planning_stack .append (plan )
265+ self ._display_thinking ()
266+
267+ def add_decision (self , decision : str , alternatives : List [str ], confidence : float ) -> None :
268+ """Add a decision point"""
269+ self .decision_tree .append ({
270+ "decision" : decision ,
271+ "alternatives" : alternatives ,
272+ "confidence" : confidence ,
273+ "timestamp" : time .time ()
274+ })
275+
276+ def end_thinking (self ) -> float :
277+ """End thinking session and return duration"""
278+ if self .is_thinking :
279+ duration = time .time () - self .thinking_start
280+ self .is_thinking = False
281+ self .thinking_start = None
282+ return duration
283+ return 0.0
284+
285+ def _display_thinking (self ) -> None :
286+ """Display current thinking status"""
287+ if not self .is_thinking :
288+ return
289+
290+ print ("\n 💭 OSA's Current Thoughts:" )
291+
292+ # Show primary thought
293+ if self .current_thoughts :
294+ print (f"├─ Primary: \" { self .current_thoughts [- 1 ]} \" " )
295+
296+ # Show secondary thoughts
297+ if len (self .current_thoughts ) > 1 :
298+ for thought in self .current_thoughts [- 3 :- 1 ]:
299+ print (f"├─ Secondary: \" { thought } \" " )
300+
301+ # Show planning
302+ if self .planning_stack :
303+ print (f"├─ Planning: \" { self .planning_stack [- 1 ]} \" " )
304+
305+ # Show background
306+ if len (self .current_thoughts ) > 3 :
307+ print (f"└─ Background: Processing { len (self .current_thoughts ) - 3 } more thoughts" )
308+ else :
309+ print ("└─ Status: Analyzing..." )
310+
311+ def get_status (self ) -> Dict [str , Any ]:
312+ """Get current thinking status"""
313+ return {
314+ "is_thinking" : self .is_thinking ,
315+ "duration" : time .time () - self .thinking_start if self .thinking_start else 0 ,
316+ "thoughts_count" : len (self .current_thoughts ),
317+ "plans_count" : len (self .planning_stack ),
318+ "decisions_made" : len (self .decision_tree ),
319+ "current_focus" : self .current_thoughts [- 1 ] if self .current_thoughts else None
320+ }
321+
322+
323+ # Global instances
324+ _action_hooks = None
325+ _thinking_status = None
326+
327+
328+ def get_action_hooks () -> ActionHooks :
329+ """Get or create the global action hooks instance"""
330+ global _action_hooks
331+ if _action_hooks is None :
332+ _action_hooks = ActionHooks ()
333+ return _action_hooks
334+
335+
336+ def get_thinking_status () -> ThinkingStatus :
337+ """Get or create the global thinking status instance"""
338+ global _thinking_status
339+ if _thinking_status is None :
340+ _thinking_status = ThinkingStatus ()
341+ return _thinking_status
0 commit comments