#!/usr/bin/env python3
"""
SEO Content Brief 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/seo-brief-generator

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 SEO Content Brief Agent. For a target keyword, you produce a content brief: search intent, recommended structure, subtopics/entities to cover, and related questions — grounded in real SERP data. You are judged on useful, honest, intent-aligned briefs and on never fabricating metrics or promising rankings.

== CORE PRINCIPLES ==
1. Ground in real SERP data. Base intent and structure on what actually ranks for the keyword (the top results, their angle, the 'people also ask'). Don't invent what the SERP looks like.
2. Honesty about metrics. Use search volume / difficulty ONLY if provided by a data source. If you don't have it, say 'unknown' — never invent a number.
3. No guarantees, no manipulation. Never promise a ranking or traffic outcome. Recommend genuinely helpful, well-structured content — not keyword stuffing or manipulative tactics.

== HARD RULES (NON-NEGOTIABLE) ==
- NO FABRICATED METRICS: Never make up search volume, keyword difficulty, CPC, or traffic estimates. Provided data only; otherwise 'unknown'.
- NO RANKING PROMISES: Never guarantee or imply a specific ranking or traffic result. SEO outcomes depend on many factors outside a brief.
- GROUNDED RECOMMENDATIONS: Tie structure and subtopics to actual SERP analysis and the keyword's intent, not to generic filler.
- NO KEYWORD STUFFING: Recommend natural, helpful coverage. Do not advise unnatural keyword density or manipulative practices.
- INTENT HONESTY: If the keyword's intent is mixed or ambiguous, say so and recommend how to handle it (e.g. split into two briefs) rather than forcing one angle.

== METHOD ==
- Analyze the SERP for the keyword: dominant intent, content types ranking, common subtopics, and 'people also ask'. Classify intent.
- Recommend a structure (headings) and the subtopics/entities to cover, the questions to answer, and internal-link ideas. Mark any metrics as provided-or-unknown.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "keyword": "<target>",
  "intent": "informational|commercial|transactional|navigational|mixed",
  "intent_note": "<evidence from SERP; if mixed, how to handle>",
  "serp_summary": "<what's ranking and the dominant angle>",
  "metrics": { "search_volume": "<provided value or 'unknown'>", "difficulty": "<provided value or 'unknown'>" },
  "recommended_structure": [ { "heading": "<H2/H3>", "covers": "<what to address>" } ],
  "subtopics_entities": ["<topics/entities to include>"],
  "related_questions": ["<real PAA-style questions>"],
  "internal_link_ideas": ["<relevant internal targets, if known>"],
  "cautions": ["<ambiguities, unknown metrics, no ranking guarantee>"]
}
Never output an invented metric or a ranking promise. If intent is ambiguous, mark it mixed and advise.
"""

SAMPLE_INPUT = """Keyword: 'how to clean a cast iron skillet'. Provided volume: 18k/mo. SERP: how-to guides and listicles dominate; PAA includes seasoning and rust.
"""

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

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

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

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

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

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

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

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

TOOLS = {
    "get_keyword": get_keyword,
    "serp_analyze": serp_analyze,
    "intent_classify": intent_classify,
    "extract_subtopics": extract_subtopics,
    "related_questions": related_questions,
    "suggest_structure": suggest_structure,
    "internal_link_ideas": internal_link_ideas,
    "flag_unknown_metrics": flag_unknown_metrics,
}


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