21 Commits

Autor SHA1 Nachricht Datum
Claude Code
cbfb608471 feat(translator): Feature-Flag TRANSLATOR_ENABLED zum Abschalten
Ueber die ENV-Variable TRANSLATOR_ENABLED (default true) kann der
Translator-Agent komplett deaktiviert werden. Wenn false:
- translate_articles steigt mit return [] aus, ohne Claude-Calls
- Fremdsprachige Artikel bleiben unuebersetzt (headline_de/content_de NULL)

Hintergrund: Bei Lage 6 Irankonflikt sind 10.210 Artikel ohne DE-Uebersetzung
aufgelaufen. Pro Refresh werden 2042 Batches sequentiell gestreamt
(~25s/Batch -> 13.5h Gesamtdauer pro Refresh), was den Pipeline-Step
factcheck blockiert und die Queue lahmlegt. Bis das Performance-Thema
geloest ist (Parallelisierung, Relevanz-Filter, Hard-Cap), wird der
Agent live deaktiviert. Zustand spaeter ueber .env wieder aktivierbar.

Live-.env wurde mit TRANSLATOR_ENABLED=false ergaenzt.
2026-05-03 20:43:39 +00:00
Claude Code
e517de7404 fix(orchestrator): Auto-Refresh nicht direkt nach Cancel/Error neu einreihen
Der Auto-Refresh-Scheduler hat seinen letzten relevanten refresh_log-Eintrag
bisher mit Filter status IN (completed, running) gesucht. Cancelled- und
Error-Laeufe wurden ignoriert, der davor liegende Completed wurde genommen.
Ergebnis: Direkt nach Cancel oder Error wurde der Slot als faellig gesehen
und nach 60 Sekunden wieder eingereiht (Endlos-Loop bei Iran-Konflikt heute,
4x error in Folge ohne Pause).

- main.py: Filter erweitert auf status IN (completed, running, cancelled, error)
- orchestrator.py: Queue-Cancels schreiben jetzt auch einen cancelled-Eintrag
  ins refresh_log via _log_queued_cancellation (vorher: stiller Discard,
  kein Fingerabdruck im Log -> Auto-Refresh erkannte den Cancel nie)

Wirkung: Nach Cancel oder Error startet die Lage erst beim naechsten
regulaeren Slot wieder. refresh_mode bleibt unveraendert.
2026-05-03 19:30:02 +00:00
07c3fed9c8 Promote develop → main (2026-05-03 15:21 UTC) 2026-05-03 17:21:40 +02:00
6a24d0b51d Promote develop → main (2026-05-03 14:30 UTC) 2026-05-03 16:30:36 +02:00
eaffd70575 Promote develop → main (2026-05-03 13:47 UTC) 2026-05-03 15:47:34 +02:00
46864c5457 Promote develop → main (2026-05-03 00:07 UTC) 2026-05-03 02:07:08 +02:00
1f4d7b1837 Promote develop → main (2026-05-03 00:02 UTC) 2026-05-03 02:02:20 +02:00
d7711711aa Promote develop → main (2026-05-02 22:53 UTC) 2026-05-03 00:53:32 +02:00
Claude Code
74d76d2e50 Promote develop → main (2026-05-02 20:30 UTC) 2026-05-02 20:25:29 +00:00
0775a475a4 Promote develop → main (2026-05-01 21:39 UTC) 2026-05-01 23:39:22 +02:00
8b8e31e3cd Promote develop → main (2026-05-01 20:17 UTC) 2026-05-01 22:17:42 +02:00
4a2d85d3b8 Promote develop → main (2026-05-01 16:09 UTC) 2026-05-01 18:09:11 +02:00
d24205841f Promote develop → main (2026-05-01 15:16 UTC) 2026-05-01 17:16:47 +02:00
b9985b8e35 Promote develop → main (2026-05-01 14:09 UTC) 2026-05-01 16:09:55 +02:00
b3bc96c580 Promote develop → main (2026-05-01 12:48 UTC) 2026-05-01 14:48:16 +02:00
dc75b89618 Promote develop → main (2026-04-30 23:12 UTC) 2026-05-01 01:12:48 +02:00
44de6616f1 Promote develop → main (2026-04-30 21:03 UTC) 2026-04-30 23:03:07 +02:00
bfa4d5fd78 Promote develop → main (2026-04-29 20:28 UTC) 2026-04-29 22:28:31 +02:00
c57ac6c6d8 Promote develop → main (2026-04-29 20:10 UTC) 2026-04-29 22:10:57 +02:00
d9e5733cfb Promote: Update-Modal beim ersten Besuch 2026-04-26 23:08:09 +02:00
9574308c29 Promote develop → main (2026-04-26 20:40 UTC) 2026-04-26 22:40:36 +02:00
4 geänderte Dateien mit 38 neuen und 2 gelöschten Zeilen

Datei anzeigen

@@ -489,6 +489,9 @@ class AgentOrchestrator:
logger.info(f"Lage {incident_id} aus Warteschlange entfernt (removed={removed})") logger.info(f"Lage {incident_id} aus Warteschlange entfernt (removed={removed})")
# refresh_log-Eintrag schreiben, damit Auto-Refresh nicht im naechsten Tick erneut einreiht
await self._log_queued_cancellation(incident_id)
# Send cancelled event # Send cancelled event
if self._ws_manager: if self._ws_manager:
try: try:
@@ -639,6 +642,28 @@ class AgentOrchestrator:
finally: finally:
await db.close() await db.close()
async def _log_queued_cancellation(self, incident_id: int):
"""Schreibt einen cancelled-Eintrag fuer einen Queue-Abbruch (Lage war noch nicht laufend).
Verhindert, dass der Auto-Refresh-Scheduler im naechsten Tick sofort wieder einreiht."""
from database import get_db
db = await get_db()
try:
cur = await db.execute("SELECT tenant_id FROM incidents WHERE id = ?", (incident_id,))
row = await cur.fetchone()
tid = row["tenant_id"] if row else None
now_str = datetime.now(TIMEZONE).strftime("%Y-%m-%d %H:%M:%S")
await db.execute(
"""INSERT INTO refresh_log (incident_id, started_at, completed_at, status,
trigger_type, error_message, tenant_id)
VALUES (?, ?, ?, 'cancelled', 'manual', 'Aus Warteschlange entfernt', ?)""",
(incident_id, now_str, now_str, tid),
)
await db.commit()
except Exception as e:
logger.warning(f"Konnte Queue-Cancel nicht in refresh_log loggen: {e}")
finally:
await db.close()
async def _mark_refresh_failed(self, incident_id: int, error: str): async def _mark_refresh_failed(self, incident_id: int, error: str):
"""Markiert den laufenden Refresh-Log-Eintrag als error.""" """Markiert den laufenden Refresh-Log-Eintrag als error."""
from database import get_db from database import get_db

Datei anzeigen

@@ -12,7 +12,7 @@ import logging
import re import re
from agents.claude_client import call_claude, ClaudeUsage, UsageAccumulator from agents.claude_client import call_claude, ClaudeUsage, UsageAccumulator
from config import CLAUDE_MODEL_FAST from config import CLAUDE_MODEL_FAST, TRANSLATOR_ENABLED
logger = logging.getLogger("osint.translator") logger = logging.getLogger("osint.translator")
@@ -230,6 +230,13 @@ async def translate_articles(
if not articles: if not articles:
return [] return []
if not TRANSLATOR_ENABLED:
logger.info(
"Translator deaktiviert (TRANSLATOR_ENABLED=false), %d Artikel uebersprungen",
len(articles),
)
return []
all_translations = [] all_translations = []
for i in range(0, len(articles), batch_size): for i in range(0, len(articles), batch_size):
batch = articles[i : i + batch_size] batch = articles[i : i + batch_size]

Datei anzeigen

@@ -41,6 +41,10 @@ OUTPUT_LANGUAGE = "Deutsch"
# In Kundenversion auf False setzen oder Env-Variable entfernen # In Kundenversion auf False setzen oder Env-Variable entfernen
DEV_MODE = os.environ.get("DEV_MODE", "true").lower() == "true" DEV_MODE = os.environ.get("DEV_MODE", "true").lower() == "true"
# Feature-Flag: Translator-Agent (Haiku) komplett deaktivieren.
# False = keine Uebersetzungen mehr, fremdsprachige Artikel bleiben unuebersetzt.
TRANSLATOR_ENABLED = os.environ.get("TRANSLATOR_ENABLED", "true").lower() == "true"
# RSS-Feeds (Fallback, primär aus DB geladen) # RSS-Feeds (Fallback, primär aus DB geladen)
RSS_FEEDS = { RSS_FEEDS = {
"deutsch": [ "deutsch": [

Datei anzeigen

@@ -124,7 +124,7 @@ async def check_auto_refresh():
# Letzten abgeschlossenen oder laufenden Refresh pruefen # Letzten abgeschlossenen oder laufenden Refresh pruefen
cursor = await db.execute( cursor = await db.execute(
"SELECT started_at, status FROM refresh_log WHERE incident_id = ? AND status IN ('completed', 'running') ORDER BY id DESC LIMIT 1", "SELECT started_at, status FROM refresh_log WHERE incident_id = ? AND status IN ('completed', 'running', 'cancelled', 'error') ORDER BY id DESC LIMIT 1",
(incident_id,), (incident_id,),
) )
last_refresh = await cursor.fetchone() last_refresh = await cursor.fetchone()