@miller-tech/uap 1.20.9 → 1.20.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
|
@@ -147,10 +147,10 @@ PROXY_TOOL_STATE_FORCED_BUDGET = int(
|
|
|
147
147
|
)
|
|
148
148
|
PROXY_TOOL_STATE_AUTO_BUDGET = int(os.environ.get("PROXY_TOOL_STATE_AUTO_BUDGET", "2"))
|
|
149
149
|
PROXY_TOOL_STATE_STAGNATION_THRESHOLD = int(
|
|
150
|
-
os.environ.get("PROXY_TOOL_STATE_STAGNATION_THRESHOLD", "
|
|
150
|
+
os.environ.get("PROXY_TOOL_STATE_STAGNATION_THRESHOLD", "5")
|
|
151
151
|
)
|
|
152
152
|
PROXY_TOOL_STATE_CYCLE_WINDOW = int(
|
|
153
|
-
os.environ.get("PROXY_TOOL_STATE_CYCLE_WINDOW", "
|
|
153
|
+
os.environ.get("PROXY_TOOL_STATE_CYCLE_WINDOW", "4")
|
|
154
154
|
)
|
|
155
155
|
PROXY_TOOL_STATE_FINALIZE_THRESHOLD = int(
|
|
156
156
|
os.environ.get("PROXY_TOOL_STATE_FINALIZE_THRESHOLD", "18")
|
|
@@ -280,9 +280,6 @@ PROXY_FORCED_TOOL_DAMPENER_REJECTIONS = int(
|
|
|
280
280
|
PROXY_TOOL_STARVATION_THRESHOLD = int(
|
|
281
281
|
os.environ.get("PROXY_TOOL_STARVATION_THRESHOLD", "5")
|
|
282
282
|
)
|
|
283
|
-
PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD = float(
|
|
284
|
-
os.environ.get("PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD", "0.70")
|
|
285
|
-
)
|
|
286
283
|
PROXY_SESSION_CONTAMINATION_BREAKER = os.environ.get(
|
|
287
284
|
"PROXY_SESSION_CONTAMINATION_BREAKER", "on"
|
|
288
285
|
).lower() not in {
|
|
@@ -2056,7 +2053,7 @@ def _resolve_state_machine_tool_choice(
|
|
|
2056
2053
|
monitor.tool_state_stagnation_streak,
|
|
2057
2054
|
monitor.tool_state_review_cycles,
|
|
2058
2055
|
)
|
|
2059
|
-
return "
|
|
2056
|
+
return "required", reason
|
|
2060
2057
|
|
|
2061
2058
|
if monitor.tool_state_forced_budget_remaining <= 0:
|
|
2062
2059
|
monitor.set_tool_turn_phase("review", reason="forced_budget_exhausted")
|
|
@@ -2071,7 +2068,7 @@ def _resolve_state_machine_tool_choice(
|
|
|
2071
2068
|
"TOOL STATE MACHINE: forced budget exhausted, entering review (cycles=%d)",
|
|
2072
2069
|
monitor.tool_state_review_cycles,
|
|
2073
2070
|
)
|
|
2074
|
-
return "
|
|
2071
|
+
return "required", "forced_budget_exhausted"
|
|
2075
2072
|
|
|
2076
2073
|
monitor.tool_state_forced_budget_remaining -= 1
|
|
2077
2074
|
return "required", "act"
|
|
@@ -2091,7 +2088,7 @@ def _resolve_state_machine_tool_choice(
|
|
|
2091
2088
|
1, PROXY_TOOL_STATE_FORCED_BUDGET // 2
|
|
2092
2089
|
)
|
|
2093
2090
|
return "required", "review_complete"
|
|
2094
|
-
return "
|
|
2091
|
+
return "required", "review"
|
|
2095
2092
|
|
|
2096
2093
|
if monitor.tool_turn_phase == "finalize":
|
|
2097
2094
|
if monitor.tool_state_auto_budget_remaining <= 0:
|
|
@@ -2388,22 +2385,6 @@ def build_openai_request(
|
|
|
2388
2385
|
if not has_tool_results:
|
|
2389
2386
|
monitor.reset_tool_turn_state(reason="no_tool_results")
|
|
2390
2387
|
|
|
2391
|
-
# CONTEXT-AWARE RELAXATION: when context utilization is high and
|
|
2392
|
-
# tool_choice was forced to required, relax to auto to let the model
|
|
2393
|
-
# emit shorter text responses instead of consuming more tokens.
|
|
2394
|
-
if openai_body.get("tool_choice") == "required":
|
|
2395
|
-
ctx_utilization = (
|
|
2396
|
-
monitor.last_input_tokens / monitor.context_window
|
|
2397
|
-
if monitor.context_window > 0
|
|
2398
|
-
else 0.0
|
|
2399
|
-
)
|
|
2400
|
-
if ctx_utilization >= PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD:
|
|
2401
|
-
openai_body["tool_choice"] = "auto"
|
|
2402
|
-
logger.warning(
|
|
2403
|
-
"CONTEXT-AWARE RELAXATION: tool_choice=auto (utilization=%.1f%% >= %.0f%% threshold)",
|
|
2404
|
-
ctx_utilization * 100,
|
|
2405
|
-
PROXY_CONTEXT_HIGH_RELAXATION_THRESHOLD * 100,
|
|
2406
|
-
)
|
|
2407
2388
|
|
|
2408
2389
|
if PROXY_DISABLE_THINKING_ON_TOOL_TURNS:
|
|
2409
2390
|
openai_body["enable_thinking"] = False
|
|
@@ -1861,7 +1861,8 @@ class TestToolTurnControls(unittest.TestCase):
|
|
|
1861
1861
|
|
|
1862
1862
|
self.assertEqual(openai_1.get("tool_choice"), "required")
|
|
1863
1863
|
self.assertEqual(openai_2.get("tool_choice"), "required")
|
|
1864
|
-
|
|
1864
|
+
# Review phase now keeps required to prevent end-turn escape
|
|
1865
|
+
self.assertEqual(openai_3.get("tool_choice"), "required")
|
|
1865
1866
|
finally:
|
|
1866
1867
|
setattr(proxy, "PROXY_TOOL_STATE_MACHINE", old_state)
|
|
1867
1868
|
setattr(proxy, "PROXY_TOOL_STATE_MIN_MESSAGES", old_min_msgs)
|
|
@@ -1938,7 +1939,8 @@ class TestToolTurnControls(unittest.TestCase):
|
|
|
1938
1939
|
}
|
|
1939
1940
|
|
|
1940
1941
|
openai = proxy.build_openai_request(body, monitor)
|
|
1941
|
-
|
|
1942
|
+
# Review phase now keeps required to prevent end-turn escape
|
|
1943
|
+
self.assertEqual(openai.get("tool_choice"), "required")
|
|
1942
1944
|
self.assertEqual(monitor.tool_turn_phase, "review")
|
|
1943
1945
|
finally:
|
|
1944
1946
|
setattr(proxy, "PROXY_TOOL_STATE_MACHINE", old_state)
|
|
@@ -2067,7 +2069,8 @@ class TestToolTurnControls(unittest.TestCase):
|
|
|
2067
2069
|
}
|
|
2068
2070
|
|
|
2069
2071
|
openai = proxy.build_openai_request(body, monitor)
|
|
2070
|
-
|
|
2072
|
+
# Review phase now keeps required to prevent end-turn escape
|
|
2073
|
+
self.assertEqual(openai.get("tool_choice"), "required")
|
|
2071
2074
|
self.assertEqual(monitor.tool_turn_phase, "review")
|
|
2072
2075
|
self.assertEqual(monitor.tool_state_review_cycles, 1)
|
|
2073
2076
|
finally:
|
|
@@ -3166,44 +3169,6 @@ class TestToolStarvationBreaker(unittest.TestCase):
|
|
|
3166
3169
|
self.assertIn("tools", result)
|
|
3167
3170
|
|
|
3168
3171
|
|
|
3169
|
-
class TestContextAwareRelaxation(unittest.TestCase):
|
|
3170
|
-
"""Tests for context-aware tool_choice relaxation."""
|
|
3171
|
-
|
|
3172
|
-
def test_relaxes_at_high_utilization(self):
|
|
3173
|
-
monitor = proxy.SessionMonitor()
|
|
3174
|
-
monitor.context_window = 100000
|
|
3175
|
-
monitor.last_input_tokens = 75000 # 75% > 70% threshold
|
|
3176
|
-
body = {
|
|
3177
|
-
"model": "qwen3.5",
|
|
3178
|
-
"messages": [
|
|
3179
|
-
{"role": "user", "content": "hello"},
|
|
3180
|
-
{"role": "assistant", "content": "text only"},
|
|
3181
|
-
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "x", "content": "ok"}]},
|
|
3182
|
-
],
|
|
3183
|
-
"tools": [{"name": "Bash", "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}}}],
|
|
3184
|
-
}
|
|
3185
|
-
result = proxy.build_openai_request(body, monitor)
|
|
3186
|
-
# Should be auto, not required
|
|
3187
|
-
self.assertEqual(result.get("tool_choice"), "auto")
|
|
3188
|
-
|
|
3189
|
-
def test_no_relaxation_below_threshold(self):
|
|
3190
|
-
monitor = proxy.SessionMonitor()
|
|
3191
|
-
monitor.context_window = 100000
|
|
3192
|
-
monitor.last_input_tokens = 50000 # 50% < 70%
|
|
3193
|
-
body = {
|
|
3194
|
-
"model": "qwen3.5",
|
|
3195
|
-
"messages": [
|
|
3196
|
-
{"role": "user", "content": "hello"},
|
|
3197
|
-
{"role": "assistant", "content": "text only"},
|
|
3198
|
-
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "x", "content": "ok"}]},
|
|
3199
|
-
],
|
|
3200
|
-
"tools": [{"name": "Bash", "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}}}],
|
|
3201
|
-
}
|
|
3202
|
-
result = proxy.build_openai_request(body, monitor)
|
|
3203
|
-
# Should still be required (state machine forces it)
|
|
3204
|
-
self.assertEqual(result.get("tool_choice"), "required")
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
3172
|
if __name__ == "__main__":
|
|
3208
3173
|
unittest.main()
|
|
3209
3174
|
|