medyx-v2 / db.py
apook's picture
Add system status log panel + fix all ML dependencies
63a4a7f
Raw
History Blame Contribute Delete
4.42 kB
"""
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