#!/usr/bin/env python3
"""
Patient Intake Summary Agent — runnable demo (Agent Blueprint from AgentKits)

This runs a REAL agent loop: it sends the kit's actual system prompt and a
realistic sample input to the model, prints the structured decision, and then
executes any proposed actions against MOCK tools (labelled [MOCK]).

The model/reasoning is real. The tools are stubs that return fake data and do
NOT touch any real system. Wiring the mock tools to your own integrations
(CRM, database, GitHub, etc.) is the final implementation step — see README.md.

Works with Anthropic OR OpenAI — it auto-detects whichever API key is set.

© 2026 AgentKits · www.agent-kits.com · Licensed CC BY 4.0 (attribution required)
Original blueprint: https://agent-kits.com/kit/patient-intake-summarizer

Setup (pick one):
    pip install anthropic   &&  export ANTHROPIC_API_KEY=sk-ant-...
    pip install openai      &&  export OPENAI_API_KEY=sk-...
Then:
    python run.py
"""
import os, json, sys

ANTHROPIC_MODEL = "claude-sonnet-4-6"
OPENAI_MODEL = "gpt-4o"

SYSTEM_PROMPT = """You are a Patient Intake Summary Agent. You organize a patient's intake information into a structured summary for a CLINICIAN to review. You do not practice medicine. You are judged on a faithful, well-structured summary and on never diagnosing, advising treatment, fabricating, or mishandling patient data.

== CORE PRINCIPLES ==
1. Summarize, don't diagnose. Organize what the patient reported. Never offer a diagnosis, differential, or treatment recommendation. Clinical conclusions are the clinician's.
2. Faithful and complete. Capture clinically relevant details exactly as reported. Don't omit, alter, downplay, or embellish symptoms, history, or medications.
3. Flag, don't interpret. Surface red-flag symptoms and missing critical information for the clinician's attention, without interpreting what they mean.

== HARD RULES (NON-NEGOTIABLE) ==
- NO DIAGNOSIS / NO MEDICAL ADVICE: Never state or imply a diagnosis, never recommend treatment, medication, or what the patient "should" do. Route all clinical questions to the clinician.
- NO FABRICATION: Never invent symptoms, history, medications, vitals, or results. Summarize only what's provided; mark what's missing or unclear.
- FAITHFUL: Preserve clinically relevant information accurately. Don't drop or soften details (e.g. a reported chest pain, a missed dose).
- FLAG RED FLAGS: Surface potential red-flag symptoms (e.g. chest pain, difficulty breathing, severe bleeding, suicidal ideation) for clinician attention, clearly labeled as a flag, not a diagnosis.
- PRIVACY (PHI): Treat all information as protected health information; keep it in scope and disclose to no one but the care team.
- CLINICIAN REVIEWS: The summary supports, never replaces, clinician review.

== METHOD ==
- Read the intake. Extract chief complaint, HPI, meds, allergies, relevant history. Flag red-flag symptoms and missing critical info. Note uncertainty. Produce a structured summary for clinician review.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "chief_complaint": "<as reported>",
  "hpi": "<history of present illness, faithful>",
  "medications": ["<as reported>"],
  "allergies": ["<as reported, or 'none reported'>"],
  "relevant_history": ["<as reported>"],
  "red_flags": ["<potential red-flag symptoms flagged for clinician, not interpreted>"],
  "missing_or_unclear": ["<critical info not provided or ambiguous>"],
  "disclaimer": "Summary of patient-reported information for clinician review. Not a diagnosis or medical advice. A clinician must review.",
  "phi_note": "Contains PHI — handle per privacy policy; care team only."
}
Never diagnose or advise treatment. Never fabricate. Flag red flags without interpreting.
"""

SAMPLE_INPUT = """Intake: 'Sore throat and cough for 3 days, mild fever. Takes lisinopril for blood pressure. Allergic to penicillin. No other major history.'
"""

# ---- MOCK tools (stubs — replace with real integrations to deploy) ----
def get_intake(args=None):
    print(f"  [MOCK] get_intake(" + str(args) + ")")
    return {"status": "mock", "tool": "get_intake"}

def extract_history(args=None):
    print(f"  [MOCK] extract_history(" + str(args) + ")")
    return {"status": "mock", "tool": "extract_history"}

def extract_symptoms(args=None):
    print(f"  [MOCK] extract_symptoms(" + str(args) + ")")
    return {"status": "mock", "tool": "extract_symptoms"}

def extract_meds(args=None):
    print(f"  [MOCK] extract_meds(" + str(args) + ")")
    return {"status": "mock", "tool": "extract_meds"}

def flag_red_flags(args=None):
    print(f"  [MOCK] flag_red_flags(" + str(args) + ")")
    return {"status": "mock", "tool": "flag_red_flags"}

def flag_missing(args=None):
    print(f"  [MOCK] flag_missing(" + str(args) + ")")
    return {"status": "mock", "tool": "flag_missing"}

def structure_summary(args=None):
    print(f"  [MOCK] structure_summary(" + str(args) + ")")
    return {"status": "mock", "tool": "structure_summary"}

def privacy_guard(args=None):
    print(f"  [MOCK] privacy_guard(" + str(args) + ")")
    return {"status": "mock", "tool": "privacy_guard"}

TOOLS = {
    "get_intake": get_intake,
    "extract_history": extract_history,
    "extract_symptoms": extract_symptoms,
    "extract_meds": extract_meds,
    "flag_red_flags": flag_red_flags,
    "flag_missing": flag_missing,
    "structure_summary": structure_summary,
    "privacy_guard": privacy_guard,
}


def call_model(system, user):
    """Dispatch to whichever provider's key is configured."""
    if os.environ.get("ANTHROPIC_API_KEY"):
        try:
            from anthropic import Anthropic
        except ImportError:
            sys.exit("Run: pip install anthropic")
        msg = Anthropic().messages.create(
            model=ANTHROPIC_MODEL,
            max_tokens=1600,
            system=system,
            messages=[{"role": "user", "content": user}],
        )
        return "".join(b.text for b in msg.content if getattr(b, "type", "") == "text")
    if os.environ.get("OPENAI_API_KEY"):
        try:
            from openai import OpenAI
        except ImportError:
            sys.exit("Run: pip install openai")
        resp = OpenAI().chat.completions.create(
            model=OPENAI_MODEL,
            max_tokens=1600,
            messages=[
                {"role": "system", "content": system},
                {"role": "user", "content": user},
            ],
        )
        return resp.choices[0].message.content
    sys.exit("Set ANTHROPIC_API_KEY or OPENAI_API_KEY first.")


def extract_json(text):
    a, b = text.find("{"), text.rfind("}")
    if a == -1 or b == -1:
        return None
    try:
        return json.loads(text[a : b + 1])
    except json.JSONDecodeError:
        return None


def main():
    print("=" * 70)
    print("INPUT:\n" + SAMPLE_INPUT)
    print("=" * 70)

    text = call_model(SYSTEM_PROMPT, SAMPLE_INPUT)
    print("AGENT DECISION:\n" + text)
    print("=" * 70)

    data = extract_json(text)
    if not data:
        print("(No JSON object parsed — the model returned prose.)")
        return

    actions = data.get("actions") or []
    if actions:
        print("EXECUTING PROPOSED ACTIONS (mock):")
        for a in actions:
            name = a.get("tool")
            args = a.get("args", {})
            if name in TOOLS:
                TOOLS[name](args)
            else:
                print(f"  [skip] no mock tool for '{name}'")
    else:
        print("This blueprint returned a structured result (no side-effecting "
              "actions in this sample). Available mock tools: "
              + ", ".join(TOOLS.keys()))


if __name__ == "__main__":
    main()
