返回 DeepSeek-Reasonix
tool_result.go
根目录 / internal / control / tool_result.go
1 package control
2
3 import (
4 "slices"
5
6 "reasonix/internal/event"
7 "reasonix/internal/provider"
8 )
9
10 // Bind stable local message IDs before publishing a result. Provider call IDs
11 // can be reused in later turns; an ambiguous source stays unavailable.
12 func bindCompletionLogSources(receipt *event.CompletionReceipt, messages []provider.Message) *event.CompletionReceipt {
13 if receipt == nil {
14 return nil
15 }
16 out := *receipt
17 out.Verifications = append([]event.ReceiptVerification(nil), receipt.Verifications...)
18 sources := make(map[string]string)
19 for _, message := range messages {
20 if message.Role != provider.RoleTool || message.ToolCallID == "" {
21 continue
22 }
23 if _, exists := sources[message.ToolCallID]; exists {
24 sources[message.ToolCallID] = ""
25 } else {
26 sources[message.ToolCallID] = message.ID
27 }
28 }
29 for i := range out.Verifications {
30 out.Verifications[i].ToolResultID = sources[out.Verifications[i].ToolCallID]
31 }
32 return &out
33 }
34
35 // ToolResultData holds the full arguments and output for one tool call, loaded
36 // on demand when a frontend expands a collapsed tool card.
37 type ToolResultData struct {
38 Name string `json:"name"`
39 Args string `json:"args"`
40 Output string `json:"output"`
41 Execution *provider.ToolExecution `json:"execution,omitempty"`
42 // MCPApp is the optional Apps presentation for inline rendering.
43 MCPApp *provider.MCPAppPresentation `json:"mcpApp,omitempty"`
44 PresentedFiles []provider.PresentedFile `json:"presentedFiles,omitempty"`
45 }
46
47 // ToolResult looks up a tool call by its ID in the session history and returns
48 // the full arguments + output that were elided from the frontend's items[].
49 // Returns nil when the tool ID isn't found (e.g. a sub-agent's tool call that
50 // lives in a different session).
51 func (c *Controller) ToolResult(toolID string) *ToolResultData {
52 if c.executor == nil {
53 return nil
54 }
55 return lookupToolResult(c.executor.Session().Snapshot(), toolID)
56 }
57
58 func lookupToolResult(msgs []provider.Message, toolID string) *ToolResultData {
59 if toolID == "" {
60 return nil
61 }
62 // Search backwards: tool result first (most recent), then find the args
63 // from the preceding assistant turn.
64 for i, msg := range slices.Backward(msgs) {
65 if msg.Role != provider.RoleTool || msg.ToolCallID != toolID {
66 continue
67 }
68 out := &ToolResultData{
69 Name: msg.Name,
70 Args: "",
71 Output: msg.Content,
72 Execution: msg.ToolExecution,
73 MCPApp: msg.MCPApp,
74 PresentedFiles: provider.PresentedFileList(msg.PresentedFiles),
75 }
76 // Walk back to find the assistant turn that issued this call.
77 for j := i; j >= 0; j-- {
78 if msgs[j].Role != provider.RoleAssistant {
79 continue
80 }
81 for _, tc := range msgs[j].ToolCalls {
82 if tc.ID == toolID {
83 out.Args = tc.Arguments
84 return out
85 }
86 }
87 }
88 return out
89 }
90 for _, msg := range slices.Backward(msgs) {
91 if msg.Role != provider.RoleAssistant {
92 continue
93 }
94 for _, search := range msg.ServerSearch {
95 if search.ID != toolID {
96 continue
97 }
98 return &ToolResultData{
99 Args: provider.FormatServerSearchArgs(search.Query),
100 Output: provider.ServerSearchDisplayOutput(search),
101 }
102 }
103 }
104 return nil
105 }
106
106 lines GO