@ultimat3/cache 28.0.0 → 29.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.
package/README.md CHANGED
@@ -430,6 +430,13 @@ is a driver seam for that reason; a Postgres/pgvector-backed implementation does
430
430
  yet here (`@ultimat3/ai`'s `postgresVectorStore()` is a separate store, for RAG retrieval, not this
431
431
  cache).
432
432
 
433
+ A similarity that is not a number is not a match. A pair with no defined cosine — one side
434
+ carrying a `NaN` component, or a dot product and norms that overflow together — makes
435
+ `cosineSimilarity` answer `NaN`: honestly, rather than a `0` that would rank a directionless
436
+ vector above every opposed one. The lookup SKIPS such a record: it misses, rather than answering
437
+ one question with another's entry. Every comparison against `NaN` is false, so a record that
438
+ reached the floor unchecked would have passed it, at any floor — `0` included.
439
+
433
440
  ## Errors
434
441
 
435
442
  | Code | Cause |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/cache",
3
- "version": "28.0.0",
3
+ "version": "29.1.0",
4
4
  "description": "Tagged caching: request memo, LRU, Redis, CDN — one invalidation graph",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,13 +25,13 @@
25
25
  "LICENSE"
26
26
  ],
27
27
  "engines": {
28
- "bun": ">=1.4.3"
28
+ "bun": ">=1.4.4"
29
29
  },
30
30
  "scripts": {
31
31
  "typecheck": "tsc --noEmit -p tsconfig.json",
32
32
  "test": "bun test"
33
33
  },
34
34
  "dependencies": {
35
- "@ultimat3/core": "28.0.0"
35
+ "@ultimat3/core": "29.1.0"
36
36
  }
37
37
  }
package/src/semantic.ts CHANGED
@@ -54,6 +54,17 @@ export interface SemanticCacheOptions {
54
54
  readonly clock?: Clock;
55
55
  }
56
56
 
57
+ /**
58
+ * Cosine similarity — and the honest answer for a pair that has none: a `NaN` component, or a
59
+ * dot product and norms that overflow together (`1e200` on both sides), leaves the cosine
60
+ * UNDEFINED, and this answers `NaN` rather than a `0` that would rank a vector with no direction
61
+ * above every opposed one (the rule `@ultimat3/ai`'s `cosine` keeps on purpose).
62
+ *
63
+ * `NaN` is not a verdict — the CALLER decides what a nonfinite similarity means. `lookup` skips
64
+ * a record whose similarity is not finite: it is not a candidate, so the entry cannot answer a
65
+ * question it was never asked. A finite similarity is the floor's business, as any other is — a
66
+ * finite dot over an overflowing norm is the `0`, and the floor decides it.
67
+ */
57
68
  export function cosineSimilarity(a: Embedding, b: Embedding): number {
58
69
  if (a.length !== b.length || a.length === 0) return 0;
59
70
  let dot = 0;
@@ -114,6 +125,11 @@ export function memorySemanticCache(options: SemanticCacheOptions = {}): Semanti
114
125
  let best: SemanticHit<T> | undefined;
115
126
  for (const record of live()) {
116
127
  const similarity = cosineSimilarity(embedding, record.embedding);
128
+ // A similarity that is not a number is not a match. Every comparison against `NaN` is
129
+ // false, so a broken vector SKIPPED the floor below, became `best`, and the cache
130
+ // answered a question it was never asked — a worse failure than a miss, because nothing
131
+ // said so. The floor did not fail to hold; the comparison against it never happened.
132
+ if (!Number.isFinite(similarity)) continue;
117
133
  if (similarity < floor) continue;
118
134
  if (best === undefined || similarity > best.similarity) {
119
135
  best = { value: record.value as T, similarity, key: record.key };