""" SQLite persistence for diagnosis history. Table: diagnoses id INTEGER PRIMARY KEY AUTOINCREMENT timestamp TEXT (ISO-8601 UTC) patient_age TEXT patient_gender TEXT symptoms_summary TEXT (first 200 chars of symptoms) top_diagnosis TEXT confidence REAL (0–100) risk_level TEXT (LOW / MEDIUM / HIGH / CRITICAL) full_result_json TEXT (complete synthesis result as JSON) """ from __future__ import annotations import json import sqlite3 from datetime import datetime, timezone from pathlib import Path DB_PATH = Path(__file__).resolve().parent / "medagent.db" _DDL = """ CREATE TABLE IF NOT EXISTS diagnoses ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, patient_age TEXT NOT NULL DEFAULT '', patient_gender TEXT NOT NULL DEFAULT '', symptoms_summary TEXT NOT NULL DEFAULT '', top_diagnosis TEXT NOT NULL DEFAULT '', confidence REAL NOT NULL DEFAULT 0, risk_level TEXT NOT NULL DEFAULT '', full_result_json TEXT NOT NULL DEFAULT '{}' ); """ def _connect() -> sqlite3.Connection: conn = sqlite3.connect(str(DB_PATH), check_same_thread=False) conn.row_factory = sqlite3.Row return conn def init_db() -> None: """Create the table if it doesn't exist. Safe to call on every startup.""" with _connect() as conn: conn.executescript(_DDL) def save_diagnosis( patient_data: dict, synthesis_result: dict, debate_result: dict, ) -> int: """ Persist one completed diagnosis. Returns the new row id. patient_data — the input dict (symptoms, medical_history, …) synthesis_result — output of SynthesisAgent.synthesize() debate_result — output of DebateAgent.debate() """ diff = synthesis_result.get("differential", []) top = diff[0] if diff else {} risk = synthesis_result.get("risk_level", {}) row = { "timestamp": datetime.now(timezone.utc).isoformat(timespec="seconds"), "patient_age": str(patient_data.get("age", "")), "patient_gender": str(patient_data.get("gender", "")), "symptoms_summary": str(patient_data.get("symptoms", ""))[:200], "top_diagnosis": top.get("disease", ""), "confidence": float(top.get("confidence_pct", 0)), "risk_level": risk.get("label", ""), "full_result_json": json.dumps({ "differential": diff, "recommendations": synthesis_result.get("recommendations", {}), "risk_level": risk, "distinguishing_test": synthesis_result.get("distinguishing_test", ""), "debate_backend": synthesis_result.get("debate_backend", "voting"), "debate_summary": debate_result.get("summary", ""), "agent_a_argument": debate_result.get("agent_a_argument", ""), "agent_b_argument": debate_result.get("agent_b_argument", ""), }), } sql = """ INSERT INTO diagnoses (timestamp, patient_age, patient_gender, symptoms_summary, top_diagnosis, confidence, risk_level, full_result_json) VALUES (:timestamp, :patient_age, :patient_gender, :symptoms_summary, :top_diagnosis, :confidence, :risk_level, :full_result_json) """ with _connect() as conn: cur = conn.execute(sql, row) return cur.lastrowid def get_recent(limit: int = 20) -> list[dict]: """Return the most recent *limit* diagnosis rows as plain dicts.""" sql = """ SELECT id, timestamp, patient_age, patient_gender, symptoms_summary, top_diagnosis, confidence, risk_level FROM diagnoses ORDER BY id DESC LIMIT ? """ with _connect() as conn: rows = conn.execute(sql, (limit,)).fetchall() return [dict(r) for r in rows] def get_full(row_id: int) -> dict | None: """Return full row including full_result_json, or None if not found.""" sql = "SELECT * FROM diagnoses WHERE id = ?" with _connect() as conn: row = conn.execute(sql, (row_id,)).fetchone() if row is None: return None d = dict(row) try: d["full_result"] = json.loads(d["full_result_json"]) except Exception: d["full_result"] = {} return d