diff --git a/frigate/api/chat.py b/frigate/api/chat.py index 10d8d5c99..e67bd5b70 100644 --- a/frigate/api/chat.py +++ b/frigate/api/chat.py @@ -398,9 +398,20 @@ async def _execute_tool_internal( elif tool_name == "get_live_context": camera = arguments.get("camera") if not camera: + logger.error( + "Tool get_live_context failed: camera parameter is required. " + "Arguments: %s", + json.dumps(arguments), + ) return {"error": "Camera parameter is required"} return await _execute_get_live_context(request, camera, allowed_cameras) else: + logger.error( + "Tool call failed: unknown tool %r. Expected one of: search_objects, get_live_context. " + "Arguments received: %s", + tool_name, + json.dumps(arguments), + ) return {"error": f"Unknown tool: {tool_name}"} @@ -425,6 +436,14 @@ async def _execute_pending_tools( tool_result = await _execute_tool_internal( tool_name, tool_args, request, allowed_cameras ) + if isinstance(tool_result, dict) and tool_result.get("error"): + logger.error( + "Tool call %s (id: %s) returned error: %s. Arguments: %s", + tool_name, + tool_call_id, + tool_result.get("error"), + json.dumps(tool_args), + ) if tool_name == "search_objects" and isinstance(tool_result, list): tool_result = _format_events_with_local_time(tool_result) _keys = { @@ -459,7 +478,11 @@ async def _execute_pending_tools( ) except Exception as e: logger.error( - f"Error executing tool {tool_name} (id: {tool_call_id}): {e}", + "Error executing tool %s (id: %s): %s. Arguments: %s", + tool_name, + tool_call_id, + e, + json.dumps(tool_args), exc_info=True, ) error_content = json.dumps({"error": f"Tool execution failed: {str(e)}"}) diff --git a/frigate/genai/llama_cpp.py b/frigate/genai/llama_cpp.py index 5b3569ea7..bed742756 100644 --- a/frigate/genai/llama_cpp.py +++ b/frigate/genai/llama_cpp.py @@ -292,19 +292,24 @@ class LlamaCppClient(GenAIClient): yield ("content_delta", delta["content"]) for tc in delta.get("tool_calls") or []: idx = tc.get("index", 0) + fn = tc.get("function") or {} if idx not in tool_calls_by_index: tool_calls_by_index[idx] = { "id": tc.get("id", ""), - "name": tc.get("name", ""), + "name": tc.get("name") or fn.get("name", ""), "arguments": "", } t = tool_calls_by_index[idx] if tc.get("id"): t["id"] = tc["id"] - if tc.get("name"): - t["name"] = tc["name"] - if tc.get("arguments"): - t["arguments"] += tc["arguments"] + name = tc.get("name") or fn.get("name") + if name: + t["name"] = name + arg = tc.get("arguments") or fn.get("arguments") + if arg is not None: + t["arguments"] += ( + arg if isinstance(arg, str) else json.dumps(arg) + ) full_content = "".join(content_parts).strip() or None tool_calls_list = self._streamed_tool_calls_to_list(tool_calls_by_index)