computeToolCallsDiff diffs engine-level tool calls from two LogMetrics values. Returns nil when both metrics have no tool call data.
(m1, m2 *LogMetrics)
| 595 | // computeToolCallsDiff diffs engine-level tool calls from two LogMetrics values. |
| 596 | // Returns nil when both metrics have no tool call data. |
| 597 | func computeToolCallsDiff(m1, m2 *LogMetrics) *ToolCallsDiff { |
| 598 | run1Tools := make(map[string]ToolCallInfo) |
| 599 | run2Tools := make(map[string]ToolCallInfo) |
| 600 | |
| 601 | // aggregateToolCall merges a tool call entry into the map, summing call counts and |
| 602 | // taking the max of size fields to handle duplicate entries across log files. |
| 603 | aggregateToolCall := func(tools map[string]ToolCallInfo, tc ToolCallInfo) { |
| 604 | if existing, ok := tools[tc.Name]; ok { |
| 605 | existing.CallCount += tc.CallCount |
| 606 | if tc.MaxInputSize > existing.MaxInputSize { |
| 607 | existing.MaxInputSize = tc.MaxInputSize |
| 608 | } |
| 609 | if tc.MaxOutputSize > existing.MaxOutputSize { |
| 610 | existing.MaxOutputSize = tc.MaxOutputSize |
| 611 | } |
| 612 | if tc.MaxDuration > existing.MaxDuration { |
| 613 | existing.MaxDuration = tc.MaxDuration |
| 614 | } |
| 615 | tools[tc.Name] = existing |
| 616 | return |
| 617 | } |
| 618 | tools[tc.Name] = tc |
| 619 | } |
| 620 | |
| 621 | if m1 != nil { |
| 622 | for _, tc := range m1.ToolCalls { |
| 623 | aggregateToolCall(run1Tools, tc) |
| 624 | } |
| 625 | } |
| 626 | if m2 != nil { |
| 627 | for _, tc := range m2.ToolCalls { |
| 628 | aggregateToolCall(run2Tools, tc) |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | if len(run1Tools) == 0 && len(run2Tools) == 0 { |
| 633 | return nil |
| 634 | } |
| 635 | |
| 636 | allNames := make(map[string]struct{}) |
| 637 | for k := range run1Tools { |
| 638 | allNames[k] = struct{}{} |
| 639 | } |
| 640 | for k := range run2Tools { |
| 641 | allNames[k] = struct{}{} |
| 642 | } |
| 643 | |
| 644 | sortedNames := sliceutil.SortedKeys(allNames) |
| 645 | |
| 646 | diff := &ToolCallsDiff{} |
| 647 | var run1Total, run2Total int |
| 648 | // Collect bash tools during the main iteration to avoid a second traversal in computeBashCommandsDiff. |
| 649 | bashRun1 := make(map[string]ToolCallInfo) |
| 650 | bashRun2 := make(map[string]ToolCallInfo) |
| 651 | |
| 652 | for _, name := range sortedNames { |
| 653 | tc1, inRun1 := run1Tools[name] |
| 654 | tc2, inRun2 := run2Tools[name] |