fix(files): force a final answer before tool cutoff
This commit is contained in:
@@ -159,6 +159,39 @@ class StabilityGuardTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
["execute_code"],
|
["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):
|
class AutoToolSelectorTests(unittest.IsolatedAsyncioTestCase):
|
||||||
async def asyncSetUp(self):
|
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,
|
Webwerkzeug-Injektion. Vor `pandas.read_csv` werden Rohvorschau, Kodierung,
|
||||||
Trennzeichen, Kopfzeile, Metadatenzeilen, Dezimal- und Datumsformat erkannt;
|
Trennzeichen, Kopfzeile, Metadatenzeilen, Dezimal- und Datumsformat erkannt;
|
||||||
damit führen deutsche Bankexporte nicht mehr unnötig zuerst zu einem
|
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
|
4. Pro Antwort sind höchstens zwölf Werkzeugrunden erlaubt. Der zweite
|
||||||
identische Aufruf wird gestoppt. Ein einzelnes Resultat ist auf 10.000, alle
|
identische Aufruf wird gestoppt. Ein einzelnes Resultat ist auf 10.000, alle
|
||||||
Resultate zusammen auf 36.000 Zeichen begrenzt.
|
Resultate zusammen auf 36.000 Zeichen begrenzt.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
title: MikeAI Stability Guard
|
title: MikeAI Stability Guard
|
||||||
author: MikeAI
|
author: MikeAI
|
||||||
version: 2.0.0
|
version: 2.1.0
|
||||||
description: Bounds tool output and context use and breaks repeated tool-call loops.
|
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
|
max_total_tool_chars: int = 36000
|
||||||
compacted_tool_chars: int = 2000
|
compacted_tool_chars: int = 2000
|
||||||
duplicate_tool_call_limit: int = 2
|
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):
|
def __init__(self):
|
||||||
self.valves = self.Valves()
|
self.valves = self.Valves()
|
||||||
@@ -149,7 +153,11 @@ class Filter:
|
|||||||
"pandas.read_csv(path, sep=None, engine='python') for discovery, then parse "
|
"pandas.read_csv(path, sep=None, engine='python') for discovery, then parse "
|
||||||
"again with explicit verified parameters. Compute exact aggregates, validate "
|
"again with explicit verified parameters. Compute exact aggregates, validate "
|
||||||
"that totals reconcile, and always return a visible final answer or a clear "
|
"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", [])
|
messages = body.setdefault("messages", [])
|
||||||
for message in messages:
|
for message in messages:
|
||||||
@@ -339,6 +347,11 @@ class Filter:
|
|||||||
breaker_reason = None
|
breaker_reason = None
|
||||||
if duplicate >= self.valves.duplicate_tool_call_limit:
|
if duplicate >= self.valves.duplicate_tool_call_limit:
|
||||||
breaker_reason = f"derselbe Aufruf wurde {duplicate}-mal wiederholt"
|
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:
|
elif len(signatures) >= self.valves.max_tool_calls_per_turn:
|
||||||
breaker_reason = f"{len(signatures)} Werkzeugaufrufe in einem Schritt"
|
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 "
|
"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 "
|
"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. "
|
"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. "
|
"Compute exact aggregates, reconcile the result, and always provide a visible final answer. "
|
||||||
"For GitHub repository implementation details, README files, source trees, "
|
"For GitHub repository implementation details, README files, source trees, "
|
||||||
"API routes, or code search, use the dedicated official GitHub repository "
|
"API routes, or code search, use the dedicated official GitHub repository "
|
||||||
|
|||||||
Reference in New Issue
Block a user