From b997fcb9f7440bdb70c25647a1acdeaaa8dfc5b1 Mon Sep 17 00:00:00 2001 From: Mikei386 <44135113+Mikei386@users.noreply.github.com> Date: Mon, 24 Aug 2026 07:12:15 +0200 Subject: [PATCH] fix(files): force a final answer before tool cutoff --- dev/test_openwebui_filters.py | 33 +++++++++++++++++++ docs/TOOLING_RELIABILITY_2026-08-24.md | 6 +++- platform/openwebui/filters/stability_guard.py | 19 +++++++++-- platform/openwebui/install-models.sh | 3 ++ 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/dev/test_openwebui_filters.py b/dev/test_openwebui_filters.py index 8b12831..e1d8e37 100644 --- a/dev/test_openwebui_filters.py +++ b/dev/test_openwebui_filters.py @@ -159,6 +159,39 @@ class StabilityGuardTests(unittest.IsolatedAsyncioTestCase): ["execute_code"], ) + async def test_private_csv_stops_before_openwebui_hard_tool_limit(self): + calls = [] + for index in range(6): + calls.extend( + [ + { + "role": "assistant", + "tool_calls": [ + { + "function": { + "name": "execute_code", + "arguments": '{"code":"step %d"}' % index, + } + } + ], + }, + {"role": "tool", "content": "ok", "tool_call_id": str(index)}, + ] + ) + body = { + "model": "qwen-fast", + "tools": [ + {"type": "function", "function": {"name": "execute_code"}}, + ], + "messages": [ + {"role": "user", "content": "Werte diese CSV aus."}, + *calls, + ], + } + result = await self.guard.inlet(body) + self.assertEqual(result["tools"], []) + self.assertIn("vorhandenen Ergebnisse", result["messages"][0]["content"]) + class AutoToolSelectorTests(unittest.IsolatedAsyncioTestCase): async def asyncSetUp(self): diff --git a/docs/TOOLING_RELIABILITY_2026-08-24.md b/docs/TOOLING_RELIABILITY_2026-08-24.md index aa77e2a..e0bb171 100644 --- a/docs/TOOLING_RELIABILITY_2026-08-24.md +++ b/docs/TOOLING_RELIABILITY_2026-08-24.md @@ -25,7 +25,11 @@ benötigte deshalb kleinere, klarere Werkzeuge und harte Abbruchgrenzen. Webwerkzeug-Injektion. Vor `pandas.read_csv` werden Rohvorschau, Kodierung, Trennzeichen, Kopfzeile, Metadatenzeilen, Dezimal- und Datumsformat erkannt; damit führen deutsche Bankexporte nicht mehr unnötig zuerst zu einem - ParserError wegen einer falschen Spaltenzahl. + ParserError wegen einer falschen Spaltenzahl. Tabellenanalysen sollen im + Regelfall mit einer Erkennungs- und einer Auswertungsrunde auskommen. Nach + sechs lokalen Dateiaufrufen erzwingt der Guard eine werkzeugfreie + Abschlussantwort, bevor OpenWebUI bei seiner harten Zwölf-Runden-Grenze + ohne sichtbares Ergebnis abbricht. 4. Pro Antwort sind höchstens zwölf Werkzeugrunden erlaubt. Der zweite identische Aufruf wird gestoppt. Ein einzelnes Resultat ist auf 10.000, alle Resultate zusammen auf 36.000 Zeichen begrenzt. diff --git a/platform/openwebui/filters/stability_guard.py b/platform/openwebui/filters/stability_guard.py index fb5e19e..bdb435d 100644 --- a/platform/openwebui/filters/stability_guard.py +++ b/platform/openwebui/filters/stability_guard.py @@ -1,7 +1,7 @@ """ title: MikeAI Stability Guard author: MikeAI -version: 2.0.0 +version: 2.1.0 description: Bounds tool output and context use and breaks repeated tool-call loops. """ @@ -29,7 +29,11 @@ class Filter: max_total_tool_chars: int = 36000 compacted_tool_chars: int = 2000 duplicate_tool_call_limit: int = 2 - max_tool_calls_per_turn: int = 12 + # OpenWebUI itself stops after twelve rounds without giving the model + # another completion. Stay below that ceiling so the model still gets + # a final, tool-free turn. + max_tool_calls_per_turn: int = 10 + max_private_table_tool_calls: int = 6 def __init__(self): self.valves = self.Valves() @@ -149,7 +153,11 @@ class Filter: "pandas.read_csv(path, sep=None, engine='python') for discovery, then parse " "again with explicit verified parameters. Compute exact aggregates, validate " "that totals reconcile, and always return a visible final answer or a clear " - "local parsing error." + "local parsing error. Consolidate discovery and calculation into as few code " + "calls as possible: normally one preview/dialect call and one complete analysis " + "call. After one failed parse, use its error to correct the next call instead " + "of repeatedly searching the filesystem. Never spend more than four code calls " + "on a table unless the user explicitly requests debugging." ) messages = body.setdefault("messages", []) for message in messages: @@ -339,6 +347,11 @@ class Filter: breaker_reason = None if duplicate >= self.valves.duplicate_tool_call_limit: breaker_reason = f"derselbe Aufruf wurde {duplicate}-mal wiederholt" + elif private_table and len(signatures) >= self.valves.max_private_table_tool_calls: + breaker_reason = ( + f"{len(signatures)} lokale Dateiaufrufe; die vorhandenen Ergebnisse " + "müssen jetzt als Antwort ausgegeben werden" + ) elif len(signatures) >= self.valves.max_tool_calls_per_turn: breaker_reason = f"{len(signatures)} Werkzeugaufrufe in einem Schritt" diff --git a/platform/openwebui/install-models.sh b/platform/openwebui/install-models.sh index b699b74..51f46e0 100755 --- a/platform/openwebui/install-models.sh +++ b/platform/openwebui/install-models.sh @@ -206,6 +206,9 @@ params = { "lines, header row, decimal separator and date format before parsing. Do not begin " "with a blind pandas.read_csv(path) call; use csv.Sniffer or sep=None with the " "Python engine for discovery, then parse with explicit verified parameters. " + "Normally use one preview/dialect call and one complete analysis call. After one failed " + "parse, correct the next call from its error instead of repeatedly searching the filesystem. " + "Never spend more than four code calls on one table unless debugging was explicitly requested. " "Compute exact aggregates, reconcile the result, and always provide a visible final answer. " "For GitHub repository implementation details, README files, source trees, " "API routes, or code search, use the dedicated official GitHub repository "