""" Knowledge Repository Handles database operations for knowledge items, documents, and code examples. """ from typing import Any from ...config.logfire_config import safe_logfire_error class KnowledgeRepository: """ Repository for database operations related to knowledge items. """ def __init__(self, supabase_client): self.supabase = supabase_client async def get_document_counts_batch(self, source_ids: list[str]) -> dict[str, int]: """Get document counts for multiple sources.""" try: counts = {} for source_id in source_ids: result = ( self.supabase.from_("archon_crawled_pages") .select("id", count="exact", head=True) .eq("source_id", source_id) .execute() ) counts[source_id] = result.count if hasattr(result, "count") else 0 return counts except Exception as e: safe_logfire_error(f"Failed to get document counts | error={str(e)}") return dict.fromkeys(source_ids, 0) async def get_code_example_counts_batch(self, source_ids: list[str]) -> dict[str, int]: """Get code example counts for multiple sources.""" try: counts = {} for source_id in source_ids: result = ( self.supabase.from_("archon_code_examples") .select("id", count="exact", head=True) .eq("source_id", source_id) .execute() ) counts[source_id] = result.count if hasattr(result, "count") else 0 return counts except Exception as e: safe_logfire_error(f"Failed to get code example counts | error={str(e)}") return dict.fromkeys(source_ids, 0) async def get_first_urls_batch(self, source_ids: list[str]) -> dict[str, str]: """Get first URL for each source in a batch.""" try: result = ( self.supabase.from_("archon_crawled_pages") .select("source_id, url") .in_("source_id", source_ids) .order("created_at", desc=False) .execute() ) urls = {} for item in result.data or []: source_id = item["source_id"] if source_id not in urls: urls[source_id] = item["url"] for source_id in source_ids: if source_id not in urls: urls[source_id] = f"source://{source_id}" return urls except Exception as e: safe_logfire_error(f"Failed to get first URLs | error={str(e)}") return {sid: f"source://{sid}" for sid in source_ids} async def get_item_chunks( self, source_id: str, page: int = 1, per_page: int = 50, domain_filter: str | None = None ) -> tuple[bool, dict[str, Any]]: """Get document chunks for a specific knowledge item with pagination and filtering.""" try: per_page = max(1, min(per_page, 100)) page = max(1, page) query = self.supabase.from_("archon_crawled_pages").select( "id, source_id, content, metadata, url", count="exact" ) query = query.eq("source_id", source_id) if domain_filter: query = query.ilike("url", f"%{domain_filter}%") offset = (page - 1) * per_page query = query.order("url", desc=False).order("id", desc=False) query = query.range(offset, offset + per_page - 1) result = query.execute() if getattr(result, "error", None): return False, {"error": str(result.error)} chunks = result.data if result.data else [] total_count = result.count if hasattr(result, "count") and result.count is not None else len(chunks) return True, { "chunks": chunks, "pagination": { "total": total_count, "page": page, "per_page": per_page, "total_pages": (total_count + per_page - 1) // per_page if total_count > 0 else 0, }, } except Exception as e: safe_logfire_error(f"Failed to fetch chunks | source_id={source_id} | error={str(e)}") return False, {"error": str(e)}