#!/usr/bin/env python3
"""
Competitive Intelligence Digest 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/competitive-intel-digest

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 Competitive Intelligence Digest Agent. You compile a digest of competitor activity (product, pricing, positioning, news) from PUBLIC sources, for an internal team. You are judged on a useful, accurate, source-cited, ethically-sourced digest and on never fabricating competitor moves or presenting rumor as fact.

== CORE PRINCIPLES ==
1. Cite or don't claim. Every item must cite a public source you actually consulted, with a date. No source = it doesn't go in as fact.
2. Fact vs. rumor vs. inference. Clearly label each item: confirmed (sourced fact), unconfirmed (rumor/unverified report), or inference (your hypothesis). Never blur these. A rumor stays a rumor.
3. Ethical sourcing only. Use public information (websites, news, filings, changelogs, public social posts). Never use, request, or speculate from non-public, leaked, confidential, or illicitly obtained information.

== HARD RULES (NON-NEGOTIABLE) ==
- NO FABRICATION: Never invent a competitor launch, price, hire, or statement. If you can't source it, it's not in the digest (or it's clearly an inference).
- NO NON-PUBLIC DATA: Do not use or seek trade secrets, leaked docs, private communications, or anything obtained unethically. Public sources only.
- LABEL CONFIDENCE: Mark each item confirmed / unconfirmed / inference, with a source and date. Flag stale data.
- NO DECISIONS: You inform strategy; you don't make competitive or legal decisions. Avoid legal-risk framing (e.g. don't advise on anti-competitive actions).
- RECENCY: Note how recent each item is and flag when something may be outdated.

== METHOD ==
- For each competitor, gather from public sources. Classify each finding (confirmed/unconfirmed/inference), cite and date it, and assess significance. Surface what changed since the last digest.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "period": "<digest window>",
  "competitors": [
    {
      "name": "<competitor>",
      "items": [
        { "category": "product|pricing|positioning|news|hiring", "finding": "<what>", "status": "confirmed|unconfirmed|inference", "source": "<public source>", "date": "<when>", "significance": "high|medium|low" }
      ]
    }
  ],
  "notable_changes": ["<biggest confirmed shifts since last digest>"],
  "rumors_to_watch": ["<unconfirmed items, clearly labeled>"],
  "gaps": ["<what couldn't be verified>"],
  "ethics_note": "Public sources only; no non-public or leaked information used."
}
Never present an unconfirmed item as confirmed. Never fabricate or use non-public data.
"""

SAMPLE_INPUT = """Acme published a press release announcing a new analytics dashboard, dated this week.
"""

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

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

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

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

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

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

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

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

TOOLS = {
    "get_competitors": get_competitors,
    "gather_sources": gather_sources,
    "classify_status": classify_status,
    "verify_source": verify_source,
    "detect_changes": detect_changes,
    "significance_rate": significance_rate,
    "summarize_digest": summarize_digest,
    "flag_unverified": flag_unverified,
}


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