@miller-tech/uap 1.15.1 → 1.15.2
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
|
@@ -898,6 +898,28 @@ def _extract_text(content) -> str:
|
|
|
898
898
|
return str(content)
|
|
899
899
|
|
|
900
900
|
|
|
901
|
+
_TOOL_CALL_APOLOGY_MARKERS = (
|
|
902
|
+
"i could not produce a valid tool-call format in this turn",
|
|
903
|
+
"i will issue exactly one valid tool call next",
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
_TOOL_CALL_RETRY_MESSAGE = (
|
|
907
|
+
"Tool-call formatting failed after automatic retries. "
|
|
908
|
+
"Please retry the same request."
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
def _contains_tool_call_apology(text: str) -> bool:
|
|
913
|
+
if not text:
|
|
914
|
+
return False
|
|
915
|
+
lowered = text.lower()
|
|
916
|
+
return any(marker in lowered for marker in _TOOL_CALL_APOLOGY_MARKERS)
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
def _sanitize_tool_call_apology_text(text: str) -> str:
|
|
920
|
+
return _TOOL_CALL_RETRY_MESSAGE if _contains_tool_call_apology(text) else text
|
|
921
|
+
|
|
922
|
+
|
|
901
923
|
def _has_tool_definitions(anthropic_body: dict) -> bool:
|
|
902
924
|
tools = anthropic_body.get("tools")
|
|
903
925
|
return isinstance(tools, list) and len(tools) > 0
|
|
@@ -1647,11 +1669,7 @@ def _looks_malformed_tool_payload(text: str) -> bool:
|
|
|
1647
1669
|
return False
|
|
1648
1670
|
|
|
1649
1671
|
lowered = text.lower()
|
|
1650
|
-
|
|
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):
|
|
1672
|
+
if _contains_tool_call_apology(text):
|
|
1655
1673
|
return True
|
|
1656
1674
|
|
|
1657
1675
|
primary_markers = ("</parameter", "<parameter", "<tool_call", "<function=")
|
|
@@ -1756,10 +1774,7 @@ def _build_clean_guardrail_openai_response(openai_resp: dict) -> dict:
|
|
|
1756
1774
|
"finish_reason": "stop",
|
|
1757
1775
|
"message": {
|
|
1758
1776
|
"role": "assistant",
|
|
1759
|
-
"content":
|
|
1760
|
-
"Tool-call formatting failed after automatic retries. "
|
|
1761
|
-
"Please retry the same request."
|
|
1762
|
-
),
|
|
1777
|
+
"content": _TOOL_CALL_RETRY_MESSAGE,
|
|
1763
1778
|
},
|
|
1764
1779
|
}
|
|
1765
1780
|
],
|
|
@@ -1959,7 +1974,17 @@ def openai_to_anthropic_response(openai_resp: dict, model: str) -> dict:
|
|
|
1959
1974
|
|
|
1960
1975
|
content = []
|
|
1961
1976
|
if message.get("content"):
|
|
1962
|
-
|
|
1977
|
+
raw_text = (
|
|
1978
|
+
message["content"]
|
|
1979
|
+
if isinstance(message["content"], str)
|
|
1980
|
+
else str(message["content"])
|
|
1981
|
+
)
|
|
1982
|
+
sanitized_text = _sanitize_tool_call_apology_text(raw_text)
|
|
1983
|
+
if sanitized_text != raw_text:
|
|
1984
|
+
logger.warning(
|
|
1985
|
+
"SANITIZE: replaced known malformed tool-call apology text in assistant response"
|
|
1986
|
+
)
|
|
1987
|
+
content.append({"type": "text", "text": sanitized_text})
|
|
1963
1988
|
|
|
1964
1989
|
# Convert tool calls
|
|
1965
1990
|
for tc in message.get("tool_calls", []):
|
|
@@ -185,6 +185,14 @@ class TestMalformedToolGuardrail(unittest.TestCase):
|
|
|
185
185
|
}
|
|
186
186
|
self.assertTrue(proxy._is_malformed_tool_response(openai_resp, anthropic_body))
|
|
187
187
|
|
|
188
|
+
def test_tool_call_apology_helper_detects_phrase(self):
|
|
189
|
+
apology_text = (
|
|
190
|
+
"I could not produce a valid tool-call format in this turn. "
|
|
191
|
+
"Please continue; I will issue exactly one valid tool call next."
|
|
192
|
+
)
|
|
193
|
+
self.assertTrue(proxy._contains_tool_call_apology(apology_text))
|
|
194
|
+
self.assertFalse(proxy._contains_tool_call_apology("normal assistant response"))
|
|
195
|
+
|
|
188
196
|
def test_clean_tool_call_response_is_not_malformed(self):
|
|
189
197
|
openai_resp = {
|
|
190
198
|
"choices": [
|
|
@@ -442,6 +450,27 @@ class TestMalformedToolGuardrail(unittest.TestCase):
|
|
|
442
450
|
self.assertIn("Please retry the same request", text)
|
|
443
451
|
self.assertNotIn("I will issue exactly one valid tool call next", text)
|
|
444
452
|
|
|
453
|
+
def test_openai_to_anthropic_response_sanitizes_tool_call_apology(self):
|
|
454
|
+
openai_resp = {
|
|
455
|
+
"choices": [
|
|
456
|
+
{
|
|
457
|
+
"finish_reason": "stop",
|
|
458
|
+
"message": {
|
|
459
|
+
"content": (
|
|
460
|
+
"I could not produce a valid tool-call format in this turn. "
|
|
461
|
+
"Please continue; I will issue exactly one valid tool call next."
|
|
462
|
+
),
|
|
463
|
+
"tool_calls": [],
|
|
464
|
+
},
|
|
465
|
+
}
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
converted = proxy.openai_to_anthropic_response(openai_resp, "test-model")
|
|
470
|
+
text = converted["content"][0]["text"]
|
|
471
|
+
self.assertIn("Please retry the same request", text)
|
|
472
|
+
self.assertNotIn("I will issue exactly one valid tool call next", text)
|
|
473
|
+
|
|
445
474
|
|
|
446
475
|
class TestToolTurnControls(unittest.TestCase):
|
|
447
476
|
def test_tool_narrowing_reduces_tool_count(self):
|