@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.15.0",
3
+ "version": "1.15.1",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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", "1")
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
- "I could not produce a valid tool-call format in this turn. "
1742
- "Please continue; I will issue exactly one valid tool call next."
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):