medbillcodes-api / app /main.py
medbillcodes-deploy
Deploy cloud pilot API
1ddeb51
Raw
History Blame Contribute Delete
4.82 kB
"""FastAPI entrypoint for the Medbillcodes OHIP billing copilot backend."""
from __future__ import annotations
import logging
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .config import settings
from .routes import analyze, approve, intake, specialties, provinces, workspace
from .weave_trace import init_weave
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(
title="Medbillcodes Billing Copilot",
version="0.3.0",
description=(
"Provincial fee suggestions grounded in the current physician schedule. "
f"Default jurisdiction: {settings.default_province_code}; "
f"default specialty: {settings.default_specialty_code}; "
f"retrieval: {settings.retrieval_backend}."
),
)
# LAN-only frontend; tighten origins for the actual clinic deployment.
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(intake.router)
app.include_router(analyze.router)
app.include_router(approve.router)
app.include_router(specialties.router)
app.include_router(provinces.router)
app.include_router(workspace.router)
def _use_sqlite() -> bool:
return settings.retrieval_backend == "sqlite"
@app.on_event("startup")
async def _startup() -> None:
init_weave()
if _use_sqlite():
from . import sqlite_store # noqa: PLC0415
from .build_sqlite import build_sqlite_index # noqa: PLC0415
sqlite_store.ensure_schema()
if sqlite_store.count_codes() == 0:
logger.info("SQLite fee index empty — building from public OHIP sources…")
try:
summary = build_sqlite_index(force=True)
logger.info("SQLite index ready: %s", summary)
except Exception as exc: # noqa: BLE001
logger.error(
"Failed to build SQLite index at startup (%s). "
"Call POST /admin/ingest after fixing data paths.",
exc,
)
else:
logger.info(
"SQLite fee index ready (%d codes)", sqlite_store.count_codes()
)
return
# Full path: OpenSearch + feedback learning (+ optional remote embeddings).
try:
from .clinic_workspace import ensure_workspace_index # noqa: PLC0415
from .feedback import ensure_feedback_index # noqa: PLC0415
from .opensearch_client import ensure_index, get_client # noqa: PLC0415
from .reconciliation import ensure_remittance_index # noqa: PLC0415
client = get_client()
ensure_index(client)
ensure_feedback_index(client)
ensure_remittance_index(client)
ensure_workspace_index(client)
logger.info(
"OpenSearch ready (index=%s, embeddings=%s)",
settings.opensearch_index,
"remote" if settings.embedding_url else "local",
)
except Exception as exc: # noqa: BLE001
logger.warning(
"OpenSearch not ready at startup (%s). Retry via /admin/ingest.", exc
)
if settings.refresh_enabled:
from .scheduler import start_scheduler # noqa: PLC0415
start_scheduler()
@app.on_event("shutdown")
async def _shutdown() -> None:
if _use_sqlite():
return
try:
from .scheduler import shutdown_scheduler # noqa: PLC0415
shutdown_scheduler()
except Exception: # noqa: BLE001
pass
@app.get("/health")
def health() -> dict:
payload: dict = {
"status": "ok",
"retrieval_backend": settings.retrieval_backend,
"pilot_mode": settings.pilot_mode,
"model": settings.llm_model,
"vllm_url": settings.vllm_url,
"embeddings": "remote" if settings.embedding_url else "local",
"embedding_model": settings.embedding_model,
"embedding_dim": settings.embedding_dim,
"fusion_technique": settings.fusion_technique,
"rrf_rank_constant": settings.rrf_rank_constant,
"feedback_learning": not _use_sqlite(),
"llm_justify_sync": settings.llm_justify,
"llm_enrich_justifications": settings.llm_enrich_justifications,
"weave_enabled": settings.weave_enabled,
"weave_project": settings.weave_project if settings.weave_enabled else None,
"weave_llm_judge": settings.weave_llm_judge,
"weave_gate_top3": settings.weave_gate_top3,
"weave_gate_mrr": settings.weave_gate_mrr,
}
if _use_sqlite():
from . import sqlite_store # noqa: PLC0415
payload["codes"] = sqlite_store.count_codes()
payload["sqlite_path"] = str(sqlite_store.db_path())
else:
payload["index"] = settings.opensearch_index
return payload