Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from supabase import create_client, Client | |
| from datetime import datetime | |
| import os | |
| def _client() -> Client: | |
| return create_client(os.environ["SUPABASE_URL"], os.environ["SUPABASE_KEY"]) | |
| # ββ ChunkResult: rich return type for similarity search ββββββββββββββββββββββ | |
| class ChunkResult: | |
| """Holds chunk text, its page of origin, and source document.""" | |
| __slots__ = ("text", "page_number", "doc_id") | |
| def __init__(self, text: str, page_number: int, doc_id: str): | |
| self.text = text | |
| self.page_number = page_number | |
| self.doc_id = doc_id | |
| # Behaves like a plain string so old code that does `"\n".join(chunks)` still works | |
| def __str__(self) -> str: return self.text | |
| def __repr__(self) -> str: return f"ChunkResult(doc={self.doc_id[:8]}, page={self.page_number})" | |
| def store_chunks( | |
| doc_id: str, | |
| user_id: str, | |
| chunks, # list[ChunkMeta] or list[str] | |
| embeddings: list[list[float]], | |
| expires_at: datetime, | |
| ) -> None: | |
| client = _client() | |
| rows = [] | |
| for i, (chunk, embedding) in enumerate(zip(chunks, embeddings)): | |
| # Support both ChunkMeta objects (with .text/.page_number) and plain strings | |
| text = chunk.text if hasattr(chunk, "text") else str(chunk) | |
| page_number = chunk.page_number if hasattr(chunk, "page_number") else 1 | |
| rows.append({ | |
| "doc_id": doc_id, | |
| "user_id": user_id, | |
| "chunk_text": text, | |
| "embedding": embedding, | |
| "chunk_index": i, | |
| "page_number": page_number, | |
| "expires_at": expires_at.isoformat(), | |
| }) | |
| # Insert in batches of 100 to avoid payload limits | |
| for i in range(0, len(rows), 100): | |
| client.table("chunks").insert(rows[i : i + 100]).execute() | |
| def similarity_search( | |
| doc_id: str, | |
| query_embedding: list[float], | |
| top_k: int = 5, | |
| ) -> list[ChunkResult]: | |
| """Search a single document and return rich ChunkResult objects.""" | |
| client = _client() | |
| result = client.rpc( | |
| "match_chunks", | |
| { | |
| "query_embedding": query_embedding, | |
| "doc_id_filter": doc_id, | |
| "match_count": top_k, | |
| }, | |
| ).execute() | |
| return [ | |
| ChunkResult( | |
| text = r["chunk_text"], | |
| page_number = r.get("page_number", 1), | |
| doc_id = str(r.get("doc_id", doc_id)), | |
| ) | |
| for r in result.data | |
| ] | |
| def similarity_search_multi( | |
| doc_ids: list[str], | |
| query_embedding: list[float], | |
| top_k: int = 20, | |
| ) -> list[ChunkResult]: | |
| """Search across multiple documents and return rich ChunkResult objects.""" | |
| if not doc_ids: | |
| return [] | |
| if len(doc_ids) == 1: | |
| return similarity_search(doc_ids[0], query_embedding, top_k) | |
| client = _client() | |
| result = client.rpc( | |
| "match_chunks_multi", | |
| { | |
| "query_embedding": query_embedding, | |
| "doc_ids_filter": doc_ids, | |
| "match_count": top_k, | |
| }, | |
| ).execute() | |
| return [ | |
| ChunkResult( | |
| text = r["chunk_text"], | |
| page_number = r.get("page_number", 1), | |
| doc_id = str(r["doc_id"]), | |
| ) | |
| for r in result.data | |
| ] | |
| def get_all_chunks(doc_id: str) -> list[ChunkResult]: | |
| """Return every chunk for a document in order, for full-context retrieval.""" | |
| client = _client() | |
| result = ( | |
| client.table("chunks") | |
| .select("chunk_text, page_number, doc_id") | |
| .eq("doc_id", doc_id) | |
| .order("chunk_index", desc=False) | |
| .execute() | |
| ) | |
| return [ | |
| ChunkResult( | |
| text = r["chunk_text"], | |
| page_number = r.get("page_number", 1), | |
| doc_id = str(r.get("doc_id", doc_id)), | |
| ) | |
| for r in result.data | |
| ] | |
| def get_all_chunks_multi(doc_ids: list[str]) -> list[ChunkResult]: | |
| """Return all chunks for multiple documents in document+chunk order.""" | |
| if not doc_ids: | |
| return [] | |
| client = _client() | |
| result = ( | |
| client.table("chunks") | |
| .select("chunk_text, page_number, doc_id, chunk_index") | |
| .in_("doc_id", doc_ids) | |
| .order("doc_id", desc=False) | |
| .order("chunk_index", desc=False) | |
| .execute() | |
| ) | |
| return [ | |
| ChunkResult( | |
| text = r["chunk_text"], | |
| page_number = r.get("page_number", 1), | |
| doc_id = str(r["doc_id"]), | |
| ) | |
| for r in result.data | |
| ] | |