spine-framework 0.3.124 → 0.3.126

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 (319) hide show
  1. package/.framework/cli/commands/agents.ts +35 -37
  2. package/.framework/cli/commands/auth.ts +35 -34
  3. package/.framework/cli/commands/create-app.ts +104 -40
  4. package/.framework/cli/commands/dev.ts +104 -69
  5. package/.framework/cli/commands/doctor.ts +179 -69
  6. package/.framework/cli/commands/generate.ts +83 -143
  7. package/.framework/cli/commands/init.ts +72 -34
  8. package/.framework/cli/commands/install-app.ts +260 -125
  9. package/.framework/cli/commands/items.ts +33 -44
  10. package/.framework/cli/commands/migrate.ts +122 -40
  11. package/.framework/cli/commands/migrations.ts +33 -40
  12. package/.framework/cli/commands/pipelines.ts +31 -39
  13. package/.framework/cli/commands/status.ts +47 -23
  14. package/.framework/cli/commands/system.ts +47 -37
  15. package/.framework/cli/commands/test.ts +112 -68
  16. package/.framework/cli/commands/validate-app.ts +215 -121
  17. package/.framework/cli/context.ts +112 -130
  18. package/.framework/cli/env-loader.ts +47 -18
  19. package/.framework/cli/index.ts +81 -42
  20. package/.framework/docs/runtime-types.md +1024 -0
  21. package/.framework/functions/_shared/agent-runner.ts +1296 -961
  22. package/.framework/functions/_shared/app-manifest.ts +82 -68
  23. package/.framework/functions/_shared/audit.ts +135 -83
  24. package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
  25. package/.framework/functions/_shared/db.ts +80 -126
  26. package/.framework/functions/_shared/hash-api-key.ts +63 -0
  27. package/.framework/functions/_shared/index.ts +224 -335
  28. package/.framework/functions/_shared/kb.ts +319 -0
  29. package/.framework/functions/_shared/llm.ts +92 -0
  30. package/.framework/functions/_shared/logging.ts +288 -0
  31. package/.framework/functions/_shared/middleware.ts +329 -365
  32. package/.framework/functions/_shared/outcomes.ts +36 -0
  33. package/.framework/functions/_shared/permissions.ts +359 -643
  34. package/.framework/functions/_shared/pipeline-runner.ts +243 -316
  35. package/.framework/functions/_shared/principal.ts +376 -468
  36. package/.framework/functions/_shared/resolve-credentials.ts +102 -0
  37. package/.framework/functions/_shared/resolve-ids.ts +188 -16
  38. package/.framework/functions/_shared/retry-deliveries.ts +104 -0
  39. package/.framework/functions/_shared/schema-utils.ts +611 -373
  40. package/.framework/functions/_shared/testing.ts +207 -137
  41. package/.framework/functions/_shared/tracing.ts +276 -0
  42. package/.framework/functions/_shared/trigger-engine.ts +159 -211
  43. package/.framework/functions/_shared/webhook-registration.ts +95 -81
  44. package/.framework/functions/_shared/webhook-registry.ts +153 -75
  45. package/.framework/functions/account-nodes.ts +31 -53
  46. package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
  47. package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
  48. package/.framework/functions/adapters/openai-outbound.ts +40 -0
  49. package/.framework/functions/admin-data.ts +685 -537
  50. package/.framework/functions/admin-funnel.ts +31 -20
  51. package/.framework/functions/admin-install-funnel.ts +47 -28
  52. package/.framework/functions/admin-signals.ts +47 -29
  53. package/.framework/functions/ai-agents.ts +122 -147
  54. package/.framework/functions/ai-interactions.ts +180 -90
  55. package/.framework/functions/api-keys.ts +250 -192
  56. package/.framework/functions/apps.ts +203 -197
  57. package/.framework/functions/auth.ts +98 -91
  58. package/.framework/functions/cil-agent-ingest.ts +773 -0
  59. package/.framework/functions/cil-agent.ts +592 -0
  60. package/.framework/functions/cil-callmap.ts +1227 -0
  61. package/.framework/functions/cil-config.ts +583 -0
  62. package/.framework/functions/cil-context.ts +445 -0
  63. package/.framework/functions/cil-doc.ts +1752 -0
  64. package/.framework/functions/cil-embeddings.ts +1202 -0
  65. package/.framework/functions/cil-graph.ts +585 -0
  66. package/.framework/functions/cil-ingest.ts +2416 -0
  67. package/.framework/functions/cil-pipeline.ts +318 -0
  68. package/.framework/functions/cil-project.ts +499 -0
  69. package/.framework/functions/cil-validate.ts +469 -0
  70. package/.framework/functions/debug-auth.ts +73 -53
  71. package/.framework/functions/deliveries.ts +117 -0
  72. package/.framework/functions/embeddings.ts +528 -516
  73. package/.framework/functions/eval-harness.ts +158 -106
  74. package/.framework/functions/integration-outbound.ts +311 -0
  75. package/.framework/functions/integration-routes.ts +240 -466
  76. package/.framework/functions/integrations.ts +132 -140
  77. package/.framework/functions/invites.ts +401 -201
  78. package/.framework/functions/item-progress.ts +113 -53
  79. package/.framework/functions/kb.ts +28 -0
  80. package/.framework/functions/logs.ts +239 -222
  81. package/.framework/functions/observability.ts +308 -259
  82. package/.framework/functions/pipeline-executions.ts +179 -235
  83. package/.framework/functions/pipelines.ts +163 -205
  84. package/.framework/functions/prompt-configs.ts +109 -156
  85. package/.framework/functions/roles.ts +97 -147
  86. package/.framework/functions/system-cron.ts +132 -218
  87. package/.framework/functions/system.ts +112 -52
  88. package/.framework/functions/tests.ts +32 -28
  89. package/.framework/functions/timers.ts +162 -210
  90. package/.framework/functions/triggers.ts +282 -346
  91. package/.framework/functions/types.ts +283 -400
  92. package/.framework/migrations/001_core_schema.sql +630 -0
  93. package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
  94. package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
  95. package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
  96. package/.framework/migrations/005_core_observability.sql +598 -0
  97. package/.framework/migrations/006_security_hardening_rls.sql +97 -0
  98. package/.framework/migrations/007_kb_article_list.sql +237 -0
  99. package/.framework/migrations/008_items_target.sql +8 -0
  100. package/.framework/migrations/009_roles_slug_unique.sql +11 -0
  101. package/.framework/public/spine.config.json +91 -0
  102. package/.framework/scripts/app-install-cli.ts +112 -98
  103. package/.framework/src/App.tsx +67 -20
  104. package/.framework/src/apps/admin/index.tsx +97 -27
  105. package/.framework/src/components/AppWrapper.tsx +50 -11
  106. package/.framework/src/components/CustomAppLoader.tsx +131 -22
  107. package/.framework/src/components/admin/AdminListPage.tsx +75 -8
  108. package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
  109. package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
  110. package/.framework/src/components/admin/PageInstructions.tsx +49 -0
  111. package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
  112. package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
  113. package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
  114. package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
  115. package/.framework/src/components/app-sidebar.tsx +50 -3
  116. package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
  117. package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
  118. package/.framework/src/components/kb/KBArticleList.tsx +557 -0
  119. package/.framework/src/components/layout/AppShell.tsx +54 -16
  120. package/.framework/src/components/layout/Header.tsx +50 -38
  121. package/.framework/src/components/layout/Layout.tsx +48 -32
  122. package/.framework/src/components/layout/Sidebar.tsx +167 -16
  123. package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
  124. package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
  125. package/.framework/src/components/runtime/DataFilters.tsx +78 -1
  126. package/.framework/src/components/runtime/DataHeader.tsx +32 -29
  127. package/.framework/src/components/runtime/DataListPage.tsx +161 -2
  128. package/.framework/src/components/runtime/DataStats.tsx +48 -1
  129. package/.framework/src/components/runtime/DataTable.tsx +153 -1
  130. package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
  131. package/.framework/src/components/runtime/index.ts +16 -9
  132. package/.framework/src/components/search-form.tsx +33 -1
  133. package/.framework/src/components/shared/AgentView.tsx +48 -51
  134. package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
  135. package/.framework/src/components/shared/InviteManager.tsx +113 -52
  136. package/.framework/src/components/shared/MemberList.tsx +182 -0
  137. package/.framework/src/components/shared/SchemaFields.tsx +96 -78
  138. package/.framework/src/components/ui/DataTable.tsx +86 -56
  139. package/.framework/src/components/ui/Form.tsx +188 -109
  140. package/.framework/src/components/ui/ItemCard.tsx +86 -58
  141. package/.framework/src/components/ui/ItemListView.tsx +88 -61
  142. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
  143. package/.framework/src/components/ui/Modal.tsx +81 -4
  144. package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
  145. package/.framework/src/components/ui/accordion.tsx +97 -5
  146. package/.framework/src/components/ui/alert-dialog.tsx +225 -13
  147. package/.framework/src/components/ui/alert.tsx +113 -6
  148. package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
  149. package/.framework/src/components/ui/avatar.tsx +129 -7
  150. package/.framework/src/components/ui/badge.tsx +65 -3
  151. package/.framework/src/components/ui/breadcrumb.tsx +145 -8
  152. package/.framework/src/components/ui/button-group.tsx +81 -4
  153. package/.framework/src/components/ui/button.tsx +49 -2
  154. package/.framework/src/components/ui/calendar.tsx +65 -3
  155. package/.framework/src/components/ui/card.tsx +145 -8
  156. package/.framework/src/components/ui/carousel.tsx +129 -7
  157. package/.framework/src/components/ui/chart.tsx +161 -10
  158. package/.framework/src/components/ui/checkbox.tsx +49 -2
  159. package/.framework/src/components/ui/collapsible.tsx +81 -5
  160. package/.framework/src/components/ui/combobox.tsx +305 -20
  161. package/.framework/src/components/ui/command.tsx +177 -11
  162. package/.framework/src/components/ui/context-menu.tsx +273 -17
  163. package/.framework/src/components/ui/dialog.tsx +209 -27
  164. package/.framework/src/components/ui/direction.tsx +64 -4
  165. package/.framework/src/components/ui/drawer.tsx +193 -12
  166. package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
  167. package/.framework/src/components/ui/empty.tsx +145 -9
  168. package/.framework/src/components/ui/field.tsx +209 -14
  169. package/.framework/src/components/ui/hover-card.tsx +81 -4
  170. package/.framework/src/components/ui/input-group.tsx +162 -10
  171. package/.framework/src/components/ui/input-otp.tsx +98 -5
  172. package/.framework/src/components/ui/input.tsx +49 -2
  173. package/.framework/src/components/ui/item.tsx +225 -13
  174. package/.framework/src/components/ui/kbd.tsx +65 -3
  175. package/.framework/src/components/ui/label.tsx +49 -2
  176. package/.framework/src/components/ui/menubar.tsx +289 -17
  177. package/.framework/src/components/ui/native-select.tsx +97 -5
  178. package/.framework/src/components/ui/navigation-menu.tsx +177 -10
  179. package/.framework/src/components/ui/pagination.tsx +161 -9
  180. package/.framework/src/components/ui/popover.tsx +145 -8
  181. package/.framework/src/components/ui/progress.tsx +49 -2
  182. package/.framework/src/components/ui/radio-group.tsx +65 -3
  183. package/.framework/src/components/ui/resizable.tsx +82 -4
  184. package/.framework/src/components/ui/scroll-area.tsx +65 -3
  185. package/.framework/src/components/ui/select.tsx +193 -11
  186. package/.framework/src/components/ui/separator.tsx +49 -2
  187. package/.framework/src/components/ui/sheet.tsx +193 -11
  188. package/.framework/src/components/ui/sidebar.tsx +481 -38
  189. package/.framework/src/components/ui/skeleton.tsx +49 -2
  190. package/.framework/src/components/ui/slider.tsx +49 -2
  191. package/.framework/src/components/ui/sonner.tsx +49 -2
  192. package/.framework/src/components/ui/spinner.tsx +49 -2
  193. package/.framework/src/components/ui/switch.tsx +49 -2
  194. package/.framework/src/components/ui/table-primitives.tsx +160 -33
  195. package/.framework/src/components/ui/table.tsx +161 -9
  196. package/.framework/src/components/ui/tabs.tsx +113 -6
  197. package/.framework/src/components/ui/textarea.tsx +49 -2
  198. package/.framework/src/components/ui/toggle-group.tsx +81 -4
  199. package/.framework/src/components/ui/toggle.tsx +65 -3
  200. package/.framework/src/components/ui/tooltip.tsx +97 -5
  201. package/.framework/src/contexts/AppContext.tsx +129 -45
  202. package/.framework/src/contexts/AuthContext.tsx +187 -144
  203. package/.framework/src/hooks/use-mobile.ts +48 -2
  204. package/.framework/src/hooks/useApi.ts +208 -159
  205. package/.framework/src/hooks/useAppPath.ts +32 -23
  206. package/.framework/src/hooks/useApps.ts +51 -20
  207. package/.framework/src/hooks/useEntityList.ts +32 -78
  208. package/.framework/src/hooks/useEntityRecord.ts +35 -96
  209. package/.framework/src/hooks/useForm.ts +32 -92
  210. package/.framework/src/hooks/useListSchema.ts +49 -78
  211. package/.framework/src/hooks/useSchemaRecord.ts +114 -105
  212. package/.framework/src/lib/api.ts +195 -116
  213. package/.framework/src/lib/supabase.ts +98 -64
  214. package/.framework/src/lib/utils.ts +287 -151
  215. package/.framework/src/main.tsx +49 -3
  216. package/.framework/src/pages/DashboardPage.tsx +52 -29
  217. package/.framework/src/pages/NotFoundPage.tsx +32 -10
  218. package/.framework/src/pages/SetupPage.tsx +32 -12
  219. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
  220. package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
  221. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
  222. package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
  223. package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
  224. package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
  225. package/.framework/src/pages/admin/AppsPage.tsx +49 -2
  226. package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
  227. package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
  228. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
  229. package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
  230. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
  231. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
  232. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
  233. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
  234. package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
  235. package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
  236. package/.framework/src/pages/admin/LogsPage.tsx +422 -75
  237. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
  238. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
  239. package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
  240. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
  241. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
  242. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
  243. package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
  244. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
  245. package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
  246. package/.framework/src/pages/admin/RolesPage.tsx +171 -18
  247. package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
  248. package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
  249. package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
  250. package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
  251. package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
  252. package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
  253. package/.framework/src/pages/admin/TimersPage.tsx +48 -20
  254. package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
  255. package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
  256. package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
  257. package/.framework/src/pages/admin/TypesPage.tsx +48 -20
  258. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
  259. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
  260. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
  261. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
  262. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
  263. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
  264. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
  265. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
  266. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
  267. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  268. package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
  269. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
  270. package/.framework/src/pages/auth/LoginPage.tsx +50 -23
  271. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
  272. package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
  273. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
  274. package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
  275. package/.framework/src/types/auth.ts +128 -71
  276. package/.framework/src/types/types.ts +373 -157
  277. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
  278. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
  279. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
  280. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
  281. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
  282. package/docs/README.md +9 -130
  283. package/netlify.toml +5 -2
  284. package/package.json +13 -25
  285. package/scripts/kb-ingest-docs.ts +203 -0
  286. package/spine.config.json +65 -4
  287. package/.framework/migrations/001_schema.sql +0 -425
  288. package/.framework/migrations/002_rls_and_functions.sql +0 -763
  289. package/.framework/migrations/004_enhancements.sql +0 -43
  290. package/.framework/migrations/005_auth_user_trigger.sql +0 -105
  291. package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
  292. package/.framework/migrations/016_invites.sql +0 -27
  293. package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
  294. package/.framework/migrations/018_apps_required_roles.sql +0 -17
  295. package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
  296. package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
  297. package/.framework/migrations/021_app_domains.sql +0 -112
  298. package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
  299. package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
  300. package/.framework/migrations/024_messages_account_id.sql +0 -9
  301. package/.framework/migrations/025_retype_threads_messages.sql +0 -240
  302. package/.framework/migrations/026_fix_rls_policies.sql +0 -47
  303. package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
  304. package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
  305. package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
  306. package/.framework/migrations/030_messages_updated_by.sql +0 -6
  307. package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
  308. package/docs/APPS.md +0 -232
  309. package/docs/ARCHITECTURE.md +0 -246
  310. package/docs/INSTALL.md +0 -155
  311. package/docs/design-schema-spec.md +0 -1837
  312. package/docs/dev-guides/01-create-an-app.md +0 -183
  313. package/docs/dev-guides/02-create-a-function.md +0 -178
  314. package/docs/dev-guides/03-create-a-component.md +0 -178
  315. package/docs/dev-guides/04-webhook-handlers.md +0 -129
  316. package/docs/dev-guides/05-testing.md +0 -168
  317. package/docs/dev-guides/README.md +0 -40
  318. package/docs/npm-packages-plan.md +0 -288
  319. package/docs/spine-core--api-reference.md +0 -822
@@ -1,534 +1,265 @@
1
- /**
2
- * @module src/pages/admin/EmbeddingDetailPage
3
- * @audience installer
4
- * @layer frontend-page
5
- * @stability stable
6
- *
7
- * Create / view / edit page for a single embedding model configuration.
8
- */
9
-
10
- import React, { useState, useEffect } from 'react'
1
+ import React, { useState } from 'react'
11
2
  import { useParams, useNavigate } from 'react-router-dom'
12
3
  import { useApi } from '../../hooks/useApi'
13
4
  import { apiFetch } from '../../lib/api'
14
5
  import { Button } from '../../components/ui/button'
15
- import { Input } from '../../components/ui/input'
16
- import { Textarea } from '../../components/ui/textarea'
17
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../components/ui/select'
18
- import { Label } from '../../components/ui/label'
19
6
  import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/card'
20
7
  import { Skeleton } from '../../components/ui/skeleton'
21
8
  import { Alert, AlertDescription, AlertTitle } from '../../components/ui/alert'
22
- import { ArrowLeft, Pencil, AlertCircle } from 'lucide-react'
9
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../../components/ui/collapsible'
10
+ import { ArrowLeft, AlertCircle, ChevronDown } from 'lucide-react'
11
+ import { formatDateTime } from '../../lib/utils'
12
+ import { PageInstructions } from '../../components/admin/PageInstructions'
23
13
 
24
- interface EmbeddingModel {
14
+ interface EmbeddingRecord {
25
15
  id: string
26
- name: string
27
- description?: string
28
- model_name: string
29
- provider: 'openai' | 'anthropic' | 'local' | 'custom'
30
- config: {
31
- dimension: number
32
- chunk_size: number
33
- overlap: number
34
- batch_size: number
35
- }
36
- is_active: boolean
16
+ account_id: string
17
+ model_id: string
18
+ document_id: string
19
+ chunk_index: number
20
+ content: string
21
+ embedding: number[]
22
+ metadata: Record<string, any> | null
23
+ approved_at: string | null
24
+ revoked_at: string | null
25
+ content_hash: string
37
26
  created_at: string
38
27
  updated_at: string
39
- created_by: string
40
- account_id: string
41
- document_count: number
42
- embedding_count: number
43
- storage_size: number
44
28
  }
45
29
 
46
30
  export function EmbeddingDetailPage() {
47
31
  const { id } = useParams<{ id: string }>()
48
32
  const navigate = useNavigate()
49
- const isCreateMode = !id || id === 'new'
50
-
51
- const [isEditing, setIsEditing] = useState(isCreateMode)
52
- const [editData, setEditData] = useState<Record<string, any>>({})
33
+ const [showVector, setShowVector] = useState(false)
53
34
 
54
- const { data: embedding, loading, error, refetch } = useApi(
35
+ const { data: record, loading, error, refetch } = useApi<EmbeddingRecord>(
55
36
  async () => {
56
- if (isCreateMode) {
57
- return {
58
- id: '',
59
- name: '',
60
- description: '',
61
- model_name: 'text-embedding-ada-002',
62
- provider: 'openai' as const,
63
- config: {
64
- dimension: 1536,
65
- chunk_size: 1000,
66
- overlap: 200,
67
- batch_size: 100
68
- },
69
- is_active: true,
70
- created_at: new Date().toISOString(),
71
- updated_at: new Date().toISOString(),
72
- created_by: '',
73
- account_id: '',
74
- document_count: 0,
75
- embedding_count: 0,
76
- storage_size: 0
77
- }
78
- }
79
-
80
- const response = await apiFetch(`/api/embeddings?action=get&id=${id}`)
81
- if (!response.ok) throw new Error('Failed to fetch embedding')
37
+ const response = await apiFetch(`/api/embeddings?method=GET&action=get&id=${id}`)
38
+ if (!response.ok) throw new Error('Failed to fetch embedding record')
82
39
  const result = await response.json()
83
- return result.data
40
+ return (result.data || result) as EmbeddingRecord
84
41
  },
85
- { immediate: !isCreateMode }
42
+ { immediate: !!id && id !== 'new' }
86
43
  )
87
44
 
88
- useEffect(() => {
89
- if (embedding) {
90
- setEditData({
91
- name: embedding.name,
92
- description: embedding.description || '',
93
- model_name: embedding.model_name,
94
- provider: embedding.provider,
95
- config: embedding.config || {
96
- dimension: 1536,
97
- chunk_size: 1000,
98
- overlap: 200,
99
- batch_size: 100
100
- },
101
- is_active: embedding.is_active
102
- })
103
- }
104
- }, [embedding])
105
-
106
- const handleSave = async () => {
107
- try {
108
- const url = isCreateMode
109
- ? '/api/embeddings?action=create'
110
- : `/api/embeddings?action=update&id=${id}`
111
-
112
- const method = isCreateMode ? 'POST' : 'PATCH'
113
-
114
- const response = await apiFetch(url, {
115
- method,
116
- headers: {
117
- 'Content-Type': 'application/json'
118
- },
119
- body: JSON.stringify(editData)
120
- })
121
-
122
- if (!response.ok) throw new Error('Failed to save embedding')
123
-
124
- if (isCreateMode) {
125
- const result = await response.json()
126
- const newId = result.data?.id || result.id
127
- navigate(`/spine-framework/admin/configs/embeddings/${newId}`)
128
- } else {
129
- await refetch()
130
- setIsEditing(false)
131
- }
132
- } catch (error) {
133
- console.error('Error saving embedding:', error)
134
- }
45
+ if (!id || id === 'new') {
46
+ return (
47
+ <div className="p-8 max-w-3xl mx-auto">
48
+ <Card>
49
+ <CardContent className="p-8 text-center">
50
+ <AlertCircle className="mx-auto h-12 w-12 text-slate-400" />
51
+ <h1 className="mt-4 text-lg font-medium">Not Found</h1>
52
+ <p className="text-sm text-slate-500 mt-2">No embedding ID was provided.</p>
53
+ <Button className="mt-4" onClick={() => navigate('/spine-framework/admin/kb/embeddings')}>
54
+ Back to Embeddings
55
+ </Button>
56
+ </CardContent>
57
+ </Card>
58
+ </div>
59
+ )
135
60
  }
136
61
 
137
- const handleCancel = () => {
138
- if (isCreateMode) {
139
- navigate('/spine-framework/admin/configs/embeddings')
140
- return
141
- }
142
-
143
- if (embedding) {
144
- setEditData({
145
- name: embedding.name,
146
- description: embedding.description || '',
147
- model_name: embedding.model_name,
148
- provider: embedding.provider,
149
- config: embedding.config || {
150
- dimension: 1536,
151
- chunk_size: 1000,
152
- overlap: 200,
153
- batch_size: 100
154
- },
155
- is_active: embedding.is_active
156
- })
157
- }
158
- setIsEditing(false)
62
+ if (loading) {
63
+ return (
64
+ <div className="p-8 space-y-6 max-w-5xl mx-auto">
65
+ <Skeleton className="h-8 w-64" />
66
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
67
+ <Skeleton className="h-32" />
68
+ <Skeleton className="h-32" />
69
+ <Skeleton className="h-32" />
70
+ <Skeleton className="h-32" />
71
+ </div>
72
+ </div>
73
+ )
159
74
  }
160
75
 
161
- const handleEdit = () => {
162
- setIsEditing(true)
76
+ if (error) {
77
+ return (
78
+ <div className="p-8 max-w-5xl mx-auto">
79
+ <Alert variant="destructive">
80
+ <AlertCircle className="h-4 w-4" />
81
+ <AlertTitle>Error</AlertTitle>
82
+ <AlertDescription>{error}</AlertDescription>
83
+ </Alert>
84
+ <div className="mt-4">
85
+ <Button onClick={() => refetch()}>Retry</Button>
86
+ <Button variant="outline" className="ml-2" onClick={() => navigate('/spine-framework/admin/kb/embeddings')}>
87
+ Back
88
+ </Button>
89
+ </div>
90
+ </div>
91
+ )
163
92
  }
164
93
 
165
- if (loading) {
94
+ if (!record) {
166
95
  return (
167
- <div className="space-y-6">
168
- <Skeleton className="h-8 w-48" />
96
+ <div className="p-8 max-w-3xl mx-auto">
169
97
  <Card>
170
- <CardHeader><Skeleton className="h-6 w-32" /></CardHeader>
171
- <CardContent className="space-y-4">
172
- <Skeleton className="h-4 w-full" />
173
- <Skeleton className="h-4 w-full" />
174
- <Skeleton className="h-4 w-2/3" />
98
+ <CardContent className="p-8 text-center">
99
+ <AlertCircle className="mx-auto h-12 w-12 text-slate-400" />
100
+ <h1 className="mt-4 text-lg font-medium">Record not found</h1>
101
+ <Button className="mt-4" onClick={() => navigate('/spine-framework/admin/kb/embeddings')}>
102
+ Back to Embeddings
103
+ </Button>
175
104
  </CardContent>
176
105
  </Card>
177
106
  </div>
178
107
  )
179
108
  }
180
109
 
181
- if (error) {
182
- return (
183
- <Alert variant="destructive">
184
- <AlertCircle className="h-4 w-4" />
185
- <AlertTitle>Failed to load embedding</AlertTitle>
186
- <AlertDescription>{String(error)}</AlertDescription>
187
- </Alert>
188
- )
189
- }
110
+ const status = record.revoked_at
111
+ ? { label: 'Revoked', color: 'bg-red-100 text-red-700' }
112
+ : record.approved_at
113
+ ? { label: 'Approved', color: 'bg-green-100 text-green-700' }
114
+ : { label: 'Pending', color: 'bg-slate-100 text-slate-600' }
190
115
 
191
116
  return (
192
- <div className="space-y-6">
117
+ <div className="p-8 space-y-6 max-w-5xl mx-auto">
193
118
  <div className="flex items-center justify-between">
194
- <div className="flex items-center gap-4">
195
- <Button variant="ghost" onClick={() => isCreateMode ? navigate('/spine-framework/admin/configs/embeddings') : navigate(-1)}>
196
- <ArrowLeft className="w-5 h-5" />
197
- </Button>
198
- <div>
199
- <h1 className="text-2xl font-bold">
200
- {isCreateMode ? 'Create Embedding Model' : embedding?.name || 'Embedding Detail'}
201
- </h1>
202
- <p className="text-sm text-muted-foreground">Embedding Model Configuration</p>
203
- </div>
204
- </div>
205
-
206
- <div className="flex space-x-2">
207
- {isEditing ? (
208
- <>
209
- <Button variant="outline" onClick={handleCancel}>Cancel</Button>
210
- <Button onClick={handleSave}>
211
- {isCreateMode ? 'Create' : 'Save Changes'}
212
- </Button>
213
- </>
214
- ) : (
215
- !isCreateMode && (
216
- <Button variant="outline" onClick={handleEdit}>
217
- <Pencil className="h-4 w-4 mr-2" />
218
- Edit
219
- </Button>
220
- )
221
- )}
119
+ <div>
120
+ <h1 className="text-xl font-semibold">Embedding Record</h1>
121
+ <p className="text-sm text-muted-foreground">{record.id}</p>
222
122
  </div>
123
+ <Button variant="outline" onClick={() => navigate('/spine-framework/admin/kb/embeddings')}>
124
+ <ArrowLeft className="mr-2 h-4 w-4" />
125
+ Back
126
+ </Button>
223
127
  </div>
224
128
 
225
- {isCreateMode ? (
129
+ <PageInstructions
130
+ title="Embedding record inspector"
131
+ description="Read-only view of a single public.embeddings row. It shows the model, document, chunk index, content, content hash, lifecycle timestamps, metadata JSON, and the stored 1536-dimensional embedding vector."
132
+ sections={[
133
+ {
134
+ title: 'Lifecycle',
135
+ content: 'approved_at marks when the chunk was accepted for use; revoked_at marks when it was removed. A record without approved_at is still pending.'
136
+ },
137
+ {
138
+ title: 'Vector',
139
+ content: 'The embedding array is hidden by default; expand the section to inspect all 1536 numeric values.'
140
+ }
141
+ ]}
142
+ />
143
+
144
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
226
145
  <Card>
227
- <CardContent className="p-6 space-y-6">
228
- <div>
229
- <h3 className="text-lg font-medium mb-4">Basic Information</h3>
230
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
231
- <div className="space-y-2">
232
- <Label>Name</Label>
233
- <Input
234
- value={editData.name || ''}
235
- onChange={(e) => setEditData({...editData, name: e.target.value})}
236
- placeholder="Enter model name"
237
- />
238
- </div>
239
- <div className="space-y-2">
240
- <Label>Provider</Label>
241
- <Select value={editData.provider || 'openai'} onValueChange={(v) => setEditData({...editData, provider: v})}>
242
- <SelectTrigger>
243
- <SelectValue />
244
- </SelectTrigger>
245
- <SelectContent>
246
- <SelectItem value="openai">OpenAI</SelectItem>
247
- <SelectItem value="anthropic">Anthropic</SelectItem>
248
- <SelectItem value="local">Local</SelectItem>
249
- <SelectItem value="custom">Custom</SelectItem>
250
- </SelectContent>
251
- </Select>
252
- </div>
253
- </div>
254
- <div className="mt-4 space-y-2">
255
- <Label>Description</Label>
256
- <Textarea
257
- value={editData.description || ''}
258
- onChange={(e) => setEditData({...editData, description: e.target.value})}
259
- rows={3}
260
- placeholder="Describe this embedding model"
261
- />
262
- </div>
263
- <div className="mt-4 space-y-2">
264
- <Label>Model Name</Label>
265
- <Input
266
- value={editData.model_name || ''}
267
- onChange={(e) => setEditData({...editData, model_name: e.target.value})}
268
- placeholder="e.g., text-embedding-ada-002"
269
- />
270
- </div>
271
- </div>
272
-
273
- <div>
274
- <h3 className="text-lg font-medium mb-4">Configuration</h3>
275
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
276
- <div className="space-y-2">
277
- <Label>Dimensions</Label>
278
- <Input
279
- type="number"
280
- value={editData.config?.dimension || 1536}
281
- onChange={(e) => setEditData({
282
- ...editData,
283
- config: {
284
- ...editData.config,
285
- dimension: parseInt(e.target.value)
286
- }
287
- })}
288
- />
289
- </div>
290
- <div className="space-y-2">
291
- <Label>Chunk Size</Label>
292
- <Input
293
- type="number"
294
- value={editData.config?.chunk_size || 1000}
295
- onChange={(e) => setEditData({
296
- ...editData,
297
- config: {
298
- ...editData.config,
299
- chunk_size: parseInt(e.target.value)
300
- }
301
- })}
302
- />
303
- </div>
304
- <div className="space-y-2">
305
- <Label>Overlap</Label>
306
- <Input
307
- type="number"
308
- value={editData.config?.overlap || 200}
309
- onChange={(e) => setEditData({
310
- ...editData,
311
- config: {
312
- ...editData.config,
313
- overlap: parseInt(e.target.value)
314
- }
315
- })}
316
- />
317
- </div>
318
- <div className="space-y-2">
319
- <Label>Batch Size</Label>
320
- <Input
321
- type="number"
322
- value={editData.config?.batch_size || 100}
323
- onChange={(e) => setEditData({
324
- ...editData,
325
- config: {
326
- ...editData.config,
327
- batch_size: parseInt(e.target.value)
328
- }
329
- })}
330
- />
331
- </div>
332
- </div>
333
- </div>
146
+ <CardHeader>
147
+ <CardTitle className="text-sm font-medium text-slate-500">Model ID</CardTitle>
148
+ </CardHeader>
149
+ <CardContent>
150
+ <p className="text-sm text-slate-900 break-all">{record.model_id}</p>
334
151
  </CardContent>
335
152
  </Card>
336
- ) : embedding ? (
337
- <div className="space-y-6">
338
- <Card>
339
- <CardContent className="p-6">
340
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
341
- <div>
342
- <h3 className="text-lg font-medium mb-4">Basic Information</h3>
343
- <div className="space-y-4">
344
- <div className="flex justify-between items-center">
345
- <span className="text-sm text-muted-foreground">Name:</span>
346
- {isEditing ? (
347
- <Input
348
- value={editData.name || ''}
349
- onChange={(e) => setEditData({...editData, name: e.target.value})}
350
- className="w-48"
351
- />
352
- ) : (
353
- <span className="text-sm font-medium">{embedding.name}</span>
354
- )}
355
- </div>
356
- <div className="flex justify-between items-center">
357
- <span className="text-sm text-muted-foreground">Provider:</span>
358
- {isEditing ? (
359
- <Select value={editData.provider || embedding.provider} onValueChange={(v) => setEditData({...editData, provider: v})}>
360
- <SelectTrigger className="w-48">
361
- <SelectValue />
362
- </SelectTrigger>
363
- <SelectContent>
364
- <SelectItem value="openai">OpenAI</SelectItem>
365
- <SelectItem value="anthropic">Anthropic</SelectItem>
366
- <SelectItem value="local">Local</SelectItem>
367
- <SelectItem value="custom">Custom</SelectItem>
368
- </SelectContent>
369
- </Select>
370
- ) : (
371
- <span className="text-sm">{embedding.provider}</span>
372
- )}
373
- </div>
374
- <div className="flex justify-between items-start">
375
- <span className="text-sm text-muted-foreground">Description:</span>
376
- {isEditing ? (
377
- <Textarea
378
- value={editData.description || ''}
379
- onChange={(e) => setEditData({...editData, description: e.target.value})}
380
- rows={3}
381
- className="w-48"
382
- />
383
- ) : (
384
- <span className="text-sm">{embedding.description || '—'}</span>
385
- )}
386
- </div>
387
- <div className="flex justify-between items-center">
388
- <span className="text-sm text-muted-foreground">Model Name:</span>
389
- {isEditing ? (
390
- <Input
391
- value={editData.model_name || embedding.model_name}
392
- onChange={(e) => setEditData({...editData, model_name: e.target.value})}
393
- className="w-48"
394
- />
395
- ) : (
396
- <span className="text-sm font-mono">{embedding.model_name}</span>
397
- )}
398
- </div>
399
- <div className="flex justify-between items-center">
400
- <span className="text-sm text-muted-foreground">Active:</span>
401
- {isEditing ? (
402
- <Select value={String(editData.is_active)} onValueChange={(v) => setEditData({...editData, is_active: v === 'true'})}>
403
- <SelectTrigger className="w-48">
404
- <SelectValue />
405
- </SelectTrigger>
406
- <SelectContent>
407
- <SelectItem value="true">Yes</SelectItem>
408
- <SelectItem value="false">No</SelectItem>
409
- </SelectContent>
410
- </Select>
411
- ) : (
412
- <span className="text-sm">{embedding.is_active ? 'Yes' : 'No'}</span>
413
- )}
414
- </div>
415
- </div>
416
- </div>
417
-
418
- <div>
419
- <h3 className="text-lg font-medium mb-4">Configuration</h3>
420
- <div className="space-y-4">
421
- <div className="flex justify-between items-center">
422
- <span className="text-sm text-muted-foreground">Dimensions:</span>
423
- {isEditing ? (
424
- <Input
425
- type="number"
426
- value={editData.config?.dimension || embedding.config?.dimension}
427
- onChange={(e) => setEditData({
428
- ...editData,
429
- config: {
430
- ...editData.config,
431
- dimension: parseInt(e.target.value)
432
- }
433
- })}
434
- className="w-24"
435
- />
436
- ) : (
437
- <span className="text-sm">{embedding.config?.dimension || '—'}</span>
438
- )}
439
- </div>
440
- <div className="flex justify-between items-center">
441
- <span className="text-sm text-muted-foreground">Chunk Size:</span>
442
- {isEditing ? (
443
- <Input
444
- type="number"
445
- value={editData.config?.chunk_size || embedding.config?.chunk_size}
446
- onChange={(e) => setEditData({
447
- ...editData,
448
- config: {
449
- ...editData.config,
450
- chunk_size: parseInt(e.target.value)
451
- }
452
- })}
453
- className="w-24"
454
- />
455
- ) : (
456
- <span className="text-sm">{embedding.config?.chunk_size || '—'}</span>
457
- )}
458
- </div>
459
- <div className="flex justify-between items-center">
460
- <span className="text-sm text-muted-foreground">Overlap:</span>
461
- {isEditing ? (
462
- <Input
463
- type="number"
464
- value={editData.config?.overlap || embedding.config?.overlap}
465
- onChange={(e) => setEditData({
466
- ...editData,
467
- config: {
468
- ...editData.config,
469
- overlap: parseInt(e.target.value)
470
- }
471
- })}
472
- className="w-24"
473
- />
474
- ) : (
475
- <span className="text-sm">{embedding.config?.overlap || '—'}</span>
476
- )}
477
- </div>
478
- <div className="flex justify-between items-center">
479
- <span className="text-sm text-muted-foreground">Batch Size:</span>
480
- {isEditing ? (
481
- <Input
482
- type="number"
483
- value={editData.config?.batch_size || embedding.config?.batch_size}
484
- onChange={(e) => setEditData({
485
- ...editData,
486
- config: {
487
- ...editData.config,
488
- batch_size: parseInt(e.target.value)
489
- }
490
- })}
491
- className="w-24"
492
- />
493
- ) : (
494
- <span className="text-sm">{embedding.config?.batch_size || '—'}</span>
495
- )}
496
- </div>
497
- </div>
498
- </div>
499
- </div>
500
- </CardContent>
501
- </Card>
502
-
503
- <Card>
504
- <CardHeader>
505
- <CardTitle>Usage Statistics</CardTitle>
506
- </CardHeader>
507
- <CardContent>
508
- <dl className="grid grid-cols-1 md:grid-cols-2 gap-4">
509
- <div><dt className="text-sm text-muted-foreground">Documents</dt><dd className="font-mono text-sm">{embedding.document_count}</dd></div>
510
- <div><dt className="text-sm text-muted-foreground">Embeddings</dt><dd className="font-mono text-sm">{embedding.embedding_count}</dd></div>
511
- <div><dt className="text-sm text-muted-foreground">Storage Size</dt><dd className="font-mono text-sm">{Math.round(embedding.storage_size / 1024 / 1024 * 100) / 100} MB</dd></div>
512
- <div><dt className="text-sm text-muted-foreground">Created</dt><dd className="font-mono text-sm">{new Date(embedding.created_at).toLocaleString()}</dd></div>
513
- </dl>
514
- </CardContent>
515
- </Card>
516
-
517
- <Card>
518
- <CardHeader>
519
- <CardTitle>Additional Information</CardTitle>
520
- </CardHeader>
153
+
154
+ <Card>
155
+ <CardHeader>
156
+ <CardTitle className="text-sm font-medium text-slate-500">Document ID</CardTitle>
157
+ </CardHeader>
158
+ <CardContent>
159
+ <p className="text-sm text-slate-900 break-all">{record.document_id}</p>
160
+ </CardContent>
161
+ </Card>
162
+
163
+ <Card>
164
+ <CardHeader>
165
+ <CardTitle className="text-sm font-medium text-slate-500">Chunk Index</CardTitle>
166
+ </CardHeader>
167
+ <CardContent>
168
+ <p className="text-sm text-slate-900">{record.chunk_index}</p>
169
+ </CardContent>
170
+ </Card>
171
+
172
+ <Card>
173
+ <CardHeader>
174
+ <CardTitle className="text-sm font-medium text-slate-500">Status</CardTitle>
175
+ </CardHeader>
176
+ <CardContent>
177
+ <span className={`inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium ${status.color}`}>
178
+ {status.label}
179
+ </span>
180
+ </CardContent>
181
+ </Card>
182
+
183
+ <Card>
184
+ <CardHeader>
185
+ <CardTitle className="text-sm font-medium text-slate-500">Content Hash</CardTitle>
186
+ </CardHeader>
187
+ <CardContent>
188
+ <p className="text-xs text-slate-500 font-mono break-all">{record.content_hash}</p>
189
+ </CardContent>
190
+ </Card>
191
+
192
+ <Card>
193
+ <CardHeader>
194
+ <CardTitle className="text-sm font-medium text-slate-500">Created / Updated</CardTitle>
195
+ </CardHeader>
196
+ <CardContent className="space-y-1">
197
+ <p className="text-sm text-slate-900">Created: {formatDateTime(record.created_at)}</p>
198
+ <p className="text-sm text-slate-900">Updated: {formatDateTime(record.updated_at)}</p>
199
+ </CardContent>
200
+ </Card>
201
+
202
+ <Card>
203
+ <CardHeader>
204
+ <CardTitle className="text-sm font-medium text-slate-500">Approved At</CardTitle>
205
+ </CardHeader>
206
+ <CardContent>
207
+ <p className="text-sm text-slate-900">{record.approved_at ? formatDateTime(record.approved_at) : '—'}</p>
208
+ </CardContent>
209
+ </Card>
210
+
211
+ <Card>
212
+ <CardHeader>
213
+ <CardTitle className="text-sm font-medium text-slate-500">Revoked At</CardTitle>
214
+ </CardHeader>
215
+ <CardContent>
216
+ <p className="text-sm text-slate-900">{record.revoked_at ? formatDateTime(record.revoked_at) : '—'}</p>
217
+ </CardContent>
218
+ </Card>
219
+ </div>
220
+
221
+ <Card>
222
+ <CardHeader>
223
+ <CardTitle className="text-sm font-medium text-slate-500">Content</CardTitle>
224
+ </CardHeader>
225
+ <CardContent>
226
+ <pre className="whitespace-pre-wrap break-words max-h-96 overflow-auto rounded bg-slate-50 p-3 text-sm font-mono border border-slate-200">
227
+ {record.content || '—'}
228
+ </pre>
229
+ </CardContent>
230
+ </Card>
231
+
232
+ <Card>
233
+ <CardHeader>
234
+ <CardTitle className="text-sm font-medium text-slate-500">Metadata</CardTitle>
235
+ </CardHeader>
236
+ <CardContent>
237
+ <pre className="whitespace-pre-wrap break-words max-h-80 overflow-auto rounded bg-slate-50 p-3 text-xs font-mono border border-slate-200">
238
+ {JSON.stringify(record.metadata, null, 2)}
239
+ </pre>
240
+ </CardContent>
241
+ </Card>
242
+
243
+ <Card>
244
+ <Collapsible open={showVector} onOpenChange={setShowVector}>
245
+ <CollapsibleTrigger asChild>
246
+ <Button
247
+ variant="ghost"
248
+ className="w-full justify-between px-6 py-4 h-auto font-medium text-sm text-slate-500 hover:text-slate-900 group"
249
+ >
250
+ <span>Embedding vector ({record.embedding?.length ?? 0} dimensions)</span>
251
+ <ChevronDown className="h-4 w-4 transition-transform group-data-[state=open]:rotate-180" />
252
+ </Button>
253
+ </CollapsibleTrigger>
254
+ <CollapsibleContent>
521
255
  <CardContent>
522
- <dl className="grid grid-cols-1 md:grid-cols-2 gap-4">
523
- <div><dt className="text-sm text-muted-foreground">ID</dt><dd className="font-mono text-sm">{embedding.id}</dd></div>
524
- <div><dt className="text-sm text-muted-foreground">Updated</dt><dd className="font-mono text-sm">{new Date(embedding.updated_at).toLocaleString()}</dd></div>
525
- <div><dt className="text-sm text-muted-foreground">Created By</dt><dd className="font-mono text-sm">{embedding.created_by}</dd></div>
526
- <div><dt className="text-sm text-muted-foreground">Account ID</dt><dd className="font-mono text-sm">{embedding.account_id}</dd></div>
527
- </dl>
256
+ <pre className="whitespace-pre-wrap break-words max-h-96 overflow-auto rounded bg-slate-50 p-3 text-xs font-mono border border-slate-200">
257
+ {JSON.stringify(record.embedding, null, 2)}
258
+ </pre>
528
259
  </CardContent>
529
- </Card>
530
- </div>
531
- ) : null}
260
+ </CollapsibleContent>
261
+ </Collapsible>
262
+ </Card>
532
263
  </div>
533
264
  )
534
265
  }