"""Catalog domain — the unified read/write API for RMI. T27 of the v4.0 guide. Every store read/write goes through CatalogService. Domain facades call the catalog; they never touch stores directly. Architecture (per v4.0 §T27): app/catalog/models.py Pydantic v2 entity schemas app/catalog/service.py CatalogService — fan-out reads, fan-in writes app/catalog/reputation.py Deployer reputation scoring (T31) app/catalog/rag_bridge.py Bridge to existing app/rag/ engine app/catalog/llm_router.py LiteLLM proxy for AI analysis Cross-store ref shape (per v4.0): "chain:address" Wallet, Token, Contract IDs UUID Entity, Alert, NewsItem, RAGFinding, Report IDs Qdrant point_id RAGFinding.vector_id, rag_embedding_id on Token Public API (re-exported): CatalogService, get_catalog Entity, Wallet, Deployer, Token, Alert, NewsItem, RAGFinding, ScanReport DeployerReputation, RECIPES """ from __future__ import annotations from app.core import health as health_mod from app.core.health import DomainHealth async def _health_check() -> DomainHealth: """Catalog health: which stores are reachable + how many entities.""" try: from app.catalog.service import get_catalog cat = get_catalog() reach = await cat.probe_stores() healthy = any(reach.values()) return DomainHealth( name="catalog", healthy=healthy, details={ "stores_reachable": {k: v for k, v in reach.items()}, "primary": "redis+rag" if healthy else "none", }, ) except Exception as e: return DomainHealth(name="catalog", healthy=False, error=str(e)) health_mod.register_health_check("catalog", _health_check) # Public API from app.catalog.models import ( # noqa: E402 COLLECTIONS, CHAIN_REGISTRY, Chain, Entity, EntityLabel, RiskTier, Token, Wallet, Deployer, Alert, NewsItem, RAGFinding, ScanReport, ) from app.catalog.service import CatalogService, get_catalog # noqa: E402 __all__ = [ "CatalogService", "get_catalog", "Entity", "Wallet", "Deployer", "Token", "Alert", "NewsItem", "RAGFinding", "ScanReport", "EntityLabel", "Chain", "RiskTier", "COLLECTIONS", "CHAIN_REGISTRY", ]