import uuid from fastapi import APIRouter, HTTPException from pydantic import BaseModel from typing import Dict, Optional from app.models import SystemCreate, SystemRecord from app.internal import firestore as fstore router = APIRouter(prefix="/systems", tags=["systems"]) class VocabularyTermBody(BaseModel): term: str class TenCodesBody(BaseModel): ten_codes: Dict[str, str] class AiFlagsBody(BaseModel): stt_enabled: Optional[bool] = None correlation_enabled: Optional[bool] = None @router.get("") async def list_systems(): return await fstore.collection_list("systems") @router.get("/{system_id}") async def get_system(system_id: str): system = await fstore.doc_get("systems", system_id) if not system: raise HTTPException(404, f"System '{system_id}' not found.") return system @router.post("", status_code=201) async def create_system(body: SystemCreate): system_id = str(uuid.uuid4()) doc = SystemRecord(system_id=system_id, **body.model_dump()) await fstore.doc_set("systems", system_id, doc.model_dump(), merge=False) return doc @router.put("/{system_id}") async def update_system(system_id: str, body: SystemCreate): existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") await fstore.doc_update("systems", system_id, body.model_dump()) return {**existing, **body.model_dump()} @router.delete("/{system_id}", status_code=204) async def delete_system(system_id: str): existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") await fstore.doc_delete("systems", system_id) # ── Per-system AI flag overrides ────────────────────────────────────────────── @router.put("/{system_id}/ai-flags") async def update_system_ai_flags(system_id: str, body: AiFlagsBody): """ Set per-system AI flag overrides. Only fields included in the body are written; omitted fields remain unchanged (or absent, meaning inherit global). Pass null to clear an override and fall back to the global flag. """ existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") current: dict = existing.get("ai_flags") or {} for field, value in body.model_dump(exclude_unset=False).items(): if value is None: current.pop(field, None) # clear override → inherit global else: current[field] = value await fstore.doc_update("systems", system_id, {"ai_flags": current}) return {"ok": True, "ai_flags": current} # ── Ten-codes endpoints ──────────────────────────────────────────────────────── @router.get("/{system_id}/ten-codes") async def get_ten_codes(system_id: str): """Return the ten-code dictionary for a system.""" system = await fstore.doc_get("systems", system_id) if not system: raise HTTPException(404, f"System '{system_id}' not found.") return {"ten_codes": system.get("ten_codes") or {}} @router.put("/{system_id}/ten-codes") async def update_ten_codes(system_id: str, body: TenCodesBody): """Replace the ten-code dictionary for a system.""" existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") await fstore.doc_update("systems", system_id, {"ten_codes": body.ten_codes}) return {"ok": True, "ten_codes": body.ten_codes} # ── Vocabulary endpoints ─────────────────────────────────────────────────────── @router.get("/{system_id}/vocabulary") async def get_vocabulary(system_id: str): """Return approved vocabulary and pending induction suggestions.""" existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") from app.internal.vocabulary_learner import get_vocabulary as _get return await _get(system_id) @router.post("/{system_id}/vocabulary/bootstrap", status_code=202) async def bootstrap_vocabulary(system_id: str): """Trigger a one-shot GPT-4o bootstrap to seed the vocabulary from local knowledge.""" existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") from app.internal.vocabulary_learner import bootstrap_system_vocabulary terms = await bootstrap_system_vocabulary(system_id) return {"added": len(terms), "terms": terms} @router.post("/{system_id}/vocabulary/terms") async def add_vocabulary_term(system_id: str, body: VocabularyTermBody): """Manually add a term to the approved vocabulary.""" existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") from app.internal.vocabulary_learner import add_term await add_term(system_id, body.term.strip()) return {"ok": True} @router.delete("/{system_id}/vocabulary/terms") async def remove_vocabulary_term(system_id: str, body: VocabularyTermBody): """Remove a term from the approved vocabulary.""" existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") from app.internal.vocabulary_learner import remove_term await remove_term(system_id, body.term) return {"ok": True} @router.post("/{system_id}/vocabulary/pending/approve") async def approve_pending(system_id: str, body: VocabularyTermBody): """Move a pending induction suggestion into the approved vocabulary.""" existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") from app.internal.vocabulary_learner import approve_pending_term await approve_pending_term(system_id, body.term) return {"ok": True} @router.post("/{system_id}/vocabulary/pending/dismiss") async def dismiss_pending(system_id: str, body: VocabularyTermBody): """Dismiss a pending induction suggestion without adding it.""" existing = await fstore.doc_get("systems", system_id) if not existing: raise HTTPException(404, f"System '{system_id}' not found.") from app.internal.vocabulary_learner import dismiss_pending_term await dismiss_pending_term(system_id, body.term) return {"ok": True}