spine-framework 0.3.125 → 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.
- package/.framework/cli/commands/agents.ts +35 -37
- package/.framework/cli/commands/auth.ts +35 -34
- package/.framework/cli/commands/create-app.ts +104 -40
- package/.framework/cli/commands/dev.ts +104 -69
- package/.framework/cli/commands/doctor.ts +169 -97
- package/.framework/cli/commands/generate.ts +82 -160
- package/.framework/cli/commands/init.ts +72 -34
- package/.framework/cli/commands/install-app.ts +260 -125
- package/.framework/cli/commands/items.ts +33 -44
- package/.framework/cli/commands/migrate.ts +122 -40
- package/.framework/cli/commands/migrations.ts +33 -40
- package/.framework/cli/commands/pipelines.ts +31 -39
- package/.framework/cli/commands/status.ts +47 -23
- package/.framework/cli/commands/system.ts +47 -37
- package/.framework/cli/commands/test.ts +112 -68
- package/.framework/cli/commands/validate-app.ts +215 -121
- package/.framework/cli/context.ts +112 -130
- package/.framework/cli/env-loader.ts +47 -18
- package/.framework/cli/index.ts +81 -42
- package/.framework/docs/runtime-types.md +1024 -0
- package/.framework/functions/_shared/agent-runner.ts +1296 -961
- package/.framework/functions/_shared/app-manifest.ts +82 -68
- package/.framework/functions/_shared/audit.ts +135 -83
- package/.framework/functions/_shared/{cil-state.ts → cil.ts} +147 -54
- package/.framework/functions/_shared/db.ts +80 -126
- package/.framework/functions/_shared/hash-api-key.ts +63 -0
- package/.framework/functions/_shared/index.ts +222 -347
- package/.framework/functions/_shared/kb.ts +319 -0
- package/.framework/functions/_shared/llm.ts +92 -0
- package/.framework/functions/_shared/logging.ts +288 -0
- package/.framework/functions/_shared/middleware.ts +329 -365
- package/.framework/functions/_shared/outcomes.ts +36 -0
- package/.framework/functions/_shared/permissions.ts +359 -643
- package/.framework/functions/_shared/pipeline-runner.ts +243 -316
- package/.framework/functions/_shared/principal.ts +376 -468
- package/.framework/functions/_shared/resolve-credentials.ts +102 -0
- package/.framework/functions/_shared/resolve-ids.ts +162 -14
- package/.framework/functions/_shared/retry-deliveries.ts +104 -0
- package/.framework/functions/_shared/schema-utils.ts +611 -373
- package/.framework/functions/_shared/testing.ts +207 -137
- package/.framework/functions/_shared/tracing.ts +276 -0
- package/.framework/functions/_shared/trigger-engine.ts +159 -211
- package/.framework/functions/_shared/webhook-registration.ts +95 -81
- package/.framework/functions/_shared/webhook-registry.ts +153 -75
- package/.framework/functions/account-nodes.ts +31 -53
- package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
- package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
- package/.framework/functions/adapters/openai-outbound.ts +40 -0
- package/.framework/functions/admin-data.ts +687 -533
- package/.framework/functions/admin-funnel.ts +31 -20
- package/.framework/functions/admin-install-funnel.ts +47 -28
- package/.framework/functions/admin-signals.ts +47 -29
- package/.framework/functions/ai-agents.ts +122 -147
- package/.framework/functions/ai-interactions.ts +180 -90
- package/.framework/functions/api-keys.ts +250 -192
- package/.framework/functions/apps.ts +203 -197
- package/.framework/functions/auth.ts +98 -91
- package/.framework/functions/cil-agent-ingest.ts +773 -0
- package/.framework/functions/cil-agent.ts +592 -0
- package/.framework/functions/cil-callmap.ts +1227 -0
- package/.framework/functions/cil-config.ts +583 -0
- package/.framework/functions/cil-context.ts +445 -0
- package/.framework/functions/cil-doc.ts +1752 -0
- package/.framework/functions/cil-embeddings.ts +1202 -0
- package/.framework/functions/cil-graph.ts +585 -0
- package/.framework/functions/cil-ingest.ts +2416 -0
- package/.framework/functions/cil-pipeline.ts +318 -0
- package/.framework/functions/cil-project.ts +499 -0
- package/.framework/functions/cil-validate.ts +469 -0
- package/.framework/functions/debug-auth.ts +73 -53
- package/.framework/functions/deliveries.ts +117 -0
- package/.framework/functions/embeddings.ts +528 -516
- package/.framework/functions/eval-harness.ts +158 -106
- package/.framework/functions/integration-outbound.ts +311 -0
- package/.framework/functions/integration-routes.ts +240 -466
- package/.framework/functions/integrations.ts +132 -140
- package/.framework/functions/invites.ts +401 -201
- package/.framework/functions/item-progress.ts +113 -53
- package/.framework/functions/kb.ts +28 -0
- package/.framework/functions/logs.ts +239 -222
- package/.framework/functions/observability.ts +308 -259
- package/.framework/functions/pipeline-executions.ts +179 -235
- package/.framework/functions/pipelines.ts +163 -205
- package/.framework/functions/prompt-configs.ts +109 -156
- package/.framework/functions/roles.ts +97 -147
- package/.framework/functions/system-cron.ts +132 -218
- package/.framework/functions/system.ts +112 -52
- package/.framework/functions/tests.ts +32 -28
- package/.framework/functions/timers.ts +162 -210
- package/.framework/functions/triggers.ts +282 -346
- package/.framework/functions/types.ts +283 -400
- package/.framework/migrations/001_core_schema.sql +630 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
- package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
- package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
- package/.framework/migrations/005_core_observability.sql +598 -0
- package/.framework/migrations/006_security_hardening_rls.sql +97 -0
- package/.framework/migrations/007_kb_article_list.sql +237 -0
- package/.framework/migrations/008_items_target.sql +8 -0
- package/.framework/migrations/009_roles_slug_unique.sql +11 -0
- package/.framework/public/spine.config.json +91 -0
- package/.framework/scripts/app-install-cli.ts +112 -98
- package/.framework/src/App.tsx +67 -20
- package/.framework/src/apps/admin/index.tsx +97 -27
- package/.framework/src/components/AppWrapper.tsx +50 -11
- package/.framework/src/components/CustomAppLoader.tsx +131 -22
- package/.framework/src/components/admin/AdminListPage.tsx +75 -8
- package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
- package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
- package/.framework/src/components/admin/PageInstructions.tsx +49 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
- package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
- package/.framework/src/components/app-sidebar.tsx +50 -3
- package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
- package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
- package/.framework/src/components/kb/KBArticleList.tsx +557 -0
- package/.framework/src/components/layout/AppShell.tsx +54 -16
- package/.framework/src/components/layout/Header.tsx +50 -38
- package/.framework/src/components/layout/Layout.tsx +48 -32
- package/.framework/src/components/layout/Sidebar.tsx +167 -16
- package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
- package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
- package/.framework/src/components/runtime/DataFilters.tsx +78 -1
- package/.framework/src/components/runtime/DataHeader.tsx +32 -29
- package/.framework/src/components/runtime/DataListPage.tsx +161 -2
- package/.framework/src/components/runtime/DataStats.tsx +48 -1
- package/.framework/src/components/runtime/DataTable.tsx +153 -1
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
- package/.framework/src/components/runtime/index.ts +16 -9
- package/.framework/src/components/search-form.tsx +33 -1
- package/.framework/src/components/shared/AgentView.tsx +48 -51
- package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
- package/.framework/src/components/shared/InviteManager.tsx +113 -52
- package/.framework/src/components/shared/MemberList.tsx +182 -0
- package/.framework/src/components/shared/SchemaFields.tsx +96 -78
- package/.framework/src/components/ui/DataTable.tsx +86 -56
- package/.framework/src/components/ui/Form.tsx +188 -109
- package/.framework/src/components/ui/ItemCard.tsx +86 -58
- package/.framework/src/components/ui/ItemListView.tsx +88 -61
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
- package/.framework/src/components/ui/Modal.tsx +81 -4
- package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
- package/.framework/src/components/ui/accordion.tsx +97 -5
- package/.framework/src/components/ui/alert-dialog.tsx +225 -13
- package/.framework/src/components/ui/alert.tsx +113 -6
- package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
- package/.framework/src/components/ui/avatar.tsx +129 -7
- package/.framework/src/components/ui/badge.tsx +65 -3
- package/.framework/src/components/ui/breadcrumb.tsx +145 -8
- package/.framework/src/components/ui/button-group.tsx +81 -4
- package/.framework/src/components/ui/button.tsx +49 -2
- package/.framework/src/components/ui/calendar.tsx +65 -3
- package/.framework/src/components/ui/card.tsx +145 -8
- package/.framework/src/components/ui/carousel.tsx +129 -7
- package/.framework/src/components/ui/chart.tsx +161 -10
- package/.framework/src/components/ui/checkbox.tsx +49 -2
- package/.framework/src/components/ui/collapsible.tsx +81 -5
- package/.framework/src/components/ui/combobox.tsx +305 -20
- package/.framework/src/components/ui/command.tsx +177 -11
- package/.framework/src/components/ui/context-menu.tsx +273 -17
- package/.framework/src/components/ui/dialog.tsx +209 -27
- package/.framework/src/components/ui/direction.tsx +64 -4
- package/.framework/src/components/ui/drawer.tsx +193 -12
- package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
- package/.framework/src/components/ui/empty.tsx +145 -9
- package/.framework/src/components/ui/field.tsx +209 -14
- package/.framework/src/components/ui/hover-card.tsx +81 -4
- package/.framework/src/components/ui/input-group.tsx +162 -10
- package/.framework/src/components/ui/input-otp.tsx +98 -5
- package/.framework/src/components/ui/input.tsx +49 -2
- package/.framework/src/components/ui/item.tsx +225 -13
- package/.framework/src/components/ui/kbd.tsx +65 -3
- package/.framework/src/components/ui/label.tsx +49 -2
- package/.framework/src/components/ui/menubar.tsx +289 -17
- package/.framework/src/components/ui/native-select.tsx +97 -5
- package/.framework/src/components/ui/navigation-menu.tsx +177 -10
- package/.framework/src/components/ui/pagination.tsx +161 -9
- package/.framework/src/components/ui/popover.tsx +145 -8
- package/.framework/src/components/ui/progress.tsx +49 -2
- package/.framework/src/components/ui/radio-group.tsx +65 -3
- package/.framework/src/components/ui/resizable.tsx +82 -4
- package/.framework/src/components/ui/scroll-area.tsx +65 -3
- package/.framework/src/components/ui/select.tsx +193 -11
- package/.framework/src/components/ui/separator.tsx +49 -2
- package/.framework/src/components/ui/sheet.tsx +193 -11
- package/.framework/src/components/ui/sidebar.tsx +481 -38
- package/.framework/src/components/ui/skeleton.tsx +49 -2
- package/.framework/src/components/ui/slider.tsx +49 -2
- package/.framework/src/components/ui/sonner.tsx +49 -2
- package/.framework/src/components/ui/spinner.tsx +49 -2
- package/.framework/src/components/ui/switch.tsx +49 -2
- package/.framework/src/components/ui/table-primitives.tsx +160 -33
- package/.framework/src/components/ui/table.tsx +161 -9
- package/.framework/src/components/ui/tabs.tsx +113 -6
- package/.framework/src/components/ui/textarea.tsx +49 -2
- package/.framework/src/components/ui/toggle-group.tsx +81 -4
- package/.framework/src/components/ui/toggle.tsx +65 -3
- package/.framework/src/components/ui/tooltip.tsx +97 -5
- package/.framework/src/contexts/AppContext.tsx +129 -45
- package/.framework/src/contexts/AuthContext.tsx +173 -145
- package/.framework/src/hooks/use-mobile.ts +48 -2
- package/.framework/src/hooks/useApi.ts +208 -159
- package/.framework/src/hooks/useAppPath.ts +32 -23
- package/.framework/src/hooks/useApps.ts +51 -20
- package/.framework/src/hooks/useEntityList.ts +32 -78
- package/.framework/src/hooks/useEntityRecord.ts +35 -96
- package/.framework/src/hooks/useForm.ts +32 -92
- package/.framework/src/hooks/useListSchema.ts +49 -78
- package/.framework/src/hooks/useSchemaRecord.ts +114 -105
- package/.framework/src/lib/api.ts +192 -115
- package/.framework/src/lib/supabase.ts +98 -64
- package/.framework/src/lib/utils.ts +287 -151
- package/.framework/src/main.tsx +49 -3
- package/.framework/src/pages/DashboardPage.tsx +52 -29
- package/.framework/src/pages/NotFoundPage.tsx +32 -10
- package/.framework/src/pages/SetupPage.tsx +32 -12
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
- package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
- package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
- package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
- package/.framework/src/pages/admin/AppsPage.tsx +49 -2
- package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
- package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
- package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
- package/.framework/src/pages/admin/LogsPage.tsx +422 -75
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
- package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
- package/.framework/src/pages/admin/RolesPage.tsx +171 -18
- package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
- package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
- package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
- package/.framework/src/pages/admin/TimersPage.tsx +48 -20
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
- package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
- package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
- package/.framework/src/pages/admin/TypesPage.tsx +48 -20
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
- package/.framework/src/pages/auth/LoginPage.tsx +50 -23
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
- package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
- package/.framework/src/types/auth.ts +128 -71
- package/.framework/src/types/types.ts +373 -157
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
- package/docs/README.md +9 -130
- package/netlify.toml +3 -3
- package/package.json +13 -25
- package/scripts/kb-ingest-docs.ts +203 -0
- package/spine.config.json +65 -4
- package/.framework/migrations/001_schema.sql +0 -425
- package/.framework/migrations/002_rls_and_functions.sql +0 -763
- package/.framework/migrations/004_enhancements.sql +0 -43
- package/.framework/migrations/005_auth_user_trigger.sql +0 -105
- package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
- package/.framework/migrations/016_invites.sql +0 -27
- package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
- package/.framework/migrations/018_apps_required_roles.sql +0 -17
- package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
- package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
- package/.framework/migrations/021_app_domains.sql +0 -112
- package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
- package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
- package/.framework/migrations/024_messages_account_id.sql +0 -9
- package/.framework/migrations/025_retype_threads_messages.sql +0 -240
- package/.framework/migrations/026_fix_rls_policies.sql +0 -47
- package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
- package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
- package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
- package/.framework/migrations/030_messages_updated_by.sql +0 -6
- package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
- package/docs/APPS.md +0 -232
- package/docs/ARCHITECTURE.md +0 -246
- package/docs/INSTALL.md +0 -155
- package/docs/design-schema-spec.md +0 -1837
- package/docs/dev-guides/01-create-an-app.md +0 -183
- package/docs/dev-guides/02-create-a-function.md +0 -178
- package/docs/dev-guides/03-create-a-component.md +0 -178
- package/docs/dev-guides/04-webhook-handlers.md +0 -129
- package/docs/dev-guides/05-testing.md +0 -168
- package/docs/dev-guides/README.md +0 -40
- package/docs/npm-packages-plan.md +0 -288
- package/docs/spine-core--api-reference.md +0 -822
|
@@ -0,0 +1,1202 @@
|
|
|
1
|
+
|
|
2
|
+
const EMBED_SOURCE_STATUS: Record<'first' | 'second', { prior: string; target: string }> = {
|
|
3
|
+
first: { prior: '5-documented', target: '6-embedded' },
|
|
4
|
+
second: { prior: '8-enriched', target: '9-embedded' },
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function getEmbedSourceFilePaths(projectItem: any, secondPass: boolean): string[] {
|
|
8
|
+
const fileStatuses: Record<string, string> = projectItem?.data?.file_statuses || {}
|
|
9
|
+
const { prior, target } = secondPass ? EMBED_SOURCE_STATUS.second : EMBED_SOURCE_STATUS.first
|
|
10
|
+
return Object.entries(fileStatuses)
|
|
11
|
+
.filter(([, status]) => status === prior || status === target)
|
|
12
|
+
.map(([filePath]) => filePath)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function loadSourceChunks(
|
|
16
|
+
projectId: string,
|
|
17
|
+
sourceFilePaths: string[],
|
|
18
|
+
offset: number,
|
|
19
|
+
limit: number
|
|
20
|
+
): Promise<{ chunks: any[]; total: number }> {
|
|
21
|
+
if (sourceFilePaths.length === 0) return { chunks: [], total: 0 }
|
|
22
|
+
|
|
23
|
+
// Keep the REST URL under the ~8k character limit while still using the
|
|
24
|
+
// indexed file_path filter instead of scanning every project chunk page.
|
|
25
|
+
const FILE_PATH_IN_BATCH = 50
|
|
26
|
+
const allIds: string[] = []
|
|
27
|
+
let total = 0
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < sourceFilePaths.length; i += FILE_PATH_IN_BATCH) {
|
|
30
|
+
const batch = sourceFilePaths.slice(i, i + FILE_PATH_IN_BATCH)
|
|
31
|
+
const [{ count }, { data, error }] = await Promise.all([
|
|
32
|
+
adminDb
|
|
33
|
+
.from('items')
|
|
34
|
+
.select('*', { count: 'exact', head: true })
|
|
35
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
36
|
+
.filter('data->>project_id', 'eq', projectId)
|
|
37
|
+
.eq('is_active', true)
|
|
38
|
+
.in('data->>file_path' as any, batch),
|
|
39
|
+
adminDb
|
|
40
|
+
.from('items')
|
|
41
|
+
.select('id, data->>file_path')
|
|
42
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
43
|
+
.filter('data->>project_id', 'eq', projectId)
|
|
44
|
+
.eq('is_active', true)
|
|
45
|
+
.in('data->>file_path' as any, batch)
|
|
46
|
+
.order('id'),
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
if (error) throw new Error(`Failed to load source chunk ids: ${error.message}`)
|
|
50
|
+
total += count || 0
|
|
51
|
+
for (const row of data || []) allIds.push(row.id)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
allIds.sort()
|
|
55
|
+
const pageIds = allIds.slice(offset, offset + limit)
|
|
56
|
+
if (pageIds.length === 0) return { chunks: [], total }
|
|
57
|
+
|
|
58
|
+
const { data: chunks, error } = await adminDb
|
|
59
|
+
.from('items')
|
|
60
|
+
.select('id, title, data')
|
|
61
|
+
.in('id', pageIds)
|
|
62
|
+
|
|
63
|
+
if (error) throw new Error(`Failed to load source chunks: ${error.message}`)
|
|
64
|
+
|
|
65
|
+
const chunkMap = new Map((chunks || []).map((c: any) => [c.id, c]))
|
|
66
|
+
const ordered = pageIds.map(id => chunkMap.get(id)).filter(Boolean) as any[]
|
|
67
|
+
return { chunks: ordered, total }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_module_header_1784235266" type="module_header" identity="module_header" parent="none"]
|
|
71
|
+
// CIL[DOC-MACRO purpose="Module header and top-level exports for cortex.functions.custom_cil-embeddings"]
|
|
72
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Own module-level imports, exports, and configuration for cortex.functions.custom_cil-embeddings"]
|
|
73
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[\".framework/migrations/015_simplify_apps_table.sql\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/components/ui/table-primitives.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
|
|
74
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="module exports and side effects" errors="none" side_effects="none"]
|
|
75
|
+
// CIL[DOC-MICRO logic_summary="Declares module scope, imports dependencies, and exports public surface of cortex.functions.custom_cil-embeddings" notable_decisions="[]"]
|
|
76
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
77
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
78
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
79
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: .framework/migrations/015_simplify_apps_table.sql, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/components/ui/table-primitives.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
|
|
80
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
81
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
82
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
83
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
84
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
import { createHandler } from './_shared/middleware'
|
|
90
|
+
import { adminDb } from './_shared/db'
|
|
91
|
+
import { generate } from './embeddings'
|
|
92
|
+
import { callOpenAIEmbeddings, callEmbeddingApi } from './_shared/llm'
|
|
93
|
+
import { FileStatus, FileTreeNode, isSelectedPath, getFileStatus, updateTreeNodeStatus, saveProjectTree } from './_shared/cil'
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_module_header_1784235266"]
|
|
102
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_status_rank_1784235266" type="variable" identity="status_rank" parent="none"]
|
|
103
|
+
// CIL[DOC-MACRO purpose="Defines status_rank configuration or state in cortex.functions.custom_cil-embeddings"]
|
|
104
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Hold the value and default configuration for status_rank"]
|
|
105
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
|
|
106
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="value of status_rank" errors="none" side_effects="none"]
|
|
107
|
+
// CIL[DOC-MICRO logic_summary="Declares status_rank with initial value or constant binding used by cortex.functions.custom_cil-embeddings" notable_decisions="[]"]
|
|
108
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
109
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
110
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/principal.ts, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
|
|
111
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
112
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
113
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
114
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
115
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
116
|
+
const STATUS_RANK: Record<string, number> = {
|
|
117
|
+
'2-discovered': 2,
|
|
118
|
+
'3-selected': 3,
|
|
119
|
+
'4-chunked': 4,
|
|
120
|
+
'5-documented': 5,
|
|
121
|
+
'6-embedded': 6,
|
|
122
|
+
'7-mapped': 7,
|
|
123
|
+
'8-enriched': 8,
|
|
124
|
+
'9-embedded': 9,
|
|
125
|
+
'10-mapped': 10,
|
|
126
|
+
|
|
127
|
+
'pass2_complete': 4,
|
|
128
|
+
'pass3_complete': 5,
|
|
129
|
+
'pass4_complete': 6,
|
|
130
|
+
'pass4b_complete': 7,
|
|
131
|
+
'pass6_complete': 8,
|
|
132
|
+
'pass7_complete': 9,
|
|
133
|
+
'pass8_complete': 10,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_status_rank_1784235266"]
|
|
140
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_rank_1784235266" type="function" identity="rank" parent="none"]
|
|
141
|
+
// CIL[DOC-MACRO purpose="Implements rank logic in cortex.functions.custom_cil-embeddings"]
|
|
142
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute rank and return its computed result"]
|
|
143
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[\".framework/functions/_shared/principal.ts\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/migrations/015_simplify_apps_table.sql\",\"\\\"]"]
|
|
144
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
145
|
+
// CIL[DOC-MICRO logic_summary="Provides rank with rank(status: string | undefined inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
146
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
147
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
148
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 1. Direct calls: 0. Related files: .framework/functions/_shared/principal.ts, .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/migrations/015_simplify_apps_table.sql, \."]
|
|
149
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 1. Total affected: 1."]
|
|
150
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
151
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
152
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
153
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
154
|
+
function rank(status: string | undefined): number {
|
|
155
|
+
return status ? STATUS_RANK[status] || 0 : 0
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_rank_1784235266"]
|
|
165
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_embedding_model_1784235266" type="variable" identity="embedding_model" parent="none"]
|
|
166
|
+
// CIL[DOC-MACRO purpose="Defines embedding_model configuration or state in cortex.functions.custom_cil-embeddings"]
|
|
167
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Hold the value and default configuration for embedding_model"]
|
|
168
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
|
|
169
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="value of embedding_model" errors="none" side_effects="none"]
|
|
170
|
+
// CIL[DOC-MICRO logic_summary="Declares embedding_model with initial value or constant binding used by cortex.functions.custom_cil-embeddings" notable_decisions="[]"]
|
|
171
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
172
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
173
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/principal.ts, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
|
|
174
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
175
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
176
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
177
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
178
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
179
|
+
const EMBEDDING_MODEL = 'text-embedding-3-small'
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_embedding_model_1784235266"]
|
|
185
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_default_batch_size_1784235266" type="variable" identity="default_batch_size" parent="none"]
|
|
186
|
+
// CIL[DOC-MACRO purpose="Defines default_batch_size configuration or state in cortex.functions.custom_cil-embeddings"]
|
|
187
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Hold the value and default configuration for default_batch_size"]
|
|
188
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
|
|
189
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="value of default_batch_size" errors="none" side_effects="none"]
|
|
190
|
+
// CIL[DOC-MICRO logic_summary="Declares default_batch_size with initial value or constant binding used by cortex.functions.custom_cil-embeddings" notable_decisions="[]"]
|
|
191
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
192
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
193
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/principal.ts, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
|
|
194
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
195
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
196
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
197
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
198
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
199
|
+
const DEFAULT_BATCH_SIZE = 20
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_default_batch_size_1784235266"]
|
|
209
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_vector_type_index_1784235266" type="variable" identity="vector_type_index" parent="none"]
|
|
210
|
+
// CIL[DOC-MACRO purpose="Defines vector_type_index configuration or state in cortex.functions.custom_cil-embeddings"]
|
|
211
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Hold the value and default configuration for vector_type_index"]
|
|
212
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
213
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="value of vector_type_index" errors="none" side_effects="none"]
|
|
214
|
+
// CIL[DOC-MICRO logic_summary="Declares vector_type_index with initial value or constant binding used by cortex.functions.custom_cil-embeddings" notable_decisions="[]"]
|
|
215
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
216
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
217
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
218
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
219
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
220
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
221
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
222
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
223
|
+
const VECTOR_TYPE_INDEX: Record<string, number> = {
|
|
224
|
+
semantic: 0,
|
|
225
|
+
structural: 1,
|
|
226
|
+
contract: 2,
|
|
227
|
+
code: 3,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_vector_type_index_1784235266"]
|
|
236
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_embedding_prices_1784235266" type="variable" identity="embedding_prices" parent="none"]
|
|
237
|
+
// CIL[DOC-MACRO purpose="Defines embedding_prices configuration or state in cortex.functions.custom_cil-embeddings"]
|
|
238
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Hold the value and default configuration for embedding_prices"]
|
|
239
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
240
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="value of embedding_prices" errors="none" side_effects="none"]
|
|
241
|
+
// CIL[DOC-MICRO logic_summary="Declares embedding_prices with initial value or constant binding used by cortex.functions.custom_cil-embeddings" notable_decisions="[]"]
|
|
242
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
243
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
244
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
245
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
246
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
247
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
248
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
249
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
250
|
+
const EMBEDDING_PRICES: Record<string, number> = {
|
|
251
|
+
'text-embedding-3-small': 0.02,
|
|
252
|
+
'text-embedding-3-large': 0.13,
|
|
253
|
+
'text-embedding-ada-002': 0.10,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_embedding_prices_1784235266"]
|
|
266
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_buildsemantictext_1784235266" type="function" identity="buildsemantictext" parent="none"]
|
|
267
|
+
// CIL[DOC-MACRO purpose="Implements buildsemantictext logic in cortex.functions.custom_cil-embeddings"]
|
|
268
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute buildsemantictext and return its computed result"]
|
|
269
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
270
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
271
|
+
// CIL[DOC-MICRO logic_summary="Provides buildsemantictext with buildSemanticText(chunk: any inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
272
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
273
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
274
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
275
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
276
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
277
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
278
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
279
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
280
|
+
export function buildSemanticText(chunk: any): string {
|
|
281
|
+
const d = chunk.data || {}
|
|
282
|
+
const parts: string[] = []
|
|
283
|
+
if (d.chunk_name) parts.push(d.chunk_name)
|
|
284
|
+
if (d.chunk_type) parts.push(`[${d.chunk_type}]`)
|
|
285
|
+
if (d.file_path) parts.push(`in ${d.file_path}`)
|
|
286
|
+
|
|
287
|
+
const vc = d.vector_content || {}
|
|
288
|
+
if (vc.context?.status === 'ready' && vc.context.content) {
|
|
289
|
+
parts.push(`Context: ${vc.context.content}`)
|
|
290
|
+
}
|
|
291
|
+
if (vc.blast_radius?.status === 'ready' && vc.blast_radius.content) {
|
|
292
|
+
parts.push(`Blast radius: ${vc.blast_radius.content}`)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (d.macro) parts.push(d.macro)
|
|
296
|
+
if (d.micro) parts.push(d.micro)
|
|
297
|
+
if (d.input_spec) parts.push(`Inputs: ${d.input_spec}`)
|
|
298
|
+
if (d.output_spec) parts.push(`Returns: ${d.output_spec}`)
|
|
299
|
+
if (Array.isArray(d.notes)) {
|
|
300
|
+
for (const note of d.notes) {
|
|
301
|
+
if (note.content) parts.push(`Note (${note.type || 'general'}): ${note.content}`)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (Array.isArray(d.learnings)) {
|
|
305
|
+
for (const learning of d.learnings) {
|
|
306
|
+
if (learning.content) parts.push(`Learning (${learning.topic || 'general'}): ${learning.content}`)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return parts.join('. ')
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_buildsemantictext_1784235266"]
|
|
315
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_buildstructuraltext_1784235266" type="function" identity="buildstructuraltext" parent="none"]
|
|
316
|
+
// CIL[DOC-MACRO purpose="Implements buildstructuraltext logic in cortex.functions.custom_cil-embeddings"]
|
|
317
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute buildstructuraltext and return its computed result"]
|
|
318
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
319
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
320
|
+
// CIL[DOC-MICRO logic_summary="Provides buildstructuraltext with buildStructuralText(chunk: any inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
321
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
322
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
323
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
324
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
325
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
326
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
327
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
328
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
329
|
+
export function buildStructuralText(chunk: any): string {
|
|
330
|
+
const d = chunk.data || {}
|
|
331
|
+
return JSON.stringify({
|
|
332
|
+
chunk_name: d.chunk_name || '',
|
|
333
|
+
chunk_type: d.chunk_type || '',
|
|
334
|
+
file_path: d.file_path || '',
|
|
335
|
+
stability: d.stability || '',
|
|
336
|
+
is_dead_code: d.is_dead_code || false,
|
|
337
|
+
boundary_confidence: d.boundary_confidence || '',
|
|
338
|
+
doc_confidence: d.doc_confidence || '',
|
|
339
|
+
review_status: d.review_status || '',
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_buildstructuraltext_1784235266"]
|
|
346
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_buildcontracttext_1784235266" type="function" identity="buildcontracttext" parent="none"]
|
|
347
|
+
// CIL[DOC-MACRO purpose="Implements buildcontracttext logic in cortex.functions.custom_cil-embeddings"]
|
|
348
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute buildcontracttext and return its computed result"]
|
|
349
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
350
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
351
|
+
// CIL[DOC-MICRO logic_summary="Provides buildcontracttext with buildContractText(chunk: any inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
352
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
353
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
354
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
355
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
356
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
357
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
358
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
359
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
360
|
+
export function buildContractText(chunk: any): string {
|
|
361
|
+
const d = chunk.data || {}
|
|
362
|
+
const parts: string[] = []
|
|
363
|
+
if (d.input_spec) parts.push(`INPUTS: ${d.input_spec}`)
|
|
364
|
+
if (d.output_spec) parts.push(`OUTPUTS: ${d.output_spec}`)
|
|
365
|
+
if (d.side_effects) parts.push(`SIDE_EFFECTS: ${d.side_effects}`)
|
|
366
|
+
if (d.macro) parts.push(`PURPOSE: ${d.macro}`)
|
|
367
|
+
return parts.join('\n') || `${d.chunk_name || 'unknown'} — no contract yet`
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
function buildCodeText(chunk: any): string {
|
|
374
|
+
const d = chunk.data || {}
|
|
375
|
+
const parts: string[] = []
|
|
376
|
+
if (d.chunk_name) parts.push(d.chunk_name)
|
|
377
|
+
if (d.file_path) parts.push(`in ${d.file_path}`)
|
|
378
|
+
if (d.chunk_type) parts.push(`[${d.chunk_type}]`)
|
|
379
|
+
if (d.macro) parts.push(d.macro)
|
|
380
|
+
if (d.micro) parts.push(d.micro)
|
|
381
|
+
if (d.code_content) {
|
|
382
|
+
parts.push('CODE:')
|
|
383
|
+
parts.push(String(d.code_content))
|
|
384
|
+
}
|
|
385
|
+
return parts.join('\n') || `${d.chunk_name || 'unknown'} — no code content`
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_buildcontracttext_1784235266"]
|
|
394
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_loadpendingchunks_1784235266" type="function" identity="loadpendingchunks" parent="none"]
|
|
395
|
+
// CIL[DOC-MACRO purpose="Implements loadpendingchunks logic in cortex.functions.custom_cil-embeddings"]
|
|
396
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute loadpendingchunks and return its computed result"]
|
|
397
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
398
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
399
|
+
// CIL[DOC-MICRO logic_summary="Provides loadpendingchunks with loadPendingChunks(projectId: string, selectedPaths: string[], requiredStatus: FileStatus inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
400
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
401
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
402
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
403
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
404
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
405
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
406
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
407
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
408
|
+
async function loadPendingChunks(projectId: string, selectedPaths: string[], requiredStatus: FileStatus): Promise<any[]> {
|
|
409
|
+
const { data: fileItems } = await adminDb
|
|
410
|
+
.from('items')
|
|
411
|
+
.select('id, data')
|
|
412
|
+
.filter('data->>kb_type', 'eq', 'cil_file')
|
|
413
|
+
.filter('data->>project_id', 'eq', projectId)
|
|
414
|
+
.eq('is_active', true)
|
|
415
|
+
|
|
416
|
+
const selectedFileIds = (fileItems || [])
|
|
417
|
+
.filter((f: any) => getFileStatus(f) === requiredStatus && isSelectedPath(f.data?.file_path || f.title, selectedPaths))
|
|
418
|
+
.map((f: any) => f.id)
|
|
419
|
+
|
|
420
|
+
if (selectedFileIds.length === 0) return []
|
|
421
|
+
|
|
422
|
+
const { data: allChunks } = await adminDb
|
|
423
|
+
.from('items')
|
|
424
|
+
.select('id, title, data')
|
|
425
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
426
|
+
.filter('data->>project_id', 'eq', projectId)
|
|
427
|
+
.eq('is_active', true)
|
|
428
|
+
|
|
429
|
+
return (allChunks || []).filter((c: any) => selectedFileIds.includes(c.data?.file_id))
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_loadpendingchunks_1784235266"]
|
|
437
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_maybeadvancefiletopass4_1784235266" type="function" identity="maybeadvancefiletopass4" parent="none"]
|
|
438
|
+
// CIL[DOC-MACRO purpose="Implements maybeadvancefiletopass4 logic in cortex.functions.custom_cil-embeddings"]
|
|
439
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute maybeadvancefiletopass4 and return its computed result"]
|
|
440
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
441
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
442
|
+
// CIL[DOC-MICRO logic_summary="Provides maybeadvancefiletopass4 with maybeAdvanceFileToPass4( inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
443
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
444
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
445
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
446
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
447
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
448
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
449
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
450
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
451
|
+
async function maybeAdvanceFileToPass4(
|
|
452
|
+
ctx: any,
|
|
453
|
+
fileId: string,
|
|
454
|
+
tree: FileTreeNode | null,
|
|
455
|
+
secondPass = false,
|
|
456
|
+
projectItem?: any
|
|
457
|
+
): Promise<void> {
|
|
458
|
+
const { data: fileChunks } = await adminDb
|
|
459
|
+
.from('items')
|
|
460
|
+
.select('id, title, data')
|
|
461
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
462
|
+
.filter('data->>file_id', 'eq', fileId)
|
|
463
|
+
.eq('is_active', true)
|
|
464
|
+
|
|
465
|
+
if (!fileChunks || fileChunks.length === 0) return
|
|
466
|
+
|
|
467
|
+
const chunkIds = fileChunks.map((c: any) => c.id)
|
|
468
|
+
const { data: embeddedDocs } = await adminDb
|
|
469
|
+
.from('embeddings')
|
|
470
|
+
.select('document_id')
|
|
471
|
+
.in('document_id', chunkIds)
|
|
472
|
+
.is('revoked_at', null)
|
|
473
|
+
|
|
474
|
+
const embeddedChunkIds = new Set((embeddedDocs || []).map((e: any) => e.document_id))
|
|
475
|
+
const allEmbedded = chunkIds.every(id => embeddedChunkIds.has(id))
|
|
476
|
+
if (!allEmbedded) return
|
|
477
|
+
|
|
478
|
+
const { data: fileItem } = await adminDb
|
|
479
|
+
.from('items')
|
|
480
|
+
.select('id, data')
|
|
481
|
+
.eq('id', fileId)
|
|
482
|
+
.maybeSingle()
|
|
483
|
+
|
|
484
|
+
if (!fileItem) return
|
|
485
|
+
|
|
486
|
+
const cil_file_status = secondPass ? 'pass7_complete' : 'pass4_complete'
|
|
487
|
+
const pipeline_status = secondPass ? '9-embedded' : '6-embedded'
|
|
488
|
+
const currentFileStatus = getFileStatus(fileItem)
|
|
489
|
+
const filePath: string = fileItem.data?.file_path
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
if (rank(currentFileStatus) < rank(cil_file_status)) {
|
|
493
|
+
const { update } = await import('./admin-data')
|
|
494
|
+
const updateCtx = { ...ctx, query: { ...(ctx.query || {}), entity: 'items', id: fileId } }
|
|
495
|
+
await update(updateCtx, {
|
|
496
|
+
data: {
|
|
497
|
+
...fileItem.data,
|
|
498
|
+
status: cil_file_status,
|
|
499
|
+
last_status_at: new Date().toISOString(),
|
|
500
|
+
},
|
|
501
|
+
}).catch((e: any) => console.error(`advance to ${cil_file_status} failed:`, e.message))
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (tree) updateTreeNodeStatus(tree, fileId, cil_file_status)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
const projectId: string = fileItem.data?.project_id
|
|
508
|
+
if (!filePath || !projectId) return
|
|
509
|
+
|
|
510
|
+
let projItem = projectItem
|
|
511
|
+
if (!projItem) {
|
|
512
|
+
const { data } = await adminDb.from('items').select('id, data').eq('id', projectId).maybeSingle()
|
|
513
|
+
projItem = data
|
|
514
|
+
}
|
|
515
|
+
if (!projItem) return
|
|
516
|
+
|
|
517
|
+
const currentPipelineStatus = projItem.data?.file_statuses?.[filePath]
|
|
518
|
+
if (rank(currentPipelineStatus) >= rank(pipeline_status)) return
|
|
519
|
+
|
|
520
|
+
const fileStatuses = { ...(projItem.data?.file_statuses || {}) }
|
|
521
|
+
fileStatuses[filePath] = pipeline_status
|
|
522
|
+
fileStatuses[`${filePath}_updated`] = new Date().toISOString()
|
|
523
|
+
const { error: fsErr } = await adminDb.from('items')
|
|
524
|
+
.update({ data: { ...projItem.data, file_statuses: fileStatuses } })
|
|
525
|
+
.eq('id', projectId)
|
|
526
|
+
.select('id')
|
|
527
|
+
if (fsErr) {
|
|
528
|
+
console.error(`pipeline status advance to ${pipeline_status} failed:`, fsErr.message)
|
|
529
|
+
return
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
if (projectItem) {
|
|
534
|
+
if (!projectItem.data) projectItem.data = {}
|
|
535
|
+
projectItem.data.file_statuses = fileStatuses
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_maybeadvancefiletopass4_1784235266"]
|
|
544
|
+
|
|
545
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_contenthash_1784235266" type="function" identity="contenthash" parent="none"]
|
|
546
|
+
// CIL[DOC-MACRO purpose="Implements contenthash logic in cortex.functions.custom_cil-embeddings"]
|
|
547
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute contenthash and return its computed result"]
|
|
548
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
549
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
550
|
+
// CIL[DOC-MICRO logic_summary="Provides contenthash with contentHash(text: string inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
551
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
552
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
553
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
554
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
555
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
556
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
557
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
558
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
559
|
+
export function contentHash(text: string): string {
|
|
560
|
+
|
|
561
|
+
let h = 2166136261
|
|
562
|
+
for (let i = 0; i < text.length; i++) {
|
|
563
|
+
h ^= text.charCodeAt(i)
|
|
564
|
+
h = Math.imul(h, 16777619)
|
|
565
|
+
}
|
|
566
|
+
return (h >>> 0).toString(16).padStart(8, '0')
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_contenthash_1784235266"]
|
|
574
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_embedchunk_1784235266" type="function" identity="embedchunk" parent="none"]
|
|
575
|
+
// CIL[DOC-MACRO purpose="Implements embedchunk logic in cortex.functions.custom_cil-embeddings"]
|
|
576
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute embedchunk and return its computed result"]
|
|
577
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
578
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
579
|
+
// CIL[DOC-MICRO logic_summary="Provides embedchunk with embedChunk( inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
580
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
581
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
582
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
583
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
584
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
585
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
586
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
587
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
588
|
+
export async function embedChunk(
|
|
589
|
+
chunkItem: any,
|
|
590
|
+
accountId: string,
|
|
591
|
+
forceRegenerate: boolean
|
|
592
|
+
): Promise<{ created: string[]; skipped: string[]; error?: string }> {
|
|
593
|
+
const chunkId = chunkItem.id
|
|
594
|
+
const created: string[] = []
|
|
595
|
+
const skipped: string[] = []
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
const vectors: { type: string; text: string }[] = [
|
|
599
|
+
{ type: 'semantic', text: buildSemanticText(chunkItem) },
|
|
600
|
+
{ type: 'structural', text: buildStructuralText(chunkItem) },
|
|
601
|
+
{ type: 'contract', text: buildContractText(chunkItem) },
|
|
602
|
+
{ type: 'code', text: buildCodeText(chunkItem) },
|
|
603
|
+
].filter(v => v.text.trim().length > 0)
|
|
604
|
+
|
|
605
|
+
if (!vectors.length) return { created, skipped: ['all:empty'] }
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
const { data: existing } = await adminDb
|
|
609
|
+
.from('embeddings')
|
|
610
|
+
.select('id, content_hash, metadata')
|
|
611
|
+
.eq('document_id', chunkId)
|
|
612
|
+
|
|
613
|
+
const existingMap = new Map<string, any>()
|
|
614
|
+
for (const row of existing || []) {
|
|
615
|
+
existingMap.set(row.metadata?.vector_type || '', row)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (forceRegenerate && (existing || []).length > 0) {
|
|
619
|
+
await adminDb.from('embeddings').delete().eq('document_id', chunkId)
|
|
620
|
+
existingMap.clear()
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
const toEmbed = vectors.filter(v => {
|
|
625
|
+
const hash = contentHash(v.text)
|
|
626
|
+
const row = existingMap.get(v.type)
|
|
627
|
+
if (row && row.content_hash === hash) { skipped.push(v.type); return false }
|
|
628
|
+
return true
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
if (!toEmbed.length) return { created, skipped }
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
const embeddings = await callEmbeddingApi(toEmbed.map(v => v.text))
|
|
635
|
+
const now = new Date().toISOString()
|
|
636
|
+
|
|
637
|
+
for (let i = 0; i < toEmbed.length; i++) {
|
|
638
|
+
const { type, text } = toEmbed[i]
|
|
639
|
+
const hash = contentHash(text)
|
|
640
|
+
const existingRow = existingMap.get(type)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
const vectorLiteral = `[${embeddings[i].join(',')}]`
|
|
644
|
+
|
|
645
|
+
const row = {
|
|
646
|
+
account_id: accountId,
|
|
647
|
+
model_id: EMBEDDING_MODEL,
|
|
648
|
+
document_id: chunkId,
|
|
649
|
+
chunk_index: VECTOR_TYPE_INDEX[type] ?? 0,
|
|
650
|
+
content: text,
|
|
651
|
+
embedding: vectorLiteral,
|
|
652
|
+
content_hash: hash,
|
|
653
|
+
approved_at: now, // CIL chunks are always "approved" — they're internal, not published content
|
|
654
|
+
revoked_at: null,
|
|
655
|
+
metadata: {
|
|
656
|
+
vector_type: type,
|
|
657
|
+
item_type: 'cil_chunk',
|
|
658
|
+
kb_type: 'cil_chunk',
|
|
659
|
+
security_level: 'internal',
|
|
660
|
+
source_item_id: chunkId,
|
|
661
|
+
chunk_name: chunkItem.data?.chunk_name || '',
|
|
662
|
+
file_path: chunkItem.data?.file_path || '',
|
|
663
|
+
project_id: chunkItem.data?.project_id || '',
|
|
664
|
+
},
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (existingRow) {
|
|
668
|
+
await adminDb.from('embeddings').update(row).eq('id', existingRow.id)
|
|
669
|
+
} else {
|
|
670
|
+
await adminDb.from('embeddings').insert(row)
|
|
671
|
+
}
|
|
672
|
+
created.push(type)
|
|
673
|
+
}
|
|
674
|
+
} catch (err) {
|
|
675
|
+
return { created, skipped, error: err instanceof Error ? err.message : String(err) }
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return { created, skipped }
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_embedchunk_1784235266"]
|
|
686
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_semanticsearch_1784235266" type="function" identity="semanticsearch" parent="none"]
|
|
687
|
+
// CIL[DOC-MACRO purpose="Implements semanticsearch logic in cortex.functions.custom_cil-embeddings"]
|
|
688
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute semanticsearch and return its computed result"]
|
|
689
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
690
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
691
|
+
// CIL[DOC-MICRO logic_summary="Provides semanticsearch with semanticSearch( inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
692
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
693
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
694
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
695
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
696
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
697
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
698
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
699
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
700
|
+
async function semanticSearch(
|
|
701
|
+
query: string,
|
|
702
|
+
projectId: string | null,
|
|
703
|
+
limit: number,
|
|
704
|
+
accountId: string
|
|
705
|
+
): Promise<any[]> {
|
|
706
|
+
const j = await callOpenAIEmbeddings(undefined, [query], EMBEDDING_MODEL)
|
|
707
|
+
const queryVector = j.data?.[0]?.embedding
|
|
708
|
+
if (!queryVector) throw new Error('Failed to embed query')
|
|
709
|
+
|
|
710
|
+
const vectorLiteral = `[${queryVector.join(',')}]`
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
const rpcParams: any = {
|
|
714
|
+
query_embedding: vectorLiteral,
|
|
715
|
+
match_count: limit * 3,
|
|
716
|
+
filter_account_ids: [accountId],
|
|
717
|
+
filter_vector_type: null,
|
|
718
|
+
similarity_threshold: 0.15,
|
|
719
|
+
filter_item_types: ['cil_chunk'],
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const { data: matches, error } = await adminDb.rpc('match_embeddings', rpcParams)
|
|
723
|
+
if (error) throw new Error(`match_embeddings RPC error: ${error.message}`)
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
const chunkIds = [...new Set((matches || []).map((m: any) => m.document_id))]
|
|
727
|
+
if (!chunkIds.length) return []
|
|
728
|
+
|
|
729
|
+
const { data: chunks } = await adminDb
|
|
730
|
+
.from('items')
|
|
731
|
+
.select('id, title, data')
|
|
732
|
+
.in('id', chunkIds)
|
|
733
|
+
|
|
734
|
+
const chunkMap = new Map((chunks || []).map((c: any) => [c.id, c]))
|
|
735
|
+
|
|
736
|
+
return (matches || [])
|
|
737
|
+
.filter((m: any) => {
|
|
738
|
+
const chunk = chunkMap.get(m.document_id)
|
|
739
|
+
if (!chunk || chunk.data?.kb_type !== 'cil_chunk') return false
|
|
740
|
+
if (!projectId) return true
|
|
741
|
+
return chunk.data?.project_id === projectId
|
|
742
|
+
})
|
|
743
|
+
.map((m: any) => {
|
|
744
|
+
const chunk = chunkMap.get(m.document_id)
|
|
745
|
+
return {
|
|
746
|
+
chunk_id: m.document_id,
|
|
747
|
+
chunk_name: chunk?.data?.chunk_name || chunk?.title || '',
|
|
748
|
+
file_path: chunk?.data?.file_path || '',
|
|
749
|
+
macro: chunk?.data?.macro || '',
|
|
750
|
+
micro: chunk?.data?.micro || '',
|
|
751
|
+
chunk_type: chunk?.data?.chunk_type || '',
|
|
752
|
+
doc_confidence: chunk?.data?.doc_confidence || '',
|
|
753
|
+
similarity: m.similarity,
|
|
754
|
+
content_snippet: (m.content || '').slice(0, 200),
|
|
755
|
+
vector_content: chunk?.data?.vector_content || null,
|
|
756
|
+
}
|
|
757
|
+
})
|
|
758
|
+
.slice(0, limit)
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_semanticsearch_1784235266"]
|
|
766
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_getplatformaccountid_1784235266" type="function" identity="getplatformaccountid" parent="none"]
|
|
767
|
+
// CIL[DOC-MACRO purpose="Implements getplatformaccountid logic in cortex.functions.custom_cil-embeddings"]
|
|
768
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Execute getplatformaccountid and return its computed result"]
|
|
769
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
770
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="return value from function execution" errors="throws on invalid input or runtime failure" side_effects="none"]
|
|
771
|
+
// CIL[DOC-MICRO logic_summary="Provides getplatformaccountid with getPlatformAccountId( inputs, handling internal flow and returning a result" notable_decisions="[]"]
|
|
772
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
773
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
774
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
775
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
776
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
777
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
778
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
779
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
780
|
+
async function getPlatformAccountId(): Promise<string> {
|
|
781
|
+
const { data } = await adminDb.from('accounts').select('id').eq('slug', 'spine-system').maybeSingle()
|
|
782
|
+
return data?.id || ''
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_getplatformaccountid_1784235266"]
|
|
790
|
+
// CIL[BEGIN chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_handler_1784235266" type="variable" identity="handler" parent="none"]
|
|
791
|
+
// CIL[DOC-MACRO purpose="Defines handler configuration or state in cortex.functions.custom_cil-embeddings"]
|
|
792
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Hold the value and default configuration for handler"]
|
|
793
|
+
// CIL[DOC-CONTEXT module="cortex.functions.custom_cil-embeddings" layer="function" related_files="[]"]
|
|
794
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="value of handler" errors="none" side_effects="none"]
|
|
795
|
+
// CIL[DOC-MICRO logic_summary="Declares handler with initial value or constant binding used by cortex.functions.custom_cil-embeddings" notable_decisions="[]"]
|
|
796
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
797
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
798
|
+
// CIL[DOC-VECTOR type="context" content="Layer: function. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
799
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
800
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
801
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
802
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
803
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
804
|
+
export const handler = createHandler(async (ctx, body) => {
|
|
805
|
+
const action = ctx.query?.action
|
|
806
|
+
const accountId = ctx.accountId || await getPlatformAccountId()
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
if (action === 'estimate') {
|
|
810
|
+
const projectId = body?.project_id || ctx.query?.project_id
|
|
811
|
+
if (!projectId) throw new Error('project_id is required')
|
|
812
|
+
const model = body?.model || ctx.query?.model || EMBEDDING_MODEL
|
|
813
|
+
const pricePerMillion = EMBEDDING_PRICES[model] ?? EMBEDDING_PRICES[EMBEDDING_MODEL]
|
|
814
|
+
const filePath = body?.file_path || ctx.query?.file_path
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
const PAGE_SIZE = 1000
|
|
818
|
+
let offset = 0
|
|
819
|
+
const chunks: any[] = []
|
|
820
|
+
while (true) {
|
|
821
|
+
let q = adminDb
|
|
822
|
+
.from('items')
|
|
823
|
+
.select('id, data')
|
|
824
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
825
|
+
.filter('data->>project_id', 'eq', projectId)
|
|
826
|
+
.filter('data->>review_status', 'eq', 'approved')
|
|
827
|
+
.eq('is_active', true)
|
|
828
|
+
.range(offset, offset + PAGE_SIZE - 1)
|
|
829
|
+
if (filePath) q = q.filter('data->>file_path', 'eq', filePath)
|
|
830
|
+
const { data: page, error } = await q
|
|
831
|
+
if (error) throw new Error(`Failed to load chunks: ${error.message}`)
|
|
832
|
+
chunks.push(...(page || []))
|
|
833
|
+
if ((page || []).length < PAGE_SIZE) break
|
|
834
|
+
offset += PAGE_SIZE
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
const chunkIds = (chunks || []).map((c: any) => c.id)
|
|
839
|
+
const existingDocIds = new Set<string>()
|
|
840
|
+
const SUPABASE_IN_LIMIT = 500
|
|
841
|
+
for (let i = 0; i < chunkIds.length; i += SUPABASE_IN_LIMIT) {
|
|
842
|
+
const { data: emb } = await adminDb
|
|
843
|
+
.from('embeddings')
|
|
844
|
+
.select('document_id')
|
|
845
|
+
.in('document_id', chunkIds.slice(i, i + SUPABASE_IN_LIMIT))
|
|
846
|
+
for (const row of emb || []) existingDocIds.add(row.document_id)
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const toEmbed = (chunks || []).filter((c: any) => !existingDocIds.has(c.id))
|
|
850
|
+
const alreadyEmbedded = (chunks || []).length - toEmbed.length
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
let totalTokens = 0
|
|
855
|
+
for (const chunk of toEmbed) {
|
|
856
|
+
const baseTokens = chunk.data?.token_count || Math.ceil((chunk.data?.code_content?.length || 200) / 4)
|
|
857
|
+
totalTokens += baseTokens * 4
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const estimatedCostUsd = (totalTokens / 1_000_000) * pricePerMillion
|
|
861
|
+
|
|
862
|
+
return {
|
|
863
|
+
project_id: projectId,
|
|
864
|
+
model,
|
|
865
|
+
file_path: filePath || null,
|
|
866
|
+
price_per_1m_tokens: pricePerMillion,
|
|
867
|
+
total_approved_chunks: (chunks || []).length,
|
|
868
|
+
chunks_to_embed: toEmbed.length,
|
|
869
|
+
already_embedded: alreadyEmbedded,
|
|
870
|
+
vectors_per_chunk: 4,
|
|
871
|
+
estimated_tokens: totalTokens,
|
|
872
|
+
estimated_cost_usd: parseFloat(estimatedCostUsd.toFixed(4)),
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
if (action === 'embed_chunk') {
|
|
878
|
+
const chunkId = body?.chunk_id || ctx.query?.chunk_id
|
|
879
|
+
if (!chunkId) throw new Error('chunk_id is required')
|
|
880
|
+
|
|
881
|
+
const { data: chunk, error } = await adminDb
|
|
882
|
+
.from('items')
|
|
883
|
+
.select('id, title, data')
|
|
884
|
+
.eq('id', chunkId)
|
|
885
|
+
.maybeSingle()
|
|
886
|
+
|
|
887
|
+
if (error || !chunk) throw new Error(`Chunk not found: ${chunkId}`)
|
|
888
|
+
|
|
889
|
+
const result = await embedChunk(chunk, accountId, body?.force_regenerate ?? false)
|
|
890
|
+
return {
|
|
891
|
+
chunk_id: chunkId,
|
|
892
|
+
chunk_name: chunk.data?.chunk_name || '',
|
|
893
|
+
...result,
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
if (action === 'embed_project' || action === 'embed_approved') {
|
|
908
|
+
const projectId = body?.project_id || ctx.query?.project_id
|
|
909
|
+
if (!projectId) throw new Error('project_id is required')
|
|
910
|
+
|
|
911
|
+
const forceRegenerate = body?.force_regenerate ?? false
|
|
912
|
+
const secondPass = body?.second_pass ?? false
|
|
913
|
+
const filePath = body?.file_path || ctx.query?.file_path
|
|
914
|
+
const pageLimit = Math.min(parseInt(body?.limit || '10', 10), 25)
|
|
915
|
+
const pageOffset = parseInt(body?.offset || '0', 10)
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
const { data: projectItem } = await adminDb.from('items').select('id, data').eq('id', projectId).maybeSingle()
|
|
919
|
+
|
|
920
|
+
const selectedPaths: string[] = projectItem?.data?.selected_paths || []
|
|
921
|
+
const fileTree = (projectItem?.data?.file_tree as FileTreeNode) || null
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
let totalChunks = 0
|
|
925
|
+
let pageChunks: any[] = []
|
|
926
|
+
let error: any = null
|
|
927
|
+
|
|
928
|
+
if (action === 'embed_approved' && selectedPaths.length > 0 && !filePath) {
|
|
929
|
+
|
|
930
|
+
const pending = await loadPendingChunks(projectId, selectedPaths, 'pass3_complete')
|
|
931
|
+
const sorted = pending.sort((a: any, b: any) => a.id.localeCompare(b.id))
|
|
932
|
+
totalChunks = sorted.length
|
|
933
|
+
pageChunks = sorted.slice(pageOffset, pageOffset + pageLimit)
|
|
934
|
+
} else {
|
|
935
|
+
const sourceFilePaths = filePath ? [filePath] : getEmbedSourceFilePaths(projectItem, secondPass)
|
|
936
|
+
const sourceChunks = await loadSourceChunks(projectId, sourceFilePaths, pageOffset, pageLimit)
|
|
937
|
+
totalChunks = sourceChunks.total
|
|
938
|
+
pageChunks = sourceChunks.chunks
|
|
939
|
+
error = null
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (error) throw new Error(`Failed to load chunks: ${error.message}`)
|
|
943
|
+
if (error) throw new Error(`Failed to load chunks: ${error.message}`)
|
|
944
|
+
if (!pageChunks || pageChunks.length === 0) {
|
|
945
|
+
return { project_id: projectId, total: totalChunks, embeddings_created: 0, embeddings_skipped: 0, errors: [], has_more: false, next_offset: null, done_total: totalChunks }
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
const pageChunkIds = pageChunks.map((c: any) => c.id)
|
|
950
|
+
const SUPABASE_IN_LIMIT = 500
|
|
951
|
+
const existingRows: any[] = []
|
|
952
|
+
for (let i = 0; i < pageChunkIds.length; i += SUPABASE_IN_LIMIT) {
|
|
953
|
+
const { data } = await adminDb
|
|
954
|
+
.from('embeddings')
|
|
955
|
+
.select('id, document_id, content_hash, metadata')
|
|
956
|
+
.in('document_id', pageChunkIds.slice(i, i + SUPABASE_IN_LIMIT))
|
|
957
|
+
existingRows.push(...(data || []))
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
const existingMap = new Map<string, Map<string, any>>()
|
|
961
|
+
for (const row of existingRows) {
|
|
962
|
+
const docId = row.document_id
|
|
963
|
+
if (!existingMap.has(docId)) existingMap.set(docId, new Map())
|
|
964
|
+
existingMap.get(docId)!.set(row.metadata?.vector_type || '', row)
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
const toEmbed: { chunkId: string; type: string; text: string; existingRowId?: string }[] = []
|
|
969
|
+
let totalSkipped = 0
|
|
970
|
+
|
|
971
|
+
for (const chunk of pageChunks) {
|
|
972
|
+
const vectors = [
|
|
973
|
+
{ type: 'semantic', text: buildSemanticText(chunk) },
|
|
974
|
+
{ type: 'structural', text: buildStructuralText(chunk) },
|
|
975
|
+
{ type: 'contract', text: buildContractText(chunk) },
|
|
976
|
+
{ type: 'code', text: buildCodeText(chunk) },
|
|
977
|
+
].filter(v => v.text.trim().length > 0)
|
|
978
|
+
|
|
979
|
+
for (const v of vectors) {
|
|
980
|
+
const hash = contentHash(v.text)
|
|
981
|
+
const existing = existingMap.get(chunk.id)?.get(v.type)
|
|
982
|
+
if (!forceRegenerate && existing && existing.content_hash === hash) {
|
|
983
|
+
totalSkipped++
|
|
984
|
+
continue
|
|
985
|
+
}
|
|
986
|
+
toEmbed.push({ chunkId: chunk.id, type: v.type, text: v.text, existingRowId: existing?.id })
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
const targetPipelineStatus = secondPass ? '9-embedded' : '6-embedded'
|
|
994
|
+
|
|
995
|
+
const advanceFiles = async () => {
|
|
996
|
+
const affectedFileIds = [...new Set(pageChunks.map(c => c.data?.file_id).filter(Boolean))]
|
|
997
|
+
for (const fileId of affectedFileIds) {
|
|
998
|
+
const chunk = pageChunks.find((c: any) => c.data?.file_id === fileId)
|
|
999
|
+
const filePath = chunk?.data?.file_path
|
|
1000
|
+
const fileStatus = projectItem?.data?.file_statuses?.[filePath]
|
|
1001
|
+
if (fileStatus === targetPipelineStatus) continue
|
|
1002
|
+
await maybeAdvanceFileToPass4(ctx, fileId, fileTree, secondPass, projectItem)
|
|
1003
|
+
}
|
|
1004
|
+
if (fileTree) {
|
|
1005
|
+
await saveProjectTree(ctx, projectId, fileTree, { last_ingested_at: new Date().toISOString() }, projectItem)
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if (toEmbed.length === 0) {
|
|
1010
|
+
await advanceFiles()
|
|
1011
|
+
const nextOffset = pageOffset + pageLimit
|
|
1012
|
+
return {
|
|
1013
|
+
project_id: projectId, total: totalChunks,
|
|
1014
|
+
embeddings_created: 0, embeddings_skipped: totalSkipped, errors: [],
|
|
1015
|
+
has_more: nextOffset < totalChunks, next_offset: nextOffset < totalChunks ? nextOffset : null,
|
|
1016
|
+
done_total: pageOffset + pageChunks.length,
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
if (forceRegenerate && pageChunkIds.length > 0) {
|
|
1022
|
+
for (let i = 0; i < pageChunkIds.length; i += SUPABASE_IN_LIMIT) {
|
|
1023
|
+
await adminDb.from('embeddings').delete().in('document_id', pageChunkIds.slice(i, i + SUPABASE_IN_LIMIT))
|
|
1024
|
+
}
|
|
1025
|
+
for (const item of toEmbed) { item.existingRowId = undefined }
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
const OPENAI_BATCH = 100
|
|
1030
|
+
const errors: string[] = []
|
|
1031
|
+
|
|
1032
|
+
|
|
1033
|
+
const batchPromises: Promise<{ startIdx: number; vecs: number[][] }>[] = []
|
|
1034
|
+
for (let i = 0; i < toEmbed.length; i += OPENAI_BATCH) {
|
|
1035
|
+
const startIdx = i
|
|
1036
|
+
const batchTexts = toEmbed.slice(i, i + OPENAI_BATCH).map(e => e.text)
|
|
1037
|
+
batchPromises.push(
|
|
1038
|
+
callEmbeddingApi(batchTexts)
|
|
1039
|
+
.then(vecs => ({ startIdx, vecs }))
|
|
1040
|
+
.catch(err => {
|
|
1041
|
+
errors.push(`OpenAI batch ${startIdx}-${startIdx + OPENAI_BATCH}: ${err instanceof Error ? err.message : String(err)}`)
|
|
1042
|
+
return { startIdx, vecs: batchTexts.map(() => []) }
|
|
1043
|
+
})
|
|
1044
|
+
)
|
|
1045
|
+
}
|
|
1046
|
+
const batchResults = await Promise.all(batchPromises)
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
const allVectors: number[][] = new Array(toEmbed.length)
|
|
1050
|
+
for (const { startIdx, vecs } of batchResults) {
|
|
1051
|
+
for (let k = 0; k < vecs.length; k++) allVectors[startIdx + k] = vecs[k]
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
const now = new Date().toISOString()
|
|
1056
|
+
const chunkMap = new Map(pageChunks.map((c: any) => [c.id, c]))
|
|
1057
|
+
|
|
1058
|
+
const rowsToInsert: object[] = []
|
|
1059
|
+
const rowsToUpdate: { id: string; row: object }[] = []
|
|
1060
|
+
|
|
1061
|
+
for (let i = 0; i < toEmbed.length; i++) {
|
|
1062
|
+
const { chunkId, type, text, existingRowId } = toEmbed[i]
|
|
1063
|
+
const vec = allVectors[i]
|
|
1064
|
+
if (!vec || vec.length === 0) continue
|
|
1065
|
+
|
|
1066
|
+
const chunk = chunkMap.get(chunkId)
|
|
1067
|
+
const row = {
|
|
1068
|
+
account_id: accountId,
|
|
1069
|
+
model_id: EMBEDDING_MODEL,
|
|
1070
|
+
document_id: chunkId,
|
|
1071
|
+
chunk_index: VECTOR_TYPE_INDEX[type] ?? 0,
|
|
1072
|
+
content: text,
|
|
1073
|
+
embedding: `[${vec.join(',')}]`,
|
|
1074
|
+
content_hash: contentHash(text),
|
|
1075
|
+
approved_at: now,
|
|
1076
|
+
revoked_at: null,
|
|
1077
|
+
metadata: {
|
|
1078
|
+
vector_type: type,
|
|
1079
|
+
item_type: 'cil_chunk',
|
|
1080
|
+
kb_type: 'cil_chunk',
|
|
1081
|
+
security_level: 'internal',
|
|
1082
|
+
source_item_id: chunkId,
|
|
1083
|
+
chunk_name: chunk?.data?.chunk_name || '',
|
|
1084
|
+
file_path: chunk?.data?.file_path || '',
|
|
1085
|
+
project_id: projectId,
|
|
1086
|
+
},
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (existingRowId) rowsToUpdate.push({ id: existingRowId, row })
|
|
1090
|
+
else rowsToInsert.push(row)
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
const INSERT_BATCH = 10
|
|
1095
|
+
for (let i = 0; i < rowsToInsert.length; i += INSERT_BATCH) {
|
|
1096
|
+
const { error: e } = await adminDb.from('embeddings').insert(rowsToInsert.slice(i, i + INSERT_BATCH))
|
|
1097
|
+
if (e) errors.push(`embedding insert batch failed: ${e.message}`)
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
const PARALLEL_UPDATE = 5
|
|
1102
|
+
const updatePromises: Promise<void>[] = []
|
|
1103
|
+
for (let i = 0; i < rowsToUpdate.length; i += PARALLEL_UPDATE) {
|
|
1104
|
+
for (const { id, row } of rowsToUpdate.slice(i, i + PARALLEL_UPDATE)) {
|
|
1105
|
+
updatePromises.push(
|
|
1106
|
+
adminDb.from('embeddings').update(row).eq('id', id)
|
|
1107
|
+
.then(({ error: e }) => { if (e) errors.push(`embedding update ${id}: ${e.message}`) })
|
|
1108
|
+
)
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
await Promise.all(updatePromises)
|
|
1113
|
+
|
|
1114
|
+
await advanceFiles()
|
|
1115
|
+
|
|
1116
|
+
const nextOffset = pageOffset + pageLimit
|
|
1117
|
+
return {
|
|
1118
|
+
project_id: projectId,
|
|
1119
|
+
file_path: filePath || null,
|
|
1120
|
+
total: totalChunks,
|
|
1121
|
+
embeddings_created: rowsToInsert.length + rowsToUpdate.length,
|
|
1122
|
+
embeddings_skipped: totalSkipped,
|
|
1123
|
+
errors,
|
|
1124
|
+
has_more: nextOffset < totalChunks,
|
|
1125
|
+
next_offset: nextOffset < totalChunks ? nextOffset : null,
|
|
1126
|
+
done_total: pageOffset + pageChunks.length,
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
if (action === 'status') {
|
|
1132
|
+
const projectId = body?.project_id || ctx.query?.project_id
|
|
1133
|
+
if (!projectId) throw new Error('project_id is required')
|
|
1134
|
+
|
|
1135
|
+
const { count: totalChunks } = await adminDb
|
|
1136
|
+
.from('items')
|
|
1137
|
+
.select('id', { count: 'exact' })
|
|
1138
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
1139
|
+
.filter('data->>project_id', 'eq', projectId)
|
|
1140
|
+
.eq('is_active', true)
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
const { data: chunkItems } = await adminDb
|
|
1144
|
+
.from('items')
|
|
1145
|
+
.select('id')
|
|
1146
|
+
.filter('data->>kb_type', 'eq', 'cil_chunk')
|
|
1147
|
+
.filter('data->>project_id', 'eq', projectId)
|
|
1148
|
+
.eq('is_active', true)
|
|
1149
|
+
|
|
1150
|
+
const chunkIds = (chunkItems || []).map((c: any) => c.id)
|
|
1151
|
+
let embeddedCount = 0
|
|
1152
|
+
|
|
1153
|
+
if (chunkIds.length > 0) {
|
|
1154
|
+
const { data: embeddedDocs } = await adminDb
|
|
1155
|
+
.from('embeddings')
|
|
1156
|
+
.select('document_id')
|
|
1157
|
+
.in('document_id', chunkIds)
|
|
1158
|
+
.is('revoked_at', null)
|
|
1159
|
+
|
|
1160
|
+
embeddedCount = new Set((embeddedDocs || []).map((e: any) => e.document_id)).size
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
return {
|
|
1164
|
+
project_id: projectId,
|
|
1165
|
+
total_chunks: totalChunks || 0,
|
|
1166
|
+
embedded_chunks: embeddedCount,
|
|
1167
|
+
unembedded_chunks: (totalChunks || 0) - embeddedCount,
|
|
1168
|
+
coverage_pct: totalChunks ? Math.round((embeddedCount / totalChunks) * 100) : 0,
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
if (action === 'search') {
|
|
1174
|
+
const query = body?.q || ctx.query?.q
|
|
1175
|
+
const projectId = body?.project_id || ctx.query?.project_id || null
|
|
1176
|
+
const limit = parseInt(body?.limit || ctx.query?.limit || '10')
|
|
1177
|
+
if (!query) throw new Error('q is required')
|
|
1178
|
+
|
|
1179
|
+
const results = await semanticSearch(query, projectId, limit, accountId)
|
|
1180
|
+
return { query, results, count: results.length }
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
|
|
1184
|
+
if (action === 'delete') {
|
|
1185
|
+
const chunkId = body?.chunk_id || ctx.query?.chunk_id
|
|
1186
|
+
if (!chunkId) throw new Error('chunk_id is required')
|
|
1187
|
+
|
|
1188
|
+
const { error } = await adminDb
|
|
1189
|
+
.from('embeddings')
|
|
1190
|
+
.delete()
|
|
1191
|
+
.eq('document_id', chunkId)
|
|
1192
|
+
|
|
1193
|
+
if (error) throw new Error(`Failed to delete embeddings: ${error.message}`)
|
|
1194
|
+
return { chunk_id: chunkId, deleted: true }
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
throw new Error(`Unknown action: ${action}. Valid: embed_chunk, embed_project, embed_approved, status, search, delete`)
|
|
1198
|
+
})
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
// CIL[END chunk_id="custom_apps_cortex_functions_custom_cil_embeddings_handler_1784235266"]
|