fix(files): force a final answer before tool cutoff
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 "
|
||||
|
||||
Reference in New Issue
Block a user