{"version":3,"file":"helpers.mjs","names":[],"sources":["../../../src/batteries/vector/helpers.ts"],"sourcesContent":["/**\n * Helper utilities for the vector storage provider battery.\n *\n * @module @nhtio/adk/batteries/vector/helpers\n */\n\nimport type { DistanceMetric } from './types'\n\n/** Whether a raw backend score is a similarity (higher = closer) or a distance (lower = closer). */\nexport type ScoreKind = 'similarity' | 'distance'\n\n/** Clamp `n` into the inclusive `[0, 1]` range. */\nexport const clamp01 = (n: number): number => Math.min(1, Math.max(0, n))\n\n/**\n * Normalize a backend's raw score into a `[0, 1]` similarity where higher is closer, accounting for\n * the distance metric and whether the raw value is a similarity or a distance.\n *\n * @param raw - The backend's raw score.\n * @param metric - The distance metric the score was computed under.\n * @param kind - Whether `raw` is a similarity or a distance.\n * @returns The normalized similarity in `[0, 1]`.\n */\nexport const normalizeScore = (raw: number, metric: DistanceMetric, kind: ScoreKind): number => {\n  if (metric === 'cosine') {\n    if (kind === 'similarity') {\n      return clamp01((raw + 1) / 2)\n    } else {\n      const sim = 1 - raw\n      return clamp01((sim + 1) / 2)\n    }\n  } else if (metric === 'dot') {\n    if (kind === 'similarity') {\n      return clamp01(1 / (1 + Math.exp(-raw)))\n    } else {\n      return clamp01(1 / (1 + Math.exp(raw)))\n    }\n  } else if (metric === 'euclidean') {\n    const d = Math.max(0, raw)\n    return clamp01(1 / (1 + d))\n  } else {\n    return clamp01(raw)\n  }\n}\n\n/** Look up the adapter-specific value for `metric` in a complete metric→value map. */\nexport const mapMetric = <T>(metric: DistanceMetric, map: Record<DistanceMetric, T>): T =>\n  map[metric]\n\n/** `true` if `vector`'s length matches `expected` (or `expected` is undefined). */\nexport const dimensionsMatch = (vector: number[], expected: number | undefined): boolean =>\n  expected === undefined ? true : vector.length === expected\n\n/** `true` if `vector` is an array of finite numbers (no NaN/Infinity). */\nexport const isFiniteVector = (vector: number[]): boolean =>\n  Array.isArray(vector) && vector.every((n) => typeof n === 'number' && Number.isFinite(n))\n\n/** Metadata keys that resolve through the prototype chain rather than an own property. */\nconst UNSAFE_METADATA_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\n/**\n * Strip `__proto__`/`prototype`/`constructor` keys from caller-supplied metadata before an\n * adapter spreads or assigns it onto a record bound for storage. Every adapter's own-object\n * targets (`{}` literals) are already immune to prototype reassignment, but a stray\n * `__proto__`-keyed record would otherwise round-trip verbatim through storage and back out\n * to a caller — this keeps the stored shape honest regardless.\n */\nexport const sanitizeMetadata = <T extends Record<string, unknown> | undefined>(metadata: T): T => {\n  if (!metadata) return metadata\n  let clean: Record<string, unknown> | undefined\n  for (const key of Object.keys(metadata)) {\n    if (UNSAFE_METADATA_KEYS.has(key)) {\n      clean ??= { ...metadata }\n      delete clean[key]\n    }\n  }\n  return (clean ?? metadata) as T\n}\n"],"mappings":";;AAYA,IAAa,WAAW,MAAsB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;;;;;;;;;;AAWxE,IAAa,kBAAkB,KAAa,QAAwB,SAA4B;CAC9F,IAAI,WAAW,UACb,IAAI,SAAS,cACX,OAAO,SAAS,MAAM,KAAK,CAAC;MAG5B,OAAO,SADK,IAAI,MACM,KAAK,CAAC;MAEzB,IAAI,WAAW,OACpB,IAAI,SAAS,cACX,OAAO,QAAQ,KAAK,IAAI,KAAK,IAAI,CAAC,GAAG,EAAE;MAEvC,OAAO,QAAQ,KAAK,IAAI,KAAK,IAAI,GAAG,EAAE;MAEnC,IAAI,WAAW,aAEpB,OAAO,QAAQ,KAAK,IADV,KAAK,IAAI,GAAG,GACE,EAAE;MAE1B,OAAO,QAAQ,GAAG;AAEtB;;AAGA,IAAa,aAAgB,QAAwB,QACnD,IAAI;;AAGN,IAAa,mBAAmB,QAAkB,aAChD,aAAa,KAAA,IAAY,OAAO,OAAO,WAAW;;AAGpD,IAAa,kBAAkB,WAC7B,MAAM,QAAQ,MAAM,KAAK,OAAO,OAAO,MAAM,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,CAAC;;AAG1F,IAAM,uBAAuB,IAAI,IAAI;CAAC;CAAa;CAAa;AAAa,CAAC;;;;;;;;AAS9E,IAAa,oBAAmE,aAAmB;CACjG,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI;CACJ,KAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,GACpC,IAAI,qBAAqB,IAAI,GAAG,GAAG;EACjC,UAAU,EAAE,GAAG,SAAS;EACxB,OAAO,MAAM;CACf;CAEF,OAAQ,SAAS;AACnB"}