Explorer
/tmp/integrate_media_brief.py
← Zurück ↓ Download
from pathlib import Path
p=Path('/opt/struktur/social-media-agent/publish_ready.py'); s=p.read_text()
# Add media_briefs table before package variants table in init_schema.
needle='''    CREATE TABLE IF NOT EXISTS package_image_variants ('''
brief_sql='''    CREATE TABLE IF NOT EXISTS media_briefs (
        media_brief_id TEXT PRIMARY KEY,
        package_id TEXT NOT NULL REFERENCES publication_packages(package_id),
        generation_group_id TEXT NOT NULL,
        content_pillar TEXT NOT NULL,
        topic TEXT NOT NULL,
        core_claim TEXT NOT NULL,
        main_motif TEXT NOT NULL,
        technical_action TEXT NOT NULL,
        required_objects_json TEXT NOT NULL DEFAULT '[]',
        technical_requirements_json TEXT NOT NULL DEFAULT '[]',
        building_context TEXT NOT NULL DEFAULT '',
        mallorca_context TEXT NOT NULL DEFAULT 'none',
        visual_style TEXT NOT NULL DEFAULT '',
        visual_effect TEXT NOT NULL DEFAULT '',
        avoid_motifs_json TEXT NOT NULL DEFAULT '[]',
        avoid_technical_errors_json TEXT NOT NULL DEFAULT '[]',
        reference_assets_json TEXT NOT NULL DEFAULT '[]',
        feedback_rules_json TEXT NOT NULL DEFAULT '[]',
        generator TEXT NOT NULL DEFAULT 'Codex built-in image_gen',
        created_at TEXT NOT NULL
    );
    CREATE INDEX IF NOT EXISTS idx_media_briefs_package ON media_briefs(package_id, created_at);
    CREATE TABLE IF NOT EXISTS package_image_variants ('''
assert s.count(needle)==1; s=s.replace(needle,brief_sql)
# Add media_brief_id to variants through ensure columns after variant feedback ensure line.
old='''{"human_rating": "TEXT NOT NULL DEFAULT ''", "human_reason_verbatim": "TEXT NOT NULL DEFAULT ''", "human_reason_structured_json": "TEXT NOT NULL DEFAULT '{}'", "human_feedback_at": "TEXT NOT NULL DEFAULT ''", "human_feedback_by": "TEXT NOT NULL DEFAULT ''", "feedback_rules_json": "TEXT NOT NULL DEFAULT '[]'"}'''
new='''{"human_rating": "TEXT NOT NULL DEFAULT ''", "human_reason_verbatim": "TEXT NOT NULL DEFAULT ''", "human_reason_structured_json": "TEXT NOT NULL DEFAULT '{}'", "human_feedback_at": "TEXT NOT NULL DEFAULT ''", "human_feedback_by": "TEXT NOT NULL DEFAULT ''", "feedback_rules_json": "TEXT NOT NULL DEFAULT '[]'", "media_brief_id": "TEXT NOT NULL DEFAULT ''"}'''
assert s.count(old)==1; s=s.replace(old,new)
# Insert brief helpers before persist variant.
needle='def persist_image_variant(con: sqlite3.Connection, package_id: str, group_id: str, order_no: int, generated: dict, prompt: str, qa: dict, feedback_rules: list[dict] | None = None) -> str:'
helper='''def build_media_brief(package: dict, feedback_rules: list[dict], reference_asset: dict | None, generation_group_id: str) -> dict:
    pillar = _text(package.get("content_pillar") or package.get("topic_category"))
    topic = _text(package.get("topic"))
    if "feuchte" in pillar or "schimmel" in topic.casefold():
        motif = "professionelles elektronisches Feuchtemessgerät mit klar erkennbarer flacher Sensorfläche an einer Innenwand"
        action = "Fachperson hält das kompakte Messgerät unmittelbar und plausibel flach an die Wandoberfläche"
        objects = ["Fachperson", "kompaktes professionelles Feuchtemessgerät", "Innenwand", "sichtbare Feuchtespur nur falls zum Thema passend"]
        requirements = ["Sensorfläche muss zum Gerät gehören", "keine externe Sonde", "keine Kabel", "keine Bohrmaschine oder Schleifgerät"]
    elif "lüft" in pillar or "lüft" in topic.casefold():
        motif = "reales dezentrales Lüftungsgerät mit klarer Innenblende und nachvollziehbarer Wanddurchführung"
        action = "Fachperson prüft die sichtbare technische Montage und den Luftweg ohne erfundene Messanordnung"
        objects = ["Fachperson", "dezentrales Lüftungsgerät", "Innenblende", "Wanddurchführung"]
        requirements = ["Bauteile geometrisch verbunden", "keine unklaren Sonden", "keine Leitungen ohne Anfang und Ende"]
    else:
        motif = "professionelles Raumluftmessgerät beziehungsweise Sensor im realistischen Innenraum"
        action = "Fachperson kontrolliert Raumluftdaten mit einem eindeutig passenden Messgerät"
        objects = ["Fachperson", "professionelles Raumluftmessgerät", "Innenraum"]
        requirements = ["Gerät und Messaufgabe funktional zusammengehörig", "keine Fantasiewerte", "keine unklaren Kabel"]
    avoid_motifs = ["Strand", "Meer", "Küste", "Sehenswürdigkeiten", "touristische Straße", "Postkartenästhetik", "Luxusvilla ohne Fachbezug", "primitive Grafik", "Comic", "Text im Bild", "Logo", "Wasserzeichen"]
    avoid_errors = ["Fantasie-Messgerät", "Kabel aus Wand oder Boden", "unklare Sonde", "widersprüchliches Messsystem", "zusätzliche funktionslose Instrumente", "unplausible Anatomie"]
    ref = [reference_asset] if reference_asset else []
    return {"media_brief_id": f"MB-{uuid.uuid4()}", "package_id": package.get("package_id", ""), "generation_group_id": generation_group_id, "content_pillar": pillar, "topic": topic, "core_claim": _text(package.get("final_text") or package.get("facebook_text") or package.get("relevance")), "main_motif": motif, "technical_action": action, "required_objects": objects, "technical_requirements": requirements, "building_context": "realistischer Innenraum einer Wohnimmobilie; technische Gebäudesubstanz widerspruchsfrei", "mallorca_context": "subtle" if "mallorca" in topic.casefold() else "none", "visual_style": "fotorealistische, sachliche dokumentarische Fachfotografie", "visual_effect": "ruhig, glaubwürdig, hochwertig, technische Handlung klar lesbar", "avoid_motifs": avoid_motifs, "avoid_technical_errors": avoid_errors, "reference_assets": ref, "feedback_rules": feedback_rules, "generator": "Codex built-in image_gen", "created_at": _now()}


def persist_media_brief(con: sqlite3.Connection, brief: dict) -> None:
    con.execute("INSERT INTO media_briefs(media_brief_id,package_id,generation_group_id,content_pillar,topic,core_claim,main_motif,technical_action,required_objects_json,technical_requirements_json,building_context,mallorca_context,visual_style,visual_effect,avoid_motifs_json,avoid_technical_errors_json,reference_assets_json,feedback_rules_json,generator,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (brief["media_brief_id"],brief["package_id"],brief["generation_group_id"],brief["content_pillar"],brief["topic"],brief["core_claim"],brief["main_motif"],brief["technical_action"],json.dumps(brief["required_objects"],ensure_ascii=False),json.dumps(brief["technical_requirements"],ensure_ascii=False),brief["building_context"],brief["mallorca_context"],brief["visual_style"],brief["visual_effect"],json.dumps(brief["avoid_motifs"],ensure_ascii=False),json.dumps(brief["avoid_technical_errors"],ensure_ascii=False),json.dumps(brief["reference_assets"],ensure_ascii=False),json.dumps(brief["feedback_rules"],ensure_ascii=False),brief["generator"],brief["created_at"]))


def media_brief_prompt(brief: dict, variant_no: int) -> str:
    variants = {1: "klare technische Hauptaufnahme und unmittelbare Tätigkeit", 2: "etwas mehr räumlicher Innenkontext bei unverändert klarer technischer Handlung", 3: "alternative glaubwürdige Perspektive oder Komposition ohne anderes Messverfahren"}
    return (f"Media Brief {brief['media_brief_id']}. Thema: {brief['topic']}. Fachliche Kernaussage: {brief['core_claim']}. "
            f"Hauptmotiv: {brief['main_motif']}. Technische Handlung: {brief['technical_action']}. "
            f"Erforderliche Objekte: {', '.join(brief['required_objects'])}. Technisch zwingend: {', '.join(brief['technical_requirements'])}. "
            f"Gebäudekontext: {brief['building_context']}. Mallorca-Kontext: {brief['mallorca_context']}. "
            f"Stil/Wirkung: {brief['visual_style']}; {brief['visual_effect']}. "
            f"Diese Variante ist bewusst: {variants[variant_no]}. "
            f"Vermeide: {', '.join(brief['avoid_motifs'] + brief['avoid_technical_errors'])}. "
            "Keine erfundenen Marken, Messwerte, Texte oder Logos; keine zusätzlichen funktionslosen Instrumente; technische Geometrie, Sensoren und Leitungen müssen physikalisch plausibel sein.")


def persist_image_variant(con: sqlite3.Connection, package_id: str, group_id: str, order_no: int, generated: dict, prompt: str, qa: dict, feedback_rules: list[dict] | None = None, media_brief_id: str = "") -> str:'''
assert s.count(needle)==1; s=s.replace(needle,helper)
# Replace persist insertion block
start=s.index('    columns="variant_id,package_id,generation_group_id', s.index('def persist_image_variant'))
end=s.index('\n    return variant_id',start)
newbody='''    columns="variant_id,package_id,generation_group_id,variant_order,image_path,origin_path,file_type,file_bytes,sha256,generator,prompt,feedback_rules_json,media_brief_id,qa_status,qa_report_json,qa_failure_reasons,created_at"
    values=(variant_id, package_id, group_id, order_no, generated.get("path", ""), generated.get("origin_path", ""), generated.get("file_type", "PNG"), int(generated.get("bytes") or 0), generated.get("sha256", ""), "Codex built-in image_gen", prompt, json.dumps(feedback_rules or [], ensure_ascii=False), media_brief_id, "passed" if qa.get("overall") == "PASS" else "failed", json.dumps(qa, ensure_ascii=False), "; ".join(qa.get("failure_reasons", [])), _now())
    con.execute(f"INSERT INTO package_image_variants({columns}) VALUES ({','.join('?' for _ in values)})", values)'''
s=s[:start]+newbody+s[end:]
# Replace generation prompt/function block
start=s.index('def generate_package_image('); end=s.index('\n\ndef _direct_url',start)
replacement='''def generate_package_image(package_id: str, *, force: bool = False) -> dict:
    """Generate three brief-driven Codex variants, each independently QA-checked."""
    con = sqlite3.connect(CASES_DB); con.row_factory = sqlite3.Row
    row = con.execute("SELECT * FROM publication_packages WHERE package_id=?", (package_id,)).fetchone()
    if not row:
        con.close(); raise ValueError("publication package not found")
    package = dict(row); generation_group_id = f"GRP-{uuid.uuid4()}"
    feedback_text, feedback_rules = feedback_prompt_text(package)
    reference_asset = None
    try:
        ref_path, ref_label = internal_asset_for_package(package)
        if ref_path and ref_path.is_file(): reference_asset = {"path": str(ref_path), "label": ref_label, "source": "media_catalog"}
    except Exception:
        reference_asset = None
    brief = build_media_brief(package, feedback_rules, reference_asset, generation_group_id)
    persist_media_brief(con, brief); con.commit()
    reports=[]
    for variant_no in range(1,4):
        prompt = media_brief_prompt(brief, variant_no)
        target = (BASE_DIR / "generated_visuals" / f"{package['signal_id']}_codex-image-gen-group-{generation_group_id[-8:]}-v{variant_no}.png").resolve()
        try:
            generated = generate_with_codex(prompt, target)
            qa = run_image_qa(target, _text(package.get("topic")), _text(package.get("final_text") or package.get("facebook_text")))
            persist_image_variant(con, package_id, generation_group_id, variant_no, generated, prompt, qa, feedback_rules, brief["media_brief_id"]); con.commit()
            reports.append({"variant":variant_no,"path":str(target),"origin_path":generated.get("origin_path"),"bytes":generated.get("bytes"),"sha256":generated.get("sha256"),"qa":qa})
        except (CodexImageError, CodexImageQAError, OSError, ValueError) as exc:
            reports.append({"variant":variant_no,"qa":{"overall":"FAIL","failure_reasons":[str(exc)]},"error":str(exc)})
    con.close()
    passed=sum(1 for r in reports if r.get("qa",{}).get("overall")=="PASS")
    return {"ok":len(reports)==3,"status":f"{len(reports)} Varianten erzeugt; {passed} technisch bestanden","generation_group_id":generation_group_id,"media_brief_id":brief["media_brief_id"],"media_brief":brief,"variants":reports,"attempts":reports}
'''
s=s[:start]+replacement+s[end:]
p.write_text(s); print('media brief integrated')