Spaces:
Paused
Paused
| from __future__ import annotations | |
| import pytest | |
| from auralynq.embeddings import get_embedder | |
| from auralynq.ingest.models import Chunk, SourceType | |
| from auralynq.retrieval.hybrid.fusion import reciprocal_rank_fusion | |
| from auralynq.retrieval.hybrid.mmr import mmr_rerank | |
| from auralynq.retrieval.hybrid.reorder import lost_in_the_middle_reorder | |
| from auralynq.retrieval.hybrid.rerank import LexicalReranker | |
| from auralynq.retrieval.hybrid.retriever import HybridRetriever | |
| from auralynq.retrieval.models import ScoredChunk | |
| from auralynq.retrieval.naive import NaiveRetriever | |
| from auralynq.retrieval.router import Route, route_query | |
| from auralynq.vectorstore.memory_store import MemoryStore | |
| def store(tmp_path, sample_chunks): | |
| emb = get_embedder() | |
| s = MemoryStore(path=tmp_path / "ms") | |
| s.upsert(sample_chunks, emb.embed([c.text for c in sample_chunks])) | |
| return s | |
| def _sc(cid, text, score): | |
| return ScoredChunk( | |
| chunk=Chunk(id=cid, doc_id="d", text=text, source_type=SourceType.text), score=score | |
| ) | |
| def test_rrf_combines_rankings(): | |
| a = [_sc("1", "x", 0.9), _sc("2", "y", 0.5)] | |
| b = [_sc("2", "y", 0.8), _sc("3", "z", 0.4)] | |
| fused = reciprocal_rank_fusion([a, b], k=60) | |
| ids = [c.chunk.id for c in fused] | |
| assert ids[0] == "2" # appears high in both | |
| assert set(ids) == {"1", "2", "3"} | |
| def test_reorder_places_best_at_edges(): | |
| chunks = [_sc(str(i), f"t{i}", 1.0 - i * 0.1) for i in range(5)] | |
| out = lost_in_the_middle_reorder(chunks) | |
| assert out[0].chunk.id == "0" # most relevant at the front | |
| assert out[-1].chunk.id == "1" # second most relevant at the end | |
| def test_mmr_reduces_redundancy(): | |
| emb = get_embedder() | |
| cands = [ | |
| _sc("a", "flow pruning paths", 1.0), | |
| _sc("b", "flow pruning paths", 0.9), | |
| _sc("c", "paris france capital", 0.8), | |
| ] | |
| q = emb.embed_query("flow pruning") | |
| out = mmr_rerank(q.dense, cands, emb, k=2, lambda_mult=0.5) | |
| ids = {c.chunk.id for c in out} | |
| assert "c" in ids # diversity pulls in the dissimilar chunk | |
| def test_mmr_caches_candidate_embeddings(): | |
| # Repeated MMR over the same candidate texts must not re-embed them: a perf | |
| # guard for the bounded per-text embedding cache. | |
| import numpy as np | |
| from auralynq.embeddings.base import EmbeddingBatch | |
| class CountingEmbedder: | |
| model = "counting" | |
| def __init__(self) -> None: | |
| self.embedded = 0 | |
| def embed(self, texts: list[str]) -> EmbeddingBatch: | |
| self.embedded += len(texts) | |
| rng = np.random.default_rng(7) | |
| return EmbeddingBatch( | |
| dense=rng.standard_normal((len(texts), 8)).astype("float32"), | |
| sparse=[{} for _ in texts], | |
| ) | |
| emb = CountingEmbedder() | |
| cands = [_sc("a", "alpha beta", 1.0), _sc("b", "gamma delta", 0.9)] | |
| q = np.zeros(8, dtype="float32") | |
| mmr_rerank(q, cands, emb, k=2) # first pass embeds both | |
| assert emb.embedded == 2 | |
| mmr_rerank(q, cands, emb, k=2) # second pass fully cached | |
| assert emb.embedded == 2 | |
| def test_lexical_reranker_orders_by_overlap(): | |
| rr = LexicalReranker() | |
| chunks = [_sc("a", "the cat sat", 0.1), _sc("b", "flow based pruning of paths", 0.1)] | |
| out = rr.rerank("flow pruning paths", chunks, k=2) | |
| assert out[0].chunk.id == "b" | |
| def test_naive_retriever(store): | |
| r = NaiveRetriever(store=store) | |
| res = r.retrieve("flow based pruning", k=3) | |
| assert res.method == "naive" | |
| assert res.chunks | |
| def test_hybrid_retriever_pipeline(store): | |
| r = HybridRetriever(store=store) | |
| res = r.retrieve("flow based pruning of relational paths", k=3) | |
| assert res.method == "hybrid" | |
| assert 0 < len(res.chunks) <= 3 | |
| assert "rrf" in res.metadata | |
| assert res.metadata.get("reorder") == "lost_in_the_middle" | |
| def test_router_simple_vs_relational(): | |
| simple = route_query("What is Paris?") | |
| assert simple.route == Route.fast | |
| relational = route_query( | |
| "How does the relationship between Paris and France connect through Europe?" | |
| ) | |
| assert relational.route in (Route.graph, Route.hybrid) | |