@kb-labs/studio-app 0.7.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +1 -0
- package/dist/main.80b70197.css +2 -0
- package/dist/main.80b70197.css.map +1 -0
- package/dist/main.ad42ef4a.js +522 -0
- package/dist/main.ad42ef4a.js.map +1 -0
- package/dist/mf-manifest.json +241 -0
- package/dist/mf-stats.json +271 -0
- package/package.json +56 -39
- package/server.js +68 -0
- package/.env.example +0 -10
- package/UI-KIT-COMPONENTS.md +0 -783
- package/UI-KIT-PLAN.md +0 -326
- package/eslint-rules/no-direct-antd-import.js +0 -151
- package/eslint.config.ts +0 -27
- package/index.html +0 -17
- package/postcss.config.js +0 -7
- package/public/favicon.ico +0 -0
- package/rspack.config.mjs +0 -118
- package/src/App.tsx +0 -46
- package/src/components/devtools/devtools-log-feed.module.css +0 -165
- package/src/components/devtools/devtools-log-feed.tsx +0 -300
- package/src/components/devtools/devtools-panel.module.css +0 -121
- package/src/components/devtools/devtools-panel.tsx +0 -115
- package/src/components/empty-state.tsx +0 -28
- package/src/components/error-boundary.tsx +0 -210
- package/src/components/health-banner.module.css +0 -103
- package/src/components/health-banner.tsx +0 -134
- package/src/components/health-indicator.tsx +0 -12
- package/src/components/page-header.tsx +0 -49
- package/src/components/page-transition.tsx +0 -24
- package/src/components/role-switcher.tsx +0 -148
- package/src/components/run-badge.tsx +0 -13
- package/src/components/ui/icons.tsx +0 -509
- package/src/components/ui/index.ts +0 -25
- package/src/components/ui/kb-config-provider.tsx +0 -130
- package/src/components/ui/kb-header.module.css +0 -51
- package/src/components/ui/kb-header.tsx +0 -112
- package/src/components/ui/kb-notification-bell.module.css +0 -103
- package/src/components/ui/kb-notification-bell.tsx +0 -199
- package/src/components/ui/kb-page-layout.tsx +0 -66
- package/src/components/ui/kb-quick-search.module.css +0 -86
- package/src/components/ui/kb-quick-search.tsx +0 -196
- package/src/components/ui/kb-sidebar.module.css +0 -47
- package/src/components/ui/kb-sidebar.tsx +0 -196
- package/src/components/ui/kb-stat-card.module.css +0 -21
- package/src/components/ui/kb-stat-card.tsx +0 -22
- package/src/components/ui/kb-stat-grid.tsx +0 -22
- package/src/components/ui/kb-status-bar.module.css +0 -49
- package/src/components/ui/kb-status-bar.tsx +0 -145
- package/src/components/ui/kb-system-health-indicator.tsx +0 -229
- package/src/components/ui/kb-theme-toggle.tsx +0 -42
- package/src/components/ui/theme-adapter.ts +0 -812
- package/src/components/ui/theme-transition-overlay.tsx +0 -70
- package/src/components/ui/utils.ts +0 -6
- package/src/components/widget-error-boundary.tsx +0 -172
- package/src/config/env.ts +0 -5
- package/src/config/feature-flags.ts +0 -482
- package/src/config/studio.config.schema.ts +0 -88
- package/src/config/studio.config.ts +0 -84
- package/src/env.d.ts +0 -23
- package/src/hooks/use-command-palette.ts +0 -6
- package/src/hooks/use-devtools-store.ts +0 -25
- package/src/hooks/use-feature-flags.ts +0 -101
- package/src/index.css +0 -10
- package/src/layouts/dashboard-grid.tsx +0 -122
- package/src/layouts/index.ts +0 -12
- package/src/layouts/two-pane.tsx +0 -134
- package/src/main.tsx +0 -15
- package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +0 -257
- package/src/modules/analytics/components/analytics-date-range-picker.tsx +0 -121
- package/src/modules/analytics/pages/analytics-cache-page.tsx +0 -285
- package/src/modules/analytics/pages/analytics-embeddings-page.tsx +0 -274
- package/src/modules/analytics/pages/analytics-events-page.tsx +0 -425
- package/src/modules/analytics/pages/analytics-llm-page.tsx +0 -434
- package/src/modules/analytics/pages/analytics-overview-page.tsx +0 -336
- package/src/modules/analytics/pages/analytics-page.tsx +0 -20
- package/src/modules/analytics/pages/analytics-storage-page.tsx +0 -344
- package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +0 -279
- package/src/modules/analytics/routes/index.tsx +0 -133
- package/src/modules/analytics/utils/formatters.test.ts +0 -148
- package/src/modules/analytics/utils/formatters.ts +0 -100
- package/src/modules/auth/pages/login-page.module.css +0 -113
- package/src/modules/auth/pages/login-page.tsx +0 -80
- package/src/modules/auth/routes/index.tsx +0 -24
- package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +0 -199
- package/src/modules/dashboard/components/AiInsightsWidget.tsx +0 -358
- package/src/modules/dashboard/components/AnimatedMetricCard.css +0 -88
- package/src/modules/dashboard/components/AnimatedMetricCard.tsx +0 -125
- package/src/modules/dashboard/components/HeroMetricCard.css +0 -114
- package/src/modules/dashboard/components/HeroMetricCard.tsx +0 -152
- package/src/modules/dashboard/components/HeroMetricsWidget.tsx +0 -146
- package/src/modules/dashboard/components/SystemResourcesWidget.tsx +0 -197
- package/src/modules/dashboard/components/dashboard-empty.tsx +0 -22
- package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +0 -59
- package/src/modules/dashboard/components/dashboard-kpis.tsx +0 -13
- package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +0 -85
- package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +0 -78
- package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +0 -67
- package/src/modules/dashboard/components/dashboard-requests-widget.tsx +0 -62
- package/src/modules/dashboard/layouts/dashboard-layout.tsx +0 -16
- package/src/modules/dashboard/pages/ai-insights-page.tsx +0 -563
- package/src/modules/dashboard/pages/dashboard-animations.css +0 -317
- package/src/modules/dashboard/pages/dashboard-page.tsx +0 -13
- package/src/modules/dashboard/routes/index.tsx +0 -70
- package/src/modules/observability/pages/devkit-page.tsx +0 -263
- package/src/modules/observability/pages/incident-detail-page.tsx +0 -571
- package/src/modules/observability/pages/incidents-page.tsx +0 -370
- package/src/modules/observability/pages/log-detail-page.tsx +0 -508
- package/src/modules/observability/pages/logs-page.tsx +0 -1129
- package/src/modules/observability/pages/prometheus-metrics-page.tsx +0 -486
- package/src/modules/observability/pages/state-broker-page.tsx +0 -229
- package/src/modules/observability/pages/system-events-page.tsx +0 -255
- package/src/modules/observability/routes/index.tsx +0 -133
- package/src/modules/plugins/components/marketplace-card.tsx +0 -153
- package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +0 -374
- package/src/modules/plugins/components/plugin-card.tsx +0 -136
- package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +0 -101
- package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +0 -101
- package/src/modules/plugins/components/plugin-detail/index.ts +0 -7
- package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +0 -64
- package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +0 -30
- package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +0 -68
- package/src/modules/plugins/components/plugin-detail/permissions/index.ts +0 -6
- package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +0 -43
- package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +0 -242
- package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +0 -96
- package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +0 -55
- package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +0 -47
- package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +0 -169
- package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +0 -159
- package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +0 -40
- package/src/modules/plugins/components/plugin-grid.tsx +0 -20
- package/src/modules/plugins/components/plugin-table.tsx +0 -178
- package/src/modules/plugins/pages/marketplace-page.tsx +0 -200
- package/src/modules/plugins/pages/plugin-detail-page.tsx +0 -1145
- package/src/modules/plugins/routes/index.tsx +0 -48
- package/src/modules/plugins/utils/index.ts +0 -1
- package/src/modules/plugins/utils/schema-helpers.ts +0 -21
- package/src/modules/settings/components/api-routes-viewer.tsx +0 -415
- package/src/modules/settings/components/appearance-settings.tsx +0 -200
- package/src/modules/settings/components/configuration-settings.tsx +0 -321
- package/src/modules/settings/components/data-privacy-settings.tsx +0 -111
- package/src/modules/settings/components/experimental-settings.tsx +0 -243
- package/src/modules/settings/components/navigation-settings.tsx +0 -198
- package/src/modules/settings/components/notifications-settings.tsx +0 -40
- package/src/modules/settings/pages/settings-page.tsx +0 -298
- package/src/modules/settings/routes/index.tsx +0 -47
- package/src/pages/README-assistant.md +0 -91
- package/src/pages/login-page.tsx +0 -177
- package/src/pages/not-found-page.tsx +0 -85
- package/src/providers/auth-provider.tsx +0 -40
- package/src/providers/command-palette-provider.tsx +0 -193
- package/src/providers/data-sources-provider.tsx +0 -27
- package/src/providers/devtools-provider.tsx +0 -72
- package/src/providers/navigation-items-provider.tsx +0 -38
- package/src/providers/registry-v2-provider.tsx +0 -93
- package/src/providers/settings-provider.tsx +0 -158
- package/src/router.tsx +0 -302
- package/src/routes/helpers.tsx +0 -25
- package/src/routes/plugin-page-v2.tsx +0 -52
- package/src/routes/types.ts +0 -18
- package/src/styles/antd-overrides.css +0 -211
- package/src/styles/base.css +0 -190
- package/src/styles/settings.css +0 -75
- package/src/styles/variables.css +0 -94
- package/src/utils/analytics.ts +0 -183
- package/src/utils/apply-navigation-categories.ts +0 -83
- package/src/utils/commands.ts +0 -252
- package/src/utils/export.ts +0 -174
- package/src/utils/format-date.ts +0 -166
- package/src/utils/logger.ts +0 -110
- package/tailwind.config.ts +0 -13
- package/tsconfig.json +0 -13
- package/vitest-setup.ts +0 -1
- /package/{public → dist}/favicon.svg +0 -0
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
UICard,
|
|
4
|
-
UIInputTextArea,
|
|
5
|
-
UIButton,
|
|
6
|
-
UITypographyText,
|
|
7
|
-
UITypographyParagraph,
|
|
8
|
-
UISpace,
|
|
9
|
-
UISpaceCompact,
|
|
10
|
-
UITag,
|
|
11
|
-
UISpin,
|
|
12
|
-
UIStatistic,
|
|
13
|
-
UICheckbox,
|
|
14
|
-
UISelect,
|
|
15
|
-
UITooltip,
|
|
16
|
-
UIMessage,
|
|
17
|
-
} from '@kb-labs/studio-ui-kit';
|
|
18
|
-
import {
|
|
19
|
-
SendOutlined,
|
|
20
|
-
RobotOutlined,
|
|
21
|
-
UserOutlined,
|
|
22
|
-
BulbOutlined,
|
|
23
|
-
CopyOutlined,
|
|
24
|
-
CheckOutlined,
|
|
25
|
-
ReloadOutlined,
|
|
26
|
-
SettingOutlined,
|
|
27
|
-
InfoCircleOutlined,
|
|
28
|
-
} from '@ant-design/icons';
|
|
29
|
-
import { useDataSources } from '../../../providers/data-sources-provider';
|
|
30
|
-
import { usePrometheusMetrics, useIncidents } from '@kb-labs/studio-data-client';
|
|
31
|
-
import { UIMarkdownViewer } from '@kb-labs/studio-ui-kit';
|
|
32
|
-
|
|
33
|
-
interface Message {
|
|
34
|
-
id: string;
|
|
35
|
-
role: 'user' | 'assistant';
|
|
36
|
-
content: string;
|
|
37
|
-
timestamp: number;
|
|
38
|
-
context?: string[];
|
|
39
|
-
thinking?: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
interface ContextConfig {
|
|
43
|
-
includeMetrics: boolean;
|
|
44
|
-
includeIncidents: boolean;
|
|
45
|
-
includeHistory: boolean;
|
|
46
|
-
timeRange: '1h' | '6h' | '24h' | '7d';
|
|
47
|
-
plugins: string[];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const SUGGESTED_QUESTIONS = [
|
|
51
|
-
{ text: 'Why is latency higher than usual?', category: 'Performance' },
|
|
52
|
-
{ text: 'Which plugin should I optimize first?', category: 'Optimization' },
|
|
53
|
-
{ text: 'Are there any anomalies in the last 24 hours?', category: 'Anomalies' },
|
|
54
|
-
{ text: 'What caused the recent error spike?', category: 'Errors' },
|
|
55
|
-
{ text: 'How is the system health compared to yesterday?', category: 'Health' },
|
|
56
|
-
{ text: 'What are the most resource-intensive operations?', category: 'Resources' },
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
const TIME_RANGE_OPTIONS = [
|
|
60
|
-
{ value: '1h', label: 'Last 1 hour' },
|
|
61
|
-
{ value: '6h', label: 'Last 6 hours' },
|
|
62
|
-
{ value: '24h', label: 'Last 24 hours' },
|
|
63
|
-
{ value: '7d', label: 'Last 7 days' },
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* AI Insights Page
|
|
68
|
-
*
|
|
69
|
-
* Full-featured chat interface for AI-powered system analysis.
|
|
70
|
-
* Uses real metrics, incidents, and historical data as context for LLM.
|
|
71
|
-
*/
|
|
72
|
-
export function AIInsightsPage() {
|
|
73
|
-
const sources = useDataSources();
|
|
74
|
-
const { data: metrics, isLoading: metricsLoading } = usePrometheusMetrics(sources.observability);
|
|
75
|
-
const { data: incidents } = useIncidents(sources.observability, { limit: 10 });
|
|
76
|
-
|
|
77
|
-
const [messages, setMessages] = useState<Message[]>([]);
|
|
78
|
-
const [input, setInput] = useState('');
|
|
79
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
80
|
-
const [copiedId, setCopiedId] = useState<string | null>(null);
|
|
81
|
-
const [showConfig, setShowConfig] = useState(false);
|
|
82
|
-
const [contextConfig, setContextConfig] = useState<ContextConfig>({
|
|
83
|
-
includeMetrics: true,
|
|
84
|
-
includeIncidents: true,
|
|
85
|
-
includeHistory: true,
|
|
86
|
-
timeRange: '24h',
|
|
87
|
-
plugins: [],
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
91
|
-
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
92
|
-
|
|
93
|
-
// Auto-scroll to bottom when new messages arrive
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
96
|
-
}, [messages]);
|
|
97
|
-
|
|
98
|
-
// Build context summary for display
|
|
99
|
-
const contextSummary = React.useMemo(() => {
|
|
100
|
-
if (!metrics) {return null;}
|
|
101
|
-
|
|
102
|
-
const errorRate = metrics.requests?.total
|
|
103
|
-
? (((metrics.requests.clientErrors ?? 0) + (metrics.requests.serverErrors ?? 0)) / metrics.requests.total * 100)
|
|
104
|
-
: 0;
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
requests: metrics.requests?.total ?? 0,
|
|
108
|
-
errorRate: errorRate.toFixed(2),
|
|
109
|
-
p99Latency: metrics.latency?.p99?.toFixed(0) ?? 'N/A',
|
|
110
|
-
pluginCount: metrics.perPlugin?.length ?? 0,
|
|
111
|
-
incidentCount: incidents?.length ?? 0,
|
|
112
|
-
};
|
|
113
|
-
}, [metrics, incidents]);
|
|
114
|
-
|
|
115
|
-
// Get available plugins for context filtering
|
|
116
|
-
const availablePlugins = React.useMemo(() => {
|
|
117
|
-
return metrics?.perPlugin?.map(p => p.pluginId) ?? [];
|
|
118
|
-
}, [metrics]);
|
|
119
|
-
|
|
120
|
-
// Generate AI response via backend API
|
|
121
|
-
const generateResponse = useCallback(async (question: string): Promise<string> => {
|
|
122
|
-
const response = await sources.observability.chatWithInsights(question, contextConfig);
|
|
123
|
-
return response.answer;
|
|
124
|
-
}, [sources.observability, contextConfig]);
|
|
125
|
-
|
|
126
|
-
// Send message
|
|
127
|
-
const handleSend = async () => {
|
|
128
|
-
if (!input.trim() || isLoading) {return;}
|
|
129
|
-
|
|
130
|
-
const userMessage: Message = {
|
|
131
|
-
id: `user-${Date.now()}`,
|
|
132
|
-
role: 'user',
|
|
133
|
-
content: input.trim(),
|
|
134
|
-
timestamp: Date.now(),
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const loadingMessage: Message = {
|
|
138
|
-
id: `assistant-${Date.now()}`,
|
|
139
|
-
role: 'assistant',
|
|
140
|
-
content: '',
|
|
141
|
-
timestamp: Date.now(),
|
|
142
|
-
thinking: true,
|
|
143
|
-
context: [
|
|
144
|
-
contextConfig.includeMetrics ? 'metrics' : '',
|
|
145
|
-
contextConfig.includeIncidents ? 'incidents' : '',
|
|
146
|
-
contextConfig.includeHistory ? 'history' : '',
|
|
147
|
-
].filter(Boolean),
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
setMessages(prev => [...prev, userMessage, loadingMessage]);
|
|
151
|
-
setInput('');
|
|
152
|
-
setIsLoading(true);
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
const response = await generateResponse(userMessage.content);
|
|
156
|
-
|
|
157
|
-
setMessages(prev =>
|
|
158
|
-
prev.map(m =>
|
|
159
|
-
m.id === loadingMessage.id
|
|
160
|
-
? { ...m, content: response, thinking: false }
|
|
161
|
-
: m
|
|
162
|
-
)
|
|
163
|
-
);
|
|
164
|
-
} catch (_error) {
|
|
165
|
-
setMessages(prev =>
|
|
166
|
-
prev.map(m =>
|
|
167
|
-
m.id === loadingMessage.id
|
|
168
|
-
? { ...m, content: 'Sorry, I encountered an error analyzing your request. Please try again.', thinking: false }
|
|
169
|
-
: m
|
|
170
|
-
)
|
|
171
|
-
);
|
|
172
|
-
} finally {
|
|
173
|
-
setIsLoading(false);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// Copy message to clipboard
|
|
178
|
-
const handleCopy = async (content: string, id: string) => {
|
|
179
|
-
await navigator.clipboard.writeText(content);
|
|
180
|
-
setCopiedId(id);
|
|
181
|
-
UIMessage.success('Copied to clipboard');
|
|
182
|
-
setTimeout(() => setCopiedId(null), 2000);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
// Handle suggested question click
|
|
186
|
-
const handleSuggestion = (question: string) => {
|
|
187
|
-
setInput(question);
|
|
188
|
-
inputRef.current?.focus();
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// Clear conversation
|
|
192
|
-
const handleClear = () => {
|
|
193
|
-
setMessages([]);
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<div style={{ display: 'flex', gap: 24, height: 'calc(100vh - 280px)', minHeight: 500 }}>
|
|
198
|
-
{/* Main Chat Panel */}
|
|
199
|
-
<UICard
|
|
200
|
-
style={{ flex: 1, display: 'flex', flexDirection: 'column' }}
|
|
201
|
-
styles={{ body: { flex: 1, display: 'flex', flexDirection: 'column', padding: 0, overflow: 'hidden' } }}
|
|
202
|
-
title={
|
|
203
|
-
<UISpace>
|
|
204
|
-
<RobotOutlined />
|
|
205
|
-
<span>AI Assistant</span>
|
|
206
|
-
{metricsLoading && <UISpin size="small" />}
|
|
207
|
-
</UISpace>
|
|
208
|
-
}
|
|
209
|
-
extra={
|
|
210
|
-
<UISpace>
|
|
211
|
-
<UITooltip title="Clear conversation">
|
|
212
|
-
<UIButton icon={<ReloadOutlined />} onClick={handleClear} disabled={messages.length === 0} />
|
|
213
|
-
</UITooltip>
|
|
214
|
-
<UITooltip title="Configure context">
|
|
215
|
-
<UIButton
|
|
216
|
-
icon={<SettingOutlined />}
|
|
217
|
-
onClick={() => setShowConfig(!showConfig)}
|
|
218
|
-
variant={showConfig ? 'primary' : 'default'}
|
|
219
|
-
/>
|
|
220
|
-
</UITooltip>
|
|
221
|
-
</UISpace>
|
|
222
|
-
}
|
|
223
|
-
>
|
|
224
|
-
{/* Messages Area */}
|
|
225
|
-
<div style={{ flex: 1, overflow: 'auto', padding: 16 }}>
|
|
226
|
-
{messages.length === 0 ? (
|
|
227
|
-
<div style={{ textAlign: 'center', padding: '48px 24px' }}>
|
|
228
|
-
<RobotOutlined style={{ fontSize: 48, color: '#bfbfbf', marginBottom: 16 }} />
|
|
229
|
-
<UITypographyText type="secondary" style={{ display: 'block', marginBottom: 24 }}>
|
|
230
|
-
Ask me anything about your system's performance, errors, or health.
|
|
231
|
-
I'll analyze real-time metrics and provide actionable insights.
|
|
232
|
-
</UITypographyText>
|
|
233
|
-
|
|
234
|
-
<div style={{ marginTop: 24 }}>
|
|
235
|
-
<UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>
|
|
236
|
-
<BulbOutlined /> Suggested questions:
|
|
237
|
-
</UITypographyText>
|
|
238
|
-
<UISpace wrap style={{ justifyContent: 'center' }}>
|
|
239
|
-
{SUGGESTED_QUESTIONS.map((q, i) => (
|
|
240
|
-
<UITag
|
|
241
|
-
key={i}
|
|
242
|
-
color="blue"
|
|
243
|
-
style={{ cursor: 'pointer', padding: '4px 12px' }}
|
|
244
|
-
onClick={() => handleSuggestion(q.text)}
|
|
245
|
-
>
|
|
246
|
-
{q.text}
|
|
247
|
-
</UITag>
|
|
248
|
-
))}
|
|
249
|
-
</UISpace>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
) : (
|
|
253
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
254
|
-
{messages.map(msg => (
|
|
255
|
-
<div
|
|
256
|
-
key={msg.id}
|
|
257
|
-
style={{
|
|
258
|
-
display: 'flex',
|
|
259
|
-
justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start',
|
|
260
|
-
}}
|
|
261
|
-
>
|
|
262
|
-
<div
|
|
263
|
-
style={{
|
|
264
|
-
maxWidth: '80%',
|
|
265
|
-
padding: '12px 16px',
|
|
266
|
-
borderRadius: 12,
|
|
267
|
-
background: msg.role === 'user' ? '#1890ff' : '#f5f5f5',
|
|
268
|
-
color: msg.role === 'user' ? '#fff' : '#000',
|
|
269
|
-
}}
|
|
270
|
-
>
|
|
271
|
-
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
|
|
272
|
-
{msg.role === 'assistant' && (
|
|
273
|
-
<RobotOutlined style={{ marginTop: 4, color: '#1890ff' }} />
|
|
274
|
-
)}
|
|
275
|
-
<div style={{ flex: 1 }}>
|
|
276
|
-
{msg.thinking ? (
|
|
277
|
-
<UISpace>
|
|
278
|
-
<UISpin size="small" />
|
|
279
|
-
<UITypographyText type="secondary">Analyzing...</UITypographyText>
|
|
280
|
-
</UISpace>
|
|
281
|
-
) : msg.role === 'assistant' ? (
|
|
282
|
-
<UIMarkdownViewer content={msg.content} className="ai-insights-markdown" />
|
|
283
|
-
) : (
|
|
284
|
-
<UITypographyParagraph
|
|
285
|
-
style={{
|
|
286
|
-
margin: 0,
|
|
287
|
-
whiteSpace: 'pre-wrap',
|
|
288
|
-
color: '#fff',
|
|
289
|
-
}}
|
|
290
|
-
>
|
|
291
|
-
{msg.content}
|
|
292
|
-
</UITypographyParagraph>
|
|
293
|
-
)}
|
|
294
|
-
{msg.context && msg.context.length > 0 && (
|
|
295
|
-
<div style={{ marginTop: 8 }}>
|
|
296
|
-
{msg.context.map(c => (
|
|
297
|
-
<UITag key={c} color="default" style={{ fontSize: 10 }}>
|
|
298
|
-
{c}
|
|
299
|
-
</UITag>
|
|
300
|
-
))}
|
|
301
|
-
</div>
|
|
302
|
-
)}
|
|
303
|
-
</div>
|
|
304
|
-
{msg.role === 'user' && <UserOutlined style={{ marginTop: 4 }} />}
|
|
305
|
-
</div>
|
|
306
|
-
{msg.role === 'assistant' && !msg.thinking && (
|
|
307
|
-
<div style={{ marginTop: 8, textAlign: 'right' }}>
|
|
308
|
-
<UIButton
|
|
309
|
-
variant="text"
|
|
310
|
-
size="small"
|
|
311
|
-
icon={copiedId === msg.id ? <CheckOutlined /> : <CopyOutlined />}
|
|
312
|
-
onClick={() => handleCopy(msg.content, msg.id)}
|
|
313
|
-
style={{ color: '#8c8c8c' }}
|
|
314
|
-
>
|
|
315
|
-
{copiedId === msg.id ? 'Copied' : 'Copy'}
|
|
316
|
-
</UIButton>
|
|
317
|
-
</div>
|
|
318
|
-
)}
|
|
319
|
-
</div>
|
|
320
|
-
</div>
|
|
321
|
-
))}
|
|
322
|
-
<div ref={messagesEndRef} />
|
|
323
|
-
</div>
|
|
324
|
-
)}
|
|
325
|
-
</div>
|
|
326
|
-
|
|
327
|
-
{/* Input Area */}
|
|
328
|
-
<div style={{ padding: 16, borderTop: '1px solid #f0f0f0' }}>
|
|
329
|
-
<UISpaceCompact style={{ width: '100%' }}>
|
|
330
|
-
<UIInputTextArea
|
|
331
|
-
ref={inputRef}
|
|
332
|
-
value={input}
|
|
333
|
-
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setInput(e.target.value)}
|
|
334
|
-
placeholder="Ask about your system..."
|
|
335
|
-
autoSize={{ minRows: 1, maxRows: 4 }}
|
|
336
|
-
onPressEnter={(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
337
|
-
if (!e.shiftKey) {
|
|
338
|
-
e.preventDefault();
|
|
339
|
-
handleSend();
|
|
340
|
-
}
|
|
341
|
-
}}
|
|
342
|
-
style={{ flex: 1 }}
|
|
343
|
-
/>
|
|
344
|
-
<UIButton
|
|
345
|
-
variant="primary"
|
|
346
|
-
icon={<SendOutlined />}
|
|
347
|
-
onClick={handleSend}
|
|
348
|
-
loading={isLoading}
|
|
349
|
-
disabled={!input.trim()}
|
|
350
|
-
>
|
|
351
|
-
Send
|
|
352
|
-
</UIButton>
|
|
353
|
-
</UISpaceCompact>
|
|
354
|
-
<UITypographyText type="secondary" style={{ fontSize: 11, marginTop: 4, display: 'block' }}>
|
|
355
|
-
Press Enter to send, Shift+Enter for new line
|
|
356
|
-
</UITypographyText>
|
|
357
|
-
</div>
|
|
358
|
-
</UICard>
|
|
359
|
-
|
|
360
|
-
{/* Context Panel */}
|
|
361
|
-
<UICard
|
|
362
|
-
title={
|
|
363
|
-
<UISpace>
|
|
364
|
-
<InfoCircleOutlined />
|
|
365
|
-
<span>Context</span>
|
|
366
|
-
</UISpace>
|
|
367
|
-
}
|
|
368
|
-
style={{ width: 320, flexShrink: 0 }}
|
|
369
|
-
styles={{ body: { padding: 16 } }}
|
|
370
|
-
>
|
|
371
|
-
{/* System Summary */}
|
|
372
|
-
{contextSummary && (
|
|
373
|
-
<div style={{ marginBottom: 24 }}>
|
|
374
|
-
<UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>System Summary</UITypographyText>
|
|
375
|
-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
|
376
|
-
<UIStatistic title="Requests" value={contextSummary.requests} valueStyle={{ fontSize: 18 }} />
|
|
377
|
-
<UIStatistic
|
|
378
|
-
title="Error Rate"
|
|
379
|
-
value={contextSummary.errorRate}
|
|
380
|
-
suffix="%"
|
|
381
|
-
valueStyle={{
|
|
382
|
-
fontSize: 18,
|
|
383
|
-
color: parseFloat(contextSummary.errorRate) > 5 ? '#ff4d4f' : parseFloat(contextSummary.errorRate) > 1 ? '#faad14' : '#52c41a',
|
|
384
|
-
}}
|
|
385
|
-
/>
|
|
386
|
-
<UIStatistic title="P99 Latency" value={contextSummary.p99Latency} suffix="ms" valueStyle={{ fontSize: 18 }} />
|
|
387
|
-
<UIStatistic title="Incidents" value={contextSummary.incidentCount} valueStyle={{ fontSize: 18 }} />
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
)}
|
|
391
|
-
|
|
392
|
-
{/* Recent Incidents */}
|
|
393
|
-
{incidents && incidents.length > 0 && (
|
|
394
|
-
<div style={{ marginBottom: 24 }}>
|
|
395
|
-
<UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>Recent Incidents</UITypographyText>
|
|
396
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
397
|
-
{incidents.slice(0, 5).map(incident => (
|
|
398
|
-
<div
|
|
399
|
-
key={incident.id}
|
|
400
|
-
style={{
|
|
401
|
-
padding: 8,
|
|
402
|
-
background: '#fafafa',
|
|
403
|
-
borderRadius: 4,
|
|
404
|
-
borderLeft: `3px solid ${
|
|
405
|
-
incident.severity === 'critical' ? '#ff4d4f' :
|
|
406
|
-
incident.severity === 'warning' ? '#faad14' : '#1890ff'
|
|
407
|
-
}`,
|
|
408
|
-
}}
|
|
409
|
-
>
|
|
410
|
-
<UITypographyText style={{ fontSize: 12 }}>{incident.title}</UITypographyText>
|
|
411
|
-
</div>
|
|
412
|
-
))}
|
|
413
|
-
</div>
|
|
414
|
-
</div>
|
|
415
|
-
)}
|
|
416
|
-
|
|
417
|
-
{/* Context Configuration */}
|
|
418
|
-
{showConfig && (
|
|
419
|
-
<div style={{ borderTop: '1px solid #f0f0f0', paddingTop: 16 }}>
|
|
420
|
-
<UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>Context Settings</UITypographyText>
|
|
421
|
-
|
|
422
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
423
|
-
<UICheckbox
|
|
424
|
-
checked={contextConfig.includeMetrics}
|
|
425
|
-
onChange={(checked) => setContextConfig(prev => ({ ...prev, includeMetrics: checked }))}
|
|
426
|
-
>
|
|
427
|
-
Include current metrics
|
|
428
|
-
</UICheckbox>
|
|
429
|
-
<UICheckbox
|
|
430
|
-
checked={contextConfig.includeIncidents}
|
|
431
|
-
onChange={(checked) => setContextConfig(prev => ({ ...prev, includeIncidents: checked }))}
|
|
432
|
-
>
|
|
433
|
-
Include incidents
|
|
434
|
-
</UICheckbox>
|
|
435
|
-
<UICheckbox
|
|
436
|
-
checked={contextConfig.includeHistory}
|
|
437
|
-
onChange={(checked) => setContextConfig(prev => ({ ...prev, includeHistory: checked }))}
|
|
438
|
-
>
|
|
439
|
-
Include historical trends
|
|
440
|
-
</UICheckbox>
|
|
441
|
-
|
|
442
|
-
<div>
|
|
443
|
-
<UITypographyText type="secondary" style={{ fontSize: 12, marginBottom: 4, display: 'block' }}>
|
|
444
|
-
Time Range
|
|
445
|
-
</UITypographyText>
|
|
446
|
-
<UISelect
|
|
447
|
-
value={contextConfig.timeRange}
|
|
448
|
-
onChange={value => setContextConfig(prev => ({ ...prev, timeRange: value as ContextConfig['timeRange'] }))}
|
|
449
|
-
options={TIME_RANGE_OPTIONS}
|
|
450
|
-
style={{ width: '100%' }}
|
|
451
|
-
size="small"
|
|
452
|
-
/>
|
|
453
|
-
</div>
|
|
454
|
-
|
|
455
|
-
{availablePlugins.length > 0 && (
|
|
456
|
-
<div>
|
|
457
|
-
<UITypographyText type="secondary" style={{ fontSize: 12, marginBottom: 4, display: 'block' }}>
|
|
458
|
-
Focus on plugins
|
|
459
|
-
</UITypographyText>
|
|
460
|
-
<UISelect
|
|
461
|
-
mode="multiple"
|
|
462
|
-
value={contextConfig.plugins}
|
|
463
|
-
onChange={value => setContextConfig(prev => ({ ...prev, plugins: value as string[] }))}
|
|
464
|
-
options={availablePlugins.map(p => ({ value: p, label: p }))}
|
|
465
|
-
style={{ width: '100%' }}
|
|
466
|
-
size="small"
|
|
467
|
-
placeholder="All plugins"
|
|
468
|
-
maxTagCount={2}
|
|
469
|
-
/>
|
|
470
|
-
</div>
|
|
471
|
-
)}
|
|
472
|
-
</div>
|
|
473
|
-
</div>
|
|
474
|
-
)}
|
|
475
|
-
|
|
476
|
-
{/* Suggested Questions */}
|
|
477
|
-
<div style={{ marginTop: 24 }}>
|
|
478
|
-
<UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>
|
|
479
|
-
<BulbOutlined /> Quick Questions
|
|
480
|
-
</UITypographyText>
|
|
481
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
482
|
-
{SUGGESTED_QUESTIONS.slice(0, 4).map((q, i) => (
|
|
483
|
-
<UIButton
|
|
484
|
-
key={i}
|
|
485
|
-
variant="text"
|
|
486
|
-
size="small"
|
|
487
|
-
style={{ textAlign: 'left', height: 'auto', padding: '4px 8px' }}
|
|
488
|
-
onClick={() => handleSuggestion(q.text)}
|
|
489
|
-
>
|
|
490
|
-
<UITypographyText ellipsis style={{ fontSize: 12 }}>{q.text}</UITypographyText>
|
|
491
|
-
</UIButton>
|
|
492
|
-
))}
|
|
493
|
-
</div>
|
|
494
|
-
</div>
|
|
495
|
-
</UICard>
|
|
496
|
-
|
|
497
|
-
{/* Custom styles for markdown in chat context */}
|
|
498
|
-
<style>{`
|
|
499
|
-
.ai-insights-markdown {
|
|
500
|
-
padding: 0;
|
|
501
|
-
background: transparent;
|
|
502
|
-
border-radius: 0;
|
|
503
|
-
}
|
|
504
|
-
.ai-insights-markdown p {
|
|
505
|
-
margin: 0 0 8px 0;
|
|
506
|
-
}
|
|
507
|
-
.ai-insights-markdown p:last-child {
|
|
508
|
-
margin-bottom: 0;
|
|
509
|
-
}
|
|
510
|
-
.ai-insights-markdown h1,
|
|
511
|
-
.ai-insights-markdown h2,
|
|
512
|
-
.ai-insights-markdown h3 {
|
|
513
|
-
margin-top: 12px;
|
|
514
|
-
margin-bottom: 8px;
|
|
515
|
-
font-size: 1em;
|
|
516
|
-
font-weight: 600;
|
|
517
|
-
border: none;
|
|
518
|
-
padding: 0;
|
|
519
|
-
}
|
|
520
|
-
.ai-insights-markdown h1 {
|
|
521
|
-
font-size: 1.1em;
|
|
522
|
-
}
|
|
523
|
-
.ai-insights-markdown ul,
|
|
524
|
-
.ai-insights-markdown ol {
|
|
525
|
-
margin: 8px 0;
|
|
526
|
-
padding-left: 20px;
|
|
527
|
-
}
|
|
528
|
-
.ai-insights-markdown li {
|
|
529
|
-
margin: 2px 0;
|
|
530
|
-
}
|
|
531
|
-
.ai-insights-markdown code {
|
|
532
|
-
background: rgba(0, 0, 0, 0.06);
|
|
533
|
-
padding: 1px 4px;
|
|
534
|
-
border-radius: 3px;
|
|
535
|
-
font-size: 0.85em;
|
|
536
|
-
}
|
|
537
|
-
.ai-insights-markdown pre {
|
|
538
|
-
background: rgba(0, 0, 0, 0.04);
|
|
539
|
-
padding: 8px 12px;
|
|
540
|
-
border-radius: 6px;
|
|
541
|
-
margin: 8px 0;
|
|
542
|
-
}
|
|
543
|
-
.ai-insights-markdown pre code {
|
|
544
|
-
background: none;
|
|
545
|
-
padding: 0;
|
|
546
|
-
}
|
|
547
|
-
.ai-insights-markdown table {
|
|
548
|
-
margin: 8px 0;
|
|
549
|
-
font-size: 0.9em;
|
|
550
|
-
}
|
|
551
|
-
.ai-insights-markdown th,
|
|
552
|
-
.ai-insights-markdown td {
|
|
553
|
-
padding: 4px 8px;
|
|
554
|
-
}
|
|
555
|
-
.ai-insights-markdown blockquote {
|
|
556
|
-
margin: 8px 0;
|
|
557
|
-
padding-left: 12px;
|
|
558
|
-
border-left-width: 3px;
|
|
559
|
-
}
|
|
560
|
-
`}</style>
|
|
561
|
-
</div>
|
|
562
|
-
);
|
|
563
|
-
}
|