@miller-tech/uap 1.15.10 → 1.15.11

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.10",
3
+ "version": "1.15.11",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1134,6 +1134,28 @@ def _has_tool_definitions(anthropic_body: dict) -> bool:
1134
1134
  return isinstance(tools, list) and len(tools) > 0
1135
1135
 
1136
1136
 
1137
+ def _should_use_guarded_non_stream(
1138
+ is_stream: bool,
1139
+ anthropic_body: dict,
1140
+ openai_body: dict,
1141
+ ) -> bool:
1142
+ if not is_stream:
1143
+ return False
1144
+
1145
+ if PROXY_FORCE_NON_STREAM:
1146
+ return True
1147
+
1148
+ has_tools = _has_tool_definitions(anthropic_body)
1149
+ if PROXY_MALFORMED_TOOL_STREAM_STRICT and has_tools:
1150
+ return True
1151
+
1152
+ return (
1153
+ has_tools
1154
+ and openai_body.get("tool_choice") == "required"
1155
+ and (PROXY_MALFORMED_TOOL_GUARDRAIL or PROXY_GUARDRAIL_RETRY)
1156
+ )
1157
+
1158
+
1137
1159
  def _message_has_tool_result(content) -> bool:
1138
1160
  return isinstance(content, list) and any(
1139
1161
  isinstance(block, dict) and block.get("type") == "tool_result"
@@ -3550,9 +3572,10 @@ async def messages(request: Request):
3550
3572
  media_type="application/json",
3551
3573
  )
3552
3574
 
3553
- use_guarded_non_stream = is_stream and (
3554
- PROXY_FORCE_NON_STREAM
3555
- or (PROXY_MALFORMED_TOOL_STREAM_STRICT and "tools" in body)
3575
+ use_guarded_non_stream = _should_use_guarded_non_stream(
3576
+ is_stream,
3577
+ body,
3578
+ openai_body,
3556
3579
  )
3557
3580
  if use_guarded_non_stream:
3558
3581
  strict_body = dict(openai_body)
@@ -3623,10 +3646,14 @@ async def messages(request: Request):
3623
3646
  logger.info(
3624
3647
  "FORCED NON-STREAM: served stream response via guarded non-stream path"
3625
3648
  )
3626
- else:
3649
+ elif PROXY_MALFORMED_TOOL_STREAM_STRICT and _has_tool_definitions(body):
3627
3650
  logger.info(
3628
3651
  "STRICT STREAM GUARDRAIL: served stream response via guarded non-stream path"
3629
3652
  )
3653
+ else:
3654
+ logger.info(
3655
+ "REQUIRED TOOL STREAM GUARDRAIL: served stream response via guarded non-stream path"
3656
+ )
3630
3657
 
3631
3658
  return StreamingResponse(
3632
3659
  stream_anthropic_message(anthropic_resp),
@@ -100,6 +100,54 @@ class TestProxyConfigTuning(unittest.TestCase):
100
100
  setattr(proxy, "PROXY_CONTEXT_PRUNE_TARGET_FRACTION", old_target)
101
101
 
102
102
 
103
+ class TestStreamGuardedPathSelection(unittest.TestCase):
104
+ def test_required_tool_turn_uses_guarded_non_stream(self):
105
+ old_force = getattr(proxy, "PROXY_FORCE_NON_STREAM")
106
+ old_strict = getattr(proxy, "PROXY_MALFORMED_TOOL_STREAM_STRICT")
107
+ old_guard = getattr(proxy, "PROXY_MALFORMED_TOOL_GUARDRAIL")
108
+ old_retry = getattr(proxy, "PROXY_GUARDRAIL_RETRY")
109
+ try:
110
+ setattr(proxy, "PROXY_FORCE_NON_STREAM", False)
111
+ setattr(proxy, "PROXY_MALFORMED_TOOL_STREAM_STRICT", False)
112
+ setattr(proxy, "PROXY_MALFORMED_TOOL_GUARDRAIL", True)
113
+ setattr(proxy, "PROXY_GUARDRAIL_RETRY", True)
114
+
115
+ selected = proxy._should_use_guarded_non_stream(
116
+ True,
117
+ {"tools": [{"name": "Read", "input_schema": {"type": "object"}}]},
118
+ {"tool_choice": "required"},
119
+ )
120
+ self.assertTrue(selected)
121
+ finally:
122
+ setattr(proxy, "PROXY_FORCE_NON_STREAM", old_force)
123
+ setattr(proxy, "PROXY_MALFORMED_TOOL_STREAM_STRICT", old_strict)
124
+ setattr(proxy, "PROXY_MALFORMED_TOOL_GUARDRAIL", old_guard)
125
+ setattr(proxy, "PROXY_GUARDRAIL_RETRY", old_retry)
126
+
127
+ def test_auto_tool_turn_keeps_true_stream_when_strict_off(self):
128
+ old_force = getattr(proxy, "PROXY_FORCE_NON_STREAM")
129
+ old_strict = getattr(proxy, "PROXY_MALFORMED_TOOL_STREAM_STRICT")
130
+ old_guard = getattr(proxy, "PROXY_MALFORMED_TOOL_GUARDRAIL")
131
+ old_retry = getattr(proxy, "PROXY_GUARDRAIL_RETRY")
132
+ try:
133
+ setattr(proxy, "PROXY_FORCE_NON_STREAM", False)
134
+ setattr(proxy, "PROXY_MALFORMED_TOOL_STREAM_STRICT", False)
135
+ setattr(proxy, "PROXY_MALFORMED_TOOL_GUARDRAIL", True)
136
+ setattr(proxy, "PROXY_GUARDRAIL_RETRY", True)
137
+
138
+ selected = proxy._should_use_guarded_non_stream(
139
+ True,
140
+ {"tools": [{"name": "Read", "input_schema": {"type": "object"}}]},
141
+ {"tool_choice": "auto"},
142
+ )
143
+ self.assertFalse(selected)
144
+ finally:
145
+ setattr(proxy, "PROXY_FORCE_NON_STREAM", old_force)
146
+ setattr(proxy, "PROXY_MALFORMED_TOOL_STREAM_STRICT", old_strict)
147
+ setattr(proxy, "PROXY_MALFORMED_TOOL_GUARDRAIL", old_guard)
148
+ setattr(proxy, "PROXY_GUARDRAIL_RETRY", old_retry)
149
+
150
+
103
151
  class TestMalformedToolGuardrail(unittest.TestCase):
104
152
  def test_detects_malformed_tool_payload(self):
105
153
  openai_resp = {