Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion holmes/core/tool_calling_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,21 @@ def call_stream(
tool_number=tool_index,
)
)
tool_params = (
json.loads(t.function.arguments) if t.function.arguments else {}
)
tool = self.tool_executor.get_tool_by_name(t.function.name)
description = (
tool.get_parameterized_one_liner(tool_params) if tool else None
)
Comment on lines +701 to +707
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against invalid JSON and custom tool calls in streaming path

Two issues:

  • json.loads on t.function.arguments is unguarded and can raise, breaking streaming.
  • Assumes t.function exists; custom tool calls (non-function) will raise AttributeError. _invoke_tool correctly guards via hasattr; mirror that here.

Apply this safer parsing and resolution:

-                    tool_params = (
-                        json.loads(t.function.arguments) if t.function.arguments else {}
-                    )
-                    tool = self.tool_executor.get_tool_by_name(t.function.name)
-                    description = (
-                        tool.get_parameterized_one_liner(tool_params) if tool else None
-                    )
+                    tool_name = None
+                    tool_params = None
+                    description = None
+                    if hasattr(t, "function"):
+                        tool_name = t.function.name
+                        args_str = getattr(t.function, "arguments", None)
+                        if args_str:
+                            try:
+                                parsed = json.loads(args_str)
+                                tool_params = parsed if isinstance(parsed, dict) else {}
+                            except Exception:
+                                logging.warning(
+                                    f"Failed to parse arguments for tool: {tool_name}. args: {args_str}"
+                                )
+                                tool_params = {}
+                        tool_obj = self.tool_executor.get_tool_by_name(tool_name)
+                        if tool_obj:
+                            try:
+                                description = tool_obj.get_parameterized_one_liner(tool_params or {})
+                            except Exception:
+                                logging.exception(
+                                    f"Failed to compute parameterized description for tool: {tool_name}"
+                                )
+                    else:
+                        logging.error(f"Unsupported custom tool call: {t}")
+                        tool_name = "unknown"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tool_params = (
json.loads(t.function.arguments) if t.function.arguments else {}
)
tool = self.tool_executor.get_tool_by_name(t.function.name)
description = (
tool.get_parameterized_one_liner(tool_params) if tool else None
)
tool_name = None
tool_params = None
description = None
if hasattr(t, "function"):
tool_name = t.function.name
args_str = getattr(t.function, "arguments", None)
if args_str:
try:
parsed = json.loads(args_str)
tool_params = parsed if isinstance(parsed, dict) else {}
except Exception:
logging.warning(
f"Failed to parse arguments for tool: {tool_name}. args: {args_str}"
)
tool_params = {}
tool_obj = self.tool_executor.get_tool_by_name(tool_name)
if tool_obj:
try:
description = tool_obj.get_parameterized_one_liner(tool_params or {})
except Exception:
logging.exception(
f"Failed to compute parameterized description for tool: {tool_name}"
)
else:
logging.error(f"Unsupported custom tool call: {t}")
tool_name = "unknown"

yield StreamMessage(
event=StreamEvents.START_TOOL,
data={"tool_name": t.function.name, "id": t.id},
data={
"tool_name": t.function.name,
"id": t.id,
"params": tool_params,
"description": description,
},
Comment on lines +710 to +715
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use computed tool_name and normalized params in START_TOOL payload

Follow-up to the parsing fix: rely on the precomputed variables and avoid direct t.function access. Also normalize params to a dict to keep payload shape stable.

-                        data={
-                            "tool_name": t.function.name,
-                            "id": t.id,
-                            "params": tool_params,
-                            "description": description,
-                        },
+                        data={
+                            "tool_name": tool_name,
+                            "id": t.id,
+                            "params": tool_params or {},
+                            "description": description,
+                        },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data={
"tool_name": t.function.name,
"id": t.id,
"params": tool_params,
"description": description,
},
data={
"tool_name": tool_name,
"id": t.id,
"params": tool_params or {},
"description": description,
},
🤖 Prompt for AI Agents
In holmes/core/tool_calling_llm.py around lines 710 to 715, the START_TOOL
payload is using t.function.name and raw tool_params directly; update it to use
the precomputed tool_name variable and a normalized params dict (e.g., ensure
params are converted/validated to a plain dict called normalized_params) so the
payload shape stays stable; replace "tool_name": t.function.name with
"tool_name": tool_name and "params": tool_params with "params":
normalized_params (or ensure tool_params is normalized to a dict before use).

)

for future in concurrent.futures.as_completed(futures):
Expand Down
Loading