@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.
Files changed (175) hide show
  1. package/dist/index.html +1 -0
  2. package/dist/main.80b70197.css +2 -0
  3. package/dist/main.80b70197.css.map +1 -0
  4. package/dist/main.ad42ef4a.js +522 -0
  5. package/dist/main.ad42ef4a.js.map +1 -0
  6. package/dist/mf-manifest.json +241 -0
  7. package/dist/mf-stats.json +271 -0
  8. package/package.json +56 -39
  9. package/server.js +68 -0
  10. package/.env.example +0 -10
  11. package/UI-KIT-COMPONENTS.md +0 -783
  12. package/UI-KIT-PLAN.md +0 -326
  13. package/eslint-rules/no-direct-antd-import.js +0 -151
  14. package/eslint.config.ts +0 -27
  15. package/index.html +0 -17
  16. package/postcss.config.js +0 -7
  17. package/public/favicon.ico +0 -0
  18. package/rspack.config.mjs +0 -118
  19. package/src/App.tsx +0 -46
  20. package/src/components/devtools/devtools-log-feed.module.css +0 -165
  21. package/src/components/devtools/devtools-log-feed.tsx +0 -300
  22. package/src/components/devtools/devtools-panel.module.css +0 -121
  23. package/src/components/devtools/devtools-panel.tsx +0 -115
  24. package/src/components/empty-state.tsx +0 -28
  25. package/src/components/error-boundary.tsx +0 -210
  26. package/src/components/health-banner.module.css +0 -103
  27. package/src/components/health-banner.tsx +0 -134
  28. package/src/components/health-indicator.tsx +0 -12
  29. package/src/components/page-header.tsx +0 -49
  30. package/src/components/page-transition.tsx +0 -24
  31. package/src/components/role-switcher.tsx +0 -148
  32. package/src/components/run-badge.tsx +0 -13
  33. package/src/components/ui/icons.tsx +0 -509
  34. package/src/components/ui/index.ts +0 -25
  35. package/src/components/ui/kb-config-provider.tsx +0 -130
  36. package/src/components/ui/kb-header.module.css +0 -51
  37. package/src/components/ui/kb-header.tsx +0 -112
  38. package/src/components/ui/kb-notification-bell.module.css +0 -103
  39. package/src/components/ui/kb-notification-bell.tsx +0 -199
  40. package/src/components/ui/kb-page-layout.tsx +0 -66
  41. package/src/components/ui/kb-quick-search.module.css +0 -86
  42. package/src/components/ui/kb-quick-search.tsx +0 -196
  43. package/src/components/ui/kb-sidebar.module.css +0 -47
  44. package/src/components/ui/kb-sidebar.tsx +0 -196
  45. package/src/components/ui/kb-stat-card.module.css +0 -21
  46. package/src/components/ui/kb-stat-card.tsx +0 -22
  47. package/src/components/ui/kb-stat-grid.tsx +0 -22
  48. package/src/components/ui/kb-status-bar.module.css +0 -49
  49. package/src/components/ui/kb-status-bar.tsx +0 -145
  50. package/src/components/ui/kb-system-health-indicator.tsx +0 -229
  51. package/src/components/ui/kb-theme-toggle.tsx +0 -42
  52. package/src/components/ui/theme-adapter.ts +0 -812
  53. package/src/components/ui/theme-transition-overlay.tsx +0 -70
  54. package/src/components/ui/utils.ts +0 -6
  55. package/src/components/widget-error-boundary.tsx +0 -172
  56. package/src/config/env.ts +0 -5
  57. package/src/config/feature-flags.ts +0 -482
  58. package/src/config/studio.config.schema.ts +0 -88
  59. package/src/config/studio.config.ts +0 -84
  60. package/src/env.d.ts +0 -23
  61. package/src/hooks/use-command-palette.ts +0 -6
  62. package/src/hooks/use-devtools-store.ts +0 -25
  63. package/src/hooks/use-feature-flags.ts +0 -101
  64. package/src/index.css +0 -10
  65. package/src/layouts/dashboard-grid.tsx +0 -122
  66. package/src/layouts/index.ts +0 -12
  67. package/src/layouts/two-pane.tsx +0 -134
  68. package/src/main.tsx +0 -15
  69. package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +0 -257
  70. package/src/modules/analytics/components/analytics-date-range-picker.tsx +0 -121
  71. package/src/modules/analytics/pages/analytics-cache-page.tsx +0 -285
  72. package/src/modules/analytics/pages/analytics-embeddings-page.tsx +0 -274
  73. package/src/modules/analytics/pages/analytics-events-page.tsx +0 -425
  74. package/src/modules/analytics/pages/analytics-llm-page.tsx +0 -434
  75. package/src/modules/analytics/pages/analytics-overview-page.tsx +0 -336
  76. package/src/modules/analytics/pages/analytics-page.tsx +0 -20
  77. package/src/modules/analytics/pages/analytics-storage-page.tsx +0 -344
  78. package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +0 -279
  79. package/src/modules/analytics/routes/index.tsx +0 -133
  80. package/src/modules/analytics/utils/formatters.test.ts +0 -148
  81. package/src/modules/analytics/utils/formatters.ts +0 -100
  82. package/src/modules/auth/pages/login-page.module.css +0 -113
  83. package/src/modules/auth/pages/login-page.tsx +0 -80
  84. package/src/modules/auth/routes/index.tsx +0 -24
  85. package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +0 -199
  86. package/src/modules/dashboard/components/AiInsightsWidget.tsx +0 -358
  87. package/src/modules/dashboard/components/AnimatedMetricCard.css +0 -88
  88. package/src/modules/dashboard/components/AnimatedMetricCard.tsx +0 -125
  89. package/src/modules/dashboard/components/HeroMetricCard.css +0 -114
  90. package/src/modules/dashboard/components/HeroMetricCard.tsx +0 -152
  91. package/src/modules/dashboard/components/HeroMetricsWidget.tsx +0 -146
  92. package/src/modules/dashboard/components/SystemResourcesWidget.tsx +0 -197
  93. package/src/modules/dashboard/components/dashboard-empty.tsx +0 -22
  94. package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +0 -59
  95. package/src/modules/dashboard/components/dashboard-kpis.tsx +0 -13
  96. package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +0 -85
  97. package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +0 -78
  98. package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +0 -67
  99. package/src/modules/dashboard/components/dashboard-requests-widget.tsx +0 -62
  100. package/src/modules/dashboard/layouts/dashboard-layout.tsx +0 -16
  101. package/src/modules/dashboard/pages/ai-insights-page.tsx +0 -563
  102. package/src/modules/dashboard/pages/dashboard-animations.css +0 -317
  103. package/src/modules/dashboard/pages/dashboard-page.tsx +0 -13
  104. package/src/modules/dashboard/routes/index.tsx +0 -70
  105. package/src/modules/observability/pages/devkit-page.tsx +0 -263
  106. package/src/modules/observability/pages/incident-detail-page.tsx +0 -571
  107. package/src/modules/observability/pages/incidents-page.tsx +0 -370
  108. package/src/modules/observability/pages/log-detail-page.tsx +0 -508
  109. package/src/modules/observability/pages/logs-page.tsx +0 -1129
  110. package/src/modules/observability/pages/prometheus-metrics-page.tsx +0 -486
  111. package/src/modules/observability/pages/state-broker-page.tsx +0 -229
  112. package/src/modules/observability/pages/system-events-page.tsx +0 -255
  113. package/src/modules/observability/routes/index.tsx +0 -133
  114. package/src/modules/plugins/components/marketplace-card.tsx +0 -153
  115. package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +0 -374
  116. package/src/modules/plugins/components/plugin-card.tsx +0 -136
  117. package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +0 -101
  118. package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +0 -101
  119. package/src/modules/plugins/components/plugin-detail/index.ts +0 -7
  120. package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +0 -64
  121. package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +0 -30
  122. package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +0 -68
  123. package/src/modules/plugins/components/plugin-detail/permissions/index.ts +0 -6
  124. package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +0 -43
  125. package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +0 -242
  126. package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +0 -96
  127. package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +0 -55
  128. package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +0 -47
  129. package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +0 -169
  130. package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +0 -159
  131. package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +0 -40
  132. package/src/modules/plugins/components/plugin-grid.tsx +0 -20
  133. package/src/modules/plugins/components/plugin-table.tsx +0 -178
  134. package/src/modules/plugins/pages/marketplace-page.tsx +0 -200
  135. package/src/modules/plugins/pages/plugin-detail-page.tsx +0 -1145
  136. package/src/modules/plugins/routes/index.tsx +0 -48
  137. package/src/modules/plugins/utils/index.ts +0 -1
  138. package/src/modules/plugins/utils/schema-helpers.ts +0 -21
  139. package/src/modules/settings/components/api-routes-viewer.tsx +0 -415
  140. package/src/modules/settings/components/appearance-settings.tsx +0 -200
  141. package/src/modules/settings/components/configuration-settings.tsx +0 -321
  142. package/src/modules/settings/components/data-privacy-settings.tsx +0 -111
  143. package/src/modules/settings/components/experimental-settings.tsx +0 -243
  144. package/src/modules/settings/components/navigation-settings.tsx +0 -198
  145. package/src/modules/settings/components/notifications-settings.tsx +0 -40
  146. package/src/modules/settings/pages/settings-page.tsx +0 -298
  147. package/src/modules/settings/routes/index.tsx +0 -47
  148. package/src/pages/README-assistant.md +0 -91
  149. package/src/pages/login-page.tsx +0 -177
  150. package/src/pages/not-found-page.tsx +0 -85
  151. package/src/providers/auth-provider.tsx +0 -40
  152. package/src/providers/command-palette-provider.tsx +0 -193
  153. package/src/providers/data-sources-provider.tsx +0 -27
  154. package/src/providers/devtools-provider.tsx +0 -72
  155. package/src/providers/navigation-items-provider.tsx +0 -38
  156. package/src/providers/registry-v2-provider.tsx +0 -93
  157. package/src/providers/settings-provider.tsx +0 -158
  158. package/src/router.tsx +0 -302
  159. package/src/routes/helpers.tsx +0 -25
  160. package/src/routes/plugin-page-v2.tsx +0 -52
  161. package/src/routes/types.ts +0 -18
  162. package/src/styles/antd-overrides.css +0 -211
  163. package/src/styles/base.css +0 -190
  164. package/src/styles/settings.css +0 -75
  165. package/src/styles/variables.css +0 -94
  166. package/src/utils/analytics.ts +0 -183
  167. package/src/utils/apply-navigation-categories.ts +0 -83
  168. package/src/utils/commands.ts +0 -252
  169. package/src/utils/export.ts +0 -174
  170. package/src/utils/format-date.ts +0 -166
  171. package/src/utils/logger.ts +0 -110
  172. package/tailwind.config.ts +0 -13
  173. package/tsconfig.json +0 -13
  174. package/vitest-setup.ts +0 -1
  175. /package/{public → dist}/favicon.svg +0 -0
@@ -1,279 +0,0 @@
1
- import { useState } from 'react';
2
- import { UIRow, UICol, UIStatistic, UIAlert, UIProgress } from '@kb-labs/studio-ui-kit';
3
- import {
4
- SearchOutlined,
5
- PlusCircleOutlined,
6
- DeleteOutlined,
7
- ClockCircleOutlined,
8
- StarOutlined,
9
- FileSearchOutlined,
10
- DatabaseOutlined,
11
- } from '@ant-design/icons';
12
- import { UIText, UIStatisticsChart, UICard, UIPage, UIPageHeader } from '@kb-labs/studio-ui-kit';
13
- import { useAdaptersVectorStoreUsage, useAdaptersVectorStoreDailyStats } from '@kb-labs/studio-data-client';
14
- import { useDataSources } from '../../../providers/data-sources-provider';
15
- import { AnalyticsDateRangePicker, type DateRangeValue } from '../components/analytics-date-range-picker';
16
- import dayjs from 'dayjs';
17
-
18
-
19
- /**
20
- * Analytics VectorStore Usage Page
21
- *
22
- * Displays vector database usage statistics:
23
- * - Search, upsert, and delete operations
24
- * - Search quality metrics (avg score)
25
- * - Performance metrics
26
- * - Storage growth tracking
27
- */
28
- export function AnalyticsVectorStorePage() {
29
- const sources = useDataSources();
30
-
31
- // Date range state (default: last 30 days)
32
- const [dateRange, setDateRange] = useState<DateRangeValue>([
33
- dayjs().subtract(30, 'days'),
34
- dayjs(),
35
- ]);
36
-
37
- const {
38
- data: stats,
39
- isLoading,
40
- error,
41
- isError,
42
- } = useAdaptersVectorStoreUsage(sources.adapters, {
43
- from: dateRange[0].toISOString(),
44
- to: dateRange[1].toISOString(),
45
- });
46
-
47
- const {
48
- data: dailyStats,
49
- isLoading: dailyStatsLoading,
50
- } = useAdaptersVectorStoreDailyStats(sources.adapters, {
51
- from: dateRange[0].toISOString(),
52
- to: dateRange[1].toISOString(),
53
- });
54
-
55
- // Check if analytics is not implemented
56
- const analyticsNotImplemented =
57
- isError &&
58
- error instanceof Error &&
59
- (error.message.includes('not support') || error.message.includes('not implement'));
60
-
61
- if (analyticsNotImplemented) {
62
- return (
63
- <UIPage>
64
- <UIPageHeader
65
- title="VectorStore Usage"
66
- description="Track vector database operations and search quality"
67
- />
68
- <UIAlert
69
- message="Analytics Not Available"
70
- description="Analytics adapter does not support reading events. Configure an analytics adapter in kb.config.json that implements getEvents()."
71
- variant="info"
72
- showIcon
73
- style={{ marginTop: 16 }}
74
- />
75
- </UIPage>
76
- );
77
- }
78
-
79
- if (isError) {
80
- return (
81
- <UIPage>
82
- <UIPageHeader title="VectorStore Usage" description="Vector database usage analytics" />
83
- <UIAlert
84
- message="Failed to Load VectorStore Usage"
85
- description={error?.message || 'Unknown error occurred'}
86
- variant="error"
87
- showIcon
88
- style={{ marginTop: 16 }}
89
- />
90
- </UIPage>
91
- );
92
- }
93
-
94
- // Format helpers
95
- const _formatNumber = (num: number) => num.toLocaleString();
96
- const _formatDuration = (ms: number) => (ms < 1000 ? `${ms.toFixed(1)}ms` : `${(ms / 1000).toFixed(2)}s`);
97
- const _formatScore = (score: number) => (score * 100).toFixed(1) + '%';
98
-
99
- const totalOperations = stats
100
- ? stats.searchQueries + stats.upsertOperations + stats.deleteOperations
101
- : 0;
102
-
103
- const searchPercentage = stats && totalOperations > 0
104
- ? ((stats.searchQueries / totalOperations) * 100).toFixed(1)
105
- : 0;
106
-
107
- const scoreStatus = stats && stats.avgSearchScore >= 0.8
108
- ? 'success'
109
- : stats && stats.avgSearchScore >= 0.6
110
- ? 'normal'
111
- : 'exception';
112
-
113
- return (
114
- <UIPage width="full">
115
- <UIPageHeader
116
- title="VectorStore Usage Analytics"
117
- description="Track vector database operations, search quality, and storage growth"
118
- actions={
119
- <AnalyticsDateRangePicker
120
- value={dateRange}
121
- onChange={setDateRange}
122
- />
123
- }
124
- />
125
-
126
- <div style={{ marginTop: 24 }}>
127
- {/* Overview Stats */}
128
- <UIRow gutter={[16, 16]}>
129
- <UICol xs={24} sm={12} lg={6}>
130
- <UICard>
131
- <UIStatistic
132
- title="Search Queries"
133
- value={stats?.searchQueries ?? 0}
134
- prefix={<SearchOutlined />}
135
- loading={isLoading}
136
- valueStyle={{ color: 'var(--info)' }}
137
- />
138
- </UICard>
139
- </UICol>
140
- <UICol xs={24} sm={12} lg={6}>
141
- <UICard>
142
- <UIStatistic
143
- title="Upsert Operations"
144
- value={stats?.upsertOperations ?? 0}
145
- prefix={<PlusCircleOutlined />}
146
- loading={isLoading}
147
- valueStyle={{ color: 'var(--success)' }}
148
- />
149
- </UICard>
150
- </UICol>
151
- <UICol xs={24} sm={12} lg={6}>
152
- <UICard>
153
- <UIStatistic
154
- title="Delete Operations"
155
- value={stats?.deleteOperations ?? 0}
156
- prefix={<DeleteOutlined />}
157
- loading={isLoading}
158
- valueStyle={{ color: 'var(--warning)' }}
159
- />
160
- </UICard>
161
- </UICol>
162
- <UICol xs={24} sm={12} lg={6}>
163
- <UICard>
164
- <UIStatistic
165
- title="Total Operations"
166
- value={totalOperations}
167
- prefix={<DatabaseOutlined />}
168
- loading={isLoading}
169
- valueStyle={{ color: 'var(--link)' }}
170
- />
171
- </UICard>
172
- </UICol>
173
- </UIRow>
174
-
175
- {/* Search Quality */}
176
- <UIRow gutter={[16, 16]} style={{ marginTop: 16 }}>
177
- <UICol xs={24} lg={12}>
178
- <UICard title="Search Quality">
179
- <UIStatistic
180
- title="Average Search Score"
181
- value={stats?.avgSearchScore ?? 0}
182
- precision={3}
183
- prefix={<StarOutlined />}
184
- loading={isLoading}
185
- valueStyle={{
186
- color: scoreStatus === 'success' ? 'var(--success)' : scoreStatus === 'normal' ? 'var(--info)' : 'var(--warning)'
187
- }}
188
- />
189
- <div style={{ marginTop: 16 }}>
190
- <UIText color="secondary">Search Quality Rating</UIText>
191
- <UIProgress
192
- percent={Number(((stats?.avgSearchScore ?? 0) * 100).toFixed(1))}
193
- status={scoreStatus}
194
- format={(percent) => `${percent}%`}
195
- />
196
- </div>
197
- <div style={{ marginTop: 16 }}>
198
- <UIStatistic
199
- title="Avg Results per Query"
200
- value={stats?.avgResultsCount ?? 0}
201
- precision={1}
202
- prefix={<FileSearchOutlined />}
203
- loading={isLoading}
204
- />
205
- </div>
206
- </UICard>
207
- </UICol>
208
- <UICol xs={24} lg={12}>
209
- <UICard title="Performance Metrics">
210
- <UIStatistic
211
- title="Avg Search Duration"
212
- value={stats?.avgSearchDuration ?? 0}
213
- prefix={<ClockCircleOutlined />}
214
- suffix="ms"
215
- precision={1}
216
- loading={isLoading}
217
- />
218
- <div style={{ marginTop: 24 }}>
219
- <UIText color="secondary">Operation Distribution</UIText>
220
- <UIProgress
221
- percent={Number(searchPercentage)}
222
- format={(percent) => `${percent}% searches`}
223
- />
224
- </div>
225
- </UICard>
226
- </UICol>
227
- </UIRow>
228
-
229
- {/* Storage Growth */}
230
- <UICard title="Storage Statistics" style={{ marginTop: 16 }}>
231
- <UIRow gutter={[16, 16]}>
232
- <UICol xs={24} sm={8}>
233
- <UIStatistic
234
- title="Vectors Added"
235
- value={stats?.totalVectorsUpserted ?? 0}
236
- prefix={<PlusCircleOutlined />}
237
- valueStyle={{ color: 'var(--success)' }}
238
- loading={isLoading}
239
- />
240
- </UICol>
241
- <UICol xs={24} sm={8}>
242
- <UIStatistic
243
- title="Vectors Deleted"
244
- value={stats?.totalVectorsDeleted ?? 0}
245
- prefix={<DeleteOutlined />}
246
- valueStyle={{ color: 'var(--error)' }}
247
- loading={isLoading}
248
- />
249
- </UICol>
250
- <UICol xs={24} sm={8}>
251
- <UIStatistic
252
- title="Net Growth"
253
- value={(stats?.totalVectorsUpserted ?? 0) - (stats?.totalVectorsDeleted ?? 0)}
254
- loading={isLoading}
255
- valueStyle={{
256
- color: (stats?.totalVectorsUpserted ?? 0) > (stats?.totalVectorsDeleted ?? 0)
257
- ? 'var(--success)'
258
- : 'var(--error)'
259
- }}
260
- />
261
- </UICol>
262
- </UIRow>
263
- </UICard>
264
-
265
- {/* Daily Usage Chart */}
266
- <UIStatisticsChart
267
- data={dailyStats}
268
- loading={dailyStatsLoading}
269
- title="Daily VectorStore Operations"
270
- metrics={[
271
- { key: 'totalSearches', label: 'Searches' },
272
- { key: 'totalUpserts', label: 'Upserts' },
273
- { key: 'totalDeletes', label: 'Deletes' },
274
- ]}
275
- />
276
- </div>
277
- </UIPage>
278
- );
279
- }
@@ -1,133 +0,0 @@
1
- /**
2
- * @module @kb-labs/studio-app/modules/analytics/routes
3
- * Analytics module routing configuration
4
- */
5
-
6
- import type { RouteObject } from 'react-router-dom';
7
- import { AnalyticsOverviewPage } from '../pages/analytics-overview-page';
8
- import { AnalyticsEventsPage } from '../pages/analytics-events-page';
9
- import { AnalyticsLLMPage } from '../pages/analytics-llm-page';
10
- import { AnalyticsEmbeddingsPage } from '../pages/analytics-embeddings-page';
11
- import { AnalyticsVectorStorePage } from '../pages/analytics-vectorstore-page';
12
- import { AnalyticsCachePage } from '../pages/analytics-cache-page';
13
- import { AnalyticsStoragePage } from '../pages/analytics-storage-page';
14
- import { ErrorBoundary } from '../../../components/error-boundary';
15
- import { renderIcon } from '../../../routes/helpers';
16
- import type { NavigationItem } from '@/components/ui';
17
-
18
- // Route paths
19
- const PATHS = {
20
- OVERVIEW: '/analytics/overview',
21
- EVENTS: '/analytics/events',
22
- LLM: '/analytics/llm',
23
- EMBEDDINGS: '/analytics/embeddings',
24
- VECTORSTORE: '/analytics/vectorstore',
25
- CACHE: '/analytics/cache',
26
- STORAGE: '/analytics/storage',
27
- } as const;
28
-
29
- // Route keys
30
- const KEYS = {
31
- MODULE: 'analytics',
32
- OVERVIEW: 'analytics-overview',
33
- EVENTS: 'analytics-events',
34
- LLM: 'analytics-llm',
35
- EMBEDDINGS: 'analytics-embeddings',
36
- VECTORSTORE: 'analytics-vectorstore',
37
- CACHE: 'analytics-cache',
38
- STORAGE: 'analytics-storage',
39
- } as const;
40
-
41
- /**
42
- * Analytics routes
43
- */
44
- export const analyticsRoutes: RouteObject[] = [
45
- {
46
- path: PATHS.OVERVIEW,
47
- element: <AnalyticsOverviewPage />,
48
- errorElement: <ErrorBoundary />,
49
- },
50
- {
51
- path: PATHS.EVENTS,
52
- element: <AnalyticsEventsPage />,
53
- errorElement: <ErrorBoundary />,
54
- },
55
- {
56
- path: PATHS.LLM,
57
- element: <AnalyticsLLMPage />,
58
- errorElement: <ErrorBoundary />,
59
- },
60
- {
61
- path: PATHS.EMBEDDINGS,
62
- element: <AnalyticsEmbeddingsPage />,
63
- errorElement: <ErrorBoundary />,
64
- },
65
- {
66
- path: PATHS.VECTORSTORE,
67
- element: <AnalyticsVectorStorePage />,
68
- errorElement: <ErrorBoundary />,
69
- },
70
- {
71
- path: PATHS.CACHE,
72
- element: <AnalyticsCachePage />,
73
- errorElement: <ErrorBoundary />,
74
- },
75
- {
76
- path: PATHS.STORAGE,
77
- element: <AnalyticsStoragePage />,
78
- errorElement: <ErrorBoundary />,
79
- },
80
- ];
81
-
82
- /**
83
- * Analytics navigation item for sidebar
84
- */
85
- export const analyticsNavigation: NavigationItem = {
86
- key: KEYS.MODULE,
87
- label: 'Analytics',
88
- icon: renderIcon('BarChartOutlined'),
89
- children: [
90
- {
91
- key: KEYS.OVERVIEW,
92
- label: 'Overview',
93
- path: PATHS.OVERVIEW,
94
- icon: renderIcon('DashboardOutlined'),
95
- },
96
- {
97
- key: KEYS.EVENTS,
98
- label: 'Events',
99
- path: PATHS.EVENTS,
100
- icon: renderIcon('ThunderboltOutlined'),
101
- },
102
- {
103
- key: KEYS.LLM,
104
- label: 'LLM Usage',
105
- path: PATHS.LLM,
106
- icon: renderIcon('RobotOutlined'),
107
- },
108
- {
109
- key: KEYS.EMBEDDINGS,
110
- label: 'Embeddings',
111
- path: PATHS.EMBEDDINGS,
112
- icon: renderIcon('NodeIndexOutlined'),
113
- },
114
- {
115
- key: KEYS.VECTORSTORE,
116
- label: 'Vector Store',
117
- path: PATHS.VECTORSTORE,
118
- icon: renderIcon('DatabaseOutlined'),
119
- },
120
- {
121
- key: KEYS.CACHE,
122
- label: 'Cache',
123
- path: PATHS.CACHE,
124
- icon: renderIcon('ThunderboltOutlined'),
125
- },
126
- {
127
- key: KEYS.STORAGE,
128
- label: 'Storage',
129
- path: PATHS.STORAGE,
130
- icon: renderIcon('SaveOutlined'),
131
- },
132
- ],
133
- };
@@ -1,148 +0,0 @@
1
- /**
2
- * @module analytics/utils/formatters.test
3
- * Tests for analytics formatters
4
- */
5
-
6
- import { describe, it, expect } from 'vitest';
7
- import { formatCost } from './formatters';
8
-
9
- describe('formatCost', () => {
10
- describe('real backend data - single LLM calls', () => {
11
- it('should format very small costs (individual API calls)', () => {
12
- // Real data from events-20260116.jsonl
13
- expect(formatCost(0.000043349999999999997)).toBe('$4.33e-5'); // 145+36 tokens
14
- expect(formatCost(0.00008730000000000001)).toBe('$8.73e-5'); // 234+87 tokens
15
- expect(formatCost(0.0005536499999999999)).toBe('$0.000554'); // 2847+211 tokens
16
- expect(formatCost(0.0001407)).toBe('$0.000141'); // 514+106 tokens
17
- });
18
-
19
- it('should format slightly larger costs', () => {
20
- expect(formatCost(0.00028695)).toBe('$0.000287'); // Original bug example
21
- expect(formatCost(0.000044399999999999995)).toBe('$4.44e-5'); // 160+34 tokens
22
- });
23
- });
24
-
25
- describe('aggregated daily costs', () => {
26
- it('should format daily totals', () => {
27
- // Simulate 100 small API calls per day
28
- const dailyTotal = 0.000043 * 100; // ≈ $0.0043
29
- expect(formatCost(dailyTotal)).toBe('$0.004300');
30
-
31
- // Simulate 1000 small API calls per day
32
- const largeDailyTotal = 0.000043 * 1000; // ≈ $0.043
33
- expect(formatCost(largeDailyTotal)).toBe('$0.0430');
34
- });
35
-
36
- it('should format typical production daily costs', () => {
37
- expect(formatCost(0.001)).toBe('$0.001000'); // 6 decimals for $0.001
38
- expect(formatCost(0.01)).toBe('$0.0100'); // 4 decimals for $0.01
39
- expect(formatCost(0.1)).toBe('$0.1000'); // 4 decimals for $0.10
40
- expect(formatCost(1.5)).toBe('$1.50'); // 2 decimals for $1.50
41
- expect(formatCost(10.99)).toBe('$10.99'); // 2 decimals for $10.99
42
- });
43
- });
44
-
45
- describe('edge cases', () => {
46
- it('should handle zero cost', () => {
47
- expect(formatCost(0)).toBe('$0.00');
48
- });
49
-
50
- it('should handle very tiny costs (scientific notation)', () => {
51
- expect(formatCost(0.00000012)).toBe('$1.20e-7');
52
- expect(formatCost(0.000000001)).toBe('$1.00e-9');
53
- });
54
-
55
- it('should handle large costs', () => {
56
- expect(formatCost(100.456)).toBe('$100.46');
57
- expect(formatCost(1000.123)).toBe('$1000.12');
58
- });
59
- });
60
-
61
- describe('precision thresholds', () => {
62
- it('should use 2 decimals for >= $1', () => {
63
- expect(formatCost(1)).toBe('$1.00');
64
- expect(formatCost(1.234)).toBe('$1.23');
65
- expect(formatCost(1.235)).toBe('$1.24'); // Rounds up
66
- });
67
-
68
- it('should use 4 decimals for >= $0.01', () => {
69
- expect(formatCost(0.01)).toBe('$0.0100');
70
- expect(formatCost(0.012345)).toBe('$0.0123');
71
- expect(formatCost(0.012355)).toBe('$0.0124'); // Rounds up
72
- });
73
-
74
- it('should use 6 decimals for >= $0.0001', () => {
75
- expect(formatCost(0.0001)).toBe('$0.000100');
76
- expect(formatCost(0.00012345)).toBe('$0.000123');
77
- expect(formatCost(0.00012355)).toBe('$0.000124'); // Rounds up
78
- });
79
-
80
- it('should use scientific notation for < $0.0001', () => {
81
- expect(formatCost(0.00009999)).toBe('$1.00e-4');
82
- expect(formatCost(0.00001)).toBe('$1.00e-5');
83
- });
84
- });
85
-
86
- describe('what we expect to see in the chart tooltip', () => {
87
- it('should show readable values for typical daily aggregates', () => {
88
- // If we have 10 requests/day with avg $0.00028695 each
89
- const cost = 10 * 0.00028695; // = $0.0028695
90
- expect(formatCost(cost)).toBe('$0.002870'); // 6 decimals, readable
91
-
92
- // If we have 100 requests/day
93
- const cost100 = 100 * 0.00028695; // = $0.028695
94
- expect(formatCost(cost100)).toBe('$0.0287'); // 4 decimals, readable
95
-
96
- // If we have 1000 requests/day
97
- const cost1000 = 1000 * 0.00028695; // = $0.28695
98
- expect(formatCost(cost1000)).toBe('$0.2869'); // 4 decimals, readable (rounds down)
99
- });
100
-
101
- it('should NOT show $0.00 for small but non-zero values', () => {
102
- // These should all be visible (not $0.00)
103
- expect(formatCost(0.00028695)).not.toBe('$0.00');
104
- expect(formatCost(0.000043)).not.toBe('$0.00');
105
- expect(formatCost(0.001)).not.toBe('$0.00');
106
- });
107
- });
108
-
109
- describe('real world scenario: what chart shows on Jan 16', () => {
110
- it('should handle sum of all LLM calls on Jan 16', () => {
111
- // Sample from real data:
112
- const costs = [
113
- 0.000043349999999999997,
114
- 0.000043349999999999997,
115
- 0.00008730000000000001,
116
- 0.0005536499999999999,
117
- 0.0001407,
118
- ];
119
-
120
- const total = costs.reduce((sum, c) => sum + c, 0);
121
- // Total ≈ 0.00086265
122
-
123
- const formatted = formatCost(total);
124
-
125
- // Should be visible with 6 decimals
126
- expect(formatted).toBe('$0.000868'); // Actual rounded value
127
-
128
- // Should NOT be $0.00
129
- expect(formatted).not.toBe('$0.00');
130
-
131
- // Should NOT be scientific notation (since > $0.0001)
132
- expect(formatted).not.toContain('e');
133
- });
134
-
135
- it('should handle very low daily total (few requests)', () => {
136
- // If only 2-3 requests on a day
137
- const lowDailyTotal = 0.000043 + 0.000087; // $0.00013
138
-
139
- const formatted = formatCost(lowDailyTotal);
140
-
141
- // Should use 6 decimals (since >= $0.0001)
142
- expect(formatted).toBe('$0.000130');
143
-
144
- // Should NOT be $0.00
145
- expect(formatted).not.toBe('$0.00');
146
- });
147
- });
148
- });
@@ -1,100 +0,0 @@
1
- /**
2
- * @module analytics/utils/formatters
3
- * Common formatting utilities for analytics pages
4
- */
5
-
6
- /**
7
- * Format cost with adaptive precision based on magnitude.
8
- *
9
- * Uses different decimal places depending on the value:
10
- * - >= $1.00: 2 decimal places ($12.34)
11
- * - >= $0.01: 4 decimal places ($0.0123)
12
- * - >= $0.0001: 6 decimal places ($0.000123)
13
- * - < $0.0001: scientific notation ($1.23e-7)
14
- *
15
- * This ensures tiny costs (like $0.00028695) are visible in charts
16
- * while keeping larger values readable.
17
- *
18
- * @example
19
- * formatCost(12.3456) // "$12.35"
20
- * formatCost(0.0123456) // "$0.0123"
21
- * formatCost(0.00028695) // "$0.000287"
22
- * formatCost(0.00000012) // "$1.20e-7"
23
- */
24
- export function formatCost(cost: number | undefined | null): string {
25
- if (cost == null || isNaN(cost)) {
26
- return '$0.00';
27
- }
28
- // Handle zero explicitly
29
- if (cost === 0) {
30
- return '$0.00';
31
- }
32
-
33
- if (cost >= 1) {
34
- return `$${cost.toFixed(2)}`;
35
- }
36
- if (cost >= 0.01) {
37
- return `$${cost.toFixed(4)}`;
38
- }
39
- if (cost >= 0.0001) {
40
- return `$${cost.toFixed(6)}`;
41
- }
42
- // For very small values (< $0.0001), use scientific notation
43
- return `$${cost.toExponential(2)}`;
44
- }
45
-
46
- /**
47
- * Format number with thousand separators.
48
- *
49
- * @example
50
- * formatNumber(1234567) // "1,234,567"
51
- */
52
- export function formatNumber(num: number): string {
53
- return num.toLocaleString();
54
- }
55
-
56
- /**
57
- * Format duration in milliseconds to human-readable string.
58
- *
59
- * @example
60
- * formatDuration(500) // "500ms"
61
- * formatDuration(2500) // "2.50s"
62
- */
63
- export function formatDuration(ms: number): string {
64
- if (ms < 1000) {
65
- return `${ms.toFixed(0)}ms`;
66
- }
67
- return `${(ms / 1000).toFixed(2)}s`;
68
- }
69
-
70
- /**
71
- * Format byte size to human-readable string.
72
- *
73
- * @example
74
- * formatSize(512) // "512 B"
75
- * formatSize(2048) // "2.00 KB"
76
- * formatSize(1048576) // "1.00 MB"
77
- */
78
- export function formatSize(bytes: number): string {
79
- if (bytes < 1024) {
80
- return `${bytes} B`;
81
- }
82
- if (bytes < 1024 * 1024) {
83
- return `${(bytes / 1024).toFixed(2)} KB`;
84
- }
85
- if (bytes < 1024 * 1024 * 1024) {
86
- return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
87
- }
88
- return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
89
- }
90
-
91
- /**
92
- * Format percentage value.
93
- *
94
- * @example
95
- * formatPercentage(0.856) // "85.6%"
96
- * formatPercentage(0.1234) // "12.3%"
97
- */
98
- export function formatPercentage(value: number): string {
99
- return `${(value * 100).toFixed(1)}%`;
100
- }