Skip to content

feat(tui): improve tool event handling with fallback matching and upd…#40

Merged
iohub merged 1 commit into
mainfrom
feat-openai-go
May 6, 2026
Merged

feat(tui): improve tool event handling with fallback matching and upd…#40
iohub merged 1 commit into
mainfrom
feat-openai-go

Conversation

@iohub
Copy link
Copy Markdown
Owner

@iohub iohub commented May 6, 2026

…ated formatting

Summary by Sourcery

Improve TUI handling and rendering of tool events, adding fallback matching by tool name and modernizing standalone tool result display.

New Features:

  • Add fallback matching of tool result events to running tool entries by tool name when no call ID match is found.

Enhancements:

  • Update standalone tool result log entries to use the new-style ToolEntry rendering, including proper success and error status handling.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 6, 2026

Reviewer's Guide

Adds fallback matching of tool result events by tool name when call IDs are missing or mismatched, and ensures standalone tool result log entries use the new ToolEntry-based rendering instead of the legacy emoji format.

Sequence diagram for tool result event handling with ID and name fallback

sequenceDiagram
    participant EventSource
    participant TeaProgram
    participant Model
    participant ToolEntryMap as ToolCallEntries
    participant LogEntries

    EventSource->>TeaProgram: tool_result MessageEvent
    TeaProgram->>Model: Update(msg)

    Model->>Model: getToolCallIDFromEventContent(msg.event.Content)
    alt call_id_found_and_matches_running_entry
        Model->>ToolEntryMap: lookup by call_id
        Model->>ToolEntryMap: update ToolEntry.SetResult(ToolResultInfo)
        Model->>LogEntries: findLogEntryByToolCallID
        Model->>LogEntries: update content and isToolRunning
        Model->>ToolEntryMap: delete entry by call_id
        Model->>Model: updateActiveAnim()
        Model->>Model: buildViewportContent()
        Model-->>TeaProgram: updated model, listenForEvents, tickCmd
    else no_matching_call_id
        Model->>Model: getToolNameFromEventContent(msg.event.Content)
        alt tool_name_found_and_running_entry_exists
            Model->>ToolEntryMap: findRunningEntryByName(tool_name)
            Model->>ToolEntryMap: update matchedEntry.SetResult(ToolResultInfo)
            Model->>LogEntries: findLogEntryByToolCallID(matchedID)
            Model->>LogEntries: update content and isToolRunning
            Model->>ToolEntryMap: delete entry by matchedID
            Model->>Model: updateActiveAnim()
            Model->>Model: buildViewportContent()
            Model-->>TeaProgram: updated model, listenForEvents, tickCmd
        else no_tool_name_match
            Model->>Model: formatEventAsEntry(event)
            Model-->>TeaProgram: updated model, listenForEvents, tickCmd
        end
    end
Loading

Updated class diagram for tool event and log entry handling

classDiagram
    class Model {
        map~string,ToolEntry~ toolCallEntries
        slice~LogEntry~ logEntries
        Update(msg tea_Msg) tea_Model
        updateActiveAnim()
        buildViewportContent()
    }

    class LogEntry {
        string content
        bool isToolRunning
        string rendered
        string toolCallID
        string toolName
        ToolEntry toolEntry
    }

    class ToolEntry {
        ToolCallInfo Call
        ToolStatus Status
        SetResult(result ToolResultInfo)
    }

    class ToolCallInfo {
        string ID
        string Name
        string Summary
    }

    class ToolResultInfo {
        string ToolCallID
        string Name
        string Content
        bool IsError
    }

    class ToolStatus {
        <<enum>>
        ToolStatusRunning
        ToolStatusOther
    }

    Model "*" --> "*" LogEntry : maintains
    Model "*" --> "*" ToolEntry : tracks_running
    LogEntry --> ToolEntry : optional_toolEntry
    ToolEntry --> ToolCallInfo : has_call
    ToolEntry --> ToolResultInfo : uses_result

    class Helpers {
        getToolCallIDFromEventContent(content interface_any) string
        getToolNameFromEventContent(content interface_any) string
        findRunningEntryByName(entries map_string_ToolEntry, toolName string) string_ToolEntry
        getResultFromEventContent(content interface_any) string
        formatEventAsEntry(event MessageEvent) LogEntry
        findLogEntryByToolCallID(entries slice_LogEntry, id string) int
    }

    Helpers ..> Model : used_by
    Helpers ..> LogEntry : creates_and_updates
    Helpers ..> ToolEntry : queries_and_updates
Loading

File-Level Changes

Change Details Files
Add fallback matching of tool result events to running tool entries using tool name when no matching call ID is found, updating associated log entries and tool state accordingly.
  • When a result event cannot be matched to a running tool by call ID, extract tool_name from the event content and attempt to find a running ToolEntry with the same name.
  • If a matching running entry is found, derive the result content, detect error results by prefix, and call SetResult on the matched ToolEntry with the inferred ToolResultInfo.
  • When updating the matched entry, update the corresponding logEntry (content, isToolRunning, rendered) by locating it via findLogEntryByToolCallID, then remove the entry from toolCallEntries and refresh animations and viewport content before returning from Update.
tui.go
Ensure standalone tool result events are rendered using the new ToolEntry format rather than legacy emoji-based rendering.
  • In formatEventAsEntry, when handling tool_result events, always create a ToolEntry for entries that previously would have been standalone, initializing it with ToolCallInfo based on the log entry’s toolCallID and toolName and a summary from extractToolSummary.
  • Populate the ToolEntry result via SetResult, marking IsError based on whether the content starts with "Error:" so the UI shows the appropriate success/error state using the new rendering pipeline.
tui.go
Introduce helpers to extract tool_name from event content and to find a running ToolEntry by tool name.
  • Add getToolNameFromEventContent to safely extract the tool_name string from event.Content when it is a map.
  • Add findRunningEntryByName to scan toolCallEntries for a ToolEntry whose Call.Name matches the given tool name and whose Status is ToolStatusRunning, returning the corresponding call ID and entry or nil if none is found.
tui.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@iohub iohub merged commit 5d8c299 into main May 6, 2026
1 check passed
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The comment on findRunningEntryByName says it returns the "most recently-added" entry, but iteration over a map is not ordered; if recency matters, consider tracking insertion order (e.g., a slice or timestamp) or adjust the comment to match the actual behavior.
  • The ToolResultInfo construction in formatEventAsEntry duplicates the same struct literal for the error and non-error cases; you could build it once and set IsError from isErr to reduce repetition and keep the two branches in sync.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The comment on `findRunningEntryByName` says it returns the "most recently-added" entry, but iteration over a `map` is not ordered; if recency matters, consider tracking insertion order (e.g., a slice or timestamp) or adjust the comment to match the actual behavior.
- The `ToolResultInfo` construction in `formatEventAsEntry` duplicates the same struct literal for the error and non-error cases; you could build it once and set `IsError` from `isErr` to reduce repetition and keep the two branches in sync.

## Individual Comments

### Comment 1
<location path="tui.go" line_range="1236-1242" />
<code_context>
+		// Create a ToolEntry for standalone results so they use new-style
+		// rendering (✓ read_file · /path/to/file) instead of the legacy
+		// emoji path (✅ read_file).
+		isErr := strings.HasPrefix(entry.content, "Error:")
+		entry.toolEntry = tui.NewToolEntry(tui.ToolCallInfo{
+			ID:      entry.toolCallID,
+			Name:    entry.toolName,
+			Summary: extractToolSummary(entry.toolName, ""),
+		})
+		if isErr {
+			entry.toolEntry.SetResult(tui.ToolResultInfo{
+				ToolCallID: entry.toolCallID,
</code_context>
<issue_to_address>
**suggestion:** The `ToolResultInfo` construction is duplicated and can be simplified using the existing `isErr` variable.

Since `isErr` is already computed, you can avoid duplicating the `ToolResultInfo` literal in both branches by constructing it once, setting `IsError: isErr`, and calling `SetResult` a single time. This removes repetition and makes future changes to the struct safer.
</issue_to_address>

### Comment 2
<location path="tui.go" line_range="1863-1866" />
<code_context>
+	return ""
+}
+
+// findRunningEntryByName finds the most recently-added running entry with the
+// given tool name in the toolCallEntries map. Returns the call ID and the entry.
+func findRunningEntryByName(entries map[string]*tui.ToolEntry, toolName string) (string, *tui.ToolEntry) {
+	for id, entry := range entries {
+		if entry.Call.Name == toolName && entry.Status == tui.ToolStatusRunning {
+			return id, entry
</code_context>
<issue_to_address>
**issue (bug_risk):** Iterating a map does not guarantee returning the most recently-added running entry, conflicting with the function’s documented behavior.

Because Go map iteration order is random, `findRunningEntryByName` may return any matching running entry when multiple calls with the same tool name are active. If true recency is required, consider tracking order explicitly (e.g., an ordered slice of IDs or a timestamp on `ToolEntry`) so you can deterministically select the newest entry, or adjust the contract/callers if any matching running entry is acceptable.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tui.go
Comment on lines +1236 to +1242
isErr := strings.HasPrefix(entry.content, "Error:")
entry.toolEntry = tui.NewToolEntry(tui.ToolCallInfo{
ID: entry.toolCallID,
Name: entry.toolName,
Summary: extractToolSummary(entry.toolName, ""),
})
if isErr {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: The ToolResultInfo construction is duplicated and can be simplified using the existing isErr variable.

Since isErr is already computed, you can avoid duplicating the ToolResultInfo literal in both branches by constructing it once, setting IsError: isErr, and calling SetResult a single time. This removes repetition and makes future changes to the struct safer.

Comment thread tui.go
Comment on lines +1863 to +1866
// findRunningEntryByName finds the most recently-added running entry with the
// given tool name in the toolCallEntries map. Returns the call ID and the entry.
func findRunningEntryByName(entries map[string]*tui.ToolEntry, toolName string) (string, *tui.ToolEntry) {
for id, entry := range entries {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Iterating a map does not guarantee returning the most recently-added running entry, conflicting with the function’s documented behavior.

Because Go map iteration order is random, findRunningEntryByName may return any matching running entry when multiple calls with the same tool name are active. If true recency is required, consider tracking order explicitly (e.g., an ordered slice of IDs or a timestamp on ToolEntry) so you can deterministically select the newest entry, or adjust the contract/callers if any matching running entry is acceptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant