Hermes haengt an jedes web_extract-Ergebnis ein leeres "error"-Feld und haelt ein Ergebnis fuer gescheitert, sobald dieses Feld in den ersten 500 Zeichen steht - bei kurzen Seiten also auch Erfolge. Der Waechter zaehlt solche Ergebnisse (mehrzeilig, mit "results") nicht mehr. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
292a9d2b6b
commit
2cc1e1cc47
@@ -248,6 +248,15 @@ def _letzter_lauf(job_id: str) -> dict | None:
|
|||||||
return {"status": row[0], "started_at": row[1], "finished_at": row[2], "error": row[3]}
|
return {"status": row[0], "started_at": row[1], "finished_at": row[2], "error": row[3]}
|
||||||
|
|
||||||
|
|
||||||
|
def _gelesene_seite(zeilen: list[str], i: int) -> bool:
|
||||||
|
"""Hermes hängt an jedes web_extract-Ergebnis ein leeres "error"-Feld und hält ein Ergebnis für
|
||||||
|
gescheitert, sobald '"error"' in seinen ersten 500 Zeichen steht — bei kurzen Seiten also auch
|
||||||
|
Erfolge (agent/display.py, 24.09. gesehen). Ein mehrzeiliges Ergebnis mit "results" ist eine
|
||||||
|
gelesene Seite, kein Werkzeugfehler; echte Fehler kommen als {"success": false, "error": …}."""
|
||||||
|
return (zeilen[i].rstrip().endswith("{") and i + 1 < len(zeilen)
|
||||||
|
and zeilen[i + 1].lstrip().startswith('"results"'))
|
||||||
|
|
||||||
|
|
||||||
def werkzeugfehler_im_lauf(zeilen: list[str], job_id: str, start_iso: str) -> tuple[int, str]:
|
def werkzeugfehler_im_lauf(zeilen: list[str], job_id: str, start_iso: str) -> tuple[int, str]:
|
||||||
"""Zählt 'Tool X returned error'-Zeilen eines Laufs in errors.log. Hermes markiert jede
|
"""Zählt 'Tool X returned error'-Zeilen eines Laufs in errors.log. Hermes markiert jede
|
||||||
Zeile mit [cron_<job>_<JJJJMMTT>_<HHMMSS>]; der Tag des Laufstarts reicht zur Zuordnung."""
|
Zeile mit [cron_<job>_<JJJJMMTT>_<HHMMSS>]; der Tag des Laufstarts reicht zur Zuordnung."""
|
||||||
@@ -256,7 +265,8 @@ def werkzeugfehler_im_lauf(zeilen: list[str], job_id: str, start_iso: str) -> tu
|
|||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return 0, ""
|
return 0, ""
|
||||||
marke = f"[cron_{job_id}_{tag}_"
|
marke = f"[cron_{job_id}_{tag}_"
|
||||||
treffer = [z for z in zeilen if marke in z and "returned error" in z]
|
treffer = [z for i, z in enumerate(zeilen)
|
||||||
|
if marke in z and "returned error" in z and not _gelesene_seite(zeilen, i)]
|
||||||
if not treffer:
|
if not treffer:
|
||||||
return 0, ""
|
return 0, ""
|
||||||
m = re.search(r"Tool (\S+) returned error[^:]*:\s*(.*)$", treffer[0])
|
m = re.search(r"Tool (\S+) returned error[^:]*:\s*(.*)$", treffer[0])
|
||||||
|
|||||||
@@ -48,6 +48,23 @@ def test_werkzeugfehler_zaehlt_nur_den_lauf_des_tages():
|
|||||||
assert beispiel.startswith("write_file: Refusing to overwrite /tmp/news-text.md")
|
assert beispiel.startswith("write_file: Refusing to overwrite /tmp/news-text.md")
|
||||||
|
|
||||||
|
|
||||||
|
def test_gelesene_kurze_seite_ist_kein_werkzeugfehler():
|
||||||
|
"""Echte Zeilen vom 24.09.: Hermes loggt ein erfolgreiches web_extract als Fehler, weil sein
|
||||||
|
leeres "error"-Feld bei kurzen Seiten in den ersten 500 Zeichen landet."""
|
||||||
|
zeilen = [
|
||||||
|
('2026-09-24 07:01:03,291 WARNING [cron_195e479e8e30_20260924_070027] agent.tool_executor: '
|
||||||
|
'Tool web_extract returned error (0.56s): {'),
|
||||||
|
' "results": [',
|
||||||
|
" {",
|
||||||
|
' "url": "https://example.org",',
|
||||||
|
('2026-09-24 07:01:38,983 WARNING [cron_195e479e8e30_20260924_070027] agent.tool_executor: '
|
||||||
|
'Tool web_extract returned error (0.26s): {"success": false, "error": "DuckDuckGo (ddgs) is a '
|
||||||
|
'search-only backend and cannot extract URL content."}'),
|
||||||
|
]
|
||||||
|
anzahl, beispiel = waechter.werkzeugfehler_im_lauf(zeilen, "195e479e8e30", "2026-09-24T07:00:27+02:00")
|
||||||
|
assert anzahl == 1 and beispiel.startswith("web_extract: DuckDuckGo (ddgs) is a search-only backend")
|
||||||
|
|
||||||
|
|
||||||
def test_werkzeugfehler_null_fuer_anderen_job_oder_kaputtes_datum():
|
def test_werkzeugfehler_null_fuer_anderen_job_oder_kaputtes_datum():
|
||||||
assert waechter.werkzeugfehler_im_lauf(ERRORS_LOG, "ca7d4ed207c2", "2026-09-23T08:00:00+02:00") == (0, "")
|
assert waechter.werkzeugfehler_im_lauf(ERRORS_LOG, "ca7d4ed207c2", "2026-09-23T08:00:00+02:00") == (0, "")
|
||||||
assert waechter.werkzeugfehler_im_lauf(ERRORS_LOG, "195e479e8e30", "kein-datum") == (0, "")
|
assert waechter.werkzeugfehler_im_lauf(ERRORS_LOG, "195e479e8e30", "kein-datum") == (0, "")
|
||||||
|
|||||||
Reference in New Issue
Block a user