@open-mercato/search 0.6.7-develop.6842.1.8cd8a8883c → 0.6.7-develop.6859.1.e4727b568a

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 (33) hide show
  1. package/AGENTS.md +2 -0
  2. package/dist/modules/search/api/openapi.js +2 -0
  3. package/dist/modules/search/api/openapi.js.map +2 -2
  4. package/dist/modules/search/api/settings/vector-store/route.js +34 -0
  5. package/dist/modules/search/api/settings/vector-store/route.js.map +2 -2
  6. package/dist/modules/search/frontend/components/SearchSettingsPageClient.js.map +2 -2
  7. package/dist/modules/search/frontend/components/sections/VectorSearchSection.js +14 -2
  8. package/dist/modules/search/frontend/components/sections/VectorSearchSection.js.map +2 -2
  9. package/dist/modules/search/i18n/de.json +2 -0
  10. package/dist/modules/search/i18n/en.json +2 -0
  11. package/dist/modules/search/i18n/es.json +2 -0
  12. package/dist/modules/search/i18n/ko.json +2 -0
  13. package/dist/modules/search/i18n/pl.json +2 -0
  14. package/dist/strategies/vector.strategy.js +3 -1
  15. package/dist/strategies/vector.strategy.js.map +2 -2
  16. package/dist/vector/drivers/pgvector/index.js +55 -1
  17. package/dist/vector/drivers/pgvector/index.js.map +2 -2
  18. package/dist/vector/types.js.map +2 -2
  19. package/package.json +4 -4
  20. package/src/__tests__/pgvector-extension-availability.test.ts +158 -0
  21. package/src/modules/search/api/openapi.ts +2 -0
  22. package/src/modules/search/api/settings/vector-store/route.ts +43 -0
  23. package/src/modules/search/frontend/components/SearchSettingsPageClient.tsx +2 -0
  24. package/src/modules/search/frontend/components/sections/VectorSearchSection.tsx +31 -1
  25. package/src/modules/search/frontend/components/sections/__tests__/VectorSearchSection.test.tsx +36 -1
  26. package/src/modules/search/i18n/de.json +2 -0
  27. package/src/modules/search/i18n/en.json +2 -0
  28. package/src/modules/search/i18n/es.json +2 -0
  29. package/src/modules/search/i18n/ko.json +2 -0
  30. package/src/modules/search/i18n/pl.json +2 -0
  31. package/src/strategies/vector.strategy.ts +7 -1
  32. package/src/vector/drivers/pgvector/index.ts +77 -1
  33. package/src/vector/types.ts +14 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/vector/drivers/pgvector/index.ts"],
4
- "sourcesContent": ["import { Pool } from 'pg'\nimport { searchDebugWarn } from '../../../lib/debug'\n\ntype PgPoolQueryResult<T> = { rows: T[]; rowCount?: number }\ntype PgPoolClient = {\n query<T = any>(text: string, params?: any[]): Promise<PgPoolQueryResult<T>>\n release(): void\n}\nexport type PgPool = {\n connect(): Promise<PgPoolClient>\n query<T = any>(text: string, params?: any[]): Promise<PgPoolQueryResult<T>>\n end(): Promise<void>\n}\nimport type {\n VectorDriver,\n VectorDriverDocument,\n VectorDriverQuery,\n VectorDriverQueryResult,\n VectorDriverListParams,\n VectorDriverCountParams,\n VectorIndexEntry,\n VectorDriverRemoveOrphansParams,\n VectorResultPresenter,\n VectorLinkDescriptor,\n} from '../../types'\n\ntype PgVectorDriverOptions = {\n pool?: PgPool\n connectionString?: string\n tableName?: string\n migrationsTable?: string\n dimension?: number\n distanceMetric?: 'cosine' | 'euclidean' | 'inner'\n}\n\nconst DEFAULT_TABLE = 'vector_search'\nconst DEFAULT_MIGRATIONS_TABLE = 'vector_search_migrations'\nconst DEFAULT_DIMENSION = 1536\nconst DRIVER_ID = 'pgvector' as const\n\nfunction assertIdentifier(name: string, defaultName: string): string {\n const candidate = name ?? defaultName\n if (!candidate || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(candidate)) return defaultName\n return candidate\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name}\"`\n}\n\nfunction toVectorLiteral(values: number[]): string {\n const formatted = values.map((n) => {\n if (!Number.isFinite(n)) return '0'\n const rounded = Math.fround(n)\n return Number.isInteger(rounded) ? `${rounded}.0` : `${rounded}`\n })\n return `[${formatted.join(',')}]`\n}\n\nfunction parseJsonColumn<T>(value: unknown): T | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') {\n try {\n return JSON.parse(value) as T\n } catch {\n // When `jsonb` stores a JSON string, node-postgres parses it into a plain JS string.\n // In that case, there is nothing to JSON.parse \u2014 return the raw string value.\n return value as unknown as T\n }\n }\n if (typeof value === 'object') {\n return value as T\n }\n return null\n}\n\nasync function withClient<T>(pool: PgPool, fn: (client: PgPoolClient) => Promise<T>): Promise<T> {\n const client = await pool.connect()\n try {\n return await fn(client)\n } finally {\n client.release()\n }\n}\n\nexport function createPgVectorDriver(opts: PgVectorDriverOptions = {}): VectorDriver {\n const tableName = assertIdentifier(opts.tableName ?? DEFAULT_TABLE, DEFAULT_TABLE)\n const migrationsTable = assertIdentifier(opts.migrationsTable ?? DEFAULT_MIGRATIONS_TABLE, DEFAULT_MIGRATIONS_TABLE)\n let dimension = opts.dimension ?? DEFAULT_DIMENSION\n const distanceMetric = opts.distanceMetric ?? 'cosine'\n const tableIdent = quoteIdent(tableName)\n const migrationsIdent = quoteIdent(migrationsTable)\n\n const pool: PgPool =\n opts.pool ??\n (() => {\n const conn = opts.connectionString ?? process.env.DATABASE_URL\n if (!conn) {\n throw new Error('[vector.pgvector] DATABASE_URL is not configured')\n }\n return new Pool({ connectionString: conn }) as unknown as PgPool\n })()\n\n let ready: Promise<void> | null = null\n\n const ensureReady = async () => {\n if (!ready) {\n ready = withClient(pool, async (client) => {\n const ensureExtension = async (extension: 'pgcrypto' | 'vector') => {\n try {\n await client.query(`CREATE EXTENSION IF NOT EXISTS ${extension}`)\n } catch (error) {\n const pgError = error as { code?: string; message?: string }\n if (pgError?.code === '42501') {\n const details = pgError.message ? ` (${pgError.message})` : ''\n searchDebugWarn('vector.pgvector', `skipping ${extension} extension creation; requires superuser${details}`)\n return\n }\n throw error\n }\n }\n\n await ensureExtension('pgcrypto')\n await ensureExtension('vector')\n\n await client.query(\n `CREATE TABLE IF NOT EXISTS ${migrationsIdent} (\n id text primary key,\n applied_at timestamptz not null default now()\n )`,\n )\n\n await client.query(\n `CREATE TABLE IF NOT EXISTS ${tableIdent} (\n id uuid primary key default gen_random_uuid(),\n driver_id text not null,\n entity_id text not null,\n record_id text not null,\n tenant_id uuid not null,\n organization_id uuid null,\n checksum text not null,\n embedding vector(${dimension}) not null,\n url text null,\n presenter jsonb null,\n links jsonb null,\n payload jsonb null,\n result_title text null,\n result_subtitle text null,\n result_icon text null,\n result_badge text null,\n result_snapshot text null,\n primary_link_href text null,\n primary_link_label text null,\n created_at timestamptz not null default now(),\n updated_at timestamptz not null default now()\n )`,\n )\n\n await client.query(\n `CREATE UNIQUE INDEX IF NOT EXISTS ${tableName}_uniq ON ${tableIdent} (driver_id, entity_id, record_id, tenant_id)`,\n )\n await client.query(\n `CREATE INDEX IF NOT EXISTS ${tableName}_lookup ON ${tableIdent} (tenant_id, organization_id, entity_id)`,\n )\n // ivfflat index only supports up to 2000 dimensions\n // For higher dimensions, skip the index (uses sequential scan, slower but works)\n // Also check actual table dimension in case driver was initialized with different value\n let actualDimension = dimension\n try {\n const dimResult = await client.query<{ atttypmod: number }>(\n `SELECT a.atttypmod\n FROM pg_attribute a\n JOIN pg_class c ON a.attrelid = c.oid\n WHERE c.relname = $1\n AND a.attname = 'embedding'\n AND a.atttypmod > 0`,\n [tableName]\n )\n if (dimResult.rows.length > 0 && dimResult.rows[0].atttypmod > 0) {\n actualDimension = dimResult.rows[0].atttypmod\n }\n } catch {\n // Ignore errors reading dimension, use configured value\n }\n\n if (actualDimension <= 2000) {\n try {\n await client.query(\n `CREATE INDEX IF NOT EXISTS ${tableName}_embedding_idx ON ${tableIdent}\n USING ivfflat (embedding vector_${distanceMetric}_ops) WITH (lists = 100)`,\n )\n } catch (indexErr: unknown) {\n // Handle case where dimension exceeds ivfflat limit\n const errorMessage = indexErr instanceof Error ? indexErr.message : String(indexErr)\n if (errorMessage.includes('2000 dimensions')) {\n searchDebugWarn('pgvector', 'Skipping ivfflat index - dimension exceeds 2000 limit. Searches will use sequential scan.')\n } else {\n throw indexErr\n }\n }\n } else {\n searchDebugWarn('pgvector', `Skipping ivfflat index - dimension ${actualDimension} exceeds 2000 limit. Searches will use sequential scan.`)\n }\n\n const columnAlters = [\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_title text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_subtitle text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_icon text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_badge text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_snapshot text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS primary_link_href text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS primary_link_label text`,\n ]\n for (const statement of columnAlters) {\n await client.query(statement)\n }\n\n await client.query(\n `INSERT INTO ${migrationsIdent} (id, applied_at) VALUES ($1, now()) ON CONFLICT (id) DO NOTHING`,\n ['0001_init'],\n )\n }).catch((err) => {\n ready = null\n throw err\n })\n }\n return ready\n }\n\n const upsert = async (doc: VectorDriverDocument) => {\n await ensureReady()\n const vectorLiteral = toVectorLiteral(doc.embedding)\n await pool.query(\n `\n INSERT INTO ${tableIdent} (\n driver_id, entity_id, record_id, tenant_id, organization_id, checksum,\n embedding, url, presenter, links, payload,\n result_title, result_subtitle, result_icon, result_badge, result_snapshot,\n primary_link_href, primary_link_label,\n created_at, updated_at\n )\n VALUES (\n $1, $2, $3, $4::uuid, $5::uuid, $6, $7::vector, $8, $9::jsonb, $10::jsonb, $11::jsonb,\n $12, $13, $14, $15, $16, $17, $18,\n now(), now()\n )\n ON CONFLICT (driver_id, entity_id, record_id, tenant_id)\n DO UPDATE SET\n organization_id = EXCLUDED.organization_id,\n checksum = EXCLUDED.checksum,\n embedding = EXCLUDED.embedding,\n url = EXCLUDED.url,\n presenter = EXCLUDED.presenter,\n links = EXCLUDED.links,\n payload = EXCLUDED.payload,\n result_title = EXCLUDED.result_title,\n result_subtitle = EXCLUDED.result_subtitle,\n result_icon = EXCLUDED.result_icon,\n result_badge = EXCLUDED.result_badge,\n result_snapshot = EXCLUDED.result_snapshot,\n primary_link_href = EXCLUDED.primary_link_href,\n primary_link_label = EXCLUDED.primary_link_label,\n updated_at = now()\n `,\n [\n doc.driverId ?? DRIVER_ID,\n doc.entityId,\n doc.recordId,\n doc.tenantId,\n doc.organizationId ?? null,\n doc.checksum,\n vectorLiteral,\n doc.url ?? null,\n doc.presenter ? JSON.stringify(doc.presenter) : null,\n doc.links ? JSON.stringify(doc.links) : null,\n doc.payload ? JSON.stringify(doc.payload) : null,\n doc.resultTitle,\n doc.resultSubtitle ?? null,\n doc.resultIcon ?? null,\n doc.resultBadge ?? null,\n doc.resultSnapshot ?? null,\n doc.primaryLinkHref ?? null,\n doc.primaryLinkLabel ?? null,\n ],\n )\n }\n\n const remove = async (entityId: string, recordId: string, tenantId: string) => {\n await ensureReady()\n await pool.query(\n `DELETE FROM ${tableIdent} WHERE driver_id = $1 AND entity_id = $2 AND record_id = $3 AND tenant_id = $4::uuid`,\n [DRIVER_ID, entityId, recordId, tenantId],\n )\n }\n\n const getChecksum = async (entityId: string, recordId: string, tenantId: string): Promise<string | null> => {\n await ensureReady()\n const res = await pool.query<{ checksum: string }>(\n `SELECT checksum FROM ${tableIdent} WHERE driver_id = $1 AND entity_id = $2 AND record_id = $3 AND tenant_id = $4::uuid`,\n [DRIVER_ID, entityId, recordId, tenantId],\n )\n return res.rowCount ? res.rows[0].checksum : null\n }\n\n const purge = async (entityId: string, tenantId: string, organizationId?: string | null) => {\n await ensureReady()\n const normalizedOrganizationId =\n typeof organizationId === 'string' && organizationId.trim().length > 0 ? organizationId.trim() : null\n const conditions = ['driver_id = $1', 'entity_id = $2', 'tenant_id = $3::uuid']\n const values: any[] = [DRIVER_ID, entityId, tenantId]\n if (normalizedOrganizationId !== null) {\n conditions.push('organization_id = $4::uuid')\n values.push(normalizedOrganizationId)\n }\n await pool.query(\n `DELETE FROM ${tableIdent} WHERE ${conditions.join(' AND ')}`,\n values,\n )\n }\n\n const query = async (input: VectorDriverQuery): Promise<VectorDriverQueryResult[]> => {\n await ensureReady()\n const vectorLiteral = toVectorLiteral(input.vector)\n const filter = input.filter ?? { tenantId: '' }\n const normalizedOrganizationId =\n typeof filter.organizationId === 'string' && filter.organizationId.trim().length > 0\n ? filter.organizationId.trim()\n : null\n const normalizedOrganizationIds = normalizedOrganizationId\n ? [normalizedOrganizationId]\n : Array.isArray(filter.organizationIds)\n ? Array.from(new Set(\n filter.organizationIds\n .map((value) => (typeof value === 'string' ? value.trim() : ''))\n .filter((value) => value.length > 0),\n ))\n : null\n if (normalizedOrganizationIds && normalizedOrganizationIds.length === 0) {\n return []\n }\n const params: any[] = [\n vectorLiteral,\n DRIVER_ID,\n filter.tenantId,\n normalizedOrganizationIds,\n Array.isArray(filter.entityIds) && filter.entityIds.length ? filter.entityIds : null,\n input.limit ?? 20,\n ]\n const res = await pool.query<{\n entity_id: string\n record_id: string\n organization_id: string | null\n checksum: string\n url: string | null\n presenter: string | null\n links: string | null\n payload: string | null\n result_title: string | null\n result_subtitle: string | null\n result_icon: string | null\n result_badge: string | null\n result_snapshot: string | null\n primary_link_href: string | null\n primary_link_label: string | null\n distance: number\n }>(\n `\n SELECT\n entity_id,\n record_id,\n organization_id,\n checksum,\n url,\n presenter,\n links,\n payload,\n result_title,\n result_subtitle,\n result_icon,\n result_badge,\n result_snapshot,\n primary_link_href,\n primary_link_label,\n embedding <=> $1::vector AS distance\n FROM ${tableIdent}\n WHERE driver_id = $2\n AND tenant_id = $3::uuid\n AND ($4::uuid[] IS NULL OR organization_id = ANY($4::uuid[]))\n AND (\n $5::text[] IS NULL OR entity_id = ANY($5::text[])\n )\n ORDER BY embedding <=> $1::vector\n LIMIT $6\n `,\n params,\n )\n return res.rows.map<VectorDriverQueryResult>((row) => {\n const distance = typeof row.distance === 'number' ? row.distance : Number(row.distance || 1)\n const score = 1 - distance\n return {\n entityId: row.entity_id,\n recordId: row.record_id,\n organizationId: row.organization_id ?? null,\n checksum: row.checksum,\n url: row.url ?? null,\n presenter: parseJsonColumn<VectorResultPresenter>(row.presenter),\n links: parseJsonColumn<VectorLinkDescriptor[]>(row.links),\n payload: parseJsonColumn<Record<string, unknown>>(row.payload),\n resultTitle: row.result_title ?? '',\n resultSubtitle: row.result_subtitle ?? null,\n resultIcon: row.result_icon ?? null,\n resultBadge: row.result_badge ?? null,\n resultSnapshot: row.result_snapshot ?? null,\n primaryLinkHref: row.primary_link_href ?? null,\n primaryLinkLabel: row.primary_link_label ?? null,\n score,\n }\n })\n }\n\n const list = async (params: VectorDriverListParams): Promise<VectorIndexEntry[]> => {\n await ensureReady()\n const limit = Math.max(1, Math.min(params.limit ?? 50, 200))\n const offset = Math.max(0, params.offset ?? 0)\n const orderColumn = params.orderBy === 'created' ? 'created_at' : 'updated_at'\n const conditions: string[] = [\n 'driver_id = $1',\n 'tenant_id = $2::uuid',\n ]\n const values: any[] = [DRIVER_ID, params.tenantId]\n let nextParam = 3\n\n const normalizedOrganizationId =\n typeof params.organizationId === 'string' && params.organizationId.trim().length > 0\n ? params.organizationId.trim()\n : null\n if (normalizedOrganizationId !== null) {\n conditions.push(`organization_id = $${nextParam}::uuid`)\n values.push(normalizedOrganizationId)\n nextParam += 1\n }\n\n if (params.entityId) {\n conditions.push(`entity_id = $${nextParam}::text`)\n values.push(params.entityId)\n nextParam += 1\n }\n\n const limitParam = nextParam\n const offsetParam = nextParam + 1\n values.push(limit, offset)\n\n const sql = `\n SELECT\n entity_id,\n record_id,\n tenant_id,\n organization_id,\n checksum,\n url,\n presenter,\n links,\n payload,\n result_title,\n result_subtitle,\n result_icon,\n result_badge,\n result_snapshot,\n primary_link_href,\n primary_link_label,\n created_at,\n updated_at\n FROM ${tableIdent}\n WHERE ${conditions.join('\\n AND ')}\n ORDER BY ${orderColumn} DESC\n LIMIT $${limitParam} OFFSET $${offsetParam}\n `\n\n const res = await pool.query<{\n entity_id: string\n record_id: string\n tenant_id: string\n organization_id: string | null\n checksum: string\n url: string | null\n presenter: string | null\n links: string | null\n payload: string | null\n result_title: string | null\n result_subtitle: string | null\n result_icon: string | null\n result_badge: string | null\n result_snapshot: string | null\n primary_link_href: string | null\n primary_link_label: string | null\n created_at: Date | string\n updated_at: Date | string\n }>(sql, values)\n return res.rows.map<VectorIndexEntry>((row) => {\n const presenter = parseJsonColumn<VectorResultPresenter>(row.presenter)\n const links = parseJsonColumn<VectorLinkDescriptor[]>(row.links)\n const payload = parseJsonColumn<Record<string, unknown>>(row.payload)\n const createdAt =\n row.created_at instanceof Date\n ? row.created_at.toISOString()\n : new Date(row.created_at ?? Date.now()).toISOString()\n const updatedAt =\n row.updated_at instanceof Date\n ? row.updated_at.toISOString()\n : new Date(row.updated_at ?? Date.now()).toISOString()\n return {\n entityId: row.entity_id,\n recordId: row.record_id,\n driverId: DRIVER_ID,\n tenantId: row.tenant_id,\n organizationId: row.organization_id ?? null,\n checksum: row.checksum,\n url: row.url ?? null,\n presenter,\n links,\n payload,\n metadata: payload,\n resultTitle: row.result_title ?? '',\n resultSubtitle: row.result_subtitle ?? null,\n resultIcon: row.result_icon ?? null,\n resultBadge: row.result_badge ?? null,\n resultSnapshot: row.result_snapshot ?? null,\n primaryLinkHref: row.primary_link_href ?? null,\n primaryLinkLabel: row.primary_link_label ?? null,\n createdAt,\n updatedAt,\n score: null,\n }\n })\n }\n\n const count = async (params: VectorDriverCountParams): Promise<number> => {\n await ensureReady()\n const conditions: string[] = [\n 'driver_id = $1',\n 'tenant_id = $2::uuid',\n ]\n const values: any[] = [DRIVER_ID, params.tenantId]\n let nextParam = 3\n\n const normalizedOrganizationId =\n typeof params.organizationId === 'string' && params.organizationId.trim().length > 0\n ? params.organizationId.trim()\n : null\n if (normalizedOrganizationId !== null) {\n conditions.push(`organization_id = $${nextParam}::uuid`)\n values.push(normalizedOrganizationId)\n nextParam += 1\n }\n if (params.entityId) {\n conditions.push(`entity_id = $${nextParam}::text`)\n values.push(params.entityId)\n nextParam += 1\n }\n\n const sql = `\n SELECT count(*)::bigint AS total\n FROM ${tableIdent}\n WHERE ${conditions.join('\\n AND ')}\n `\n const res = await pool.query<{ total: string }>(sql, values)\n const raw = res.rows?.[0]?.total\n if (!raw) return 0\n const parsed = Number(raw)\n return Number.isFinite(parsed) ? parsed : 0\n }\n\n const removeOrphans = async (params: VectorDriverRemoveOrphansParams): Promise<number> => {\n await ensureReady()\n const conditions: string[] = [\n 'driver_id = $1',\n 'entity_id = $2',\n 'updated_at < $3::timestamptz',\n ]\n const values: any[] = [DRIVER_ID, params.entityId, (params.olderThan instanceof Date ? params.olderThan : new Date(params.olderThan)).toISOString()]\n let nextParam = 4\n\n if (params.tenantId !== undefined) {\n conditions.push(`tenant_id is not distinct from $${nextParam}::uuid`)\n values.push(params.tenantId)\n nextParam += 1\n }\n\n if (params.organizationId !== undefined) {\n conditions.push(`organization_id is not distinct from $${nextParam}::uuid`)\n values.push(params.organizationId)\n nextParam += 1\n }\n\n const sql = `\n DELETE FROM ${tableIdent}\n WHERE ${conditions.join('\\n AND ')}\n `\n const res = await pool.query(sql, values)\n return res.rowCount ?? 0\n }\n\n const getTableDimension = async (): Promise<number | null> => {\n try {\n const res = await pool.query<{ atttypmod: number }>(\n `SELECT a.atttypmod\n FROM pg_attribute a\n JOIN pg_class c ON a.attrelid = c.oid\n WHERE c.relname = $1\n AND a.attname = 'embedding'\n AND a.atttypmod > 0`,\n [tableName]\n )\n if (res.rows.length > 0 && res.rows[0].atttypmod > 0) {\n return res.rows[0].atttypmod\n }\n return null\n } catch {\n return null\n }\n }\n\n const recreateWithDimension = async (newDimension: number): Promise<void> => {\n await withClient(pool, async (client) => {\n await client.query(`DROP TABLE IF EXISTS ${tableIdent} CASCADE`)\n await client.query(`DROP TABLE IF EXISTS ${migrationsIdent} CASCADE`)\n })\n ready = null\n dimension = newDimension\n await ensureReady()\n }\n\n return {\n id: 'pgvector',\n ensureReady,\n upsert,\n delete: remove,\n getChecksum,\n purge,\n query,\n list,\n count,\n removeOrphans,\n getTableDimension,\n recreateWithDimension,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Pool } from 'pg'\nimport { searchDebugWarn, searchWarn } from '../../../lib/debug'\n\ntype PgPoolQueryResult<T> = { rows: T[]; rowCount?: number }\ntype PgPoolClient = {\n query<T = any>(text: string, params?: any[]): Promise<PgPoolQueryResult<T>>\n release(): void\n}\nexport type PgPool = {\n connect(): Promise<PgPoolClient>\n query<T = any>(text: string, params?: any[]): Promise<PgPoolQueryResult<T>>\n end(): Promise<void>\n}\nimport type {\n VectorDriver,\n VectorDriverDocument,\n VectorDriverQuery,\n VectorDriverQueryResult,\n VectorDriverListParams,\n VectorDriverCountParams,\n VectorIndexEntry,\n VectorDriverRemoveOrphansParams,\n VectorResultPresenter,\n VectorLinkDescriptor,\n VectorDriverStatus,\n} from '../../types'\n\ntype PgVectorDriverOptions = {\n pool?: PgPool\n connectionString?: string\n tableName?: string\n migrationsTable?: string\n dimension?: number\n distanceMetric?: 'cosine' | 'euclidean' | 'inner'\n}\n\nconst DEFAULT_TABLE = 'vector_search'\nconst DEFAULT_MIGRATIONS_TABLE = 'vector_search_migrations'\nconst DEFAULT_DIMENSION = 1536\nconst DRIVER_ID = 'pgvector' as const\n\n// `undefined_file` is what Postgres raises for `extension \"vector\" is not available`\n// (the pgvector shared library is not installed on the server); `feature_not_supported`\n// covers managed providers that refuse the extension outright, and `undefined_object`\n// the case where the extension record is missing. None of these can be resolved by\n// retrying, so the driver reports itself unhealthy instead of failing every record.\nconst EXTENSION_MISSING_CODES = new Set(['58P01', '0A000', '42704'])\nconst EXTENSION_UNAVAILABLE_REASON = 'extension \"vector\" is not available on this PostgreSQL server'\n// Re-probe occasionally so installing pgvector recovers without restarting the process.\nconst UNAVAILABLE_RECHECK_MS = 60_000\n\nfunction assertIdentifier(name: string, defaultName: string): string {\n const candidate = name ?? defaultName\n if (!candidate || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(candidate)) return defaultName\n return candidate\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name}\"`\n}\n\nfunction toVectorLiteral(values: number[]): string {\n const formatted = values.map((n) => {\n if (!Number.isFinite(n)) return '0'\n const rounded = Math.fround(n)\n return Number.isInteger(rounded) ? `${rounded}.0` : `${rounded}`\n })\n return `[${formatted.join(',')}]`\n}\n\nfunction parseJsonColumn<T>(value: unknown): T | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') {\n try {\n return JSON.parse(value) as T\n } catch {\n // When `jsonb` stores a JSON string, node-postgres parses it into a plain JS string.\n // In that case, there is nothing to JSON.parse \u2014 return the raw string value.\n return value as unknown as T\n }\n }\n if (typeof value === 'object') {\n return value as T\n }\n return null\n}\n\nasync function withClient<T>(pool: PgPool, fn: (client: PgPoolClient) => Promise<T>): Promise<T> {\n const client = await pool.connect()\n try {\n return await fn(client)\n } finally {\n client.release()\n }\n}\n\nexport function createPgVectorDriver(opts: PgVectorDriverOptions = {}): VectorDriver {\n const tableName = assertIdentifier(opts.tableName ?? DEFAULT_TABLE, DEFAULT_TABLE)\n const migrationsTable = assertIdentifier(opts.migrationsTable ?? DEFAULT_MIGRATIONS_TABLE, DEFAULT_MIGRATIONS_TABLE)\n let dimension = opts.dimension ?? DEFAULT_DIMENSION\n const distanceMetric = opts.distanceMetric ?? 'cosine'\n const tableIdent = quoteIdent(tableName)\n const migrationsIdent = quoteIdent(migrationsTable)\n\n const pool: PgPool =\n opts.pool ??\n (() => {\n const conn = opts.connectionString ?? process.env.DATABASE_URL\n if (!conn) {\n throw new Error('[vector.pgvector] DATABASE_URL is not configured')\n }\n return new Pool({ connectionString: conn }) as unknown as PgPool\n })()\n\n let ready: Promise<void> | null = null\n let status: { value: VectorDriverStatus; at: number } | null = null\n let reportedReason: string | null = null\n\n const cacheStatus = (value: VectorDriverStatus): VectorDriverStatus => {\n status = { value, at: Date.now() }\n if (value.available) {\n reportedReason = null\n } else if (value.reason && value.reason !== reportedReason) {\n reportedReason = value.reason\n // Always visible: replacing a per-record error storm with silence would hide the fact\n // that vector indexing has stopped until someone opens Settings > Search.\n searchWarn('vector.pgvector', `vector store unavailable \u2014 skipping vector indexing and search (${value.reason})`)\n }\n return value\n }\n\n // The operator-facing reason is curated rather than the raw Postgres message, which the\n // settings API surfaces to the browser.\n const markExtensionMissing = () => {\n cacheStatus({ available: false, reason: EXTENSION_UNAVAILABLE_REASON })\n }\n\n const readCachedStatus = (): VectorDriverStatus | null => {\n if (!status) return null\n return Date.now() - status.at < UNAVAILABLE_RECHECK_MS ? status.value : null\n }\n\n const getStatus = async (): Promise<VectorDriverStatus> => {\n const cached = readCachedStatus()\n if (cached) return cached\n try {\n const res = await pool.query<{ installed: boolean; installable: boolean }>(\n `SELECT\n EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') AS installed,\n EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'vector') AS installable`,\n )\n const row = res.rows[0]\n if (row?.installed || row?.installable) return cacheStatus({ available: true })\n return cacheStatus({ available: false, reason: EXTENSION_UNAVAILABLE_REASON })\n } catch {\n // A probe that cannot run (connection refused, permissions on the catalogs)\n // says nothing about pgvector itself \u2014 stay optimistic so genuinely transient\n // database problems keep their existing retry semantics.\n return { available: true }\n }\n }\n\n const isHealthy = async (): Promise<boolean> => (await getStatus()).available\n\n const ensureReady = async () => {\n const cached = readCachedStatus()\n if (cached && !cached.available) {\n throw new Error(`[vector.pgvector] ${cached.reason ?? 'vector store unavailable'}`)\n }\n if (!ready) {\n ready = withClient(pool, async (client) => {\n const ensureExtension = async (extension: 'pgcrypto' | 'vector') => {\n try {\n await client.query(`CREATE EXTENSION IF NOT EXISTS ${extension}`)\n } catch (error) {\n const pgError = error as { code?: string; message?: string }\n if (pgError?.code === '42501') {\n const details = pgError.message ? ` (${pgError.message})` : ''\n searchDebugWarn('vector.pgvector', `skipping ${extension} extension creation; requires superuser${details}`)\n return\n }\n if (extension === 'vector' && pgError?.code && EXTENSION_MISSING_CODES.has(pgError.code)) {\n markExtensionMissing()\n }\n throw error\n }\n }\n\n await ensureExtension('pgcrypto')\n await ensureExtension('vector')\n\n await client.query(\n `CREATE TABLE IF NOT EXISTS ${migrationsIdent} (\n id text primary key,\n applied_at timestamptz not null default now()\n )`,\n )\n\n await client.query(\n `CREATE TABLE IF NOT EXISTS ${tableIdent} (\n id uuid primary key default gen_random_uuid(),\n driver_id text not null,\n entity_id text not null,\n record_id text not null,\n tenant_id uuid not null,\n organization_id uuid null,\n checksum text not null,\n embedding vector(${dimension}) not null,\n url text null,\n presenter jsonb null,\n links jsonb null,\n payload jsonb null,\n result_title text null,\n result_subtitle text null,\n result_icon text null,\n result_badge text null,\n result_snapshot text null,\n primary_link_href text null,\n primary_link_label text null,\n created_at timestamptz not null default now(),\n updated_at timestamptz not null default now()\n )`,\n )\n\n await client.query(\n `CREATE UNIQUE INDEX IF NOT EXISTS ${tableName}_uniq ON ${tableIdent} (driver_id, entity_id, record_id, tenant_id)`,\n )\n await client.query(\n `CREATE INDEX IF NOT EXISTS ${tableName}_lookup ON ${tableIdent} (tenant_id, organization_id, entity_id)`,\n )\n // ivfflat index only supports up to 2000 dimensions\n // For higher dimensions, skip the index (uses sequential scan, slower but works)\n // Also check actual table dimension in case driver was initialized with different value\n let actualDimension = dimension\n try {\n const dimResult = await client.query<{ atttypmod: number }>(\n `SELECT a.atttypmod\n FROM pg_attribute a\n JOIN pg_class c ON a.attrelid = c.oid\n WHERE c.relname = $1\n AND a.attname = 'embedding'\n AND a.atttypmod > 0`,\n [tableName]\n )\n if (dimResult.rows.length > 0 && dimResult.rows[0].atttypmod > 0) {\n actualDimension = dimResult.rows[0].atttypmod\n }\n } catch {\n // Ignore errors reading dimension, use configured value\n }\n\n if (actualDimension <= 2000) {\n try {\n await client.query(\n `CREATE INDEX IF NOT EXISTS ${tableName}_embedding_idx ON ${tableIdent}\n USING ivfflat (embedding vector_${distanceMetric}_ops) WITH (lists = 100)`,\n )\n } catch (indexErr: unknown) {\n // Handle case where dimension exceeds ivfflat limit\n const errorMessage = indexErr instanceof Error ? indexErr.message : String(indexErr)\n if (errorMessage.includes('2000 dimensions')) {\n searchDebugWarn('pgvector', 'Skipping ivfflat index - dimension exceeds 2000 limit. Searches will use sequential scan.')\n } else {\n throw indexErr\n }\n }\n } else {\n searchDebugWarn('pgvector', `Skipping ivfflat index - dimension ${actualDimension} exceeds 2000 limit. Searches will use sequential scan.`)\n }\n\n const columnAlters = [\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_title text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_subtitle text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_icon text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_badge text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS result_snapshot text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS primary_link_href text`,\n `ALTER TABLE ${tableIdent} ADD COLUMN IF NOT EXISTS primary_link_label text`,\n ]\n for (const statement of columnAlters) {\n await client.query(statement)\n }\n\n await client.query(\n `INSERT INTO ${migrationsIdent} (id, applied_at) VALUES ($1, now()) ON CONFLICT (id) DO NOTHING`,\n ['0001_init'],\n )\n }).then(() => {\n cacheStatus({ available: true })\n }).catch((err) => {\n ready = null\n const pgError = err as { code?: string; message?: string }\n // Without the extension the `vector` column type does not exist either, so the\n // table DDL fails even when `CREATE EXTENSION` was skipped rather than raised.\n if (pgError?.code === '42704' && /\\bvector\\b/.test(pgError.message ?? '')) {\n markExtensionMissing()\n }\n throw err\n })\n }\n return ready\n }\n\n const upsert = async (doc: VectorDriverDocument) => {\n await ensureReady()\n const vectorLiteral = toVectorLiteral(doc.embedding)\n await pool.query(\n `\n INSERT INTO ${tableIdent} (\n driver_id, entity_id, record_id, tenant_id, organization_id, checksum,\n embedding, url, presenter, links, payload,\n result_title, result_subtitle, result_icon, result_badge, result_snapshot,\n primary_link_href, primary_link_label,\n created_at, updated_at\n )\n VALUES (\n $1, $2, $3, $4::uuid, $5::uuid, $6, $7::vector, $8, $9::jsonb, $10::jsonb, $11::jsonb,\n $12, $13, $14, $15, $16, $17, $18,\n now(), now()\n )\n ON CONFLICT (driver_id, entity_id, record_id, tenant_id)\n DO UPDATE SET\n organization_id = EXCLUDED.organization_id,\n checksum = EXCLUDED.checksum,\n embedding = EXCLUDED.embedding,\n url = EXCLUDED.url,\n presenter = EXCLUDED.presenter,\n links = EXCLUDED.links,\n payload = EXCLUDED.payload,\n result_title = EXCLUDED.result_title,\n result_subtitle = EXCLUDED.result_subtitle,\n result_icon = EXCLUDED.result_icon,\n result_badge = EXCLUDED.result_badge,\n result_snapshot = EXCLUDED.result_snapshot,\n primary_link_href = EXCLUDED.primary_link_href,\n primary_link_label = EXCLUDED.primary_link_label,\n updated_at = now()\n `,\n [\n doc.driverId ?? DRIVER_ID,\n doc.entityId,\n doc.recordId,\n doc.tenantId,\n doc.organizationId ?? null,\n doc.checksum,\n vectorLiteral,\n doc.url ?? null,\n doc.presenter ? JSON.stringify(doc.presenter) : null,\n doc.links ? JSON.stringify(doc.links) : null,\n doc.payload ? JSON.stringify(doc.payload) : null,\n doc.resultTitle,\n doc.resultSubtitle ?? null,\n doc.resultIcon ?? null,\n doc.resultBadge ?? null,\n doc.resultSnapshot ?? null,\n doc.primaryLinkHref ?? null,\n doc.primaryLinkLabel ?? null,\n ],\n )\n }\n\n const remove = async (entityId: string, recordId: string, tenantId: string) => {\n await ensureReady()\n await pool.query(\n `DELETE FROM ${tableIdent} WHERE driver_id = $1 AND entity_id = $2 AND record_id = $3 AND tenant_id = $4::uuid`,\n [DRIVER_ID, entityId, recordId, tenantId],\n )\n }\n\n const getChecksum = async (entityId: string, recordId: string, tenantId: string): Promise<string | null> => {\n await ensureReady()\n const res = await pool.query<{ checksum: string }>(\n `SELECT checksum FROM ${tableIdent} WHERE driver_id = $1 AND entity_id = $2 AND record_id = $3 AND tenant_id = $4::uuid`,\n [DRIVER_ID, entityId, recordId, tenantId],\n )\n return res.rowCount ? res.rows[0].checksum : null\n }\n\n const purge = async (entityId: string, tenantId: string, organizationId?: string | null) => {\n await ensureReady()\n const normalizedOrganizationId =\n typeof organizationId === 'string' && organizationId.trim().length > 0 ? organizationId.trim() : null\n const conditions = ['driver_id = $1', 'entity_id = $2', 'tenant_id = $3::uuid']\n const values: any[] = [DRIVER_ID, entityId, tenantId]\n if (normalizedOrganizationId !== null) {\n conditions.push('organization_id = $4::uuid')\n values.push(normalizedOrganizationId)\n }\n await pool.query(\n `DELETE FROM ${tableIdent} WHERE ${conditions.join(' AND ')}`,\n values,\n )\n }\n\n const query = async (input: VectorDriverQuery): Promise<VectorDriverQueryResult[]> => {\n await ensureReady()\n const vectorLiteral = toVectorLiteral(input.vector)\n const filter = input.filter ?? { tenantId: '' }\n const normalizedOrganizationId =\n typeof filter.organizationId === 'string' && filter.organizationId.trim().length > 0\n ? filter.organizationId.trim()\n : null\n const normalizedOrganizationIds = normalizedOrganizationId\n ? [normalizedOrganizationId]\n : Array.isArray(filter.organizationIds)\n ? Array.from(new Set(\n filter.organizationIds\n .map((value) => (typeof value === 'string' ? value.trim() : ''))\n .filter((value) => value.length > 0),\n ))\n : null\n if (normalizedOrganizationIds && normalizedOrganizationIds.length === 0) {\n return []\n }\n const params: any[] = [\n vectorLiteral,\n DRIVER_ID,\n filter.tenantId,\n normalizedOrganizationIds,\n Array.isArray(filter.entityIds) && filter.entityIds.length ? filter.entityIds : null,\n input.limit ?? 20,\n ]\n const res = await pool.query<{\n entity_id: string\n record_id: string\n organization_id: string | null\n checksum: string\n url: string | null\n presenter: string | null\n links: string | null\n payload: string | null\n result_title: string | null\n result_subtitle: string | null\n result_icon: string | null\n result_badge: string | null\n result_snapshot: string | null\n primary_link_href: string | null\n primary_link_label: string | null\n distance: number\n }>(\n `\n SELECT\n entity_id,\n record_id,\n organization_id,\n checksum,\n url,\n presenter,\n links,\n payload,\n result_title,\n result_subtitle,\n result_icon,\n result_badge,\n result_snapshot,\n primary_link_href,\n primary_link_label,\n embedding <=> $1::vector AS distance\n FROM ${tableIdent}\n WHERE driver_id = $2\n AND tenant_id = $3::uuid\n AND ($4::uuid[] IS NULL OR organization_id = ANY($4::uuid[]))\n AND (\n $5::text[] IS NULL OR entity_id = ANY($5::text[])\n )\n ORDER BY embedding <=> $1::vector\n LIMIT $6\n `,\n params,\n )\n return res.rows.map<VectorDriverQueryResult>((row) => {\n const distance = typeof row.distance === 'number' ? row.distance : Number(row.distance || 1)\n const score = 1 - distance\n return {\n entityId: row.entity_id,\n recordId: row.record_id,\n organizationId: row.organization_id ?? null,\n checksum: row.checksum,\n url: row.url ?? null,\n presenter: parseJsonColumn<VectorResultPresenter>(row.presenter),\n links: parseJsonColumn<VectorLinkDescriptor[]>(row.links),\n payload: parseJsonColumn<Record<string, unknown>>(row.payload),\n resultTitle: row.result_title ?? '',\n resultSubtitle: row.result_subtitle ?? null,\n resultIcon: row.result_icon ?? null,\n resultBadge: row.result_badge ?? null,\n resultSnapshot: row.result_snapshot ?? null,\n primaryLinkHref: row.primary_link_href ?? null,\n primaryLinkLabel: row.primary_link_label ?? null,\n score,\n }\n })\n }\n\n const list = async (params: VectorDriverListParams): Promise<VectorIndexEntry[]> => {\n await ensureReady()\n const limit = Math.max(1, Math.min(params.limit ?? 50, 200))\n const offset = Math.max(0, params.offset ?? 0)\n const orderColumn = params.orderBy === 'created' ? 'created_at' : 'updated_at'\n const conditions: string[] = [\n 'driver_id = $1',\n 'tenant_id = $2::uuid',\n ]\n const values: any[] = [DRIVER_ID, params.tenantId]\n let nextParam = 3\n\n const normalizedOrganizationId =\n typeof params.organizationId === 'string' && params.organizationId.trim().length > 0\n ? params.organizationId.trim()\n : null\n if (normalizedOrganizationId !== null) {\n conditions.push(`organization_id = $${nextParam}::uuid`)\n values.push(normalizedOrganizationId)\n nextParam += 1\n }\n\n if (params.entityId) {\n conditions.push(`entity_id = $${nextParam}::text`)\n values.push(params.entityId)\n nextParam += 1\n }\n\n const limitParam = nextParam\n const offsetParam = nextParam + 1\n values.push(limit, offset)\n\n const sql = `\n SELECT\n entity_id,\n record_id,\n tenant_id,\n organization_id,\n checksum,\n url,\n presenter,\n links,\n payload,\n result_title,\n result_subtitle,\n result_icon,\n result_badge,\n result_snapshot,\n primary_link_href,\n primary_link_label,\n created_at,\n updated_at\n FROM ${tableIdent}\n WHERE ${conditions.join('\\n AND ')}\n ORDER BY ${orderColumn} DESC\n LIMIT $${limitParam} OFFSET $${offsetParam}\n `\n\n const res = await pool.query<{\n entity_id: string\n record_id: string\n tenant_id: string\n organization_id: string | null\n checksum: string\n url: string | null\n presenter: string | null\n links: string | null\n payload: string | null\n result_title: string | null\n result_subtitle: string | null\n result_icon: string | null\n result_badge: string | null\n result_snapshot: string | null\n primary_link_href: string | null\n primary_link_label: string | null\n created_at: Date | string\n updated_at: Date | string\n }>(sql, values)\n return res.rows.map<VectorIndexEntry>((row) => {\n const presenter = parseJsonColumn<VectorResultPresenter>(row.presenter)\n const links = parseJsonColumn<VectorLinkDescriptor[]>(row.links)\n const payload = parseJsonColumn<Record<string, unknown>>(row.payload)\n const createdAt =\n row.created_at instanceof Date\n ? row.created_at.toISOString()\n : new Date(row.created_at ?? Date.now()).toISOString()\n const updatedAt =\n row.updated_at instanceof Date\n ? row.updated_at.toISOString()\n : new Date(row.updated_at ?? Date.now()).toISOString()\n return {\n entityId: row.entity_id,\n recordId: row.record_id,\n driverId: DRIVER_ID,\n tenantId: row.tenant_id,\n organizationId: row.organization_id ?? null,\n checksum: row.checksum,\n url: row.url ?? null,\n presenter,\n links,\n payload,\n metadata: payload,\n resultTitle: row.result_title ?? '',\n resultSubtitle: row.result_subtitle ?? null,\n resultIcon: row.result_icon ?? null,\n resultBadge: row.result_badge ?? null,\n resultSnapshot: row.result_snapshot ?? null,\n primaryLinkHref: row.primary_link_href ?? null,\n primaryLinkLabel: row.primary_link_label ?? null,\n createdAt,\n updatedAt,\n score: null,\n }\n })\n }\n\n const count = async (params: VectorDriverCountParams): Promise<number> => {\n await ensureReady()\n const conditions: string[] = [\n 'driver_id = $1',\n 'tenant_id = $2::uuid',\n ]\n const values: any[] = [DRIVER_ID, params.tenantId]\n let nextParam = 3\n\n const normalizedOrganizationId =\n typeof params.organizationId === 'string' && params.organizationId.trim().length > 0\n ? params.organizationId.trim()\n : null\n if (normalizedOrganizationId !== null) {\n conditions.push(`organization_id = $${nextParam}::uuid`)\n values.push(normalizedOrganizationId)\n nextParam += 1\n }\n if (params.entityId) {\n conditions.push(`entity_id = $${nextParam}::text`)\n values.push(params.entityId)\n nextParam += 1\n }\n\n const sql = `\n SELECT count(*)::bigint AS total\n FROM ${tableIdent}\n WHERE ${conditions.join('\\n AND ')}\n `\n const res = await pool.query<{ total: string }>(sql, values)\n const raw = res.rows?.[0]?.total\n if (!raw) return 0\n const parsed = Number(raw)\n return Number.isFinite(parsed) ? parsed : 0\n }\n\n const removeOrphans = async (params: VectorDriverRemoveOrphansParams): Promise<number> => {\n await ensureReady()\n const conditions: string[] = [\n 'driver_id = $1',\n 'entity_id = $2',\n 'updated_at < $3::timestamptz',\n ]\n const values: any[] = [DRIVER_ID, params.entityId, (params.olderThan instanceof Date ? params.olderThan : new Date(params.olderThan)).toISOString()]\n let nextParam = 4\n\n if (params.tenantId !== undefined) {\n conditions.push(`tenant_id is not distinct from $${nextParam}::uuid`)\n values.push(params.tenantId)\n nextParam += 1\n }\n\n if (params.organizationId !== undefined) {\n conditions.push(`organization_id is not distinct from $${nextParam}::uuid`)\n values.push(params.organizationId)\n nextParam += 1\n }\n\n const sql = `\n DELETE FROM ${tableIdent}\n WHERE ${conditions.join('\\n AND ')}\n `\n const res = await pool.query(sql, values)\n return res.rowCount ?? 0\n }\n\n const getTableDimension = async (): Promise<number | null> => {\n try {\n const res = await pool.query<{ atttypmod: number }>(\n `SELECT a.atttypmod\n FROM pg_attribute a\n JOIN pg_class c ON a.attrelid = c.oid\n WHERE c.relname = $1\n AND a.attname = 'embedding'\n AND a.atttypmod > 0`,\n [tableName]\n )\n if (res.rows.length > 0 && res.rows[0].atttypmod > 0) {\n return res.rows[0].atttypmod\n }\n return null\n } catch {\n return null\n }\n }\n\n const recreateWithDimension = async (newDimension: number): Promise<void> => {\n await withClient(pool, async (client) => {\n await client.query(`DROP TABLE IF EXISTS ${tableIdent} CASCADE`)\n await client.query(`DROP TABLE IF EXISTS ${migrationsIdent} CASCADE`)\n })\n ready = null\n dimension = newDimension\n await ensureReady()\n }\n\n return {\n id: 'pgvector',\n ensureReady,\n isHealthy,\n getStatus,\n upsert,\n delete: remove,\n getChecksum,\n purge,\n query,\n list,\n count,\n removeOrphans,\n getTableDimension,\n recreateWithDimension,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/vector/types.ts"],
4
- "sourcesContent": ["import type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type {\n VectorDriverId,\n VectorIndexSource,\n VectorModuleConfig,\n VectorEntityConfig,\n VectorLinkDescriptor,\n VectorResultPresenter,\n VectorSearchHit,\n VectorQueryRequest,\n VectorIndexEntry,\n} from '@open-mercato/shared/modules/vector'\n\nexport type {\n VectorDriverId,\n VectorIndexSource,\n VectorModuleConfig,\n VectorEntityConfig,\n VectorLinkDescriptor,\n VectorResultPresenter,\n VectorSearchHit,\n VectorQueryRequest,\n VectorIndexEntry,\n}\n\nexport type VectorDriverDocument = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n checksum: string\n embedding: number[]\n url?: string | null\n presenter?: VectorResultPresenter | null\n links?: VectorLinkDescriptor[] | null\n payload?: Record<string, unknown> | null\n driverId: VectorDriverId\n resultTitle: string\n resultSubtitle?: string | null\n resultIcon?: string | null\n resultBadge?: string | null\n resultSnapshot?: string | null\n primaryLinkHref?: string | null\n primaryLinkLabel?: string | null\n}\n\nexport type VectorDriverQuery = {\n vector: number[]\n limit?: number\n filter?: {\n entityIds?: EntityId[]\n organizationId?: string | null\n organizationIds?: string[] | null\n tenantId: string\n }\n}\n\nexport type VectorDriverQueryResult = {\n entityId: EntityId\n recordId: string\n organizationId?: string | null\n score: number\n checksum: string\n url?: string | null\n presenter?: VectorResultPresenter | null\n links?: VectorLinkDescriptor[] | null\n payload?: Record<string, unknown> | null\n resultTitle: string\n resultSubtitle?: string | null\n resultIcon?: string | null\n resultBadge?: string | null\n resultSnapshot?: string | null\n primaryLinkHref?: string | null\n primaryLinkLabel?: string | null\n}\n\nexport type VectorDriverListParams = {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n limit?: number\n offset?: number\n orderBy?: 'created' | 'updated'\n}\n\nexport type VectorDriverCountParams = {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n}\n\nexport type VectorDriverRemoveOrphansParams = {\n entityId: EntityId\n tenantId?: string | null\n organizationId?: string | null\n olderThan: Date\n}\n\nexport interface VectorDriver {\n readonly id: VectorDriverId\n ensureReady(): Promise<void>\n upsert(doc: VectorDriverDocument): Promise<void>\n delete(entityId: EntityId, recordId: string, tenantId: string): Promise<void>\n query(input: VectorDriverQuery): Promise<VectorDriverQueryResult[]>\n getChecksum(entityId: EntityId, recordId: string, tenantId: string): Promise<string | null>\n purge?(entityId: EntityId, tenantId: string, organizationId?: string | null): Promise<void>\n list?(params: VectorDriverListParams): Promise<VectorIndexEntry[]>\n count?(params: VectorDriverCountParams): Promise<number>\n removeOrphans?(params: VectorDriverRemoveOrphansParams): Promise<number | void>\n getTableDimension?(): Promise<number | null>\n recreateWithDimension?(newDimension: number): Promise<void>\n}\n\n// ============================================================================\n// Embedding Provider Configuration Types\n// ============================================================================\n\nexport type EmbeddingProviderId =\n | 'openai'\n | 'google'\n | 'mistral'\n | 'cohere'\n | 'bedrock'\n | 'ollama'\n\nexport type EmbeddingProviderConfig = {\n providerId: EmbeddingProviderId\n model: string\n dimension: number\n outputDimensionality?: number\n baseUrl?: string\n updatedAt: string\n}\n\nexport type EmbeddingModelInfo = {\n id: string\n name: string\n dimension: number\n configurableDimension?: boolean\n minDimension?: number\n maxDimension?: number\n}\n\nexport type EmbeddingProviderInfo = {\n name: string\n envKeyRequired: string\n defaultModel: string\n models: EmbeddingModelInfo[]\n}\n\nexport const EMBEDDING_PROVIDERS: Record<EmbeddingProviderId, EmbeddingProviderInfo> = {\n openai: {\n name: 'OpenAI',\n envKeyRequired: 'OPENAI_API_KEY',\n defaultModel: 'text-embedding-3-small',\n models: [\n { id: 'text-embedding-3-small', name: 'text-embedding-3-small', dimension: 1536 },\n { id: 'text-embedding-3-large', name: 'text-embedding-3-large', dimension: 3072, configurableDimension: true, minDimension: 256, maxDimension: 3072 },\n { id: 'text-embedding-ada-002', name: 'text-embedding-ada-002', dimension: 1536 },\n ],\n },\n google: {\n name: 'Google Generative AI',\n envKeyRequired: 'GOOGLE_GENERATIVE_AI_API_KEY',\n defaultModel: 'text-embedding-004',\n models: [\n { id: 'text-embedding-004', name: 'text-embedding-004', dimension: 768, configurableDimension: true, minDimension: 1, maxDimension: 768 },\n { id: 'embedding-001', name: 'embedding-001', dimension: 768 },\n ],\n },\n mistral: {\n name: 'Mistral',\n envKeyRequired: 'MISTRAL_API_KEY',\n defaultModel: 'mistral-embed',\n models: [\n { id: 'mistral-embed', name: 'mistral-embed', dimension: 1024 },\n ],\n },\n cohere: {\n name: 'Cohere',\n envKeyRequired: 'COHERE_API_KEY',\n defaultModel: 'embed-english-v3.0',\n models: [\n { id: 'embed-english-v3.0', name: 'embed-english-v3.0', dimension: 1024 },\n { id: 'embed-multilingual-v3.0', name: 'embed-multilingual-v3.0', dimension: 1024 },\n { id: 'embed-english-light-v3.0', name: 'embed-english-light-v3.0', dimension: 384 },\n { id: 'embed-multilingual-light-v3.0', name: 'embed-multilingual-light-v3.0', dimension: 384 },\n ],\n },\n bedrock: {\n name: 'Amazon Bedrock',\n envKeyRequired: 'AWS_ACCESS_KEY_ID',\n defaultModel: 'amazon.titan-embed-text-v2:0',\n models: [\n { id: 'amazon.titan-embed-text-v2:0', name: 'Titan Embed Text v2', dimension: 1024, configurableDimension: true, minDimension: 256, maxDimension: 1024 },\n { id: 'amazon.titan-embed-text-v1', name: 'Titan Embed Text v1', dimension: 1536 },\n { id: 'cohere.embed-english-v3', name: 'Cohere Embed English v3', dimension: 1024 },\n { id: 'cohere.embed-multilingual-v3', name: 'Cohere Embed Multilingual v3', dimension: 1024 },\n ],\n },\n ollama: {\n name: 'Ollama (Local)',\n envKeyRequired: 'OLLAMA_BASE_URL',\n defaultModel: 'nomic-embed-text',\n models: [\n { id: 'nomic-embed-text', name: 'nomic-embed-text', dimension: 768 },\n { id: 'mxbai-embed-large', name: 'mxbai-embed-large', dimension: 1024 },\n { id: 'all-minilm', name: 'all-minilm', dimension: 384 },\n { id: 'snowflake-arctic-embed', name: 'snowflake-arctic-embed', dimension: 1024 },\n ],\n },\n}\n\nexport const EMBEDDING_CONFIG_KEY = 'embedding_provider'\n\nexport const DEFAULT_EMBEDDING_CONFIG: EmbeddingProviderConfig = {\n providerId: 'openai',\n model: 'text-embedding-3-small',\n dimension: 1536,\n updatedAt: new Date().toISOString(),\n}\n"],
5
- "mappings": "AAsJO,MAAM,sBAA0E;AAAA,EACrF,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,KAAK;AAAA,MAChF,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,MAAM,uBAAuB,MAAM,cAAc,KAAK,cAAc,KAAK;AAAA,MACpJ,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,KAAK;AAAA,IAClF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,sBAAsB,MAAM,sBAAsB,WAAW,KAAK,uBAAuB,MAAM,cAAc,GAAG,cAAc,IAAI;AAAA,MACxI,EAAE,IAAI,iBAAiB,MAAM,iBAAiB,WAAW,IAAI;AAAA,IAC/D;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,iBAAiB,MAAM,iBAAiB,WAAW,KAAK;AAAA,IAChE;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,sBAAsB,MAAM,sBAAsB,WAAW,KAAK;AAAA,MACxE,EAAE,IAAI,2BAA2B,MAAM,2BAA2B,WAAW,KAAK;AAAA,MAClF,EAAE,IAAI,4BAA4B,MAAM,4BAA4B,WAAW,IAAI;AAAA,MACnF,EAAE,IAAI,iCAAiC,MAAM,iCAAiC,WAAW,IAAI;AAAA,IAC/F;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,gCAAgC,MAAM,uBAAuB,WAAW,MAAM,uBAAuB,MAAM,cAAc,KAAK,cAAc,KAAK;AAAA,MACvJ,EAAE,IAAI,8BAA8B,MAAM,uBAAuB,WAAW,KAAK;AAAA,MACjF,EAAE,IAAI,2BAA2B,MAAM,2BAA2B,WAAW,KAAK;AAAA,MAClF,EAAE,IAAI,gCAAgC,MAAM,gCAAgC,WAAW,KAAK;AAAA,IAC9F;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,oBAAoB,MAAM,oBAAoB,WAAW,IAAI;AAAA,MACnE,EAAE,IAAI,qBAAqB,MAAM,qBAAqB,WAAW,KAAK;AAAA,MACtE,EAAE,IAAI,cAAc,MAAM,cAAc,WAAW,IAAI;AAAA,MACvD,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,KAAK;AAAA,IAClF;AAAA,EACF;AACF;AAEO,MAAM,uBAAuB;AAE7B,MAAM,2BAAoD;AAAA,EAC/D,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AACpC;",
4
+ "sourcesContent": ["import type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type {\n VectorDriverId,\n VectorIndexSource,\n VectorModuleConfig,\n VectorEntityConfig,\n VectorLinkDescriptor,\n VectorResultPresenter,\n VectorSearchHit,\n VectorQueryRequest,\n VectorIndexEntry,\n} from '@open-mercato/shared/modules/vector'\n\nexport type {\n VectorDriverId,\n VectorIndexSource,\n VectorModuleConfig,\n VectorEntityConfig,\n VectorLinkDescriptor,\n VectorResultPresenter,\n VectorSearchHit,\n VectorQueryRequest,\n VectorIndexEntry,\n}\n\nexport type VectorDriverDocument = {\n entityId: EntityId\n recordId: string\n tenantId: string\n organizationId?: string | null\n checksum: string\n embedding: number[]\n url?: string | null\n presenter?: VectorResultPresenter | null\n links?: VectorLinkDescriptor[] | null\n payload?: Record<string, unknown> | null\n driverId: VectorDriverId\n resultTitle: string\n resultSubtitle?: string | null\n resultIcon?: string | null\n resultBadge?: string | null\n resultSnapshot?: string | null\n primaryLinkHref?: string | null\n primaryLinkLabel?: string | null\n}\n\nexport type VectorDriverQuery = {\n vector: number[]\n limit?: number\n filter?: {\n entityIds?: EntityId[]\n organizationId?: string | null\n organizationIds?: string[] | null\n tenantId: string\n }\n}\n\nexport type VectorDriverQueryResult = {\n entityId: EntityId\n recordId: string\n organizationId?: string | null\n score: number\n checksum: string\n url?: string | null\n presenter?: VectorResultPresenter | null\n links?: VectorLinkDescriptor[] | null\n payload?: Record<string, unknown> | null\n resultTitle: string\n resultSubtitle?: string | null\n resultIcon?: string | null\n resultBadge?: string | null\n resultSnapshot?: string | null\n primaryLinkHref?: string | null\n primaryLinkLabel?: string | null\n}\n\nexport type VectorDriverListParams = {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n limit?: number\n offset?: number\n orderBy?: 'created' | 'updated'\n}\n\nexport type VectorDriverCountParams = {\n tenantId: string\n organizationId?: string | null\n entityId?: EntityId\n}\n\nexport type VectorDriverRemoveOrphansParams = {\n entityId: EntityId\n tenantId?: string | null\n organizationId?: string | null\n olderThan: Date\n}\n\nexport type VectorDriverStatus = {\n available: boolean\n reason?: string\n}\n\nexport interface VectorDriver {\n readonly id: VectorDriverId\n ensureReady(): Promise<void>\n /**\n * Cheap, cached probe answering whether the backing store can serve reads and\n * writes at all. Drivers that cannot satisfy their hard requirements (for\n * pgvector: the `vector` extension) MUST report `false` here so callers skip\n * the strategy instead of failing every record. Optional for backward\n * compatibility \u2014 an absent implementation is treated as available.\n */\n isHealthy?(): Promise<boolean>\n getStatus?(): Promise<VectorDriverStatus>\n upsert(doc: VectorDriverDocument): Promise<void>\n delete(entityId: EntityId, recordId: string, tenantId: string): Promise<void>\n query(input: VectorDriverQuery): Promise<VectorDriverQueryResult[]>\n getChecksum(entityId: EntityId, recordId: string, tenantId: string): Promise<string | null>\n purge?(entityId: EntityId, tenantId: string, organizationId?: string | null): Promise<void>\n list?(params: VectorDriverListParams): Promise<VectorIndexEntry[]>\n count?(params: VectorDriverCountParams): Promise<number>\n removeOrphans?(params: VectorDriverRemoveOrphansParams): Promise<number | void>\n getTableDimension?(): Promise<number | null>\n recreateWithDimension?(newDimension: number): Promise<void>\n}\n\n// ============================================================================\n// Embedding Provider Configuration Types\n// ============================================================================\n\nexport type EmbeddingProviderId =\n | 'openai'\n | 'google'\n | 'mistral'\n | 'cohere'\n | 'bedrock'\n | 'ollama'\n\nexport type EmbeddingProviderConfig = {\n providerId: EmbeddingProviderId\n model: string\n dimension: number\n outputDimensionality?: number\n baseUrl?: string\n updatedAt: string\n}\n\nexport type EmbeddingModelInfo = {\n id: string\n name: string\n dimension: number\n configurableDimension?: boolean\n minDimension?: number\n maxDimension?: number\n}\n\nexport type EmbeddingProviderInfo = {\n name: string\n envKeyRequired: string\n defaultModel: string\n models: EmbeddingModelInfo[]\n}\n\nexport const EMBEDDING_PROVIDERS: Record<EmbeddingProviderId, EmbeddingProviderInfo> = {\n openai: {\n name: 'OpenAI',\n envKeyRequired: 'OPENAI_API_KEY',\n defaultModel: 'text-embedding-3-small',\n models: [\n { id: 'text-embedding-3-small', name: 'text-embedding-3-small', dimension: 1536 },\n { id: 'text-embedding-3-large', name: 'text-embedding-3-large', dimension: 3072, configurableDimension: true, minDimension: 256, maxDimension: 3072 },\n { id: 'text-embedding-ada-002', name: 'text-embedding-ada-002', dimension: 1536 },\n ],\n },\n google: {\n name: 'Google Generative AI',\n envKeyRequired: 'GOOGLE_GENERATIVE_AI_API_KEY',\n defaultModel: 'text-embedding-004',\n models: [\n { id: 'text-embedding-004', name: 'text-embedding-004', dimension: 768, configurableDimension: true, minDimension: 1, maxDimension: 768 },\n { id: 'embedding-001', name: 'embedding-001', dimension: 768 },\n ],\n },\n mistral: {\n name: 'Mistral',\n envKeyRequired: 'MISTRAL_API_KEY',\n defaultModel: 'mistral-embed',\n models: [\n { id: 'mistral-embed', name: 'mistral-embed', dimension: 1024 },\n ],\n },\n cohere: {\n name: 'Cohere',\n envKeyRequired: 'COHERE_API_KEY',\n defaultModel: 'embed-english-v3.0',\n models: [\n { id: 'embed-english-v3.0', name: 'embed-english-v3.0', dimension: 1024 },\n { id: 'embed-multilingual-v3.0', name: 'embed-multilingual-v3.0', dimension: 1024 },\n { id: 'embed-english-light-v3.0', name: 'embed-english-light-v3.0', dimension: 384 },\n { id: 'embed-multilingual-light-v3.0', name: 'embed-multilingual-light-v3.0', dimension: 384 },\n ],\n },\n bedrock: {\n name: 'Amazon Bedrock',\n envKeyRequired: 'AWS_ACCESS_KEY_ID',\n defaultModel: 'amazon.titan-embed-text-v2:0',\n models: [\n { id: 'amazon.titan-embed-text-v2:0', name: 'Titan Embed Text v2', dimension: 1024, configurableDimension: true, minDimension: 256, maxDimension: 1024 },\n { id: 'amazon.titan-embed-text-v1', name: 'Titan Embed Text v1', dimension: 1536 },\n { id: 'cohere.embed-english-v3', name: 'Cohere Embed English v3', dimension: 1024 },\n { id: 'cohere.embed-multilingual-v3', name: 'Cohere Embed Multilingual v3', dimension: 1024 },\n ],\n },\n ollama: {\n name: 'Ollama (Local)',\n envKeyRequired: 'OLLAMA_BASE_URL',\n defaultModel: 'nomic-embed-text',\n models: [\n { id: 'nomic-embed-text', name: 'nomic-embed-text', dimension: 768 },\n { id: 'mxbai-embed-large', name: 'mxbai-embed-large', dimension: 1024 },\n { id: 'all-minilm', name: 'all-minilm', dimension: 384 },\n { id: 'snowflake-arctic-embed', name: 'snowflake-arctic-embed', dimension: 1024 },\n ],\n },\n}\n\nexport const EMBEDDING_CONFIG_KEY = 'embedding_provider'\n\nexport const DEFAULT_EMBEDDING_CONFIG: EmbeddingProviderConfig = {\n providerId: 'openai',\n model: 'text-embedding-3-small',\n dimension: 1536,\n updatedAt: new Date().toISOString(),\n}\n"],
5
+ "mappings": "AAoKO,MAAM,sBAA0E;AAAA,EACrF,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,KAAK;AAAA,MAChF,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,MAAM,uBAAuB,MAAM,cAAc,KAAK,cAAc,KAAK;AAAA,MACpJ,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,KAAK;AAAA,IAClF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,sBAAsB,MAAM,sBAAsB,WAAW,KAAK,uBAAuB,MAAM,cAAc,GAAG,cAAc,IAAI;AAAA,MACxI,EAAE,IAAI,iBAAiB,MAAM,iBAAiB,WAAW,IAAI;AAAA,IAC/D;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,iBAAiB,MAAM,iBAAiB,WAAW,KAAK;AAAA,IAChE;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,sBAAsB,MAAM,sBAAsB,WAAW,KAAK;AAAA,MACxE,EAAE,IAAI,2BAA2B,MAAM,2BAA2B,WAAW,KAAK;AAAA,MAClF,EAAE,IAAI,4BAA4B,MAAM,4BAA4B,WAAW,IAAI;AAAA,MACnF,EAAE,IAAI,iCAAiC,MAAM,iCAAiC,WAAW,IAAI;AAAA,IAC/F;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,gCAAgC,MAAM,uBAAuB,WAAW,MAAM,uBAAuB,MAAM,cAAc,KAAK,cAAc,KAAK;AAAA,MACvJ,EAAE,IAAI,8BAA8B,MAAM,uBAAuB,WAAW,KAAK;AAAA,MACjF,EAAE,IAAI,2BAA2B,MAAM,2BAA2B,WAAW,KAAK;AAAA,MAClF,EAAE,IAAI,gCAAgC,MAAM,gCAAgC,WAAW,KAAK;AAAA,IAC9F;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,MACN,EAAE,IAAI,oBAAoB,MAAM,oBAAoB,WAAW,IAAI;AAAA,MACnE,EAAE,IAAI,qBAAqB,MAAM,qBAAqB,WAAW,KAAK;AAAA,MACtE,EAAE,IAAI,cAAc,MAAM,cAAc,WAAW,IAAI;AAAA,MACvD,EAAE,IAAI,0BAA0B,MAAM,0BAA0B,WAAW,KAAK;AAAA,IAClF;AAAA,EACF;AACF;AAEO,MAAM,uBAAuB;AAE7B,MAAM,2BAAoD;AAAA,EAC/D,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AACpC;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/search",
3
- "version": "0.6.7-develop.6842.1.8cd8a8883c",
3
+ "version": "0.6.7-develop.6859.1.e4727b568a",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -127,9 +127,9 @@
127
127
  "zod": "^4.4.3"
128
128
  },
129
129
  "peerDependencies": {
130
- "@open-mercato/core": "0.6.7-develop.6842.1.8cd8a8883c",
131
- "@open-mercato/queue": "0.6.7-develop.6842.1.8cd8a8883c",
132
- "@open-mercato/shared": "0.6.7-develop.6842.1.8cd8a8883c"
130
+ "@open-mercato/core": "0.6.7-develop.6859.1.e4727b568a",
131
+ "@open-mercato/queue": "0.6.7-develop.6859.1.e4727b568a",
132
+ "@open-mercato/shared": "0.6.7-develop.6859.1.e4727b568a"
133
133
  },
134
134
  "devDependencies": {
135
135
  "@types/jest": "^30.0.0",
@@ -0,0 +1,158 @@
1
+ import { createPgVectorDriver, type PgPool } from '../vector/drivers/pgvector'
2
+ import { VectorSearchStrategy } from '../strategies/vector.strategy'
3
+ import { SearchService } from '../service'
4
+ import type { IndexableRecord } from '../types'
5
+
6
+ type QueryCall = { text: string; params?: unknown[] }
7
+
8
+ function pgError(code: string, message: string): Error & { code: string } {
9
+ return Object.assign(new Error(message), { code })
10
+ }
11
+
12
+ function createPool(options: {
13
+ extensionInstalled?: boolean
14
+ extensionInstallable?: boolean
15
+ calls: QueryCall[]
16
+ }): PgPool {
17
+ const { calls } = options
18
+ const run = async (text: string, params?: unknown[]) => {
19
+ calls.push({ text, params })
20
+ if (/pg_available_extensions/.test(text)) {
21
+ return {
22
+ rows: [{
23
+ installed: options.extensionInstalled ?? false,
24
+ installable: options.extensionInstallable ?? false,
25
+ }],
26
+ }
27
+ }
28
+ if (/CREATE EXTENSION IF NOT EXISTS vector/.test(text)) {
29
+ if (options.extensionInstalled || options.extensionInstallable) return { rows: [] }
30
+ throw pgError('58P01', 'extension "vector" is not available')
31
+ }
32
+ return { rows: [] }
33
+ }
34
+ return {
35
+ connect: async () => ({ query: run as PgPool['query'], release: () => {} }),
36
+ query: run as PgPool['query'],
37
+ end: async () => {},
38
+ }
39
+ }
40
+
41
+ describe('pgvector extension availability', () => {
42
+ it('reports the driver unhealthy when the extension cannot be installed', async () => {
43
+ const calls: QueryCall[] = []
44
+ const driver = createPgVectorDriver({ pool: createPool({ calls }) })
45
+
46
+ expect(await driver.isHealthy!()).toBe(false)
47
+ const status = await driver.getStatus!()
48
+ expect(status.available).toBe(false)
49
+ // Curated, not the raw Postgres message: the settings API surfaces this to the browser.
50
+ expect(status.reason).toBe('extension "vector" is not available on this PostgreSQL server')
51
+ })
52
+
53
+ it('probes the catalog once instead of running DDL for every record', async () => {
54
+ const calls: QueryCall[] = []
55
+ const driver = createPgVectorDriver({ pool: createPool({ calls }) })
56
+
57
+ for (let attempt = 0; attempt < 5; attempt += 1) {
58
+ expect(await driver.isHealthy!()).toBe(false)
59
+ }
60
+
61
+ const probes = calls.filter((call) => /pg_available_extensions/.test(call.text))
62
+ expect(probes).toHaveLength(1)
63
+ expect(calls.some((call) => /CREATE TABLE/.test(call.text))).toBe(false)
64
+ })
65
+
66
+ it('short-circuits ensureReady once the extension is known to be missing', async () => {
67
+ const calls: QueryCall[] = []
68
+ const driver = createPgVectorDriver({ pool: createPool({ calls }) })
69
+
70
+ await expect(driver.ensureReady()).rejects.toThrow(/vector/)
71
+ const afterFirst = calls.length
72
+ await expect(driver.ensureReady()).rejects.toThrow(/vector/)
73
+
74
+ expect(calls.length).toBe(afterFirst)
75
+ })
76
+
77
+ it('stays healthy when the extension is installable', async () => {
78
+ const calls: QueryCall[] = []
79
+ const driver = createPgVectorDriver({ pool: createPool({ calls, extensionInstallable: true }) })
80
+
81
+ expect(await driver.isHealthy!()).toBe(true)
82
+ await expect(driver.ensureReady()).resolves.toBeUndefined()
83
+ })
84
+
85
+ it('keeps the existing superuser-less degradation path', async () => {
86
+ const calls: QueryCall[] = []
87
+ const pool = createPool({ calls, extensionInstalled: true })
88
+ const originalQuery = pool.query
89
+ pool.query = (async (text: string, params?: unknown[]) => {
90
+ if (/CREATE EXTENSION IF NOT EXISTS vector/.test(text)) {
91
+ calls.push({ text, params })
92
+ throw pgError('42501', 'permission denied to create extension "vector"')
93
+ }
94
+ return originalQuery(text, params)
95
+ }) as PgPool['query']
96
+ pool.connect = async () => ({ query: pool.query as never, release: () => {} })
97
+
98
+ const driver = createPgVectorDriver({ pool })
99
+ await expect(driver.ensureReady()).resolves.toBeUndefined()
100
+ expect(await driver.isHealthy!()).toBe(true)
101
+ })
102
+ })
103
+
104
+ describe('vector strategy availability', () => {
105
+ const embeddingService = { available: true, createEmbedding: jest.fn() }
106
+
107
+ function createDriver(overrides: Partial<Record<string, unknown>> = {}) {
108
+ return {
109
+ id: 'pgvector' as const,
110
+ ensureReady: jest.fn().mockResolvedValue(undefined),
111
+ upsert: jest.fn().mockResolvedValue(undefined),
112
+ delete: jest.fn().mockResolvedValue(undefined),
113
+ query: jest.fn().mockResolvedValue([]),
114
+ getChecksum: jest.fn().mockResolvedValue(null),
115
+ ...overrides,
116
+ }
117
+ }
118
+
119
+ it('is unavailable when the store reports itself unhealthy', async () => {
120
+ const driver = createDriver({ isHealthy: jest.fn().mockResolvedValue(false) })
121
+ const strategy = new VectorSearchStrategy(embeddingService, driver)
122
+
123
+ expect(await strategy.isAvailable()).toBe(false)
124
+ })
125
+
126
+ it('stays available for drivers that expose no probe', async () => {
127
+ const strategy = new VectorSearchStrategy(embeddingService, createDriver())
128
+
129
+ expect(await strategy.isAvailable()).toBe(true)
130
+ })
131
+
132
+ it('never touches the store on delete when it is unhealthy', async () => {
133
+ const driver = createDriver({ isHealthy: jest.fn().mockResolvedValue(false) })
134
+ const strategy = new VectorSearchStrategy(embeddingService, driver)
135
+ const service = new SearchService({ strategies: [strategy] })
136
+
137
+ await expect(service.delete('customers:customer_deal', 'rec-1', 'tenant-1')).resolves.toBeUndefined()
138
+ expect(driver.ensureReady).not.toHaveBeenCalled()
139
+ expect(driver.delete).not.toHaveBeenCalled()
140
+ })
141
+
142
+ it('never touches the store on index when it is unhealthy', async () => {
143
+ const driver = createDriver({ isHealthy: jest.fn().mockResolvedValue(false) })
144
+ const strategy = new VectorSearchStrategy(embeddingService, driver)
145
+ const service = new SearchService({ strategies: [strategy] })
146
+ const record: IndexableRecord = {
147
+ entityId: 'customers:customer_deal',
148
+ recordId: 'rec-1',
149
+ tenantId: 'tenant-1',
150
+ fields: { name: 'Deal' },
151
+ text: 'Deal',
152
+ }
153
+
154
+ await expect(service.index(record)).resolves.toBeUndefined()
155
+ expect(driver.ensureReady).not.toHaveBeenCalled()
156
+ expect(driver.upsert).not.toHaveBeenCalled()
157
+ })
158
+ })
@@ -246,6 +246,8 @@ export const vectorDriverStatusSchema = z.object({
246
246
  name: z.string(),
247
247
  configured: z.boolean(),
248
248
  implemented: z.boolean(),
249
+ available: z.boolean().nullable(),
250
+ unavailableReason: z.string().nullable(),
249
251
  envVars: z.array(vectorDriverEnvVarSchema),
250
252
  })
251
253
 
@@ -1,7 +1,9 @@
1
1
  import { NextResponse } from 'next/server'
2
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
2
3
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
3
4
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
4
5
  import type { VectorDriverId } from '@open-mercato/shared/modules/vector'
6
+ import type { VectorDriver } from '../../../../../vector'
5
7
  import { vectorStoreSettingsOpenApi } from '../../openapi'
6
8
 
7
9
  export const metadata = {
@@ -13,6 +15,9 @@ type DriverStatus = {
13
15
  name: string
14
16
  configured: boolean
15
17
  implemented: boolean
18
+ /** Runtime reachability of the store itself; `null` when the driver exposes no probe. */
19
+ available: boolean | null
20
+ unavailableReason: string | null
16
21
  envVars: {
17
22
  name: string
18
23
  set: boolean
@@ -26,6 +31,24 @@ type VectorStoreConfigResponse = {
26
31
  drivers: DriverStatus[]
27
32
  }
28
33
 
34
+ async function probeDriverStatus(
35
+ container: { resolve: <T = unknown>(name: string) => T },
36
+ driverId: VectorDriverId,
37
+ ): Promise<{ available: boolean | null; unavailableReason: string | null }> {
38
+ try {
39
+ const drivers = container.resolve<VectorDriver[]>('vectorDrivers')
40
+ const driver = drivers.find((entry) => entry.id === driverId)
41
+ if (!driver?.getStatus) return { available: null, unavailableReason: null }
42
+ const status = await driver.getStatus()
43
+ return {
44
+ available: status.available,
45
+ unavailableReason: status.available ? null : status.reason ?? null,
46
+ }
47
+ } catch {
48
+ return { available: null, unavailableReason: null }
49
+ }
50
+ }
51
+
29
52
  const unauthorized = async () => {
30
53
  const { t } = await resolveTranslations()
31
54
  return NextResponse.json({ error: t('api.errors.unauthorized', 'Unauthorized') }, { status: 401 })
@@ -45,12 +68,28 @@ export async function GET(req: Request) {
45
68
  // Check chromadb - would need CHROMA_URL
46
69
  const chromaUrlSet = Boolean(process.env.CHROMA_URL?.trim())
47
70
 
71
+ let pgvectorStatus: { available: boolean | null; unavailableReason: string | null } = {
72
+ available: null,
73
+ unavailableReason: null,
74
+ }
75
+ if (databaseUrlSet) {
76
+ const container = await createRequestContainer()
77
+ try {
78
+ pgvectorStatus = await probeDriverStatus(container, 'pgvector')
79
+ } finally {
80
+ const disposable = container as { dispose?: () => Promise<unknown> }
81
+ if (typeof disposable.dispose === 'function') await disposable.dispose()
82
+ }
83
+ }
84
+
48
85
  const drivers: DriverStatus[] = [
49
86
  {
50
87
  id: 'pgvector',
51
88
  name: 'PostgreSQL (pgvector)',
52
89
  configured: databaseUrlSet,
53
90
  implemented: true,
91
+ available: pgvectorStatus.available,
92
+ unavailableReason: pgvectorStatus.unavailableReason,
54
93
  envVars: [
55
94
  {
56
95
  name: 'DATABASE_URL',
@@ -64,6 +103,8 @@ export async function GET(req: Request) {
64
103
  name: 'Qdrant',
65
104
  configured: qdrantUrlSet,
66
105
  implemented: false,
106
+ available: null,
107
+ unavailableReason: null,
67
108
  envVars: [
68
109
  {
69
110
  name: 'QDRANT_URL',
@@ -82,6 +123,8 @@ export async function GET(req: Request) {
82
123
  name: 'ChromaDB',
83
124
  configured: chromaUrlSet,
84
125
  implemented: false,
126
+ available: null,
127
+ unavailableReason: null,
85
128
  envVars: [
86
129
  {
87
130
  name: 'CHROMA_URL',
@@ -118,6 +118,8 @@ type VectorDriverStatus = {
118
118
  name: string
119
119
  configured: boolean
120
120
  implemented: boolean
121
+ available?: boolean | null
122
+ unavailableReason?: string | null
121
123
  envVars: VectorDriverEnvVar[]
122
124
  }
123
125
 
@@ -79,6 +79,8 @@ type VectorDriverStatus = {
79
79
  name: string
80
80
  configured: boolean
81
81
  implemented: boolean
82
+ available?: boolean | null
83
+ unavailableReason?: string | null
82
84
  envVars: VectorDriverEnvVar[]
83
85
  }
84
86
 
@@ -447,6 +449,31 @@ export function VectorSearchSection({
447
449
  const isEmbeddingConfigured = embeddingSettings?.configuredProviders?.includes(savedProvider)
448
450
  const providerOptions: EmbeddingProviderId[] = ['openai', 'google', 'mistral', 'cohere', 'bedrock', 'ollama']
449
451
 
452
+ const activeDriver = vectorStoreConfig?.drivers.find((driver) => driver.id === vectorStoreConfig.currentDriver)
453
+ const isStoreUnavailable = activeDriver?.available === false
454
+ const storeUnavailableReason = activeDriver?.unavailableReason ?? null
455
+
456
+ const storeUnavailableBanner = isStoreUnavailable ? (
457
+ <div className="p-4 rounded-md bg-status-warning-bg border border-status-warning-border">
458
+ <div className="flex items-start gap-3">
459
+ <svg className="h-5 w-5 text-status-warning-icon flex-shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
460
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
461
+ </svg>
462
+ <div>
463
+ <p className="text-sm font-medium text-status-warning-text">
464
+ {t('search.settings.vector.storeUnavailable', 'Vector store is not available')}
465
+ </p>
466
+ <p className="text-xs text-status-warning-text mt-1">
467
+ {t('search.settings.vector.storeUnavailableHint', 'Vector indexing and semantic search are skipped until the store is reachable. Install the pgvector extension on your PostgreSQL server; the status is rechecked about once a minute.')}
468
+ </p>
469
+ {storeUnavailableReason ? (
470
+ <p className="text-xs text-status-warning-text mt-1 font-mono">{storeUnavailableReason}</p>
471
+ ) : null}
472
+ </div>
473
+ </div>
474
+ </div>
475
+ ) : null
476
+
450
477
  const autoIndexingChecked = embeddingSettings ? embeddingSettings.autoIndexingEnabled : true
451
478
  const autoIndexingDisabled = embeddingLoading || embeddingSaving || Boolean(embeddingSettings?.autoIndexingLocked)
452
479
 
@@ -481,13 +508,14 @@ export function VectorSearchSection({
481
508
  </div>
482
509
  ) : (
483
510
  <div className="space-y-4">
511
+ {storeUnavailableBanner}
484
512
  {/* Vector Store Driver Status */}
485
513
  <div>
486
514
  <h3 className="text-sm font-semibold mb-2">{t('search.settings.vector.store', 'Vector Store')}</h3>
487
515
  <div className="grid gap-2 sm:grid-cols-3">
488
516
  {vectorStoreConfig?.drivers.map((driver) => {
489
517
  const isCurrent = driver.id === vectorStoreConfig.currentDriver
490
- const isReady = driver.configured && driver.implemented
518
+ const isReady = driver.configured && driver.implemented && driver.available !== false
491
519
  return (
492
520
  <div
493
521
  key={driver.id}
@@ -746,6 +774,8 @@ export function VectorSearchSection({
746
774
  <Spinner size="sm" />
747
775
  <span>{t('search.settings.loadingLabel', 'Loading settings...')}</span>
748
776
  </div>
777
+ ) : isStoreUnavailable ? (
778
+ storeUnavailableBanner
749
779
  ) : !isEmbeddingConfigured ? (
750
780
  <div className="p-4 rounded-md bg-status-warning-bg border border-status-warning-border">
751
781
  <div className="flex items-start gap-3">
@@ -50,13 +50,14 @@ const vectorStoreConfig: NonNullable<VectorSearchSectionProps['vectorStoreConfig
50
50
 
51
51
  function renderSection(
52
52
  settings: NonNullable<VectorSearchSectionProps['embeddingSettings']> = embeddingSettings,
53
+ storeConfig: NonNullable<VectorSearchSectionProps['vectorStoreConfig']> = vectorStoreConfig,
53
54
  ) {
54
55
  return render(
55
56
  <I18nProvider locale="en" dict={{}}>
56
57
  <VectorSearchSection
57
58
  embeddingSettings={settings}
58
59
  embeddingLoading={false}
59
- vectorStoreConfig={vectorStoreConfig}
60
+ vectorStoreConfig={storeConfig}
60
61
  vectorStoreConfigLoading={false}
61
62
  vectorReindexLock={null}
62
63
  onEmbeddingSettingsUpdate={jest.fn()}
@@ -113,3 +114,37 @@ describe('VectorSearchSection provider controls', () => {
113
114
  expect(document.getElementById('provider-ollama-configuration')).toBeNull()
114
115
  })
115
116
  })
117
+
118
+ describe('VectorSearchSection store availability', () => {
119
+ beforeEach(() => {
120
+ Object.defineProperty(globalThis, 'fetch', {
121
+ configurable: true,
122
+ value: jest.fn(() => new Promise(() => undefined)),
123
+ })
124
+ })
125
+
126
+ it('warns that the vector store is unavailable and surfaces the driver reason', () => {
127
+ renderSection(embeddingSettings, {
128
+ ...vectorStoreConfig,
129
+ drivers: [
130
+ {
131
+ ...vectorStoreConfig.drivers[0],
132
+ available: false,
133
+ unavailableReason: 'extension "vector" is not available on this PostgreSQL server',
134
+ },
135
+ ],
136
+ })
137
+
138
+ expect(screen.getByText('Vector store is not available')).toBeTruthy()
139
+ expect(screen.getByText(/extension "vector" is not available/)).toBeTruthy()
140
+ })
141
+
142
+ it('keeps the banner hidden while the store is reachable', () => {
143
+ renderSection(embeddingSettings, {
144
+ ...vectorStoreConfig,
145
+ drivers: [{ ...vectorStoreConfig.drivers[0], available: true, unavailableReason: null }],
146
+ })
147
+
148
+ expect(screen.queryByText('Vector store is not available')).toBeNull()
149
+ })
150
+ })