@miller-tech/uap 1.15.0 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -162,7 +162,7 @@ PROXY_MALFORMED_TOOL_GUARDRAIL = os.environ.get(
|
|
|
162
162
|
"no",
|
|
163
163
|
}
|
|
164
164
|
PROXY_MALFORMED_TOOL_RETRY_MAX = int(
|
|
165
|
-
os.environ.get("PROXY_MALFORMED_TOOL_RETRY_MAX", "
|
|
165
|
+
os.environ.get("PROXY_MALFORMED_TOOL_RETRY_MAX", "2")
|
|
166
166
|
)
|
|
167
167
|
PROXY_MALFORMED_TOOL_RETRY_MAX_TOKENS = int(
|
|
168
168
|
os.environ.get("PROXY_MALFORMED_TOOL_RETRY_MAX_TOKENS", "2048")
|
|
@@ -1647,6 +1647,13 @@ def _looks_malformed_tool_payload(text: str) -> bool:
|
|
|
1647
1647
|
return False
|
|
1648
1648
|
|
|
1649
1649
|
lowered = text.lower()
|
|
1650
|
+
apology_markers = (
|
|
1651
|
+
"i could not produce a valid tool-call format in this turn",
|
|
1652
|
+
"i will issue exactly one valid tool call next",
|
|
1653
|
+
)
|
|
1654
|
+
if any(marker in lowered for marker in apology_markers):
|
|
1655
|
+
return True
|
|
1656
|
+
|
|
1650
1657
|
primary_markers = ("</parameter", "<parameter", "<tool_call", "<function=")
|
|
1651
1658
|
if any(marker in lowered for marker in primary_markers):
|
|
1652
1659
|
return True
|
|
@@ -1705,6 +1712,18 @@ def _build_malformed_retry_body(openai_body: dict, anthropic_body: dict) -> dict
|
|
|
1705
1712
|
retry_body["tool_choice"] = "required"
|
|
1706
1713
|
retry_body["temperature"] = PROXY_MALFORMED_TOOL_RETRY_TEMPERATURE
|
|
1707
1714
|
|
|
1715
|
+
malformed_retry_instruction = {
|
|
1716
|
+
"role": "user",
|
|
1717
|
+
"content": (
|
|
1718
|
+
"Your previous response had invalid tool-call formatting. "
|
|
1719
|
+
"Respond with exactly one valid tool call using the provided tools. "
|
|
1720
|
+
"Do not output prose, markdown, XML tags, or schema snippets."
|
|
1721
|
+
),
|
|
1722
|
+
}
|
|
1723
|
+
existing_messages = retry_body.get("messages")
|
|
1724
|
+
if isinstance(existing_messages, list) and existing_messages:
|
|
1725
|
+
retry_body["messages"] = [*existing_messages, malformed_retry_instruction]
|
|
1726
|
+
|
|
1708
1727
|
if PROXY_MALFORMED_TOOL_RETRY_MAX_TOKENS > 0:
|
|
1709
1728
|
current_max = int(
|
|
1710
1729
|
retry_body.get("max_tokens", PROXY_MALFORMED_TOOL_RETRY_MAX_TOKENS)
|
|
@@ -1738,8 +1757,8 @@ def _build_clean_guardrail_openai_response(openai_resp: dict) -> dict:
|
|
|
1738
1757
|
"message": {
|
|
1739
1758
|
"role": "assistant",
|
|
1740
1759
|
"content": (
|
|
1741
|
-
"
|
|
1742
|
-
"Please
|
|
1760
|
+
"Tool-call formatting failed after automatic retries. "
|
|
1761
|
+
"Please retry the same request."
|
|
1743
1762
|
),
|
|
1744
1763
|
},
|
|
1745
1764
|
}
|
|
@@ -164,6 +164,27 @@ class TestMalformedToolGuardrail(unittest.TestCase):
|
|
|
164
164
|
}
|
|
165
165
|
self.assertTrue(proxy._is_malformed_tool_response(openai_resp, anthropic_body))
|
|
166
166
|
|
|
167
|
+
def test_detects_tool_call_apology_text_as_malformed(self):
|
|
168
|
+
openai_resp = {
|
|
169
|
+
"choices": [
|
|
170
|
+
{
|
|
171
|
+
"finish_reason": "stop",
|
|
172
|
+
"message": {
|
|
173
|
+
"content": (
|
|
174
|
+
"I could not produce a valid tool-call format in this turn. "
|
|
175
|
+
"Please continue; I will issue exactly one valid tool call next."
|
|
176
|
+
),
|
|
177
|
+
"tool_calls": [],
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
anthropic_body = {
|
|
183
|
+
"tools": [{"name": "Read", "input_schema": {"type": "object"}}],
|
|
184
|
+
"messages": [{"role": "user", "content": "fix this"}],
|
|
185
|
+
}
|
|
186
|
+
self.assertTrue(proxy._is_malformed_tool_response(openai_resp, anthropic_body))
|
|
187
|
+
|
|
167
188
|
def test_clean_tool_call_response_is_not_malformed(self):
|
|
168
189
|
openai_resp = {
|
|
169
190
|
"choices": [
|
|
@@ -385,6 +406,7 @@ class TestMalformedToolGuardrail(unittest.TestCase):
|
|
|
385
406
|
openai_body = {
|
|
386
407
|
"model": "test",
|
|
387
408
|
"max_tokens": 4000,
|
|
409
|
+
"messages": [{"role": "user", "content": "fix the issue"}],
|
|
388
410
|
"tools": [{"type": "function", "function": {"name": "Read"}}],
|
|
389
411
|
}
|
|
390
412
|
anthropic_body = {
|
|
@@ -402,11 +424,24 @@ class TestMalformedToolGuardrail(unittest.TestCase):
|
|
|
402
424
|
self.assertEqual(retry["max_tokens"], 512)
|
|
403
425
|
self.assertEqual(len(retry["tools"]), 3)
|
|
404
426
|
self.assertFalse(retry["enable_thinking"])
|
|
427
|
+
self.assertEqual(retry["messages"][-1]["role"], "user")
|
|
428
|
+
self.assertIn(
|
|
429
|
+
"invalid tool-call formatting",
|
|
430
|
+
retry["messages"][-1]["content"],
|
|
431
|
+
)
|
|
405
432
|
finally:
|
|
406
433
|
setattr(proxy, "PROXY_MALFORMED_TOOL_RETRY_MAX_TOKENS", old_cap)
|
|
407
434
|
setattr(proxy, "PROXY_MALFORMED_TOOL_RETRY_TEMPERATURE", old_temp)
|
|
408
435
|
setattr(proxy, "PROXY_DISABLE_THINKING_ON_TOOL_TURNS", old_disable)
|
|
409
436
|
|
|
437
|
+
def test_clean_guardrail_response_does_not_promise_future_tool_call(self):
|
|
438
|
+
guardrail = proxy._build_clean_guardrail_openai_response(
|
|
439
|
+
{"model": "test-model"}
|
|
440
|
+
)
|
|
441
|
+
text = guardrail["choices"][0]["message"]["content"]
|
|
442
|
+
self.assertIn("Please retry the same request", text)
|
|
443
|
+
self.assertNotIn("I will issue exactly one valid tool call next", text)
|
|
444
|
+
|
|
410
445
|
|
|
411
446
|
class TestToolTurnControls(unittest.TestCase):
|
|
412
447
|
def test_tool_narrowing_reduces_tool_count(self):
|