Explorer
/proc/184/root/tmp/aa050_patch.py
← Zurück ↓ Download
from pathlib import Path

p=Path('/opt/struktur/social-media-radar/mas_relevance.py')
s=p.read_text()
old='def assess_mas_relevance(text: str, *, fields: dict | None = None) -> MASRelevance:\n'
new='''def content_eligibility(signal: dict, *, require_persisted_gate: bool = True) -> tuple[bool, str]:
    """Zentrales, fail-closed MAS-Content-Gate für alle nachgelagerten Consumer.

    Content darf nur entstehen, wenn die persistierte Neubewertung ausdrücklich
    mas_relevant=1 trägt und topic/short_summary nach derselben Fachlogik relevant
    sind. NULL, 0, fehlende Felder und widersprüchliche Altwerte werden blockiert.
    """
    if require_persisted_gate and signal.get("mas_relevant") != 1:
        return False, "mas_relevant fehlt oder ist nicht 1"
    if signal.get("content_blocked") == 1:
        return False, signal.get("content_block_reason") or "fachlich/ethisch blockiert"
    result = assess_mas_relevance(
        " ".join(str(signal.get(k) or "") for k in ("topic", "short_summary")),
        fields={"topic": signal.get("topic"), "short_summary": signal.get("short_summary")},
    )
    if not result.relevant:
        return False, result.reason or "Kein fachlicher MAS-Anker"
    return True, "MAS-relevant und persistiertes Fachgate=1"


def assess_mas_relevance(text: str, *, fields: dict | None = None) -> MASRelevance:
'''
assert s.count(old)==1
p.write_text(s.replace(old,new))

p=Path('/opt/struktur/social-media-radar/caption_gate.py'); s=p.read_text()
s=s.replace('from mas_relevance import assess_mas_relevance','from mas_relevance import content_eligibility')
old='''    relevance = assess_mas_relevance(" ".join(str(signal.get(k) or "") for k in ("topic", "short_summary", "source_name")), fields=signal)
    if not relevance.relevant:
        return _block(f"[{sid}] " + ("Nicht für MAS-Content geeignet: sensibles/fachfremdes Ereignis" if relevance.content_blocked else "Kein fachlicher MAS-Bezug"))
'''
new='''    eligible, reason = content_eligibility(signal)
    if not eligible:
        return _block(f"[{sid}] {reason}")
'''
assert old in s
p.write_text(s.replace(old,new))

p=Path('/opt/struktur/social-media-radar/visual_gate.py'); s=p.read_text()
s=s.replace('from content_safety import find_safety_block','from content_safety import find_safety_block\nfrom mas_relevance import content_eligibility')
old='''    # Relevanz, Score, briefing_winkel und expires_at sind Anzeige-/Entscheidungsdaten.
    # Sie dürfen die Vorschau nicht blockieren; der Operator entscheidet über Nutzung.
    score = signal.get("radar_score") or 0
'''
new='''    eligible, reason = content_eligibility(signal)
    if not eligible:
        return _block(f"[{sid}] {reason}")

    score = signal.get("radar_score") or 0
'''
assert old in s
p.write_text(s.replace(old,new))

p=Path('/opt/struktur/social-media-agent/publish_ready.py'); s=p.read_text()
old='''from human_feedback import feedback_prompt_text, save_variant_feedback
'''
new='''from human_feedback import feedback_prompt_text, save_variant_feedback
sys.path.insert(0, str(RADAR_DIR))
from mas_relevance import content_eligibility
'''
# RADAR_DIR defined after imports, so import must be after constants instead
new='''from human_feedback import feedback_prompt_text, save_variant_feedback
'''
# only replace _relevant and load query; import locally avoids constant-order issue
oldrel='''def _relevant(signal: dict) -> bool:
    category = _text(signal.get("signal_category")).lower()
    haystack = " ".join(_text(signal.get(k)) for k in ("topic", "topic_de", "short_summary", "briefing_winkel")).lower()
    return category in RELEVANT_CATEGORIES or any(term in haystack for term in RELEVANT_TERMS)
'''
newrel='''def _relevant(signal: dict) -> bool:
    sys.path.insert(0, str(RADAR_DIR))
    from mas_relevance import content_eligibility
    eligible, _ = content_eligibility(signal)
    return eligible
'''
assert oldrel in s
s=s.replace(oldrel,newrel)
oldq='''        "SELECT * FROM signals WHERE COALESCE(status,'') NOT IN ('ignoriert','veroeffentlicht') "
        "AND COALESCE(publish_status,'') NOT IN ('gepostet','veroeffentlicht','manuell_veroeffentlicht') "
'''
newq='''        "SELECT * FROM signals WHERE COALESCE(status,'') NOT IN ('ignoriert','veroeffentlicht') "
        "AND COALESCE(publish_status,'') NOT IN ('gepostet','veroeffentlicht','manuell_veroeffentlicht') "
        "AND mas_relevant=1 AND COALESCE(content_blocked,0)=0 "
'''
assert oldq in s
s=s.replace(oldq,newq)
# refresh path must refuse old source rows unless gate passes
old='''            if source:
                result.append(transform_signal(dict(source)) or dict(source))
'''
new='''            if source:
                candidate = dict(source)
                if _relevant(candidate):
                    result.append(transform_signal(candidate) or candidate)
'''
assert old in s
s=s.replace(old,new)
p.write_text(s)

# harden direct package loader as defense-in-depth
p=Path('/opt/struktur/social-media-agent/posting_package.py'); s=p.read_text()
s=s.replace('from caption_gate   import can_generate_caption','from caption_gate   import can_generate_caption\nfrom mas_relevance import content_eligibility')
old='''    package_key = loaded_signal_id

    cap_gate = can_generate_caption(signal)
'''
new='''    package_key = loaded_signal_id

    eligible, eligibility_reason = content_eligibility(signal)
    if not eligible:
        return _fail(eligibility_reason)
    cap_gate = can_generate_caption(signal)
'''
assert old in s
p.write_text(s)