@mmnto/totem 1.124.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/artifacts/grounding.d.ts +27 -7
  2. package/dist/artifacts/grounding.d.ts.map +1 -1
  3. package/dist/artifacts/grounding.js +23 -5
  4. package/dist/artifacts/grounding.js.map +1 -1
  5. package/dist/artifacts/grounding.test.js +28 -0
  6. package/dist/artifacts/grounding.test.js.map +1 -1
  7. package/dist/artifacts/schema.d.ts +63 -18
  8. package/dist/artifacts/schema.d.ts.map +1 -1
  9. package/dist/artifacts/schema.js +13 -4
  10. package/dist/artifacts/schema.js.map +1 -1
  11. package/dist/compiler.d.ts +12 -0
  12. package/dist/compiler.d.ts.map +1 -1
  13. package/dist/compiler.js +17 -3
  14. package/dist/compiler.js.map +1 -1
  15. package/dist/config-schema.d.ts +95 -15
  16. package/dist/config-schema.d.ts.map +1 -1
  17. package/dist/config-schema.js +99 -14
  18. package/dist/config-schema.js.map +1 -1
  19. package/dist/config-schema.test.js +110 -1
  20. package/dist/config-schema.test.js.map +1 -1
  21. package/dist/describe.d.ts +19 -0
  22. package/dist/describe.d.ts.map +1 -1
  23. package/dist/describe.js +83 -12
  24. package/dist/describe.js.map +1 -1
  25. package/dist/describe.test.js +69 -2
  26. package/dist/describe.test.js.map +1 -1
  27. package/dist/index.d.ts +11 -6
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +10 -6
  30. package/dist/index.js.map +1 -1
  31. package/dist/ingest/pipeline.d.ts +12 -0
  32. package/dist/ingest/pipeline.d.ts.map +1 -1
  33. package/dist/ingest/pipeline.js +5 -0
  34. package/dist/ingest/pipeline.js.map +1 -1
  35. package/dist/ingest/pipeline.test.js +9 -0
  36. package/dist/ingest/pipeline.test.js.map +1 -1
  37. package/dist/selection-manifest.d.ts +4 -4
  38. package/dist/selection-manifest.d.ts.map +1 -1
  39. package/dist/selection-manifest.js +2 -0
  40. package/dist/selection-manifest.js.map +1 -1
  41. package/dist/store/lance-search.d.ts +1 -1
  42. package/dist/store/lance-search.d.ts.map +1 -1
  43. package/dist/store/lance-search.js +89 -18
  44. package/dist/store/lance-search.js.map +1 -1
  45. package/dist/store/lance-search.test.js +207 -26
  46. package/dist/store/lance-search.test.js.map +1 -1
  47. package/dist/store/lance-store.d.ts.map +1 -1
  48. package/dist/store/lance-store.js +1 -1
  49. package/dist/store/lance-store.js.map +1 -1
  50. package/dist/store/relevance.d.ts +70 -0
  51. package/dist/store/relevance.d.ts.map +1 -0
  52. package/dist/store/relevance.js +115 -0
  53. package/dist/store/relevance.js.map +1 -0
  54. package/dist/store/relevance.sdk.test.d.ts +20 -0
  55. package/dist/store/relevance.sdk.test.d.ts.map +1 -0
  56. package/dist/store/relevance.sdk.test.js +145 -0
  57. package/dist/store/relevance.sdk.test.js.map +1 -0
  58. package/dist/store/relevance.test.d.ts +2 -0
  59. package/dist/store/relevance.test.d.ts.map +1 -0
  60. package/dist/store/relevance.test.js +192 -0
  61. package/dist/store/relevance.test.js.map +1 -0
  62. package/dist/types.d.ts +4 -2
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Metric-bound relevance (mmnto-ai/totem#2738).
3
+ *
4
+ * A distance is only interpretable against the metric that produced it. The
5
+ * pre-#2738 code hard-coded `1 / (1 + _distance)` at the row-mapping site with
6
+ * no record of which metric LanceDB had actually used, so a metric change
7
+ * anywhere would have silently re-scaled every relevance number in the system.
8
+ * This module makes the metric a named fact and the normalization a map keyed
9
+ * by it: one truth, stated once, quoted in the doc comments below from the
10
+ * LanceDB SDK's own definitions.
11
+ */
12
+ /** The distance metrics the LanceDB TypeScript SDK can be asked for (`VectorQuery.distanceType`). */
13
+ export type DistanceMetric = 'l2' | 'cosine' | 'dot';
14
+ /**
15
+ * Every spelling the SDK accepts — the allow-list `assertDistanceMetric` gates on.
16
+ * Frozen at runtime as well as `readonly` in the type: a JS caller that pushed a
17
+ * spelling onto it would otherwise pass the gate and reach an undefined map entry
18
+ * (CodeRabbit on mmnto-ai/totem#2761).
19
+ */
20
+ export declare const DISTANCE_METRICS: readonly DistanceMetric[];
21
+ /**
22
+ * The ONE metric every Totem vector query is issued with (mmnto-ai/totem#2738).
23
+ *
24
+ * Both query sites in `lance-search.ts` chain `.distanceType(VECTOR_DISTANCE_METRIC)`
25
+ * explicitly, so the metric is a recorded fact in the query rather than the SDK
26
+ * default it happens to coincide with. `runSync` also records it in
27
+ * `.totem/index-manifest.json` as `vectorDistanceMetric`.
28
+ */
29
+ export declare const VECTOR_DISTANCE_METRIC: DistanceMetric;
30
+ /**
31
+ * Why a computed relevance can leave [0, 1] — the cause is METRIC-SPECIFIC
32
+ * (mmnto-ai/totem#2738 falsification round, F1), so the warning must not name a
33
+ * cause the metric cannot have.
34
+ *
35
+ * Under `l2` the SDK returns a squared distance, which is `≥ 0` by
36
+ * construction, so `1 / (1 + d) ∈ (0, 1]` for every value the SDK can legally
37
+ * return: a breach there is NOT a non-unit-norm embedder, it is a fault. And it
38
+ * is always a NEGATIVE, FINITE `_distance` — the search layer discards a
39
+ * non-finite `_distance` BEFORE the map runs (both the row-mapping site and the
40
+ * pre-fusion tally require `Number.isFinite`), so a non-finite value produces no
41
+ * relevance at all and can never reach this warning. The string names only what
42
+ * the warning can actually report (fold 2, F1).
43
+ *
44
+ * Under `cosine` / `dot` the mapping DOES leave [0, 1] for vectors that are not
45
+ * unit-norm, which is the real embedder-profile signal.
46
+ */
47
+ export declare const OUT_OF_RANGE_CAUSE: Record<DistanceMetric, string>;
48
+ /**
49
+ * Narrow an untrusted value (a manifest field, a config key, a record read off
50
+ * disk) to a `DistanceMetric`, or throw loudly naming the value and every
51
+ * allowed spelling. The type makes an unmapped metric unreachable in code; this
52
+ * is the runtime gate for values that did not come through the type.
53
+ */
54
+ export declare function assertDistanceMetric(value: unknown): DistanceMetric;
55
+ /**
56
+ * Normalize a LanceDB `_distance` into a relevance under the given metric.
57
+ *
58
+ * A pure map with one job: it never warns, never throws ON RANGE, and never
59
+ * clamps. The caller judges the result with {@link isRelevanceInRange} and
60
+ * decides what a range breach means at that call site.
61
+ *
62
+ * The METRIC is gated: `assertDistanceMetric` runs first (mmnto-ai/totem#2738
63
+ * falsification round, F5), so a spelling that reached here past the type — an
64
+ * `any`, a value read off a record, a JS caller — raises the named
65
+ * `TotemError` instead of a bare `TypeError` from an undefined map entry.
66
+ */
67
+ export declare function relevanceFromDistance(metric: DistanceMetric, distance: number): number;
68
+ /** Whether a relevance is a finite number inside the closed interval [0, 1]. */
69
+ export declare function isRelevanceInRange(relevance: number): boolean;
70
+ //# sourceMappingURL=relevance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevance.d.ts","sourceRoot":"","sources":["../../src/store/relevance.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,qGAAqG;AACrG,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAI3C,CAAC;AAEZ;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAAqB,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAI7D,CAAC;AA0CF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAUnE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtF;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,115 @@
1
+ import { TotemError } from '../errors.js';
2
+ /**
3
+ * Every spelling the SDK accepts — the allow-list `assertDistanceMetric` gates on.
4
+ * Frozen at runtime as well as `readonly` in the type: a JS caller that pushed a
5
+ * spelling onto it would otherwise pass the gate and reach an undefined map entry
6
+ * (CodeRabbit on mmnto-ai/totem#2761).
7
+ */
8
+ export const DISTANCE_METRICS = Object.freeze([
9
+ 'l2',
10
+ 'cosine',
11
+ 'dot',
12
+ ]);
13
+ /**
14
+ * The ONE metric every Totem vector query is issued with (mmnto-ai/totem#2738).
15
+ *
16
+ * Both query sites in `lance-search.ts` chain `.distanceType(VECTOR_DISTANCE_METRIC)`
17
+ * explicitly, so the metric is a recorded fact in the query rather than the SDK
18
+ * default it happens to coincide with. `runSync` also records it in
19
+ * `.totem/index-manifest.json` as `vectorDistanceMetric`.
20
+ */
21
+ export const VECTOR_DISTANCE_METRIC = 'l2';
22
+ /**
23
+ * Why a computed relevance can leave [0, 1] — the cause is METRIC-SPECIFIC
24
+ * (mmnto-ai/totem#2738 falsification round, F1), so the warning must not name a
25
+ * cause the metric cannot have.
26
+ *
27
+ * Under `l2` the SDK returns a squared distance, which is `≥ 0` by
28
+ * construction, so `1 / (1 + d) ∈ (0, 1]` for every value the SDK can legally
29
+ * return: a breach there is NOT a non-unit-norm embedder, it is a fault. And it
30
+ * is always a NEGATIVE, FINITE `_distance` — the search layer discards a
31
+ * non-finite `_distance` BEFORE the map runs (both the row-mapping site and the
32
+ * pre-fusion tally require `Number.isFinite`), so a non-finite value produces no
33
+ * relevance at all and can never reach this warning. The string names only what
34
+ * the warning can actually report (fold 2, F1).
35
+ *
36
+ * Under `cosine` / `dot` the mapping DOES leave [0, 1] for vectors that are not
37
+ * unit-norm, which is the real embedder-profile signal.
38
+ */
39
+ export const OUT_OF_RANGE_CAUSE = {
40
+ l2: 'a negative _distance, which squared L2 cannot produce: an SDK or data fault',
41
+ cosine: "the embedder's vectors may not be unit-norm",
42
+ dot: "the embedder's vectors may not be unit-norm",
43
+ };
44
+ /**
45
+ * distance → relevance, one entry per SDK metric.
46
+ *
47
+ * Private on purpose: the map is an implementation of `relevanceFromDistance`,
48
+ * not a second surface callers can reach around it to index.
49
+ */
50
+ const RELEVANCE_FROM_DISTANCE = {
51
+ /**
52
+ * SDK: `"l2"` — Euclidean distance, "range of [0, ∞)". MEASURED (R1,
53
+ * mmnto-ai/totem#2738, 3/3 trials, |diff| < 1e-5): what the SDK actually
54
+ * returns in `_distance` for `l2` is the SQUARED Euclidean distance
55
+ * (`lance-linalg` sums `diff * diff` with no sqrt) — a self-hit returns 0,
56
+ * and a pair at cosine 0.845 returned 0.3096 = |a-b|², not |a-b| = 0.5564.
57
+ *
58
+ * On unit-norm vectors |a-b|² = 2 - 2·cos ∈ [0, 4], so relevance ∈ [0.2, 1]
59
+ * (minimum observed: 0.5022 over 3,795 hits on this repo's index).
60
+ *
61
+ * LangChain's euclidean constant presumes a NON-squared distance and is
62
+ * deliberately NOT borrowed — applied to a squared distance it would be
63
+ * wrong. The doctrine (bind the normalization to a named metric) is
64
+ * borrowed; the constant is not.
65
+ */
66
+ l2: (distance) => 1 / (1 + distance),
67
+ /**
68
+ * SDK: `"cosine"` — "Cosine distance ... has a range of [0, 2]", i.e.
69
+ * `1 - cos(a, b)`, unaffected by vector magnitude. `1 - distance / 2` maps
70
+ * [0, 2] onto relevance [0, 1] linearly in the cosine.
71
+ */
72
+ cosine: (distance) => 1 - distance / 2,
73
+ /**
74
+ * SDK: `"dot"` — "Dot distance has a range of (-∞, ∞). If the vectors are
75
+ * normalized (i.e. their l2 norm is 1), then dot distance is equivalent to
76
+ * the cosine distance" — so LanceDB's dot distance is the `1 - a·b` form and
77
+ * takes the SAME map as `cosine`. On NON-unit vectors it can leave [0, 2] and
78
+ * the relevance then leaves [0, 1]: this is the case the out-of-range warning
79
+ * at the search call sites exists for.
80
+ */
81
+ dot: (distance) => 1 - distance / 2,
82
+ };
83
+ /**
84
+ * Narrow an untrusted value (a manifest field, a config key, a record read off
85
+ * disk) to a `DistanceMetric`, or throw loudly naming the value and every
86
+ * allowed spelling. The type makes an unmapped metric unreachable in code; this
87
+ * is the runtime gate for values that did not come through the type.
88
+ */
89
+ export function assertDistanceMetric(value) {
90
+ if (typeof value === 'string' && DISTANCE_METRICS.includes(value)) {
91
+ return value;
92
+ }
93
+ const allowed = DISTANCE_METRICS.map((m) => `"${m}"`).join(', ');
94
+ throw new TotemError('CONFIG_INVALID', `Unknown vector distance metric ${JSON.stringify(value)} — the LanceDB SDK accepts only ${allowed}.`, `Use one of ${allowed}. These are the SDK's own spellings; "ip"/"inner_product"/"euclidean" are not among them.`);
95
+ }
96
+ /**
97
+ * Normalize a LanceDB `_distance` into a relevance under the given metric.
98
+ *
99
+ * A pure map with one job: it never warns, never throws ON RANGE, and never
100
+ * clamps. The caller judges the result with {@link isRelevanceInRange} and
101
+ * decides what a range breach means at that call site.
102
+ *
103
+ * The METRIC is gated: `assertDistanceMetric` runs first (mmnto-ai/totem#2738
104
+ * falsification round, F5), so a spelling that reached here past the type — an
105
+ * `any`, a value read off a record, a JS caller — raises the named
106
+ * `TotemError` instead of a bare `TypeError` from an undefined map entry.
107
+ */
108
+ export function relevanceFromDistance(metric, distance) {
109
+ return RELEVANCE_FROM_DISTANCE[assertDistanceMetric(metric)](distance);
110
+ }
111
+ /** Whether a relevance is a finite number inside the closed interval [0, 1]. */
112
+ export function isRelevanceInRange(relevance) {
113
+ return Number.isFinite(relevance) && relevance >= 0 && relevance <= 1;
114
+ }
115
+ //# sourceMappingURL=relevance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevance.js","sourceRoot":"","sources":["../../src/store/relevance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiB1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA8B,MAAM,CAAC,MAAM,CAAC;IACvE,IAAI;IACJ,QAAQ;IACR,KAAK;CACG,CAAC,CAAC;AAEZ;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAmB,IAAI,CAAC;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAmC;IAChE,EAAE,EAAE,6EAA6E;IACjF,MAAM,EAAE,6CAA6C;IACrD,GAAG,EAAE,6CAA6C;CACnD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,uBAAuB,GAAyD;IACpF;;;;;;;;;;;;;;OAcG;IACH,EAAE,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IAC5C;;;;OAIG;IACH,MAAM,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC;IAC9C;;;;;;;OAOG;IACH,GAAG,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC;CAC5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAK,gBAAsC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzF,OAAO,KAAuB,CAAC;IACjC,CAAC;IACD,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,IAAI,UAAU,CAClB,gBAAgB,EAChB,kCAAkC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,mCAAmC,OAAO,GAAG,EACpG,cAAc,OAAO,2FAA2F,CACjH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAsB,EAAE,QAAgB;IAC5E,OAAO,uBAAuB,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,IAAI,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * SDK-MEASURED metric pinning (mmnto-ai/totem#2738 falsification round, F7).
3
+ *
4
+ * The rest of the relevance suite pins the MAP; it cannot tell you whether the
5
+ * map matches what LanceDB actually returns. The `dot` entry in particular was
6
+ * derived from the SDK's DOC COMMENT ("if the vectors are normalized … dot
7
+ * distance is equivalent to the cosine distance") and never measured — a doc
8
+ * comment is a claim, not a measurement, and it is silent about what happens
9
+ * off the unit sphere. This file is the falsifier that gap wanted: it builds a
10
+ * throwaway LanceDB table with hand-chosen vectors, queries it under each of
11
+ * the SDK's three metrics, and asserts the RETURNED `_distance` before feeding
12
+ * each measured value through the map.
13
+ *
14
+ * If a future SDK bump changes a distance convention — l2 stops being squared,
15
+ * dot flips sign, the default stops being l2 — this file goes red at the exact
16
+ * number that moved, instead of every relevance in the system quietly
17
+ * re-scaling behind a green suite.
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=relevance.sdk.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevance.sdk.test.d.ts","sourceRoot":"","sources":["../../src/store/relevance.sdk.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * SDK-MEASURED metric pinning (mmnto-ai/totem#2738 falsification round, F7).
3
+ *
4
+ * The rest of the relevance suite pins the MAP; it cannot tell you whether the
5
+ * map matches what LanceDB actually returns. The `dot` entry in particular was
6
+ * derived from the SDK's DOC COMMENT ("if the vectors are normalized … dot
7
+ * distance is equivalent to the cosine distance") and never measured — a doc
8
+ * comment is a claim, not a measurement, and it is silent about what happens
9
+ * off the unit sphere. This file is the falsifier that gap wanted: it builds a
10
+ * throwaway LanceDB table with hand-chosen vectors, queries it under each of
11
+ * the SDK's three metrics, and asserts the RETURNED `_distance` before feeding
12
+ * each measured value through the map.
13
+ *
14
+ * If a future SDK bump changes a distance convention — l2 stops being squared,
15
+ * dot flips sign, the default stops being l2 — this file goes red at the exact
16
+ * number that moved, instead of every relevance in the system quietly
17
+ * re-scaling behind a green suite.
18
+ */
19
+ import * as fs from 'node:fs';
20
+ import * as os from 'node:os';
21
+ import * as path from 'node:path';
22
+ import * as lancedb from '@lancedb/lancedb';
23
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
24
+ import { cleanTmpDir } from '../test-utils.js';
25
+ import { isRelevanceInRange, relevanceFromDistance, VECTOR_DISTANCE_METRIC } from './relevance.js';
26
+ /** The query vector every measurement below is taken against. */
27
+ const QUERY = [1, 0, 0];
28
+ /**
29
+ * Hand-chosen probes. Four are unit-norm (where all three metrics are supposed
30
+ * to agree up to their own scale); `scaled-2x` is deliberately NOT, because
31
+ * that is the only place `cosine` and `dot` come apart and the only way to
32
+ * observe a `dot` distance outside [0, 2].
33
+ */
34
+ const ROWS = [
35
+ { id: 'identical', vector: [1, 0, 0] },
36
+ { id: 'orthogonal', vector: [0, 1, 0] },
37
+ { id: 'opposite', vector: [-1, 0, 0] },
38
+ { id: 'unit-0.6', vector: [0.6, 0.8, 0] },
39
+ { id: 'scaled-2x', vector: [2, 0, 0] },
40
+ ];
41
+ /** MEASURED `_distance` per metric, asserted to 1e-6 against the live SDK. */
42
+ const EXPECTED_DISTANCE = {
43
+ // Squared Euclidean: |q - v|². identical 0; orthogonal 2; opposite 4;
44
+ // unit-0.6 0.4² + 0.8² = 0.8; scaled-2x |(-1,0,0)|² = 1.
45
+ l2: { identical: 0, orthogonal: 2, opposite: 4, 'unit-0.6': 0.8, 'scaled-2x': 1 },
46
+ // 1 - cos: magnitude-independent, so scaled-2x collapses onto identical.
47
+ cosine: { identical: 0, orthogonal: 1, opposite: 2, 'unit-0.6': 0.4, 'scaled-2x': 0 },
48
+ // 1 - q·v: identical to cosine on unit vectors, and NOT on scaled-2x, where
49
+ // 1 - 2 = -1 leaves the [0, 2] interval entirely.
50
+ dot: { identical: 0, orthogonal: 1, opposite: 2, 'unit-0.6': 0.4, 'scaled-2x': -1 },
51
+ };
52
+ /** The relevance the map must produce for each MEASURED distance above. */
53
+ const EXPECTED_RELEVANCE = {
54
+ l2: { identical: 1, orthogonal: 1 / 3, opposite: 0.2, 'unit-0.6': 5 / 9, 'scaled-2x': 0.5 },
55
+ cosine: { identical: 1, orthogonal: 0.5, opposite: 0, 'unit-0.6': 0.8, 'scaled-2x': 1 },
56
+ dot: { identical: 1, orthogonal: 0.5, opposite: 0, 'unit-0.6': 0.8, 'scaled-2x': 1.5 },
57
+ };
58
+ describe('LanceDB SDK distance metrics, measured (mmnto-ai/totem#2738, F7)', () => {
59
+ let tmpDir;
60
+ let table;
61
+ beforeEach(async () => {
62
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lance-metric-'));
63
+ const db = await lancedb.connect(tmpDir);
64
+ table = await db.createTable('probes', ROWS);
65
+ });
66
+ afterEach(() => {
67
+ cleanTmpDir(tmpDir);
68
+ });
69
+ /** Query under `metric`, or under the SDK DEFAULT when none is given. */
70
+ async function measure(metric) {
71
+ let q = table.vectorSearch(QUERY).limit(ROWS.length);
72
+ if (metric !== undefined)
73
+ q = q.distanceType(metric);
74
+ const rows = await q.toArray();
75
+ const byId = new Map();
76
+ for (const row of rows)
77
+ byId.set(String(row['id']), Number(row['_distance']));
78
+ return byId;
79
+ }
80
+ it('the SDK DEFAULT is l2, and l2 is the SQUARED Euclidean distance', async () => {
81
+ const byDefault = await measure();
82
+ const byL2 = await measure('l2');
83
+ for (const { id } of ROWS) {
84
+ const expected = EXPECTED_DISTANCE.l2[id];
85
+ expect(byDefault.get(id)).toBeCloseTo(expected, 6);
86
+ expect(byL2.get(id)).toBeCloseTo(expected, 6);
87
+ }
88
+ // `opposite` is the discriminator: |q - v| = 2 but |q - v|² = 4. A
89
+ // non-squared l2 would return 2 here and the whole [0.2, 1] bound would be
90
+ // wrong. This is what the store relies on, hence the constant.
91
+ expect(byL2.get('opposite')).toBeCloseTo(4, 6);
92
+ expect(VECTOR_DISTANCE_METRIC).toBe('l2');
93
+ });
94
+ it('cosine returns 1 - cos over [0, 2] and ignores magnitude', async () => {
95
+ const byCosine = await measure('cosine');
96
+ for (const { id } of ROWS) {
97
+ expect(byCosine.get(id)).toBeCloseTo(EXPECTED_DISTANCE.cosine[id], 6);
98
+ }
99
+ // Magnitude-independence, measured: a 2x-scaled copy of the query is at
100
+ // distance 0, exactly where the query itself is.
101
+ expect(byCosine.get('scaled-2x')).toBeCloseTo(byCosine.get('identical'), 6);
102
+ });
103
+ it('dot returns 1 - a·b: equal to cosine on unit vectors, and NEGATIVE off them', async () => {
104
+ const byDot = await measure('dot');
105
+ const byCosine = await measure('cosine');
106
+ for (const { id } of ROWS) {
107
+ expect(byDot.get(id)).toBeCloseTo(EXPECTED_DISTANCE.dot[id], 6);
108
+ }
109
+ // The SDK's documented equivalence, now MEASURED rather than quoted — on
110
+ // the four unit-norm probes only.
111
+ for (const id of ['identical', 'orthogonal', 'opposite', 'unit-0.6']) {
112
+ expect(byDot.get(id)).toBeCloseTo(byCosine.get(id), 6);
113
+ }
114
+ // And where the doc comment is silent: off the unit sphere the two diverge
115
+ // and dot leaves [0, 2] altogether.
116
+ expect(byDot.get('scaled-2x')).toBeCloseTo(-1, 6);
117
+ expect(byDot.get('scaled-2x')).not.toBeCloseTo(byCosine.get('scaled-2x'), 6);
118
+ });
119
+ it('the map turns every MEASURED distance into the pinned relevance', async () => {
120
+ for (const metric of ['l2', 'cosine', 'dot']) {
121
+ const measured = await measure(metric);
122
+ for (const { id } of ROWS) {
123
+ const relevance = relevanceFromDistance(metric, measured.get(id));
124
+ expect(relevance).toBeCloseTo(EXPECTED_RELEVANCE[metric][id], 6);
125
+ }
126
+ }
127
+ });
128
+ it('every measured l2 relevance lands in [0.2, 1]; only dot off the unit sphere breaches', async () => {
129
+ const byL2 = await measure('l2');
130
+ for (const { id } of ROWS) {
131
+ const relevance = relevanceFromDistance('l2', byL2.get(id));
132
+ expect(relevance).toBeGreaterThanOrEqual(0.2);
133
+ expect(relevance).toBeLessThanOrEqual(1);
134
+ expect(isRelevanceInRange(relevance)).toBe(true);
135
+ }
136
+ // The single measured breach in the whole matrix — the case the
137
+ // out-of-range warning exists for, and the reason its cause text is
138
+ // metric-specific (F1).
139
+ const byDot = await measure('dot');
140
+ const breach = relevanceFromDistance('dot', byDot.get('scaled-2x'));
141
+ expect(breach).toBeCloseTo(1.5, 6);
142
+ expect(isRelevanceInRange(breach)).toBe(false);
143
+ });
144
+ });
145
+ //# sourceMappingURL=relevance.sdk.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevance.sdk.test.js","sourceRoot":"","sources":["../../src/store/relevance.sdk.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=relevance.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevance.test.d.ts","sourceRoot":"","sources":["../../src/store/relevance.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,192 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { TotemError } from '../errors.js';
3
+ import { assertDistanceMetric, DISTANCE_METRICS, isRelevanceInRange, OUT_OF_RANGE_CAUSE, relevanceFromDistance, VECTOR_DISTANCE_METRIC, } from './relevance.js';
4
+ // ─── The pinned map (mmnto-ai/totem#2738) ────────────────
5
+ //
6
+ // These numbers are the contract, not an implementation echo. `l2` is pinned to
7
+ // the MEASURED fact that LanceDB's `_distance` for `l2` is the SQUARED
8
+ // Euclidean distance (R1, 3/3 trials): a self-hit is 0, and the measured pair
9
+ // at cosine 0.845 came back as 0.3096 = |a-b|². On unit-norm vectors that puts
10
+ // `_distance` in [0, 4] and therefore relevance in [0.2, 1].
11
+ describe('relevanceFromDistance — l2', () => {
12
+ it('maps a self-hit (d = 0) to relevance 1', () => {
13
+ expect(relevanceFromDistance('l2', 0)).toBe(1);
14
+ });
15
+ it('maps the antipodal unit-vector bound (d = 4) to relevance 0.2', () => {
16
+ expect(relevanceFromDistance('l2', 4)).toBeCloseTo(0.2, 10);
17
+ });
18
+ it('maps the R1-measured pair (d = 0.3096, cosine 0.845) to ~0.7636', () => {
19
+ expect(relevanceFromDistance('l2', 0.3096)).toBeCloseTo(0.7636, 4);
20
+ });
21
+ it('is monotone decreasing in distance', () => {
22
+ const distances = [0, 0.25, 0.3096, 0.5, 1, 2, 3, 4, 10];
23
+ const relevances = distances.map((d) => relevanceFromDistance('l2', d));
24
+ for (let i = 1; i < relevances.length; i += 1) {
25
+ expect(relevances[i]).toBeLessThan(relevances[i - 1]);
26
+ }
27
+ });
28
+ it('INVARIANT: unit-norm rows produce relevance in [0.2, 1]', () => {
29
+ // On unit vectors |a-b|² = 2 - 2·cos ∈ [0, 4]; the sweep walks that interval.
30
+ for (const d of [0, 0.5, 1, 2, 3, 4]) {
31
+ const relevance = relevanceFromDistance('l2', d);
32
+ expect(relevance).toBeGreaterThanOrEqual(0.2);
33
+ expect(relevance).toBeLessThanOrEqual(1);
34
+ expect(isRelevanceInRange(relevance)).toBe(true);
35
+ }
36
+ });
37
+ });
38
+ describe('relevanceFromDistance — cosine', () => {
39
+ it('maps d = 0 to 1', () => {
40
+ expect(relevanceFromDistance('cosine', 0)).toBe(1);
41
+ });
42
+ it('maps the far bound d = 2 to 0', () => {
43
+ expect(relevanceFromDistance('cosine', 2)).toBe(0);
44
+ });
45
+ it('maps the orthogonal midpoint d = 1 to 0.5', () => {
46
+ expect(relevanceFromDistance('cosine', 1)).toBe(0.5);
47
+ });
48
+ });
49
+ describe('relevanceFromDistance — dot', () => {
50
+ // The SDK: "If the vectors are normalized (i.e. their l2 norm is 1), then dot
51
+ // distance is equivalent to the cosine distance" — so `dot` takes the same map.
52
+ it('maps d = 0 to 1', () => {
53
+ expect(relevanceFromDistance('dot', 0)).toBe(1);
54
+ });
55
+ it('maps d = 2 to 0', () => {
56
+ expect(relevanceFromDistance('dot', 2)).toBe(0);
57
+ });
58
+ it('maps d = 1 to 0.5', () => {
59
+ expect(relevanceFromDistance('dot', 1)).toBe(0.5);
60
+ });
61
+ it('agrees with cosine at every pinned point (the SDK equivalence on unit vectors)', () => {
62
+ for (const d of [0, 0.5, 1, 1.5, 2]) {
63
+ expect(relevanceFromDistance('dot', d)).toBe(relevanceFromDistance('cosine', d));
64
+ }
65
+ });
66
+ });
67
+ // ─── Out-of-range detection ─────────────────────────────
68
+ //
69
+ // `relevanceFromDistance` computes; it never warns, throws or clamps. The range
70
+ // judgment is `isRelevanceInRange`, exercised at the search call sites.
71
+ describe('isRelevanceInRange', () => {
72
+ it('is false for an l2 relevance above 1 (a negative _distance)', () => {
73
+ const relevance = relevanceFromDistance('l2', -0.5);
74
+ expect(relevance).toBeCloseTo(2, 10);
75
+ expect(isRelevanceInRange(relevance)).toBe(false);
76
+ });
77
+ it('is false for a cosine relevance below 0 (a distance past the [0, 2] bound)', () => {
78
+ const relevance = relevanceFromDistance('cosine', 3);
79
+ expect(relevance).toBeCloseTo(-0.5, 10);
80
+ expect(isRelevanceInRange(relevance)).toBe(false);
81
+ });
82
+ it('is false for NaN and Infinity', () => {
83
+ expect(isRelevanceInRange(Number.NaN)).toBe(false);
84
+ expect(isRelevanceInRange(Number.POSITIVE_INFINITY)).toBe(false);
85
+ expect(isRelevanceInRange(Number.NEGATIVE_INFINITY)).toBe(false);
86
+ });
87
+ it('is true at both closed bounds', () => {
88
+ expect(isRelevanceInRange(0)).toBe(true);
89
+ expect(isRelevanceInRange(1)).toBe(true);
90
+ });
91
+ });
92
+ // ─── The loud gate ──────────────────────────────────────
93
+ describe('assertDistanceMetric', () => {
94
+ it('throws a TotemError naming the value and the three allowed spellings', () => {
95
+ // 'ip' is a metric name from OTHER vector databases; the LanceDB SDK has no
96
+ // such spelling, and guessing it silently would be exactly the drift #2738
97
+ // exists to stop.
98
+ expect(() => assertDistanceMetric('ip')).toThrow(TotemError);
99
+ let thrown;
100
+ try {
101
+ assertDistanceMetric('ip');
102
+ }
103
+ catch (err) {
104
+ thrown = err;
105
+ }
106
+ const thrownError = thrown;
107
+ expect(thrownError.code).toBe('CONFIG_INVALID');
108
+ expect(thrownError.message).toContain('"ip"');
109
+ expect(thrownError.message).toContain('"l2"');
110
+ expect(thrownError.message).toContain('"cosine"');
111
+ expect(thrownError.message).toContain('"dot"');
112
+ });
113
+ it('returns the metric unchanged for a valid spelling', () => {
114
+ expect(assertDistanceMetric('cosine')).toBe('cosine');
115
+ });
116
+ it('accepts every listed metric and rejects non-strings', () => {
117
+ for (const metric of DISTANCE_METRICS) {
118
+ expect(assertDistanceMetric(metric)).toBe(metric);
119
+ }
120
+ expect(() => assertDistanceMetric(undefined)).toThrow(TotemError);
121
+ expect(() => assertDistanceMetric(null)).toThrow(TotemError);
122
+ expect(() => assertDistanceMetric(2)).toThrow(TotemError);
123
+ });
124
+ });
125
+ describe('VECTOR_DISTANCE_METRIC', () => {
126
+ it('is the l2 metric the store queries with', () => {
127
+ expect(VECTOR_DISTANCE_METRIC).toBe('l2');
128
+ expect(DISTANCE_METRICS).toContain(VECTOR_DISTANCE_METRIC);
129
+ });
130
+ });
131
+ // ─── the falsification round's folds (mmnto-ai/totem#2738) ───
132
+ describe('relevanceFromDistance gates its metric (F5)', () => {
133
+ it('raises the named TotemError for a spelling that reached it past the type', () => {
134
+ // A value read off a record, an `any`, or a JS caller can carry 'ip' here.
135
+ // Indexing the map with it would have thrown a bare TypeError ("is not a
136
+ // function"); the named error says what is wrong and what is allowed.
137
+ const badMetric = 'ip';
138
+ expect(() => relevanceFromDistance(badMetric, 0.5)).toThrow(TotemError);
139
+ let thrown;
140
+ try {
141
+ relevanceFromDistance(badMetric, 0.5);
142
+ }
143
+ catch (err) {
144
+ thrown = err;
145
+ }
146
+ expect(thrown.code).toBe('CONFIG_INVALID');
147
+ expect(thrown.message).toContain('"ip"');
148
+ });
149
+ it('still computes for every valid metric', () => {
150
+ for (const metric of DISTANCE_METRICS) {
151
+ expect(Number.isFinite(relevanceFromDistance(metric, 0.5))).toBe(true);
152
+ }
153
+ });
154
+ });
155
+ describe('OUT_OF_RANGE_CAUSE is metric-specific (F1)', () => {
156
+ it('names a FAULT for l2, never a non-unit-norm embedder', () => {
157
+ // Squared L2 is >= 0 by construction, so 1/(1+d) is in (0, 1] for every
158
+ // value the SDK can legally return: no vector norm can push it out.
159
+ expect(OUT_OF_RANGE_CAUSE.l2).toContain('which squared L2 cannot produce');
160
+ expect(OUT_OF_RANGE_CAUSE.l2).not.toContain('unit-norm');
161
+ });
162
+ it('names ONLY the negative-distance case for l2 — a non-finite one never reaches it', () => {
163
+ // The search layer requires `Number.isFinite` before the map runs at BOTH
164
+ // sites, so a non-finite `_distance` yields no relevance and cannot produce
165
+ // a breach. Naming it would describe a report this warning cannot make.
166
+ expect(OUT_OF_RANGE_CAUSE.l2).toContain('a negative _distance');
167
+ expect(OUT_OF_RANGE_CAUSE.l2).not.toContain('non-finite');
168
+ });
169
+ it('names the unit-norm cause for cosine and dot, where the mapping really can leave [0, 1]', () => {
170
+ expect(OUT_OF_RANGE_CAUSE.cosine).toContain('unit-norm');
171
+ expect(OUT_OF_RANGE_CAUSE.dot).toContain('unit-norm');
172
+ // `dot` on a non-unit vector is the live case: q·[2,0,0] = 2 → d = -1 → 1.5.
173
+ expect(isRelevanceInRange(relevanceFromDistance('dot', -1))).toBe(false);
174
+ });
175
+ it('has an entry for every metric', () => {
176
+ for (const metric of DISTANCE_METRICS) {
177
+ expect(OUT_OF_RANGE_CAUSE[metric].length).toBeGreaterThan(0);
178
+ }
179
+ });
180
+ });
181
+ describe('DISTANCE_METRICS', () => {
182
+ // The allow-list `assertDistanceMetric` gates on is frozen at runtime, not only
183
+ // `readonly` in the type: a JS caller that pushed a spelling onto it would
184
+ // otherwise pass the gate and reach an undefined map entry (CodeRabbit on
185
+ // mmnto-ai/totem#2761). Pinned so a refactor cannot drop the freeze unnoticed.
186
+ it('is frozen at runtime', () => {
187
+ expect(Object.isFrozen(DISTANCE_METRICS)).toBe(true);
188
+ expect(() => DISTANCE_METRICS.push('euclidean')).toThrow();
189
+ expect(DISTANCE_METRICS).toEqual(['l2', 'cosine', 'dot']);
190
+ });
191
+ });
192
+ //# sourceMappingURL=relevance.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relevance.test.js","sourceRoot":"","sources":["../../src/store/relevance.test.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -102,8 +102,10 @@ export interface SearchResult {
102
102
  label: string;
103
103
  score: number;
104
104
  /**
105
- * True per-hit relevance signal in the 0..1 range — the vector-leg
106
- * cosine similarity `1/(1+_distance)` (mmnto-ai/totem#2463). Distinct from
105
+ * True per-hit relevance signal — the vector-leg relevance from
106
+ * `relevanceFromDistance(VECTOR_DISTANCE_METRIC, _distance)`; on unit-norm
107
+ * vectors under `l2` it lies in [0.2, 1]; absent on FTS-only rows
108
+ * (mmnto-ai/totem#2463, metric-bound in mmnto-ai/totem#2738). Distinct from
107
109
  * `score`, which in hybrid/federated modes is an RRF rank artifact
108
110
  * (`1/(60+rank)` ≈ 0.016) that carries ordering but destroys the relevance
109
111
  * magnitude. Populated by `rowToSearchResult` for vector-derived rows and
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}