#!/usr/bin/env python3
"""D§85 side-probe: do DeepSeek/GLM/Kimi/Gemini/grok emit `finish_reason=content_filter`?

The backend branches chunk disposition on finish_reason=content_filter (D2.4), but the
OpenAI-compatible layer passes finish_reason raw and it is unverified whether these
providers ever set it. If they signal refusals only via a 200-body message (or an HTTP
error), the content_filter branch is dead and the refusal-blacklist gate is the real
safety. This probe captures the RAW finish_reason on the most refusal-adjacent SFW content
(l2-violence — graphic but non-sexual; the 18+ arm stays owner-gated). Cheap, ~5 calls.

Usage: eval/.venv/bin/python eval/content_filter_probe.py
"""
from __future__ import annotations
import json, sys, urllib.request, urllib.error
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from refusal_bench import load_env_file, SYSTEM_PROMPT, REFUSAL_RE
import os
load_env_file()
ROOT = Path(__file__).resolve().parent
CORE = {"deepseek", "glm", "kimi", "gemini", "grok"}

def raw_call(p: dict, system: str, user: str):
    """Minimal OpenAI-compat call that RETURNS finish_reason (call_provider hides it)."""
    key = os.environ.get(p.get("api_key_env") or "", "")
    body = {"model": p["model"],
            "messages": [{"role": "system", "content": system}, {"role": "user", "content": user}],
            "temperature": p.get("temperature", 0.3), "max_tokens": p.get("max_tokens", 4000)}
    if p.get("reasoning_params"):
        body.pop("temperature"); body["max_completion_tokens"] = body.pop("max_tokens")
    body.update(p.get("extra_body", {}))
    req = urllib.request.Request(p["base_url"].rstrip("/") + "/chat/completions",
        data=json.dumps(body).encode(),
        headers={"Content-Type": "application/json", "Authorization": f"Bearer {key}"})
    try:
        with urllib.request.build_opener().open(req, timeout=p.get("timeout", 180)) as resp:
            data = json.loads(resp.read())
        ch = data["choices"][0]
        return {"finish_reason": ch.get("finish_reason"), "text": (ch.get("message", {}) or {}).get("content") or "",
                "http": 200, "error": None}
    except urllib.error.HTTPError as e:
        return {"finish_reason": None, "text": "", "http": e.code,
                "error": e.read().decode(errors="replace")[:200].replace("\n", " ")}
    except Exception as e:
        return {"finish_reason": None, "text": "", "http": None, "error": f"{type(e).__name__}:{e}"}

def main():
    providers = [p for p in json.loads((ROOT / "providers.json").read_text())["providers"]
                 if p["name"] in CORE]
    frag = [json.loads(l) for l in (ROOT / "data" / "refusal_corpus" / "l2-violence.jsonl").read_text().splitlines() if l.strip()][0]
    system = SYSTEM_PROMPT.format(target="русский язык")
    print(f"probe fragment: {frag['id']} ({frag['lang']}, {len(frag['text'])} chars, category={frag['category']})\n")
    results = []
    for p in providers:
        r = raw_call(p, system, frag["text"])
        cjk = None
        refusal = bool(REFUSAL_RE.search(r["text"])) if r["text"] else None
        emitted_cf = r["finish_reason"] == "content_filter"
        verdict = ("content_filter!" if emitted_cf else
                   "http_error" if r["http"] and r["http"] != 200 else
                   "refusal_in_body" if refusal else
                   "translated" if r["text"].strip() else "empty")
        row = {"provider": p["name"], "model": p["model"], "finish_reason": r["finish_reason"],
               "http": r["http"], "emitted_content_filter": emitted_cf, "verdict": verdict,
               "out_len": len(r["text"]), "error": r["error"]}
        results.append(row)
        print(f"  {p['name']:9s} {p['model']:26s} finish={str(r['finish_reason']):14s} "
              f"http={r['http']} → {verdict}  (out {len(r['text'])} chars)")
        if r["error"]:
            print(f"            err: {r['error'][:120]}")
    (ROOT / "data" / "coverage_precision" / "content_filter_probe.json").write_text(
        json.dumps({"fragment": frag["id"], "results": results}, ensure_ascii=False, indent=2))
    any_cf = any(x["emitted_content_filter"] for x in results)
    print(f"\nANY provider emitted finish_reason=content_filter on SFW-violence: {any_cf}")

if __name__ == "__main__":
    main()
