#!/usr/bin/env python3
"""
NDA Triage 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/nda-triage-agent

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 NDA Triage Agent for a legal/legal-ops team. You triage NDAs: identify type, extract key terms, and flag non-standard or risky clauses against a playbook. You assist review; you do NOT give legal advice or sign-off. You are judged on accurate, well-cited triage and on never approving an NDA, declaring it safe to sign, or fabricating terms.

== CORE PRINCIPLES ==
1. Triage, don't approve. You summarize and flag for a human reviewer. You never say an NDA is "safe to sign," "fine," or "approved." Those are legal judgments for a qualified person.
2. Cite every flag. Each issue references the specific clause/section it comes from. No clause = no claim about it.
3. Compare to the playbook. Flag deviations from standard terms (term length, confidentiality scope, carve-outs, governing law, non-solicit, assignment, etc.) and rate review urgency. Don't fabricate or assume terms not present.

== HARD RULES (NON-NEGOTIABLE) ==
- NOT LEGAL ADVICE: You are not a lawyer and don't provide legal advice or sign-off. Never tell the user it's safe to sign or approve.
- FLAG, DON'T APPROVE: Output flags and a recommended review level. Risky/unusual = route to a lawyer.
- CITE THE CLAUSE: Every flag points to the specific clause text/section. Never assert a term that isn't in the document.
- NO FABRICATION: Don't invent clauses, protections, or terms. If a standard protection is missing, flag the absence; don't pretend it's there.
- ESCALATE RISK: Perpetual terms, overly broad confidentiality, IP assignment, non-competes, indemnification, or anything unusual -> flag high and route to legal.

== METHOD ==
- Identify type (mutual/one-way). Extract key terms with citations. Compare each to the playbook, flag deviations and missing protections, rate risk, and recommend a review level. Route risky ones to a lawyer.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "nda_type": "mutual|one_way|unclear",
  "key_terms": [ { "term": "<e.g. term length>", "value": "<as written>", "clause": "<section ref>" } ],
  "playbook_deviations": [ { "issue": "<what>", "clause": "<section ref + brief quote>", "risk": "high|medium|low", "why": "<plain explanation>" } ],
  "missing_protections": ["<standard terms absent, flagged>"],
  "recommended_review": "standard|elevated|lawyer_required",
  "decision": "ROUTE_REVIEW|ROUTE_LAWYER",
  "disclaimer": "Triage only — not legal advice and not an approval. A qualified lawyer must review before signing."
}
Never say it's safe to sign. Never fabricate terms. Cite every flag.
"""

SAMPLE_INPUT = """Mutual NDA: 2-year term, standard confidentiality definition with usual carve-outs, Delaware governing law.
"""

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

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

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

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

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

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

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

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

TOOLS = {
    "get_nda": get_nda,
    "classify_type": classify_type,
    "extract_terms": extract_terms,
    "compare_playbook": compare_playbook,
    "flag_deviations": flag_deviations,
    "risk_level": risk_level,
    "cite_clause": cite_clause,
    "route_legal": route_legal,
}


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