From 212460289b51fe1ff64c0cf5deb450f4c9b709fe Mon Sep 17 00:00:00 2001 From: teknium1 Date: Tue, 3 Feb 2026 15:26:59 -0800 Subject: [PATCH] Enhance skills tool to have an arg so it is more reliably called, and error handling in agent - Updated the `skills_categories` function to include a `verbose` parameter, allowing users to request skill counts per category. - Modified the `handle_skills_function_call` method to pass the `verbose` argument to `skills_categories`. - Improved error handling in the `AIAgent` class by injecting a recovery message when invalid JSON arguments are detected, guiding users on how to correct their tool calls. - Enhanced the `GatewayRunner` to return a user-friendly error message if the agent fails to generate a final response, improving overall user experience. --- gateway/run.py | 11 ++++++++++- model_tools.py | 12 +++++++++--- run_agent.py | 24 ++++++++++++++---------- tools/skills_tool.py | 3 ++- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/gateway/run.py b/gateway/run.py index 113707f6..b2159e28 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -451,7 +451,16 @@ class GatewayRunner: # TODO: Implement proper history restoration result = agent.run_conversation(message) - return result.get("final_response", "(No response)") + + # Return final response, or a message if something went wrong + final_response = result.get("final_response") + if final_response: + return final_response + elif result.get("error"): + # Agent couldn't recover - show the error + return f"⚠️ {result['error']}" + else: + return "(No response generated)" # Start progress message sender if enabled progress_task = None diff --git a/model_tools.py b/model_tools.py index 0b488557..e78323f6 100644 --- a/model_tools.py +++ b/model_tools.py @@ -406,10 +406,15 @@ def get_skills_tool_definitions() -> List[Dict[str, Any]]: "type": "function", "function": { "name": "skills_categories", - "description": "List available skill categories. Call first if you want to discover categories, then use skills_list(category) to filter, or call skills_list if unsure.", + "description": "List available skill categories. Call this first to discover what skill categories exist, then use skills_list(category) to see skills in a category.", "parameters": { "type": "object", - "properties": {}, + "properties": { + "verbose": { + "type": "boolean", + "description": "If true, include skill counts per category. Default: false." + } + }, "required": [] } } @@ -907,7 +912,8 @@ def handle_skills_function_call(function_name: str, function_args: Dict[str, Any str: Function result as JSON string """ if function_name == "skills_categories": - return skills_categories() + verbose = function_args.get("verbose", False) + return skills_categories(verbose=verbose) elif function_name == "skills_list": category = function_args.get("category") diff --git a/run_agent.py b/run_agent.py index c88d2e60..502e6f60 100644 --- a/run_agent.py +++ b/run_agent.py @@ -1725,16 +1725,20 @@ class AIAgent: # Don't add anything to messages, just retry the API call continue else: - print(f"{self.log_prefix}❌ Max retries (3) for invalid JSON arguments exceeded. Stopping as partial.") - self._invalid_json_retries = 0 # Reset for next conversation - return { - "final_response": None, - "messages": messages, # Messages up to last valid point - "api_calls": api_call_count, - "completed": False, - "partial": True, - "error": f"Model generated invalid JSON arguments for tool '{tool_name}': {error_msg}" - } + # Instead of returning partial, inject a helpful message and let model recover + print(f"{self.log_prefix}⚠️ Injecting recovery message for invalid JSON...") + self._invalid_json_retries = 0 # Reset for next attempt + + # Add a user message explaining the issue + recovery_msg = ( + f"Your tool call to '{tool_name}' had invalid JSON arguments. " + f"Error: {error_msg}. " + f"For tools with no required parameters, use an empty object: {{}}. " + f"Please either retry the tool call with valid JSON, or respond without using that tool." + ) + messages.append({"role": "user", "content": recovery_msg}) + # Continue the loop - model will see this message and can recover + continue # Reset retry counter on successful JSON validation self._invalid_json_retries = 0 diff --git a/tools/skills_tool.py b/tools/skills_tool.py index 258284ad..a275c58d 100644 --- a/tools/skills_tool.py +++ b/tools/skills_tool.py @@ -349,7 +349,7 @@ def _load_category_description(category_dir: Path) -> Optional[str]: return None -def skills_categories(task_id: str = None) -> str: +def skills_categories(verbose: bool = False, task_id: str = None) -> str: """ List available skill categories with descriptions (progressive disclosure tier 0). @@ -358,6 +358,7 @@ def skills_categories(task_id: str = None) -> str: or first paragraph to explain what skills are in that category. Args: + verbose: If True, include skill counts per category (default: False, but currently always included) task_id: Optional task identifier (unused, for API consistency) Returns: