MCPcopy Create free account
hub / github.com/diillson/chatcli / AnalyzeIssue

Method AnalyzeIssue

server/handler_analysis.go:51–114  ·  view source on GitHub ↗

AnalyzeIssue uses the LLM to analyze an AIOps issue and return recommendations.

(ctx context.Context, req *pb.AnalyzeIssueRequest)

Source from the content-addressed store, hash-verified

49
50// AnalyzeIssue uses the LLM to analyze an AIOps issue and return recommendations.
51func (h *Handler) AnalyzeIssue(ctx context.Context, req *pb.AnalyzeIssueRequest) (*pb.AnalyzeIssueResponse, error) {
52 if req.IssueName == "" {
53 return nil, status.Errorf(codes.InvalidArgument, "%s", i18n.T("server.analysis.issue_name_required"))
54 }
55
56 llmClient, err := h.getClient(req.Provider, req.Model, "", nil)
57 if err != nil {
58 h.logger.Error(i18n.T("server.analysis.llm_client_failed"), zap.Error(err))
59 return nil, status.Errorf(codes.Internal, "%s", i18n.T("server.analysis.get_client_error", err))
60 }
61
62 prompt := buildAnalysisPrompt(req)
63
64 // NOTE: Do NOT call enrichPrompt() here. The operator sends its own enriched
65 // kubernetes_context (up to 30KB with logs, metrics, GitOps, source code, cascade
66 // analysis) in the RPC request. enrichPrompt() is for interactive CLI sessions only
67 // and would duplicate/conflict with the operator's context.
68 response, err := llmClient.SendPrompt(ctx, prompt, nil, 0)
69 if err != nil {
70 h.logger.Error(i18n.T("server.analysis.llm_failed"), zap.Error(err), zap.String("issue", req.IssueName))
71 return nil, status.Errorf(codes.Internal, "%s", i18n.T("server.analysis.llm_error", err))
72 }
73
74 analysis := parseAnalysisResponse(response)
75
76 // GAP-01 fix: detect when the analysis text describes a containment scenario
77 // ("scale to 0", "stop the bleeding", "containment", "unrecoverable") but the
78 // first proposed action is a diagnostic. The chaos test (2026-05-23) showed
79 // this exact divergence: AIInsight said "Scaling to 0 is the only correct
80 // containment action" while actions[0] was ExecDiagnostic. Logging it as a
81 // warning preserves the LLM's autonomy while making the regression visible
82 // in operator logs; reordering happens downstream via the agentic guard.
83 if firstActionDivergesFromAnalysis(analysis) {
84 h.logger.Warn(i18n.T("server.analysis.action_diverges_from_analysis"),
85 zap.String("issue", req.IssueName),
86 zap.String("analysis_preview", truncate(analysis.Analysis, 200)),
87 zap.String("first_action", firstActionType(analysis.Actions)))
88 }
89
90 provider := req.Provider
91 if provider == "" {
92 provider = h.defaultProvider
93 }
94
95 // Map parsed actions to proto SuggestedAction
96 suggestedActions := make([]*pb.SuggestedAction, 0, len(analysis.Actions))
97 for _, a := range analysis.Actions {
98 suggestedActions = append(suggestedActions, &pb.SuggestedAction{
99 Name: a.Name,
100 Action: a.Action,
101 Description: a.Description,
102 Params: a.Params,
103 })
104 }
105
106 return &pb.AnalyzeIssueResponse{
107 Analysis: analysis.Analysis,
108 Confidence: analysis.Confidence,

Callers

nothing calls this directly

Calls 13

getClientMethod · 0.95
TFunction · 0.92
buildAnalysisPromptFunction · 0.85
parseAnalysisResponseFunction · 0.85
firstActionTypeFunction · 0.85
ErrorfMethod · 0.80
WarnMethod · 0.80
truncateFunction · 0.70
ErrorMethod · 0.65
SendPromptMethod · 0.65
GetModelNameMethod · 0.65

Tested by

no test coverage detected