diff --git a/apps/web/src/app/[locale]/page.tsx b/apps/web/src/app/[locale]/page.tsx index 9c7ee0fbd..e5f5cd3b4 100644 --- a/apps/web/src/app/[locale]/page.tsx +++ b/apps/web/src/app/[locale]/page.tsx @@ -31,12 +31,95 @@ import { IncidentCardGrid, IncidentEmptyState, ThinkingTerminal, - DEMO_DECISION_CHAIN, DualStateIncidentCard, } from '@/components/incident' import { AlertTriangle } from 'lucide-react' import type { IncidentResponse } from '@/lib/api-client' +// ============================================================================= +// Types for AI Decision Chain Display +// ============================================================================= + +interface ReasoningStep { + step: string + reasoning: string + timestamp?: string +} + +interface DecisionChainDisplay { + analysis_type: 'blast_radius' | 'root_cause' | 'action_suggestion' + target_service: string + reasoning_steps: ReasoningStep[] + conclusion: string + confidence: number +} + +// ============================================================================= +// Utility: Convert API proposal_data to DecisionChain format +// ============================================================================= + +function convertToDecisionChain( + incident: IncidentResponse | null | undefined +): DecisionChainDisplay | null { + if (!incident?.decision?.proposal_data) { + return null + } + + const data = incident.decision.proposal_data + const target = incident.affected_services?.[0] || 'unknown-service' + const source = data.source || 'unknown' + const now = new Date().toISOString() + + // 建構推理步驟 (從 API 資料) + const steps: ReasoningStep[] = [ + { + step: 'SIGNAL_RECEIVED', + reasoning: `收到 ${incident.signal_count || 1} 筆告警,影響服務: ${target}`, + timestamp: incident.created_at, + }, + { + step: 'SEVERITY_EVALUATION', + reasoning: `告警等級: ${incident.severity} | 狀態: ${incident.status}`, + }, + { + step: 'AI_ENGINE', + reasoning: `決策引擎: ${source === 'expert_system' ? 'Expert System (規則引擎)' : 'OpenClaw LLM (智能分析)'}`, + }, + ] + + // 加入 AI 推理 (如果有) + if (data.reasoning) { + steps.push({ + step: 'ROOT_CAUSE_ANALYSIS', + reasoning: data.reasoning, + }) + } + + // 加入描述 (如果有) + if (data.description) { + steps.push({ + step: 'ANALYSIS_RESULT', + reasoning: data.description, + }) + } + + // 加入建議動作 + if (data.action || data.kubectl_command) { + steps.push({ + step: 'ACTION_RECOMMENDATION', + reasoning: `建議動作: ${data.action || data.kubectl_command}\n風險等級: ${data.risk_level || 'medium'}`, + }) + } + + return { + analysis_type: 'action_suggestion', + target_service: target, + reasoning_steps: steps, + conclusion: data.action || '等待 AI 分析完成...', + confidence: data.confidence ?? 0.75, + } +} + // ============================================================================= // Utility: Map IncidentResponse to DualStateIncidentCard props // ============================================================================= @@ -236,13 +319,17 @@ export default function Home({ params }: { params: { locale: string } }) { )} - {/* OpenClaw Thinking Terminal (Phase 7: 決策鏈視覺化) */} + {/* OpenClaw Thinking Terminal (Phase 7: 決策鏈視覺化 - 真實 AI 資料) */} 0 ? "thinking" : "healthy"} > 0 ? DEMO_DECISION_CHAIN : null} + decisionChain={ + (incidents?.length || 0) > 0 + ? convertToDecisionChain(incidents?.[0]) + : null + } incidentId={(incidents?.length || 0) > 0 ? incidents?.[0]?.incident_id : undefined} autoPlay={(incidents?.length || 0) > 0} maxHeight="300px"