Ctrl K
ComunidadGuías…

src/lib/resume-similarity.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * How much of one sentence survives in another, 0-1.
 *
 * Dice rather than shared-over-longest, because a bullet written from a note
 * usually says more than the note did: "Ran the Postgres migration" becoming
 * "Led the Postgres migration across six services" keeps everything that
 * mattered and scores 0.43 by the longest measure.
 *
 * Deliberately not in resume-diff.ts. That module compares bullets by exact
 * string and says why — the reader sees both wordings rather than a score's
 * opinion of whether they are the same bullet. This is a different question:
 * whether two claims are the same claim.
 *
 * Pure and free of Prisma so both sides of the question can share it: tracing a
 * resume's bullets back to a person's own material (`trace_resume_evidence`)
 * and deciding whether a re-imported resume is telling us anything new
 * (`import_resume`). Two implementations of "are these the same bullet" would
 * disagree the first time one of them was tuned.
 */
/**
 * The words of one line, remembered. A trace or a usage count compares every
 * bullet of every resume with every line of evidence, and splitting both
 * strings again on each comparison was most of the work; the answer is the
 * same either way. Bounded, so a long session cannot grow it forever.
 */
const TOKEN_CACHE = new Map<string, Set<string>>();
function tokens(value: string): Set<string> {
  const known = TOKEN_CACHE.get(value);
  if (known) return known;
  const found = new Set(
    value
      .toLowerCase()
      .split(/[^a-z0-9+#.]+/)
      .filter((token) => token.length > 1),
  );
  if (TOKEN_CACHE.size >= 5000) TOKEN_CACHE.clear();
  TOKEN_CACHE.set(value, found);
  return found;
}

export function bulletSimilarity(a: string, b: string): number {
  const left = tokens(a);
  const right = tokens(b);
  if (left.size === 0 || right.size === 0) return 0;
  let shared = 0;
  for (const token of left) if (right.has(token)) shared += 1;
  return (2 * shared) / (left.size + right.size);
}

/**
 * Above this, two bullets are the same bullet reworded.
 *
 * Tuned for the import, where the cost of the two errors is lopsided: a
 * duplicate bullet on a role is visible clutter a person has to delete, while a
 * missed one is a line they can add back from the document they still have. So
 * this leans toward treating a near-match as already on file.
 */
export const SAME_BULLET = 0.72;