Spaces:
Sleeping
Sleeping
| """ChromaDB-backed vector store (persistent on disk, cosine space). | |
| Collection-generic: the triage app keeps two collections — the policy rules and past | |
| adjudicated cases — so every function takes a collection name. We supply our own embeddings | |
| (no Chroma embedding function); `search()` converts cosine distance to similarity via 1 - dist. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| from dataclasses import dataclass, field | |
| from functools import lru_cache | |
| from .config import CONFIG | |
| from .embed import embed_query, embed_texts | |
| class Retrieved: | |
| text: str | |
| score: float | |
| metadata: dict = field(default_factory=dict) | |
| def _client(): | |
| # One PersistentClient per process. Re-instantiating Chroma's client against the same | |
| # path repeatedly corrupts its shared-system-client lifecycle (tenant / RustBindings | |
| # errors), which surfaced as flaky failures across the agent's many tool calls. | |
| import chromadb | |
| os.makedirs(CONFIG.chroma_dir, exist_ok=True) | |
| return chromadb.PersistentClient(path=CONFIG.chroma_dir) | |
| def get_collection(name: str): | |
| return _client().get_or_create_collection( | |
| name=name, | |
| metadata={"hnsw:space": "cosine"}, | |
| ) | |
| def index_documents(collection: str, ids, texts, metadatas, batch_size: int = 128) -> int: | |
| """Embed and upsert documents into a named collection. Returns count indexed.""" | |
| ids, texts, metadatas = list(ids), list(texts), list(metadatas) | |
| col = get_collection(collection) | |
| for i in range(0, len(texts), batch_size): | |
| sl = slice(i, i + batch_size) | |
| col.upsert( | |
| ids=ids[sl], | |
| documents=texts[sl], | |
| embeddings=embed_texts(texts[sl]), | |
| metadatas=metadatas[sl], | |
| ) | |
| return len(texts) | |
| def count(collection: str) -> int: | |
| try: | |
| return get_collection(collection).count() | |
| except Exception: | |
| return 0 | |
| def search(collection: str, query: str, top_k: int = 5) -> list[Retrieved]: | |
| col = get_collection(collection) | |
| n = col.count() | |
| if n == 0: | |
| return [] | |
| res = col.query( | |
| query_embeddings=[embed_query(query)], | |
| n_results=min(top_k, n), | |
| include=["documents", "metadatas", "distances"], | |
| ) | |
| out: list[Retrieved] = [] | |
| for doc, meta, dist in zip(res["documents"][0], res["metadatas"][0], res["distances"][0]): | |
| out.append(Retrieved(text=doc, score=1.0 - float(dist), metadata=meta or {})) | |
| return out | |