Banner-Text + AI-Disclaimer-Modal + Translator-Robustheit
#28 Banner-Text bei Token-Budget aufgebraucht: - middleware/license_check.py + static/js/app.js: Statt "Bitte Verwaltung kontaktieren" jetzt konkreter Upgrade-Pfad mit info@aegis-sight.de. #29 AI-Hallucination-Disclaimer: - Neue static/js/ai-disclaimer.js (analog zu update-system.js): IIFE-Modul, localStorage-versioniert (aegis_ai_disclaimer_seen=v1), inline-CSS mit Theme-Variablen, Modal mit Lucide-Info-Icon. - Wird beim ersten Login einmalig gezeigt; ueber Header-User-Dropdown Eintrag "Ueber KI-Inhalte" jederzeit erneut oeffenbar. - dashboard.html: Script-Tag + Dropdown-Button mit Lucide-SVG. - style.css: kleiner Stil-Block fuer .header-dropdown-action. Translator-Robustheit (Bonus): - agents/translator.py: Parser akzeptiert jetzt auch von Claude wrapped Antworten ({{translations: [...]}}, {{items: [...]}}, einzelnes Object). Behebt Wrapper-Bug der gestern beim Backfill 75% der Calls fehlschlagen liess. - Prompt deutlicher: "flaches JSON-Array, kein Wrapper".
Dieser Commit ist enthalten in:
@@ -95,10 +95,15 @@ WICHTIG:
|
||||
- Wenn der Artikel schon auf {lang_label} ist (z.B. source_lang="{output_lang}"),
|
||||
kopiere headline und content unveraendert.
|
||||
|
||||
Antworte AUSSCHLIESSLICH als JSON-Array - eine Liste von Objekten in der Form:
|
||||
[{{"id": <int>, "headline_de": "<uebersetzter Titel>", "content_de": "<uebersetzter Text>"}}, ...]
|
||||
Antworte AUSSCHLIESSLICH mit einem flachen JSON-Array (kein Wrapper-Objekt!).
|
||||
Format genau so:
|
||||
[
|
||||
{{"id": 1, "headline_de": "Titel auf Deutsch", "content_de": "Inhalt auf Deutsch"}},
|
||||
{{"id": 2, "headline_de": "...", "content_de": "..."}}
|
||||
]
|
||||
|
||||
Keine Einleitung, keine Erklaerung, nur das JSON-Array.
|
||||
NICHT erlaubt: {{"translations": [...]}} oder {{"items": [...]}} oder Markdown-Codefences.
|
||||
Nur das Array, ohne Einleitung, ohne Erklaerung.
|
||||
|
||||
ARTIKEL:
|
||||
{json.dumps(items, ensure_ascii=False, indent=2)}
|
||||
@@ -134,6 +139,19 @@ def _parse_response(text: str) -> list[dict]:
|
||||
else:
|
||||
data = _extract_complete_objects(text)
|
||||
|
||||
# Claude wraps das Array gelegentlich in {"translations": [...]} oder {"items": [...]}
|
||||
if isinstance(data, dict):
|
||||
for key in ("translations", "items", "results", "data"):
|
||||
if isinstance(data.get(key), list):
|
||||
data = data[key]
|
||||
break
|
||||
else:
|
||||
# Einzelnes Objekt? Dann als Liste mit einem Element behandeln
|
||||
if "id" in data:
|
||||
data = [data]
|
||||
else:
|
||||
raise ValueError(f"Translator-Antwort: Dict ohne erwarteten Array-Key (keys={list(data.keys())[:5]})")
|
||||
|
||||
if not isinstance(data, list):
|
||||
raise ValueError(f"Translator-Antwort ist kein Array: {type(data).__name__}")
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren