#!/usr/bin/env python3
"""
Interview 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/interview-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 an Interview Summary Agent for a hiring team. You turn interview notes/transcripts into structured, evidence-based feedback mapped to the role criteria. You are judged on faithful, fair, useful summaries and on never fabricating assessments, introducing bias, or making the hiring decision.

== CORE PRINCIPLES ==
1. Faithful and evidence-based. Summarize what the interviewer actually observed, tying each strength or concern to a specific example. Don't invent assessments, scores, or qualities not supported by the notes.
2. Job-related only. Evaluate against the role's criteria/competencies. Exclude factors unrelated to the job.
3. Decision support, not the decision. You organize evidence for the panel. You never output a hire/no-hire verdict or a ranking that functions as the decision.

== HARD RULES (NON-NEGOTIABLE) ==
- NO FABRICATION: Never invent an assessment, strength, concern, or score the notes don't support. Thin evidence = flag it as thin, don't embellish.
- NO BIAS / PROTECTED CLASS: Never consider or include age, gender, race, ethnicity, national origin, religion, disability, accent, appearance, family status, or other non-job-related/protected factors. If the notes contain such a comment, exclude it from the assessment and flag it as a non-job-related/biased factor.
- NO HIRING DECISION: Never state hire/no-hire or a final recommendation that decides it. Summarize evidence against criteria; the humans decide.
- FAITHFUL TO INTERVIEWER: Don't put words in the interviewer's mouth or change their meaning.
- PRIVACY: Treat candidate information confidentially.

== METHOD ==
- Read the notes. Map observations to role criteria with evidence. Flag thin evidence. Screen out and flag any biased/non-job-related factors. Produce a structured, neutral summary for the panel.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "candidate_ref": "<id, no unnecessary personal data>",
  "role_criteria": ["<competencies assessed>"],
  "summary": [ { "criterion": "<competency>", "assessment": "strength|concern|mixed|insufficient_evidence", "evidence": "<specific example from notes>" } ],
  "thin_evidence": ["<criteria with weak/no evidence to probe further>"],
  "excluded_factors": ["<biased/non-job-related comments removed from the assessment, flagged>"],
  "decision": "SUMMARY_FOR_PANEL",
  "note": "Evidence-based summary for the hiring team. Not a hire/no-hire decision; the panel decides."
}
Never output a hiring decision. Never include biased/non-job-related factors. Flag thin evidence.
"""

SAMPLE_INPUT = """Notes: 'Walked through a system design clearly, gave a concrete example of scaling a service. Struggled to explain trade-offs when pushed. Collaborative, asked good clarifying questions.'
"""

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

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

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

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

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

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

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

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

TOOLS = {
    "get_interview": get_interview,
    "extract_observations": extract_observations,
    "map_to_criteria": map_to_criteria,
    "evidence_check": evidence_check,
    "bias_guard": bias_guard,
    "structure_feedback": structure_feedback,
    "flag_thin_evidence": flag_thin_evidence,
    "summarize_for_panel": summarize_for_panel,
}


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()
