-
Notifications
You must be signed in to change notification settings - Fork 181
Exclude TodoWrite tool from numbering and display #887
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Skip numbering for TodoWrite tool calls in both regular and streaming modes - Filter TodoWrite tools from /show command history - Hide TodoWrite tools from /last command and auto-display output - Maintains correct sequential numbering for other tools
WalkthroughAdjusts tool numbering in tool_calling_llm to skip TodoWrite tools and uses computed tool_name for events; updates interactive UI to filter out TodoWrite tool calls from displays, history, and completions in multiple flows, including streaming and synchronous paths. No public API changes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant LLM as LLM
participant TC as ToolCaller
participant Tool as Tool.invoke
LLM->>TC: tools_to_call (mixed, incl. TodoWrite)
loop For each tool t
TC->>TC: tool_name = t.function.name or t.custom.name
alt tool_name == "TodoWrite"
TC->>Tool: invoke(t, tool_number=None)
else Non-TodoWrite
TC->>Tool: invoke(t, tool_number=offset + non_todo_write_count)
TC->>TC: non_todo_write_count++
end
TC-->>LLM: START_TOOL event with tool_name
end
TC->>TC: offset += non_todo_write_count
TC-->>LLM: results (streaming or sync)
sequenceDiagram
autonumber
participant UI as Interactive UI
participant Resp as LLM Response
participant Hist as History/Completer
Resp-->>UI: response.tool_calls (may include TodoWrite)
UI->>UI: non_todo_write_tools = filter(tool_name != "TodoWrite")
alt non_todo_write_tools empty
UI-->>User: "No displayable tool calls..." (where relevant)
else
UI-->>User: Display panels, counts
UI->>Hist: extend with non_todo_write_tools
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
holmes/core/tool_calling_llm.py (4)
386-401
: Skip-numbering logic for TodoWrite is correct; consider extracting a robust tool_name helper to avoid duplication and edge cases.The conditional increment using non_todo_write_count achieves the intended numbering behavior. However, the tool_name extraction and TodoWrite check are duplicated here and in call_stream(), and the hasattr(...) else t.custom.name branch can still throw if a future SDK shape lacks both attributes. Extract a single helper and use safe getattr to make this future-proof and DRY.
Apply this diff in-place:
- for t in tools_to_call: + 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" + # Derive tool name in a robust way (handles function/custom/unknown) + tool_name = self._get_tool_name_from_call(t) + is_todo_write = tool_name == "TodoWrite"Add this helper once in the class (outside the selected lines), then reuse it in both call() and call_stream():
# Place inside class ToolCallingLLM (e.g., near other @staticmethods) @staticmethod def _get_tool_name_from_call(tool_call: ChatCompletionMessageToolCall) -> str: # Prefer .function.name when available; fall back to .custom.name; else "unknown" fn = getattr(tool_call, "function", None) if fn and getattr(fn, "name", None): return fn.name custom = getattr(tool_call, "custom", None) if custom and getattr(custom, "name", None): return custom.name return "unknown"
709-723
: Duplicate TodoWrite detection and numbering logic in call_stream — reuse the same helper.Same suggestion as in call(): de-duplicate and harden name extraction to avoid attribute errors and keep behavior consistent.
Apply this diff:
- 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" + for t in tools_to_call: # type: ignore + tool_name = self._get_tool_name_from_call(t) + is_todo_write = tool_name == "TodoWrite"
724-736
: Consider including tool_number in START_TOOL stream events for non-TodoWrite tools.This can help stream consumers show consistent numbering during live updates (even if completion order differs from execution order). Keep it omitted for TodoWrite by adding only when not None.
Apply this diff:
- yield StreamMessage( - event=StreamEvents.START_TOOL, - data={"tool_name": tool_name, "id": t.id}, - ) + event_data = {"tool_name": tool_name, "id": t.id} + if tool_num is not None: + event_data["tool_number"] = tool_num + yield StreamMessage( + event=StreamEvents.START_TOOL, + data=event_data, + )
434-435
: Type-hint tool_number as Optional[int] for mypy and caller clarity.This parameter is now semantically optional. Add the type hint to satisfy the project’s typing guideline.
- tool_number=None, + tool_number: Optional[int] = None,holmes/interactive.py (2)
748-759
: Good: TodoWrite calls are hidden from /last; centralize the hidden-set to avoid string duplication.Filtering here achieves the PR goal. To prevent drift, define a constant set of hidden tool names once and reuse it across the file.
Apply this diff in place:
- non_todo_write_tools = [ - tool_call - for tool_call in last_response.tool_calls - if tool_call.tool_name != "TodoWrite" - ] + non_todo_write_tools = [ + tool_call + for tool_call in last_response.tool_calls + if tool_call.tool_name not in HIDDEN_TOOL_NAMES + ]Add this once near the imports (outside the selected lines):
# Tools hidden from numbering/display flows HIDDEN_TOOL_NAMES = {"TodoWrite"}
785-806
: DRY the TodoWrite filtering with a small helper to keep behavior consistent across code paths.The same filter appears here and in handle_last_command and run_interactive_loop. Consider a helper filter_displayable_tool_calls(...) to reduce duplication and future mistakes.
Apply this diff in place:
- non_todo_write_tools = [ - tool_call for tool_call in tool_calls if tool_call.tool_name != "TodoWrite" - ] + non_todo_write_tools = filter_displayable_tool_calls(tool_calls)Add this helper once (outside selected lines):
def filter_displayable_tool_calls(tool_calls: List[ToolCallResult]) -> List[ToolCallResult]: return [tc for tc in tool_calls if tc.tool_name not in HIDDEN_TOOL_NAMES]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
holmes/core/tool_calling_llm.py
(4 hunks)holmes/interactive.py
(3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py
: ALWAYS place Python imports at the top of the file, not inside functions or methods
Type hints are required (project is type-checked with mypy)
Use Ruff for formatting and linting (configured in pyproject.toml)
Files:
holmes/core/tool_calling_llm.py
holmes/interactive.py
🧬 Code graph analysis (2)
holmes/core/tool_calling_llm.py (2)
holmes/core/tracing.py (1)
DummySpan
(101-117)holmes/utils/stream.py (2)
StreamMessage
(18-20)StreamEvents
(10-15)
holmes/interactive.py (1)
holmes/core/tools.py (1)
to_emoji
(36-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: build (3.12)
- GitHub Check: build (3.10)
- GitHub Check: build (3.11)
- GitHub Check: llm_evals
- GitHub Check: Pre-commit checks
🔇 Additional comments (4)
holmes/core/tool_calling_llm.py (2)
420-422
: Offset update matches non-TodoWrite count — numbering stays contiguous across batches.No issues. This ensures the next iteration starts numbering at the correct offset, ignoring TodoWrite calls.
402-410
: Awaiting results from thetool_num
lookup to confirm it’s never zero. This will verify thattool_number=None
is the only falsey case and thus downstream formatting is safe.holmes/interactive.py (2)
761-765
: Usage header correctly reflects only displayable tools.This keeps user-facing counts aligned with /show indexing. No changes needed.
1054-1061
: Confirming /show Indexing ConsistencyBased on the ripgrep output:
- In
holmes/interactive.py
at line 1043,tool_number_offset=len(all_tool_calls_history)
is indeed the only offset passed into the LLM invocation (corresponding to the/show
command) – nothing else in this file mutates or supplies a different offset.- Immediately after receiving
response.tool_calls
, the code filters out"TodoWrite"
and extendsall_tool_calls_history
only with the non-TodoWrite
calls (lines 1057–1060), ensuring the history used for/show
remains aligned with the numbering basis.- In
holmes/core/tool_calling_llm.py
, the defaulttool_number_offset = 0
at line 622 is only ever used when no external offset is supplied (i.e., in contexts outsideinteractive.py
).There are no other occurrences of
tool_number_offset
in the repository. This confirms that:
- The numbering offset for
/show
is always derived from the length of the filtered history.- No hidden or alternate offset sources exist that could desynchronize the displayed indices.
No changes are required. Everything aligns with the intended non-
TodoWrite
numbering.
Before
CleanShot 2025年08月21日 at 18 45 48@2xAfter
CleanShot 2025年08月21日 at 19 04 19@2x