# ============================================================ # SPID — optional FastAPI service # # uvicorn spid.api:app --reload --port 8088 # # Read-only HTTP layer over the project DB: structured search, # semantic search, project detail, analytics. # ============================================================ from __future__ import annotations try: from fastapi import FastAPI, Query except ImportError as e: # pragma: no cover raise SystemExit("FastAPI not installed. `pip install fastapi uvicorn`.") from e from spid import analytics from spid.embed import semantic_search from spid.search import get_project, search_projects from spid.store import Store app = FastAPI(title="SPID — SEC Project Intelligence", version="0.1.0") def _store() -> Store: return Store(read_only=True) @app.get("/health") def health(): s = _store() try: return {"status": "ok", "projects": s.count("projects"), "mentions": s.count("project_mentions")} finally: s.close() @app.get("/projects") def projects(q: str | None = None, type: str | None = None, sector: str | None = None, tech: str | None = None, location: str | None = None, ticker: str | None = None, min_amount: float | None = None, status: str | None = None, year_from: int | None = None, year_to: int | None = None, limit: int = Query(25, le=200)): s = _store() try: return search_projects(s, keyword=q, project_type=type, sector=sector, technology=tech, location=location, ticker=ticker, min_amount=min_amount, status=status, year_from=year_from, year_to=year_to, limit=limit) finally: s.close() @app.get("/projects/{project_id}") def project_detail(project_id: str): s = _store() try: return get_project(s, project_id) or {"error": "not found"} finally: s.close() @app.get("/semantic") def semantic(q: str, limit: int = Query(10, le=100)): s = _store() try: return semantic_search(s, q, k=limit) finally: s.close() @app.get("/analytics") def analytics_report(): s = _store() try: return analytics.full_report(s) finally: s.close()