feat(semantle,doantu): calibrate cosine score via normalized sigmoid

BGE embeddings occupy a narrow cone in vector space, so raw cosine of
two unrelated words already sits at ~0.40-0.55. Displaying `raw * 100`
made every random guess read as 40-70% warm, which defeated the warmth
UX.

format.js now applies a normalized sigmoid (FLOOR 0.40, CENTER 0.60,
SCALE 8) to remap raw cosine → displayed 0-100. Unrelated pairs drop
to ≤30, loose relation lands around 40-55, clear synonyms hit 85+, and
exact match stays at 100. Emoji buckets were rebased onto the calibrated
score; formatWarmth lost its sign column (calibrated output is always
non-negative).

render.js rounds once and feeds the integer to both formatWarmth and
warmthEmoji so the display value and bucket stay in sync.

Constants are empirical — retune if swapping to a non-BGE model.
This commit is contained in:
2026-04-23 00:33:54 +07:00
parent 4f7f6896c5
commit fd5a1d2903
9 changed files with 370 additions and 90 deletions
+2 -1
View File
@@ -113,7 +113,8 @@ describe("semantle/handlers", () => {
expect(ctx.reply).toHaveBeenCalledOnce();
expect(ctx.replies[0].text).toContain("orange");
expect(ctx.replies[0].text).toContain("+45");
// raw 0.45 is below FLOOR of 0.40 (just barely above) → calibrate ≈ 08
expect(ctx.replies[0].text).toContain("08");
});
it("solves when guess equals target (case-insensitive)", async () => {