| from __future__ import annotations |
|
|
| import re |
| from typing import Any |
|
|
| from src.embeddings import EmbeddingModel |
| from src.vector_store import EbmVectorStore, RetrievalResult |
|
|
|
|
| CODE_PATTERN = re.compile(r"\b\d{5}\b") |
|
|
|
|
| class EbmRetriever: |
| def __init__(self, store: EbmVectorStore, embedding_model: EmbeddingModel | None = None): |
| self.store = store |
| self.embedding_model = embedding_model or EmbeddingModel(store.embedding_model_name) |
|
|
| def retrieve(self, query: str, top_k: int = 5, chapter: str | None = None) -> list[dict[str, Any]]: |
| if not query.strip(): |
| return [] |
|
|
| embeddings = self.embedding_model.encode([query]) |
| results = self.store.search(embeddings, top_k=top_k * 3 if chapter and chapter != "All" else top_k) |
|
|
| payloads = [self._to_payload(result) for result in results] |
| if chapter and chapter != "All": |
| payloads = [item for item in payloads if item.get("chapter_name") == chapter] |
| return payloads[:top_k] |
|
|
| def get_by_code(self, code: str) -> dict[str, Any] | None: |
| code = code.strip() |
| for doc in self.store.documents: |
| if str(doc.get("code") or "") == code: |
| return dict(doc) |
| return None |
|
|
| def random_document(self) -> dict[str, Any]: |
| import random |
|
|
| if not self.store.documents: |
| raise ValueError("No documents available.") |
| return dict(random.choice(self.store.documents)) |
|
|
| def list_chapters(self) -> list[str]: |
| chapters = sorted( |
| { |
| str(doc.get("chapter_name")) |
| for doc in self.store.documents |
| if doc.get("chapter_name") |
| } |
| ) |
| return chapters |
|
|
| def search(self, query: str, top_k: int = 10, chapter: str | None = None) -> list[dict[str, Any]]: |
| return self.retrieve(query=query, top_k=top_k, chapter=chapter) |
|
|
| def code_from_text(self, text: str) -> str | None: |
| match = CODE_PATTERN.search(text or "") |
| return match.group(0) if match else None |
|
|
| @staticmethod |
| def _to_payload(result: RetrievalResult) -> dict[str, Any]: |
| payload = dict(result.structured) |
| payload["score"] = result.score |
| payload["title"] = result.title |
| payload["text"] = result.text |
| payload["confidence"] = max(0.0, min(1.0, (result.score + 1.0) / 2.0)) |
| payload["exclusions_text"] = [ |
| item.get("code") |
| for item in payload.get("exclusions", []) |
| if isinstance(item, dict) and item.get("code") |
| ] |
| return payload |
|
|