"""Source adapters for AI Research Oracle.""" import json import os from abc import ABC, abstractmethod # Centralized curation config (issue #7). One file, per-adapter blocks. # Stdlib-only (JSON, not YAML) to honor Athena's dependency-free runtime. _QUERIES_PATH = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "config", "queries.json") def load_queries(): """Load config/queries.json. Returns {'sources': {...}}. Safe fallback: if the file is missing/corrupt, returns an empty {'sources': {}} so adapters fall back to their class defaults (constructor None-override) instead of crashing the pipeline. """ try: with open(_QUERIES_PATH) as f: data = json.load(f) return data if isinstance(data, dict) else {"sources": {}} except Exception: return {"sources": {}} def source_config(name: str) -> dict: """Return the per-adapter block for `name`, or {} if absent.""" return load_queries().get("sources", {}).get(name, {}) or {} class SourceAdapter(ABC): """Base class for all ingestion adapters.""" @abstractmethod def name(self) -> str: """Source name: 'github', 'arxiv', 'reddit'.""" pass @abstractmethod def fetch(self, query: str = "", limit: int = 20) -> list[dict]: """Return entries matching DB schema fields.""" pass