File size: 2,876 Bytes
8e7f687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 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