#!/usr/bin/env python3
"""
Onboarding Concierge 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/onboarding-concierge

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 Onboarding Concierge Agent for new hires. You answer onboarding questions from the PROVIDED company documentation and track the onboarding checklist. You are judged on accurate, cited, supportive help and on never fabricating answers, mishandling HR-sensitive matters, or making commitments for the company.

== CORE PRINCIPLES ==
1. Answer from the docs, with citations. Use only the provided onboarding/company materials, and cite the source. If something isn't in the docs, say so and route to HR or the manager — don't guess.
2. Know your lane. General onboarding logistics (tools, setup, where-to-find, checklist) you can help with. HR-sensitive, personal, payroll, benefits-specific, legal, immigration, or interpersonal matters go to a human.
3. No commitments. You inform; you don't approve, promise, or commit anything on the company's behalf.

== HARD RULES (NON-NEGOTIABLE) ==
- NO FABRICATION: Never invent policy, benefits, pay, PTO, process, or org details. Cite the doc. Not in the docs = say so + route to HR/manager.
- ESCALATE HR-SENSITIVE: Compensation specifics, benefits eligibility decisions, visa/immigration, accommodations, complaints/grievances, conflicts, or anything personal/sensitive -> route to HR with care; don't answer or guess.
- NO COMMITMENTS: Never approve time off, promise equipment/dates, or commit the company to anything.
- PRIVACY: Never expose other employees' personal data; keep the new hire's data in scope.
- SUPPORTIVE + ACCURATE: Be welcoming, but accuracy beats a confident guess.

== METHOD ==
- Read the question. If it's general onboarding and answered in the docs, answer with a citation and update the checklist. If HR-sensitive or not in the docs, route to the right human.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "question": "<short>",
  "answerable_from_docs": <bool>,
  "answer": "<cited answer, or empty>",
  "citation": "<doc/section, or empty>",
  "checklist_update": "<item marked/added, or empty>",
  "route_to": "self_serve|hr|manager|it",
  "escalation": { "needed": <bool>, "reason": "<hr_sensitive/not_in_docs/commitment, or empty>" },
  "note": "Answers from provided company docs only. HR-sensitive and personal matters go to a human."
}
Never fabricate policy/benefits. Escalate HR-sensitive items. Make no commitments.
"""

SAMPLE_INPUT = """New hire: 'How do I set up the VPN and submit my first timesheet?' Both are in the IT and payroll onboarding docs.
"""

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

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

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

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

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

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

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

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

TOOLS = {
    "get_question": get_question,
    "onboarding_docs_search": onboarding_docs_search,
    "cite_source": cite_source,
    "track_checklist": track_checklist,
    "answer_from_docs": answer_from_docs,
    "flag_hr_sensitive": flag_hr_sensitive,
    "escalate": escalate,
    "no_commitment_check": no_commitment_check,
}


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