{"version":3,"sources":["../src/query/dialect/vector/libsql-strategy.ts"],"names":["assertFiniteEmbedding","sql","vectorPhysicalName","quoteIdentifier","VECTOR_CONTRIBUTION_PREFIX","vectorScoreExpression","conditions","vectorMinScoreCondition","pageClause","vectorPageClause"],"mappings":"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","file":"chunk-CBH7KZOS.cjs","sourcesContent":["/**\n * libSQL native vector strategy.\n *\n * libSQL ships vector search in core — no extension to load — so this\n * strategy is wired unconditionally by `createLibsqlBackend`. It backs each\n * `(nodeKind, fieldPath)` with its own `F32_BLOB(N)` table (the spike proved\n * the dimension MUST live in the column type for `libsql_vector_idx` to\n * build), brute-forces with `vector_distance_cos` / `vector_distance_l2`, and\n * accelerates with DiskANN (`libsql_vector_idx` + `vector_top_k`) when the\n * slot declares an approximate index.\n *\n * Metric support is `cosine` + `l2` (no `inner_product`), matching sqlite-vec\n * and advertised as data on `capabilities`. The generic `\"hnsw\"` index intent\n * (the portable \"give me an ANN index\" signal that `embedding()` defaults to)\n * is realized here as libSQL's DiskANN index; `\"ivfflat\"` is not supported and\n * a slot declaring it is materialized brute-force-only.\n *\n * The `pgvectorStrategy` and `sqliteVecStrategy` siblings follow the same\n * `VectorStrategy` contract for their engines.\n */\nimport { type StrategyTableContribution } from \"../../../backend/table-contribution\";\nimport {\n  type DeleteEmbeddingParams,\n  type UpsertEmbeddingBatchParams,\n  type UpsertEmbeddingParams,\n  type VectorCapabilities,\n  type VectorMetric,\n  type VectorSearchParams,\n} from \"../../../backend/types\";\nimport { sql, type SqlFragment } from \"../../sql-fragment\";\nimport {\n  assertFiniteEmbedding,\n  quoteIdentifier,\n  VECTOR_CONTRIBUTION_PREFIX,\n  vectorMinScoreCondition,\n  vectorPhysicalName,\n  vectorScoreExpression,\n  type VectorSlot,\n  type VectorStrategy,\n} from \"../vector-strategy\";\nimport { vectorPageClause } from \"./pagination\";\n\n/** Physical-name prefixes for the per-field table and its DiskANN index. */\nconst TABLE_PREFIX = \"tg_vec\";\nconst INDEX_PREFIX = \"tg_vecidx\";\n\n/**\n * libSQL `F32_BLOB` caps at 65,536 dimensions (per Turso docs / spike §2).\n */\nconst LIBSQL_MAX_DIMENSIONS = 65_536;\n\nconst LIBSQL_CAPABILITIES: VectorCapabilities = {\n  supported: true,\n  metrics: [\"cosine\", \"l2\"],\n  // \"hnsw\" = portable ANN intent, realized as DiskANN. \"none\" = brute-force.\n  indexTypes: [\"hnsw\", \"none\"],\n  maxDimensions: LIBSQL_MAX_DIMENSIONS,\n  // `vector_top_k` is a table function: no filter pushdown, no way to re-enter\n  // the index. A filtered DiskANN search over-fetches by\n  // CANDIDATE_FILTER_OVERFETCH and filters afterwards, so it under-fills once\n  // more than the headroom is filtered out. Unlike sqlite-vec (pushdown) and\n  // pgvector >= 0.8 (iterative scan), libSQL has no recovery at all.\n  filteredApproximateSearch: {\n    mode: \"post-filter\",\n    guaranteesFullPage: false,\n  },\n  // `vector_top_k(idx, q, k)` is a table function whose only parameters are\n  // the index, the query vector, and `k`. DiskANN's search-list width\n  // (`search_l`) is fixed when the index is created by\n  // `libsql_vector_idx(...)` and cannot be varied per query, so there is no\n  // per-search frontier for `efSearch` to set.\n  searchFrontierTuning: {\n    tunable: false,\n    reason:\n      \"`vector_top_k` takes only (index, query, k); DiskANN's search_l is fixed at index-creation time\",\n  },\n};\n\n/** Whether a slot's declared index type maps to a real libSQL ANN index. */\nfunction usesAnnIndex(slot: VectorSlot): boolean {\n  return slot.indexType === \"hnsw\";\n}\n\n/** libSQL metric token accepted by `libsql_vector_idx(col, 'metric=…')`. */\nfunction libsqlMetricToken(metric: VectorMetric): string {\n  switch (metric) {\n    case \"cosine\": {\n      return \"cosine\";\n    }\n    case \"l2\": {\n      return \"l2\";\n    }\n    case \"inner_product\": {\n      throw new Error(\n        \"libSQL vector search does not support inner_product. Use 'cosine' or 'l2'.\",\n      );\n    }\n    default: {\n      const _exhaustive: never = metric;\n      throw new Error(`Unsupported vector metric: ${String(_exhaustive)}`);\n    }\n  }\n}\n\n/**\n * Formats an embedding as the text argument to `vector32(...)`. The value is\n * bound as a parameter (not interpolated) — `vector32` parses it into the\n * F32 blob. Finiteness is validated first so a NaN names its index.\n */\nfunction vector32Literal(\n  embedding: readonly number[],\n  name: string,\n): SqlFragment {\n  assertFiniteEmbedding(embedding, name);\n  return sql`vector32(${`[${embedding.join(\",\")}]`})`;\n}\n\nfunction distanceExpression(\n  embeddingColumn: SqlFragment,\n  queryEmbedding: readonly number[],\n  metric: VectorMetric,\n): SqlFragment {\n  const query = vector32Literal(queryEmbedding, \"queryEmbedding\");\n  switch (metric) {\n    case \"cosine\": {\n      return sql`vector_distance_cos(${embeddingColumn}, ${query})`;\n    }\n    case \"l2\": {\n      return sql`vector_distance_l2(${embeddingColumn}, ${query})`;\n    }\n    case \"inner_product\": {\n      throw new Error(\n        \"libSQL vector search does not support inner_product. Use 'cosine' or 'l2'.\",\n      );\n    }\n    default: {\n      const _exhaustive: never = metric;\n      throw new Error(`Unsupported vector metric: ${String(_exhaustive)}`);\n    }\n  }\n}\n\nfunction tableName(\n  graphId: string,\n  nodeKind: string,\n  fieldPath: string,\n): string {\n  return vectorPhysicalName(TABLE_PREFIX, graphId, nodeKind, fieldPath);\n}\n\nexport const libsqlVectorStrategy: VectorStrategy = {\n  name: \"libsql-native\",\n  capabilities: LIBSQL_CAPABILITIES,\n\n  tableName,\n\n  ownedTables(slot): readonly StrategyTableContribution[] {\n    const table = tableName(slot.graphId, slot.nodeKind, slot.fieldPath);\n    const name = quoteIdentifier(table);\n\n    // No standalone graph_id index: the PRIMARY KEY (graph_id, node_id) already\n    // covers `WHERE graph_id = ?` via its leading column, so a separate index\n    // would be pure write amplification.\n    const createDdl = [\n      `CREATE TABLE IF NOT EXISTS ${name} (\n  \"graph_id\" TEXT NOT NULL,\n  \"node_id\" TEXT NOT NULL,\n  \"embedding\" F32_BLOB(${slot.dimensions}) NOT NULL,\n  \"created_at\" TEXT NOT NULL,\n  \"updated_at\" TEXT NOT NULL,\n  PRIMARY KEY (\"graph_id\", \"node_id\")\n);`,\n    ];\n\n    if (usesAnnIndex(slot)) {\n      // The DiskANN index DDL is deterministic raw SQL; fold it into the\n      // contribution so materialization creates table + index together.\n      createDdl.push(libsqlVectorIndexDdl(table, slot));\n    }\n\n    return [\n      {\n        scope: \"graph\",\n        logicalName: `${VECTOR_CONTRIBUTION_PREFIX}:${slot.nodeKind}.${slot.fieldPath}`,\n        owner: \"libsql-native\",\n        tableName: table,\n        createDdl,\n        runtimeEnsure: true,\n      },\n    ];\n  },\n\n  buildUpsert(\n    slot,\n    params: UpsertEmbeddingParams,\n    timestamp,\n  ): readonly SqlFragment[] {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    const value = vector32Literal(params.embedding, \"embedding\");\n    return [\n      sql`\n        INSERT INTO ${table} (\"graph_id\", \"node_id\", \"embedding\", \"created_at\", \"updated_at\")\n        VALUES (${params.graphId}, ${params.nodeId}, ${value}, ${timestamp}, ${timestamp})\n        ON CONFLICT (\"graph_id\", \"node_id\")\n        DO UPDATE SET \"embedding\" = ${value}, \"updated_at\" = ${timestamp}\n      `,\n    ];\n  },\n\n  buildUpsertBatch(\n    slot,\n    params: UpsertEmbeddingBatchParams,\n    timestamp,\n  ): readonly SqlFragment[] {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    const valueRows = sql.join(\n      params.rows.map(\n        (row) =>\n          sql`(${params.graphId}, ${row.nodeId}, ${vector32Literal(row.embedding, \"embedding\")}, ${timestamp}, ${timestamp})`,\n      ),\n      sql`, `,\n    );\n    // `excluded.\"embedding\"` reuses the row's already-converted F32_BLOB\n    // value, so the multi-row form needs no per-row conversion in the\n    // update arm (unlike buildUpsert's single bound value).\n    return [\n      sql`\n        INSERT INTO ${table} (\"graph_id\", \"node_id\", \"embedding\", \"created_at\", \"updated_at\")\n        VALUES ${valueRows}\n        ON CONFLICT (\"graph_id\", \"node_id\")\n        DO UPDATE SET \"embedding\" = excluded.\"embedding\", \"updated_at\" = excluded.\"updated_at\"\n      `,\n    ];\n  },\n\n  buildDelete(slot, params: DeleteEmbeddingParams): readonly SqlFragment[] {\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    return [\n      sql`\n        DELETE FROM ${table}\n        WHERE \"graph_id\" = ${params.graphId} AND \"node_id\" = ${params.nodeId}\n      `,\n    ];\n  },\n\n  buildDeleteBatch(\n    slot,\n    params: Omit<DeleteEmbeddingParams, \"nodeId\"> &\n      Readonly<{ nodeIds: readonly string[] }>,\n  ): readonly SqlFragment[] {\n    if (params.nodeIds.length === 0) return [];\n    const table = sql.identifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    return [\n      sql`DELETE FROM ${table} WHERE \"graph_id\" = ${params.graphId} AND \"node_id\" IN (${sql.join(\n        params.nodeIds.map((nodeId) => sql`${nodeId}`),\n        sql`, `,\n      )})`,\n    ];\n  },\n\n  buildSearch(\n    slot,\n    params: VectorSearchParams,\n    candidates?: SqlFragment,\n  ): SqlFragment {\n    const table = tableName(slot.graphId, slot.nodeKind, slot.fieldPath);\n    const quoted = sql.identifier(table);\n    const embeddingColumn = sql`${quoted}.\"embedding\"`;\n    const distance = distanceExpression(\n      embeddingColumn,\n      params.queryEmbedding,\n      params.metric,\n    );\n    const score = vectorScoreExpression(distance, params.metric);\n\n    if (usesAnnIndex(slot)) {\n      // DiskANN: vector_top_k returns rowids from the per-field table; join\n      // back, then scope by graph and compute the real distance/score.\n      // `vector_top_k` is table-global, so single-graph deployments are exact;\n      // multi-graph recall is bounded by the over-fetched k (documented).\n      const indexName = libsqlIndexName(slot);\n      const query = vector32Literal(params.queryEmbedding, \"queryEmbedding\");\n      const conditions: SqlFragment[] = [\n        sql`${quoted}.\"graph_id\" = ${params.graphId}`,\n      ];\n      if (params.minScore !== undefined) {\n        conditions.push(\n          vectorMinScoreCondition(distance, params.metric, params.minScore),\n        );\n      }\n      // `vector_top_k` is a table function with no filter pushdown, so a\n      // candidate filter can only be applied AFTER the ANN retrieval.\n      // Over-fetch the DiskANN k to leave headroom for filtered-out rows;\n      // recall is bounded by the over-fetch, and the page under-fills once\n      // more than the headroom is filtered out. Declared, not merely noted:\n      // `capabilities.filteredApproximateSearch.mode === \"post-filter\"`.\n      if (candidates !== undefined) {\n        conditions.push(sql`${quoted}.\"node_id\" IN (${candidates})`);\n      }\n      // `pageK` covers the requested page (`limit + offset`); the\n      // candidate-filter over-fetch multiplies on top of it.\n      const pageK = params.limit + (params.offset ?? 0);\n      const annK =\n        candidates === undefined ? pageK : pageK * CANDIDATE_FILTER_OVERFETCH;\n      const pageClause = vectorPageClause(params.limit, params.offset);\n      return sql`\n        SELECT ${quoted}.\"node_id\" AS node_id, ${score} AS score\n        FROM vector_top_k(${indexName}, ${query}, ${annK})\n        JOIN ${quoted} ON ${quoted}.rowid = id\n        WHERE ${sql.join(conditions, sql` AND `)}\n        ORDER BY ${distance} ASC\n        ${pageClause}\n      `;\n    }\n\n    // Brute-force scan.\n    const conditions: SqlFragment[] = [\n      sql`${quoted}.\"graph_id\" = ${params.graphId}`,\n    ];\n    if (candidates !== undefined) {\n      conditions.push(sql`${quoted}.\"node_id\" IN (${candidates})`);\n    }\n    if (params.minScore !== undefined) {\n      conditions.push(\n        vectorMinScoreCondition(distance, params.metric, params.minScore),\n      );\n    }\n    const pageClause = vectorPageClause(params.limit, params.offset);\n    return sql`\n      SELECT ${quoted}.\"node_id\" AS node_id, ${score} AS score\n      FROM ${quoted}\n      WHERE ${sql.join(conditions, sql` AND `)}\n      ORDER BY ${distance} ASC\n      ${pageClause}\n    `;\n  },\n\n  distanceExpression(embeddingColumn, queryEmbedding, metric) {\n    return distanceExpression(embeddingColumn, queryEmbedding, metric);\n  },\n\n  buildCreateIndex(slot): SqlFragment | undefined {\n    if (!usesAnnIndex(slot)) return undefined;\n    return sql.raw(\n      libsqlVectorIndexDdl(\n        tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n        slot,\n      ),\n    );\n  },\n\n  buildDropIndex(slot): SqlFragment | undefined {\n    if (!usesAnnIndex(slot)) return undefined;\n    const indexName = libsqlIndexName(slot);\n    return sql.raw(`DROP INDEX IF EXISTS ${quoteIdentifier(indexName)}`);\n  },\n\n  buildDropStorage(slot): readonly string[] {\n    const table = quoteIdentifier(\n      tableName(slot.graphId, slot.nodeKind, slot.fieldPath),\n    );\n    const statements: string[] = [];\n    // Drop the DiskANN index first so libSQL reclaims its shadow tables,\n    // then the table itself.\n    if (usesAnnIndex(slot)) {\n      statements.push(\n        `DROP INDEX IF EXISTS ${quoteIdentifier(libsqlIndexName(slot))}`,\n      );\n    }\n    statements.push(`DROP TABLE IF EXISTS ${table}`);\n    return statements;\n  },\n};\n\n/**\n * DiskANN over-fetch multiplier applied when a candidate filter is present:\n * `vector_top_k` cannot pre-filter, so fetch `4k` neighbors and filter after.\n * Mirrors the hybrid facade's 4x over-fetch. Recall for the filtered search\n * is bounded by this headroom — if more than `3k` of the top `4k` neighbors\n * are filtered out, fewer than `k` rows return. That is the observable\n * meaning of `capabilities.filteredApproximateSearch.guaranteesFullPage`.\n */\nconst CANDIDATE_FILTER_OVERFETCH = 4;\n\nfunction libsqlIndexName(slot: VectorSlot): string {\n  return vectorPhysicalName(\n    INDEX_PREFIX,\n    slot.graphId,\n    slot.nodeKind,\n    slot.fieldPath,\n  );\n}\n\nfunction libsqlVectorIndexDdl(table: string, slot: VectorSlot): string {\n  const indexName = quoteIdentifier(libsqlIndexName(slot));\n  const metric = libsqlMetricToken(slot.metric);\n  return `CREATE INDEX IF NOT EXISTS ${indexName} ON ${quoteIdentifier(table)}(libsql_vector_idx(\"embedding\", 'metric=${metric}'));`;\n}\n"]}