| 141 | } |
| 142 | |
| 143 | func buildLogsObservabilityInsights(processedRuns []ProcessedRun, toolUsage []ToolUsageSummary) []ObservabilityInsight { |
| 144 | if len(processedRuns) == 0 { |
| 145 | return nil |
| 146 | } |
| 147 | |
| 148 | observabilityInsightsLog.Printf("Building logs observability insights: processed_runs=%d tool_usage_entries=%d", len(processedRuns), len(toolUsage)) |
| 149 | insights := make([]ObservabilityInsight, 0, 6) |
| 150 | workflowStats := make(map[string]*workflowObservabilityStats) |
| 151 | writeRuns := 0 |
| 152 | readOnlyRuns := 0 |
| 153 | |
| 154 | for _, pr := range processedRuns { |
| 155 | wfID := workflowIDFromRun(pr.Run.WorkflowPath, pr.Run.WorkflowName) |
| 156 | stats, exists := workflowStats[wfID] |
| 157 | if !exists { |
| 158 | stats = &workflowObservabilityStats{ |
| 159 | workflowName: wfID, |
| 160 | minTurns: pr.Run.Turns, |
| 161 | maxTurns: pr.Run.Turns, |
| 162 | } |
| 163 | workflowStats[wfID] = stats |
| 164 | } |
| 165 | |
| 166 | stats.runs++ |
| 167 | stats.totalTurns += pr.Run.Turns |
| 168 | if stats.runs == 1 || pr.Run.Turns < stats.minTurns { |
| 169 | stats.minTurns = pr.Run.Turns |
| 170 | } |
| 171 | if pr.Run.Turns > stats.maxTurns { |
| 172 | stats.maxTurns = pr.Run.Turns |
| 173 | } |
| 174 | if pr.Run.Conclusion == "failure" { |
| 175 | stats.failures++ |
| 176 | } |
| 177 | if pr.Run.Conclusion == "timed_out" { |
| 178 | stats.timedOuts++ |
| 179 | } |
| 180 | stats.missingTools += len(pr.MissingTools) |
| 181 | stats.mcpFailures += len(pr.MCPFailures) |
| 182 | stats.missingData += len(pr.MissingData) |
| 183 | stats.safeItems += pr.Run.SafeItemsCount |
| 184 | if pr.Run.SafeItemsCount > 0 { |
| 185 | writeRuns++ |
| 186 | } else { |
| 187 | readOnlyRuns++ |
| 188 | } |
| 189 | if pr.FirewallAnalysis != nil { |
| 190 | stats.blocked += pr.FirewallAnalysis.BlockedRequests |
| 191 | stats.totalNet += pr.FirewallAnalysis.TotalRequests |
| 192 | if pr.FirewallAnalysis.BlockedRequests >= firewallBlockedRequestCap { |
| 193 | stats.blockedAtCap = true |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | var failureHotspot *workflowObservabilityStats |
| 199 | for _, stats := range workflowStats { |
| 200 | if stats.failures == 0 { |