Spaces:
Running
Running
# Tool handler extracted from app.py | |
import json | |
import inspect | |
from .implementations import record_user_details, record_resume_gap | |
class ToolHandler: | |
"""Handles tool execution with resilient error handling""" | |
def __init__(self, pushover_service=None): | |
self.pushover_service = pushover_service | |
# Tool implementations with dependency injection | |
self.tool_impl = { | |
"record_user_details": lambda **kwargs: record_user_details(**kwargs, pushover_service=self.pushover_service), | |
"record_resume_gap": lambda **kwargs: record_resume_gap(**kwargs, pushover_service=self.pushover_service), | |
} | |
def _safe_parse_args(self, raw): | |
"""Safely parse tool arguments from various formats""" | |
# Some SDKs already hand a dict; otherwise be forgiving with JSON | |
if isinstance(raw, dict): | |
return raw | |
try: | |
return json.loads(raw or "{}") | |
except Exception: | |
try: | |
return json.loads((raw or "{}").replace("'", '"')) | |
except Exception: | |
print(f"[WARN] Unable to parse tool args: {raw}", flush=True) | |
return {} | |
def handle_tool_calls(self, tool_calls): | |
"""Execute tool calls and return results""" | |
results = [] | |
for tool_call in tool_calls: | |
tool_name = tool_call.function.name | |
raw_args = tool_call.function.arguments or "{}" | |
print(f"[TOOL] {tool_name} args (raw): {raw_args}", flush=True) | |
args = self._safe_parse_args(raw_args) | |
impl = self.tool_impl.get(tool_name) | |
if not impl: | |
print(f"[WARN] Unknown tool: {tool_name}", flush=True) | |
results.append({ | |
"role": "tool", | |
"content": json.dumps({"error": f"unknown tool {tool_name}"}), | |
"tool_call_id": tool_call.id | |
}) | |
continue | |
try: | |
out = impl(**args) | |
except TypeError as e: | |
# Model sent unexpected params; retry with filtered args | |
sig = inspect.signature(impl) | |
filtered = {k: v for k, v in args.items() if k in sig.parameters} | |
try: | |
out = impl(**filtered) | |
except Exception as e2: | |
print(f"[ERROR] Tool '{tool_name}' failed: {e2}", flush=True) | |
out = {"error": "tool execution failed"} | |
except Exception as e: | |
print(f"[ERROR] Tool '{tool_name}' crashed: {e}", flush=True) | |
out = {"error": "tool execution crashed"} | |
results.append({ | |
"role": "tool", | |
"content": json.dumps(out), | |
"tool_call_id": tool_call.id | |
}) | |
return results |