Skip to content
Open
Show file tree
Hide file tree
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
46 changes: 37 additions & 9 deletions holmes/core/tool_calling_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,15 +383,29 @@ def call( # type: ignore
perf_timing.measure("pre-tool-calls")
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
futures = []
for tool_index, t in enumerate(tools_to_call, 1):
non_todo_write_count = 0
for t in tools_to_call:
logging.debug(f"Tool to call: {t}")
# Check if this is a TodoWrite tool
tool_name = (
t.function.name if hasattr(t, "function") else t.custom.name
)
is_todo_write = tool_name == "TodoWrite"

# Only assign a tool number to non-TodoWrite tools
if not is_todo_write:
non_todo_write_count += 1
tool_num = tool_number_offset + non_todo_write_count
else:
tool_num = None

futures.append(
executor.submit(
self._invoke_tool,
tool_to_call=t,
previous_tool_calls=tool_calls,
trace_span=trace_span,
tool_number=tool_number_offset + tool_index,
tool_number=tool_num,
)
)

Expand All @@ -403,8 +417,8 @@ def call( # type: ignore

perf_timing.measure(f"tool completed {tool_call_result.tool_name}")

# Update the tool number offset for the next iteration
tool_number_offset += len(tools_to_call)
# Update the tool number offset only for non-TodoWrite tools
tool_number_offset += non_todo_write_count

# Add a blank line after all tools in this batch complete
if tools_to_call:
Expand Down Expand Up @@ -692,19 +706,33 @@ def call_stream(
perf_timing.measure("pre-tool-calls")
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
futures = []
for tool_index, t in enumerate(tools_to_call, 1): # type: ignore
non_todo_write_count = 0
for t in tools_to_call: # type: ignore
# Check if this is a TodoWrite tool
tool_name = (
t.function.name if hasattr(t, "function") else t.custom.name
)
is_todo_write = tool_name == "TodoWrite"

# Only assign a tool number to non-TodoWrite tools
if not is_todo_write:
non_todo_write_count += 1
tool_num = tool_number_offset + non_todo_write_count
else:
tool_num = None

futures.append(
executor.submit(
self._invoke_tool,
tool_to_call=t, # type: ignore
previous_tool_calls=tool_calls,
trace_span=DummySpan(), # Streaming mode doesn't support tracing yet
tool_number=tool_number_offset + tool_index,
tool_number=tool_num,
)
)
yield StreamMessage(
event=StreamEvents.START_TOOL,
data={"tool_name": t.function.name, "id": t.id},
data={"tool_name": tool_name, "id": t.id},
)

for future in concurrent.futures.as_completed(futures):
Expand All @@ -720,8 +748,8 @@ def call_stream(
data=tool_call_result.as_streaming_tool_result_response(),
)

# Update the tool number offset for the next iteration
tool_number_offset += len(tools_to_call)
# Update the tool number offset only for non-TodoWrite tools
tool_number_offset += non_todo_write_count

raise Exception(
f"Too many LLM calls - exceeded max_steps: {i}/{self.max_steps}"
Expand Down
57 changes: 41 additions & 16 deletions holmes/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,10 +745,23 @@ def handle_last_command(
)
return

# Filter out TodoWrite tools from display
non_todo_write_tools = [
tool_call
for tool_call in last_response.tool_calls
if tool_call.tool_name != "TodoWrite"
]

if not non_todo_write_tools:
console.print(
f"[bold {ERROR_COLOR}]No displayable tool calls from the last response (TodoWrite calls are hidden).[/bold {ERROR_COLOR}]"
)
return

console.print(
f"[bold {TOOLS_COLOR}]Used {len(last_response.tool_calls)} tools[/bold {TOOLS_COLOR}]"
f"[bold {TOOLS_COLOR}]Used {len(non_todo_write_tools)} tools[/bold {TOOLS_COLOR}]"
)
for tool_call in last_response.tool_calls:
for tool_call in non_todo_write_tools:
tool_index = find_tool_index_in_history(tool_call, all_tool_calls_history)
preview_output = format_tool_call_output(tool_call, tool_index)
title = f"{tool_call.result.status.to_emoji()} {tool_call.description} -> returned {tool_call.result.return_code}"
Expand All @@ -769,22 +782,28 @@ def display_recent_tool_outputs(
all_tool_calls_history: List[ToolCallResult],
) -> None:
"""Display recent tool outputs in rich panels (for auto-display after responses)."""
console.print(
f"[bold {TOOLS_COLOR}]Used {len(tool_calls)} tools[/bold {TOOLS_COLOR}]"
)
for tool_call in tool_calls:
tool_index = find_tool_index_in_history(tool_call, all_tool_calls_history)
preview_output = format_tool_call_output(tool_call, tool_index)
title = f"{tool_call.result.status.to_emoji()} {tool_call.description} -> returned {tool_call.result.return_code}"
# Filter out TodoWrite tools from display
non_todo_write_tools = [
tool_call for tool_call in tool_calls if tool_call.tool_name != "TodoWrite"
]

if non_todo_write_tools:
console.print(
Panel(
preview_output,
padding=(1, 2),
border_style=TOOLS_COLOR,
title=title,
)
f"[bold {TOOLS_COLOR}]Used {len(non_todo_write_tools)} tools[/bold {TOOLS_COLOR}]"
)
for tool_call in non_todo_write_tools:
tool_index = find_tool_index_in_history(tool_call, all_tool_calls_history)
preview_output = format_tool_call_output(tool_call, tool_index)
title = f"{tool_call.result.status.to_emoji()} {tool_call.description} -> returned {tool_call.result.return_code}"

console.print(
Panel(
preview_output,
padding=(1, 2),
border_style=TOOLS_COLOR,
title=title,
)
)


def run_interactive_loop(
Expand Down Expand Up @@ -1032,7 +1051,13 @@ def get_bottom_toolbar():
last_response = response

if response.tool_calls:
all_tool_calls_history.extend(response.tool_calls)
# Filter out TodoWrite tools from the history used for /show command
non_todo_write_tools = [
tool_call
for tool_call in response.tool_calls
if tool_call.tool_name != "TodoWrite"
]
all_tool_calls_history.extend(non_todo_write_tools)
# Update the show completer with the latest tool call history
show_completer.update_history(all_tool_calls_history)

Expand Down
Loading