@@ -25,6 +25,7 @@ def __init__(self, config: AgentConfig):
2525 self .system_prompt = config .system_prompt
2626 self .expertise = config .expertise
2727 self ._skill_runtime = SkillRuntime (config )
28+ self ._last_skill_events : List [Dict [str , Any ]] = []
2829
2930 def _execute_custom_function (self , messages : List [Dict [str , str ]]) -> str :
3031 """Dynamically loads and invokes a custom python function for inference."""
@@ -62,11 +63,20 @@ def _execute_custom_function(self, messages: List[Dict[str, str]]) -> str:
6263 logger .error (f"Error executing custom function '{ func_name } ' in { file_path } : { e } " )
6364 return f"[Error: Custom function failed. Details: { str (e )} ]"
6465
65- def generate_response (self , context_messages : List [Dict [str , str ]], override_params : Optional [Dict [str , Any ]] = None ) -> str :
66+ def consume_last_skill_events (self ) -> List [Dict [str , Any ]]:
67+ """Return and clear structured skill events for the last generation call."""
68+ events = list (self ._last_skill_events )
69+ self ._last_skill_events = []
70+ return events
71+
72+ def generate_response_with_events (
73+ self , context_messages : List [Dict [str , str ]], override_params : Optional [Dict [str , Any ]] = None
74+ ) -> Dict [str , Any ]:
6675 """
67- Generate a response using LiteLLM or a custom function .
76+ Generate a response and structured skill execution events .
6877 """
6978 params = override_params or {}
79+ self ._last_skill_events = []
7080
7181 # Build the system message for this agent
7282 full_system_prompt = self .config .system_prompt
@@ -85,7 +95,8 @@ def generate_response(self, context_messages: List[Dict[str, str]], override_par
8595 messages .extend (context_messages )
8696
8797 if self .model_type == ModelType .CUSTOM_FUNCTION :
88- return self ._execute_custom_function (messages )
98+ content = self ._execute_custom_function (messages )
99+ return {"content" : content , "skill_events" : []}
89100
90101 try :
91102 # LiteLLM handling
@@ -101,7 +112,7 @@ def generate_response(self, context_messages: List[Dict[str, str]], override_par
101112 litellm_params .update (params )
102113 tools = self ._skill_runtime .get_tools ()
103114 if self ._skill_runtime .has_skills and self ._skill_runtime .load_error :
104- return f"[Error: { self ._skill_runtime .load_error } ]"
115+ return { "content" : f"[Error: { self ._skill_runtime .load_error } ]" , "skill_events" : []}
105116 if tools :
106117 litellm_params ["tools" ] = tools
107118 litellm_params ["tool_choice" ] = "auto"
@@ -112,13 +123,17 @@ def generate_response(self, context_messages: List[Dict[str, str]], override_par
112123
113124 if tools and tool_calls :
114125 if len (tool_calls ) > self .config .max_skill_calls_per_turn :
115- return (
116- "[Error: Model requested too many tool calls in one turn "
117- f"({ len (tool_calls )} > { self .config .max_skill_calls_per_turn } )]"
118- )
126+ return {
127+ "content" : (
128+ "[Error: Model requested too many tool calls in one turn "
129+ f"({ len (tool_calls )} > { self .config .max_skill_calls_per_turn } )]"
130+ ),
131+ "skill_events" : [],
132+ }
119133
120134 tool_call_payload : List [Dict [str , Any ]] = []
121135 tool_results : List [Dict [str , Any ]] = []
136+ skill_events : List [Dict [str , Any ]] = []
122137 for tc in tool_calls :
123138 func = getattr (tc , "function" , None )
124139 tool_name = getattr (func , "name" , "" )
@@ -128,6 +143,15 @@ def generate_response(self, context_messages: List[Dict[str, str]], override_par
128143 except Exception : # noqa: BLE001
129144 parsed_args = {}
130145 execution = self ._skill_runtime .execute_tool (tool_name , parsed_args , self .config .timeout )
146+ skill_events .append (
147+ {
148+ "event_type" : "skill_execution" ,
149+ "tool_name" : tool_name ,
150+ "arguments" : parsed_args ,
151+ "result" : execution ,
152+ "ok" : bool (execution .get ("ok" )),
153+ }
154+ )
131155
132156 tc_id = getattr (tc , "id" , f"call_{ len (tool_call_payload )} " )
133157 tool_call_payload .append (
@@ -149,16 +173,28 @@ def generate_response(self, context_messages: List[Dict[str, str]], override_par
149173 second_params = dict (litellm_params )
150174 second_params ["messages" ] = messages
151175 second_response = litellm .completion (** second_params )
152- return (second_response .choices [0 ].message .content or "" ).strip ()
176+ content = (second_response .choices [0 ].message .content or "" ).strip ()
177+ self ._last_skill_events = skill_events
178+ return {"content" : content , "skill_events" : skill_events }
153179
154- return (first_message .content or "" ).strip ()
180+ return { "content" : (first_message .content or "" ).strip (), "skill_events" : []}
155181
156182 except litellm .Timeout as e :
157183 logger .error (f"Timeout logic executed for agent '{ self .name } ' on model '{ self .model } ': { e } " )
158- return f"[Timeout Error: The model '{ self .model } ' took too long to respond ({ self .config .timeout } s)]"
184+ return {
185+ "content" : f"[Timeout Error: The model '{ self .model } ' took too long to respond ({ self .config .timeout } s)]" ,
186+ "skill_events" : [],
187+ }
159188 except Exception as e :
160189 logger .error (f"Error getting response from agent '{ self .name } ' on model '{ self .model } ': { e } " )
161- return f"[Error: Could not generate response. Details: { str (e )} ]"
190+ return {"content" : f"[Error: Could not generate response. Details: { str (e )} ]" , "skill_events" : []}
191+
192+ def generate_response (self , context_messages : List [Dict [str , str ]], override_params : Optional [Dict [str , Any ]] = None ) -> str :
193+ """
194+ Backward-compatible wrapper that returns only human-readable content.
195+ """
196+ result = self .generate_response_with_events (context_messages , override_params = override_params )
197+ return str (result .get ("content" , "" )).strip ()
162198
163199 def __repr__ (self ):
164200 return f"<Agent name={ self .name } model={ self .model } >"
0 commit comments