Files
stack/tests/llm/test_rerank.py

86 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""llm.rerank — similarity × recency blend, pure."""
from datetime import date
import pytest
from llm.rerank import Hit, blend, filter_since, recency
NOW = date(2026, 9, 3)
def _hit(key, distance, when, kind="comment"):
return Hit(
text=f"text {key}",
metadata={"item_key": key, "date": when, "kind": kind},
distance=distance,
)
class TestRecency:
def test_today_is_one(self):
assert recency("2026-09-03", now=NOW, half_life_days=365) == pytest.approx(1.0)
def test_one_half_life_is_half(self):
assert recency("2025-09-03", now=NOW, half_life_days=365) == pytest.approx(0.5)
def test_future_dates_clamp_to_one(self):
assert recency("2027-01-01", now=NOW, half_life_days=365) == 1.0
def test_undated_or_garbage_is_zero(self):
assert recency("", now=NOW, half_life_days=365) == 0.0
assert recency("not a date", now=NOW, half_life_days=365) == 0.0
def test_datetime_prefix_accepted(self):
assert recency("2026-09-03T12:00:00Z", now=NOW, half_life_days=365) == 1.0
class TestBlend:
def test_recent_beats_slightly_closer_old_hit(self):
old = _hit("OLD", distance=0.20, when="2019-01-01")
new = _hit("NEW", distance=0.25, when="2026-08-19")
out = blend([old, new], weight=0.3, half_life_days=365, now=NOW, top_n=8)
assert [h.metadata["item_key"] for h in out] == ["NEW", "OLD"]
assert out[0].score > out[1].score
def test_weight_zero_is_pure_similarity(self):
old = _hit("OLD", distance=0.20, when="2019-01-01")
new = _hit("NEW", distance=0.25, when="2026-08-19")
out = blend([old, new], weight=0.0, half_life_days=365, now=NOW, top_n=8)
assert [h.metadata["item_key"] for h in out] == ["OLD", "NEW"]
def test_dedupes_per_item_keeping_best_chunk(self):
a1 = _hit("A", distance=0.30, when="2026-01-01")
a2 = _hit("A", distance=0.10, when="2026-01-01")
b = _hit("B", distance=0.20, when="2026-01-01")
out = blend([a1, a2, b], weight=0.3, half_life_days=365, now=NOW, top_n=8)
assert [(h.metadata["item_key"], h.distance) for h in out] == [
("A", 0.10),
("B", 0.20),
]
def test_top_n_truncates(self):
hits = [_hit(f"K{i}", 0.1 * i, "2026-01-01") for i in range(5)]
assert len(blend(hits, weight=0.3, half_life_days=365, now=NOW, top_n=2)) == 2
def test_distance_clamped_into_unit_range(self):
far = _hit("F", distance=1.7, when="")
(out,) = blend([far], weight=0.0, half_life_days=365, now=NOW, top_n=1)
assert out.score == 0.0
class TestFilterSince:
def test_keeps_on_or_after_and_drops_undated(self):
hits = [
_hit("A", 0.1, "2025-12-31"),
_hit("B", 0.1, "2026-01-01"),
_hit("C", 0.1, ""),
]
assert [h.metadata["item_key"] for h in filter_since(hits, "2026-01-01")] == [
"B"
]
def test_empty_since_is_noop(self):
hits = [_hit("A", 0.1, ""), _hit("B", 0.1, "2020-01-01")]
assert filter_since(hits, "") == hits