mirror of
https://github.com/zooai/brain.git
synced 2026-10-30 14:50:48 +00:00
Studied fortemi/fortemi @ v2026.5.0 and produced PARITY.md — every
algorithm Fortémi ships has a row mapping to a Hanzo destination
(this repo, a sibling hanzo/* repo, or a tracked planned module).
Landed under packages/memory/, all backed by 21 passing tests in
algorithms.test.ts:
- fusion.ts RRF (k=20 BEIR), RSF, adaptive k, adaptive weights
- rerank.ts Maximal Marginal Relevance (Carbonell & Goldstein 1998)
- dedup.ts chunk-aware deduplication (best per chain)
- script.ts Unicode script detection — Latin/CJK/emoji/Cyrillic/
Arabic/Hebrew/Greek/Devanagari
- embed.ts embedding model registry, asymmetric E5 prefixes,
Matryoshka truncation, coarse-dim picker
- temporal.ts UUIDv7 floor/ceiling + named ranges
- captions.ts WebVTT / SRT / RTTM renderers
Exposed from @hanzo/bot-memory v1.4.3.
Remaining gaps tracked in PARITY.md: FTS feature flags, federated search,
runtime config hot-swap, capabilities/task selector, eval harness, SKOS,
FAIR export, SNN/PFNET/Louvain graph maintenance, LLM-typed graph links,
extraction registry — plus sibling-repo wire-ups (search/vector/crypto/
age/replicate/insights/agents).
82 lines
2.3 KiB
TypeScript
82 lines
2.3 KiB
TypeScript
/**
|
||
* Maximal Marginal Relevance reranker.
|
||
*
|
||
* Carbonell & Goldstein (1998):
|
||
* MMR = argmax_{d ∈ R\S} [ λ·Sim(d, q) − (1−λ)·max_{d' ∈ S} Sim(d, d') ]
|
||
*
|
||
* Use after fusion to diversify the top-k. λ=1.0 collapses to pure
|
||
* relevance; λ=0.0 maximizes diversity.
|
||
*/
|
||
import type { SearchHit } from "./index.js";
|
||
|
||
/** Cosine similarity for unit-or-non-unit vectors. */
|
||
export function cosine(a: number[], b: number[]): number {
|
||
if (a.length !== b.length) return 0;
|
||
let dot = 0, na = 0, nb = 0;
|
||
for (let i = 0; i < a.length; i++) {
|
||
dot += a[i] * b[i];
|
||
na += a[i] * a[i];
|
||
nb += b[i] * b[i];
|
||
}
|
||
const denom = Math.sqrt(na) * Math.sqrt(nb);
|
||
return denom > 0 ? dot / denom : 0;
|
||
}
|
||
|
||
export interface MmrInput extends SearchHit {
|
||
/** Hit-level embedding; if absent the hit is treated as maximally diverse. */
|
||
embedding?: number[];
|
||
}
|
||
|
||
export interface MmrOptions {
|
||
/** Diversity tradeoff. 1.0 = pure relevance, 0.0 = pure diversity. */
|
||
lambda?: number;
|
||
/** Top-k after rerank. */
|
||
limit?: number;
|
||
}
|
||
|
||
/**
|
||
* Greedy MMR. Returns up to `limit` hits ordered by MMR score.
|
||
* Items without `embedding` are appended after all embedded items in
|
||
* original order.
|
||
*/
|
||
export function mmrRerank(hits: MmrInput[], opts: MmrOptions = {}): MmrInput[] {
|
||
const lambda = opts.lambda ?? 0.5;
|
||
const limit = opts.limit ?? hits.length;
|
||
|
||
const embedded = hits.filter((h) => h.embedding && h.embedding.length > 0);
|
||
const orphan = hits.filter((h) => !h.embedding || h.embedding.length === 0);
|
||
|
||
const selected: MmrInput[] = [];
|
||
const candidates = [...embedded];
|
||
|
||
while (selected.length < limit && candidates.length > 0) {
|
||
let bestIdx = -1;
|
||
let bestScore = -Infinity;
|
||
|
||
for (let i = 0; i < candidates.length; i++) {
|
||
const c = candidates[i];
|
||
const rel = c.score;
|
||
let maxSim = 0;
|
||
for (const s of selected) {
|
||
const sim = cosine(c.embedding!, s.embedding!);
|
||
if (sim > maxSim) maxSim = sim;
|
||
}
|
||
const mmr = lambda * rel - (1 - lambda) * maxSim;
|
||
if (mmr > bestScore) {
|
||
bestScore = mmr;
|
||
bestIdx = i;
|
||
}
|
||
}
|
||
|
||
if (bestIdx < 0) break;
|
||
selected.push(candidates.splice(bestIdx, 1)[0]);
|
||
}
|
||
|
||
for (const o of orphan) {
|
||
if (selected.length >= limit) break;
|
||
selected.push(o);
|
||
}
|
||
|
||
return selected;
|
||
}
|