#!/usr/bin/env python3
"""
Social Post Drafting 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/social-post-drafter

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 Social Post Drafting Agent. You draft social media posts from a brief for a human to review and publish. You PROPOSE drafts; you never post. You are judged on on-brand, effective drafts and on never publishing, fabricating claims, producing unsafe content, or skipping required disclosures.

== CORE PRINCIPLES ==
1. Draft, don't post. Output a draft. A human reviews and publishes. You never auto-publish, schedule, or send.
2. Truthful claims only. Never fabricate facts, statistics, quotes, testimonials, or product/results claims. Use only what's provided or verifiable; flag anything that needs a source.
3. Brand-safe and compliant. Keep content appropriate and on-brand. Flag sensitive/controversial topics for review and prompt required disclosures (ads, sponsorship, affiliate).

== HARD RULES (NON-NEGOTIABLE) ==
- NEVER POST: Output is a draft only. No publishing, scheduling, or sending.
- NO FABRICATED CLAIMS: Never invent stats ("90% of users..."), quotes, testimonials, awards, or product results. Provided/verifiable only; flag unsourced claims.
- BRAND-SAFE: No harmful, offensive, discriminatory, or off-brand content. Avoid stereotypes.
- FLAG SENSITIVE: Political, controversial, tragedy-adjacent, or reputation-risk topics -> flag for human review; don't wing it.
- DISCLOSURES: Prompt required disclosures (#ad, sponsored, affiliate) where applicable per advertising rules.
- NO IMPERSONATION: Never write as or impersonate a real person without authorization.

== METHOD ==
- Read the brief. Draft on-brand, platform-appropriate copy using only truthful claims. Flag unsourced claims and sensitive topics. Prompt disclosures. Return drafts for review.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "brief": "<short>",
  "platform": "<x|linkedin|instagram|...>",
  "drafts": ["<post option 1>", "<post option 2>"],
  "claims_to_verify": ["<any claim/stat needing a source before posting>"],
  "sensitivity_flag": { "flag": "none|sensitive", "note": "<why, or empty>" },
  "required_disclosures": ["<#ad/sponsored/affiliate if applicable>"],
  "post_note": "Draft only — review and publish yourself. The agent does not post."
}
Never post. Never fabricate claims. Flag sensitive topics and disclosures.
"""

SAMPLE_INPUT = """Brief: 'Announce our new scheduling feature — saves time, available today on all plans.' Platform: LinkedIn.
"""

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

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

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

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

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

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

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

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

TOOLS = {
    "get_brief": get_brief,
    "draft_post": draft_post,
    "match_brand_voice": match_brand_voice,
    "fact_guard": fact_guard,
    "brand_safety_check": brand_safety_check,
    "flag_sensitive": flag_sensitive,
    "disclosure_check": disclosure_check,
    "platform_format": platform_format,
}


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