@miller-tech/uap 1.210.3 → 1.210.4
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 +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/scripts/anthropic_proxy.py +70 -11
- package/tools/agents/tests/test_models_context_window.py +123 -6
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -54,8 +54,10 @@ Configuration (Environment Variables)
|
|
|
54
54
|
PROXY_MAX_CONNECTIONS Max concurrent connections to upstream
|
|
55
55
|
Default: 20
|
|
56
56
|
|
|
57
|
-
PROXY_CONTEXT_WINDOW
|
|
58
|
-
upstream /slots
|
|
57
|
+
PROXY_CONTEXT_WINDOW Startup FALLBACK for the context window, used only
|
|
58
|
+
when the upstream /slots probe fails. The detected
|
|
59
|
+
rail wins (re-checked every 60s) and is what
|
|
60
|
+
/v1/models advertises. Not a cap.
|
|
59
61
|
Default: 0 (auto-detect)
|
|
60
62
|
|
|
61
63
|
PROXY_CONTEXT_PRUNE_THRESHOLD Fraction of context window at which
|
|
@@ -2407,6 +2409,11 @@ class SessionMonitor:
|
|
|
2407
2409
|
|
|
2408
2410
|
session_monitors: dict[str, SessionMonitor] = {}
|
|
2409
2411
|
default_context_window = 0
|
|
2412
|
+
# True only when `default_context_window` came from the SERVER (a /slots read)
|
|
2413
|
+
# or from an explicit operator setting — never when it is the hardcoded
|
|
2414
|
+
# fallback. /v1/models publishes the window to clients as fact, and a guess
|
|
2415
|
+
# published as fact is the failure `_model_entry` exists to prevent.
|
|
2416
|
+
_context_window_measured: bool = False
|
|
2410
2417
|
last_session_id = ""
|
|
2411
2418
|
_last_ctx_recheck_ts: float = 0.0
|
|
2412
2419
|
_CTX_RECHECK_INTERVAL: float = 60.0 # Re-detect context window every 60s
|
|
@@ -2476,13 +2483,41 @@ def _cleanup_stale_monitors(now_ts: float) -> None:
|
|
|
2476
2483
|
session_monitors.pop(sid, None)
|
|
2477
2484
|
|
|
2478
2485
|
|
|
2486
|
+
def _effective_context_window() -> int:
|
|
2487
|
+
"""The window this proxy actually ENFORCES, in tokens (0 = unknown).
|
|
2488
|
+
|
|
2489
|
+
The detected value wins over the env pin because the pin goes stale: the
|
|
2490
|
+
upstream server can restart with a different --ctx-size, and
|
|
2491
|
+
`_maybe_recheck_context_window` tracks that. `PROXY_CONTEXT_WINDOW` is the
|
|
2492
|
+
fallback for when detection has not run or could not reach the server.
|
|
2493
|
+
|
|
2494
|
+
This is the idiom already used at the count_tokens compaction-forcing call
|
|
2495
|
+
sites; it exists as a function so the process-wide consumers — the forcing
|
|
2496
|
+
scale and the /v1/models advertisement — resolve the window from ONE
|
|
2497
|
+
source. They did not: /v1/models stamped the raw env value while everything
|
|
2498
|
+
else used the detected rail. Live on 2026-08-16 that meant the endpoint
|
|
2499
|
+
advertised 65,536 while the proxy enforced 199,680, and the two errors
|
|
2500
|
+
compounded. A client sizing itself to 65,536 while receiving counts
|
|
2501
|
+
inflated 1.73x for compaction forcing compacts at ~35k REAL tokens — 18%
|
|
2502
|
+
of the rail it was given.
|
|
2503
|
+
|
|
2504
|
+
NOT every window in the process: a request carrying a model profile
|
|
2505
|
+
overrides `monitor.context_window` (see `messages`), and the pruner reads
|
|
2506
|
+
that per-session value. This function is the process default, not a claim
|
|
2507
|
+
about every session.
|
|
2508
|
+
"""
|
|
2509
|
+
if default_context_window > 0:
|
|
2510
|
+
return default_context_window
|
|
2511
|
+
return max(PROXY_CONTEXT_WINDOW, 0)
|
|
2512
|
+
|
|
2513
|
+
|
|
2479
2514
|
async def _maybe_recheck_context_window() -> None:
|
|
2480
2515
|
"""Periodically re-query the upstream server's context window.
|
|
2481
2516
|
|
|
2482
2517
|
Handles server restarts with different --ctx-size mid-session.
|
|
2483
2518
|
Non-blocking: skips if the check interval hasn't elapsed.
|
|
2484
2519
|
"""
|
|
2485
|
-
global default_context_window, _last_ctx_recheck_ts
|
|
2520
|
+
global default_context_window, _last_ctx_recheck_ts, _context_window_measured
|
|
2486
2521
|
now = time.time()
|
|
2487
2522
|
if now - _last_ctx_recheck_ts < _CTX_RECHECK_INTERVAL:
|
|
2488
2523
|
return
|
|
@@ -2496,6 +2531,8 @@ async def _maybe_recheck_context_window() -> None:
|
|
|
2496
2531
|
slots = resp.json()
|
|
2497
2532
|
if slots and isinstance(slots, list):
|
|
2498
2533
|
n_ctx = slots[0].get("n_ctx", 0)
|
|
2534
|
+
if n_ctx > 0:
|
|
2535
|
+
_context_window_measured = True
|
|
2499
2536
|
if n_ctx > 0 and n_ctx != default_context_window:
|
|
2500
2537
|
old = default_context_window
|
|
2501
2538
|
default_context_window = n_ctx
|
|
@@ -2533,10 +2570,17 @@ async def detect_context_window(client: httpx.AsyncClient) -> int:
|
|
|
2533
2570
|
|
|
2534
2571
|
Queries the /slots endpoint (llama.cpp) to get the actual n_ctx value.
|
|
2535
2572
|
Falls back to PROXY_CONTEXT_WINDOW env var, then to a safe default.
|
|
2573
|
+
|
|
2574
|
+
The probe runs FIRST, which is what this docstring always claimed but the
|
|
2575
|
+
code did not do: it returned the env value without asking the server, so a
|
|
2576
|
+
stale setting governed until the first /v1/messages request triggered the
|
|
2577
|
+
60s recheck — and SDK clients read /v1/models before ever sending a message.
|
|
2578
|
+
The env value is a hand-maintained copy of this same number (the operator
|
|
2579
|
+
file that carries it says "re-derive it whenever --parallel or --ctx-size
|
|
2580
|
+
moves"), so asking the server is strictly better information; the setting
|
|
2581
|
+
stays as the answer for when the server cannot be reached.
|
|
2536
2582
|
"""
|
|
2537
|
-
|
|
2538
|
-
logger.info("Using configured context window: %d tokens", PROXY_CONTEXT_WINDOW)
|
|
2539
|
-
return PROXY_CONTEXT_WINDOW
|
|
2583
|
+
global _context_window_measured
|
|
2540
2584
|
|
|
2541
2585
|
try:
|
|
2542
2586
|
slots_url = LLAMA_CPP_BASE.replace("/v1", "/slots")
|
|
@@ -2551,13 +2595,21 @@ async def detect_context_window(client: httpx.AsyncClient) -> int:
|
|
|
2551
2595
|
n_ctx,
|
|
2552
2596
|
len(slots),
|
|
2553
2597
|
)
|
|
2598
|
+
_context_window_measured = True
|
|
2554
2599
|
return n_ctx
|
|
2555
2600
|
except Exception as exc:
|
|
2556
2601
|
logger.warning("Failed to auto-detect context window: %s", exc)
|
|
2557
2602
|
|
|
2558
|
-
|
|
2603
|
+
if PROXY_CONTEXT_WINDOW > 0:
|
|
2604
|
+
logger.info("Using configured context window: %d tokens", PROXY_CONTEXT_WINDOW)
|
|
2605
|
+
_context_window_measured = True # an operator setting is an assertion
|
|
2606
|
+
return PROXY_CONTEXT_WINDOW
|
|
2607
|
+
|
|
2608
|
+
# Safe default: 128K (common for modern models). NOT measured — the pruner
|
|
2609
|
+
# may use it as a backstop, but it must never be published as fact.
|
|
2559
2610
|
default = 131072
|
|
2560
2611
|
logger.warning("Using default context window: %d tokens", default)
|
|
2612
|
+
_context_window_measured = False
|
|
2561
2613
|
return default
|
|
2562
2614
|
|
|
2563
2615
|
|
|
@@ -11485,7 +11537,7 @@ async def count_tokens(request: Request):
|
|
|
11485
11537
|
"fires at ~%d real tokens, before the pruner",
|
|
11486
11538
|
scale,
|
|
11487
11539
|
PROXY_CLIENT_ASSUMED_WINDOW,
|
|
11488
|
-
|
|
11540
|
+
_effective_context_window(),
|
|
11489
11541
|
int(PROXY_CLIENT_ASSUMED_WINDOW * 0.925 / scale),
|
|
11490
11542
|
)
|
|
11491
11543
|
return {"input_tokens": scaled}
|
|
@@ -11510,7 +11562,7 @@ def _count_tokens_scale() -> float:
|
|
|
11510
11562
|
return max(1.0, float(raw))
|
|
11511
11563
|
except ValueError:
|
|
11512
11564
|
return 1.0
|
|
11513
|
-
window =
|
|
11565
|
+
window = _effective_context_window()
|
|
11514
11566
|
if window <= 0:
|
|
11515
11567
|
return 1.0
|
|
11516
11568
|
frac = (
|
|
@@ -12845,9 +12897,10 @@ def _model_entry(model_id: str) -> dict:
|
|
|
12845
12897
|
nothing and leave the client on its own defaults.
|
|
12846
12898
|
"""
|
|
12847
12899
|
entry = {"id": model_id, "object": "model"}
|
|
12848
|
-
|
|
12900
|
+
window = _effective_context_window() if _context_window_measured else 0
|
|
12901
|
+
if window > 0 and not _should_passthrough_model(model_id):
|
|
12849
12902
|
for key in _CONTEXT_WINDOW_KEYS:
|
|
12850
|
-
entry[key] =
|
|
12903
|
+
entry[key] = window
|
|
12851
12904
|
return entry
|
|
12852
12905
|
|
|
12853
12906
|
|
|
@@ -12867,6 +12920,12 @@ async def models():
|
|
|
12867
12920
|
ANTHROPIC_PASSTHROUGH_MODELS=__local_only__ is set, all IDs (including
|
|
12868
12921
|
the Claude ones below) are served by the local llama.cpp backend.
|
|
12869
12922
|
"""
|
|
12923
|
+
# Refresh the rail before answering. This endpoint is the FIRST thing SDK
|
|
12924
|
+
# clients call (it is in _PROXY_AUTH_OPEN_PATHS precisely so discovery
|
|
12925
|
+
# works), and clients cache the model list — so answering from a window
|
|
12926
|
+
# that only refreshes on /v1/messages means the number a client keeps for
|
|
12927
|
+
# the whole session is the one from before any traffic existed.
|
|
12928
|
+
await _maybe_recheck_context_window()
|
|
12870
12929
|
return {"data": [_model_entry(mid) for mid in ADVERTISED_MODEL_IDS]}
|
|
12871
12930
|
|
|
12872
12931
|
|
|
@@ -11,6 +11,7 @@ window at all.
|
|
|
11
11
|
|
|
12
12
|
A client that cannot discover the window cannot size its history to it.
|
|
13
13
|
"""
|
|
14
|
+
import asyncio
|
|
14
15
|
import importlib.util
|
|
15
16
|
import os
|
|
16
17
|
import unittest
|
|
@@ -19,9 +20,18 @@ from pathlib import Path
|
|
|
19
20
|
proxy_path = Path(__file__).resolve().parents[3] / "tools" / "agents" / "scripts" / "anthropic_proxy.py"
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def load_proxy(window="130048", passthrough=None):
|
|
23
|
-
"""Import a fresh proxy module under the given env (constants bind at import).
|
|
23
|
+
def load_proxy(window="130048", passthrough=None, measured=True):
|
|
24
|
+
"""Import a fresh proxy module under the given env (constants bind at import).
|
|
25
|
+
|
|
26
|
+
`measured` mirrors what a real startup leaves behind: True once /slots (or
|
|
27
|
+
an explicit setting) supplied the window, False when it is the hardcoded
|
|
28
|
+
guess. /v1/models only publishes a measured window.
|
|
29
|
+
"""
|
|
24
30
|
os.environ["PROXY_CONTEXT_WINDOW"] = window
|
|
31
|
+
# Pin the forcing scale: `auto` is the default, but a developer shell or a
|
|
32
|
+
# systemd unit exporting a fixed value would silently change what
|
|
33
|
+
# _count_tokens_scale returns and fail these tests for unrelated reasons.
|
|
34
|
+
os.environ["PROXY_COUNT_TOKENS_SCALE"] = "auto"
|
|
25
35
|
if passthrough is None:
|
|
26
36
|
os.environ.pop("ANTHROPIC_PASSTHROUGH_MODELS", None)
|
|
27
37
|
else:
|
|
@@ -29,6 +39,7 @@ def load_proxy(window="130048", passthrough=None):
|
|
|
29
39
|
spec = importlib.util.spec_from_file_location("anthropic_proxy_ctx", proxy_path)
|
|
30
40
|
mod = importlib.util.module_from_spec(spec)
|
|
31
41
|
spec.loader.exec_module(mod)
|
|
42
|
+
mod._context_window_measured = measured
|
|
32
43
|
return mod
|
|
33
44
|
|
|
34
45
|
|
|
@@ -36,6 +47,7 @@ class ModelsAdvertiseContextWindowTest(unittest.TestCase):
|
|
|
36
47
|
def tearDown(self):
|
|
37
48
|
os.environ.pop("PROXY_CONTEXT_WINDOW", None)
|
|
38
49
|
os.environ.pop("ANTHROPIC_PASSTHROUGH_MODELS", None)
|
|
50
|
+
os.environ.pop("PROXY_COUNT_TOKENS_SCALE", None)
|
|
39
51
|
|
|
40
52
|
def test_local_model_carries_the_window(self):
|
|
41
53
|
ap = load_proxy()
|
|
@@ -57,21 +69,77 @@ class ModelsAdvertiseContextWindowTest(unittest.TestCase):
|
|
|
57
69
|
# Stamping the local llama.cpp figure on it would make clients truncate
|
|
58
70
|
# needlessly — worse than the bug being fixed.
|
|
59
71
|
ap = load_proxy(passthrough=None) # default patterns: Claude passes through
|
|
72
|
+
ap.default_context_window = 199680 # exercise the DETECTED arm, not the fallback
|
|
60
73
|
for mid in ("claude-sonnet-4-6", "claude-haiku-4-5-20251001"):
|
|
61
74
|
self.assertNotIn("context_length", ap._model_entry(mid), mid)
|
|
62
75
|
|
|
63
76
|
def test_local_only_sentinel_means_every_id_is_local(self):
|
|
64
77
|
ap = load_proxy(passthrough="__local_only__")
|
|
78
|
+
ap.default_context_window = 199680
|
|
65
79
|
for mid in ap.ADVERTISED_MODEL_IDS:
|
|
66
|
-
self.assertEqual(ap._model_entry(mid).get("context_length"),
|
|
80
|
+
self.assertEqual(ap._model_entry(mid).get("context_length"), 199680, mid)
|
|
67
81
|
|
|
68
|
-
def
|
|
69
|
-
# Better to say nothing than to assert a wrong number.
|
|
70
|
-
|
|
82
|
+
def test_unknown_window_advertises_nothing(self):
|
|
83
|
+
# Better to say nothing than to assert a wrong number. "Unknown" means
|
|
84
|
+
# BOTH: no setting and no detected rail.
|
|
85
|
+
ap = load_proxy(window="0", measured=False)
|
|
86
|
+
ap.default_context_window = 0
|
|
71
87
|
self.assertEqual(
|
|
72
88
|
ap._model_entry("qwen36-35b-a3b-iq4xs"), {"id": "qwen36-35b-a3b-iq4xs", "object": "model"}
|
|
73
89
|
)
|
|
74
90
|
|
|
91
|
+
def test_a_guessed_window_is_never_published_as_fact(self):
|
|
92
|
+
# detect_context_window falls back to a hardcoded 131072 when the
|
|
93
|
+
# upstream is unreachable. The pruner may use that as a backstop, but
|
|
94
|
+
# advertising it would state a number nobody measured — the very
|
|
95
|
+
# failure this endpoint exists to prevent. Boot with llama down.
|
|
96
|
+
ap = load_proxy(window="0", measured=False)
|
|
97
|
+
ap.default_context_window = 131072
|
|
98
|
+
self.assertEqual(
|
|
99
|
+
ap._model_entry("qwen36-35b-a3b-iq4xs"), {"id": "qwen36-35b-a3b-iq4xs", "object": "model"}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def test_advertises_the_detected_rail_when_no_pin_is_set(self):
|
|
103
|
+
# Detection (from /slots) is the normal case: PROXY_CONTEXT_WINDOW=0 is
|
|
104
|
+
# what tells the launcher to auto-detect. Advertising nothing there left
|
|
105
|
+
# exactly the clients this endpoint exists for — the ones that size
|
|
106
|
+
# themselves from it — back on their own defaults.
|
|
107
|
+
ap = load_proxy(window="0")
|
|
108
|
+
ap.default_context_window = 199680
|
|
109
|
+
self.assertEqual(ap._model_entry("qwen36-35b-a3b-iq4xs")["context_length"], 199680)
|
|
110
|
+
|
|
111
|
+
def test_detected_rail_wins_over_a_stale_pin(self):
|
|
112
|
+
# THE BUG (live, 2026-08-16): the pin said 65,536 while the proxy
|
|
113
|
+
# enforced the detected 199,680. Clients were told the small number and
|
|
114
|
+
# compacted against it; the pruner guarded the large one. Whatever the
|
|
115
|
+
# number is, the advertised and enforced windows must be the same one.
|
|
116
|
+
ap = load_proxy(window="65536")
|
|
117
|
+
ap.default_context_window = 199680
|
|
118
|
+
self.assertEqual(ap._model_entry("qwen36-35b-a3b-iq4xs")["context_length"], 199680)
|
|
119
|
+
self.assertEqual(ap._effective_context_window(), 199680)
|
|
120
|
+
|
|
121
|
+
def test_pin_is_the_fallback_until_detection_lands(self):
|
|
122
|
+
# At startup, before the first /slots probe, the pin is all there is.
|
|
123
|
+
ap = load_proxy(window="65536")
|
|
124
|
+
ap.default_context_window = 0
|
|
125
|
+
self.assertEqual(ap._model_entry("qwen36-35b-a3b-iq4xs")["context_length"], 65536)
|
|
126
|
+
self.assertEqual(ap._effective_context_window(), 65536)
|
|
127
|
+
|
|
128
|
+
def test_advertised_window_equals_the_forcing_scale_denominator(self):
|
|
129
|
+
# The compaction-forcing scale and the advertisement are two halves of
|
|
130
|
+
# one contract: the client is told a window AND handed counts scaled to
|
|
131
|
+
# make it compact inside that window. Derived from different numbers,
|
|
132
|
+
# they fight — the 65,536/199,680 split had clients compacting at ~18%
|
|
133
|
+
# of the rail.
|
|
134
|
+
ap = load_proxy(window="65536")
|
|
135
|
+
ap.default_context_window = 199680
|
|
136
|
+
advertised = ap._model_entry("qwen36-35b-a3b-iq4xs")["context_length"]
|
|
137
|
+
frac = ap.PROXY_COMPACT_TARGET_FRACTION
|
|
138
|
+
if not (0 < frac < 1):
|
|
139
|
+
frac = min(0.9, ap.PROXY_CONTEXT_PRUNE_THRESHOLD * 0.95)
|
|
140
|
+
expected = ap.PROXY_CLIENT_ASSUMED_WINDOW / (advertised * frac)
|
|
141
|
+
self.assertAlmostEqual(ap._count_tokens_scale(), max(1.0, expected), places=6)
|
|
142
|
+
|
|
75
143
|
def test_entry_always_keeps_the_openai_shape(self):
|
|
76
144
|
ap = load_proxy()
|
|
77
145
|
for mid in ap.ADVERTISED_MODEL_IDS:
|
|
@@ -80,5 +148,54 @@ class ModelsAdvertiseContextWindowTest(unittest.TestCase):
|
|
|
80
148
|
self.assertEqual(e["object"], "model")
|
|
81
149
|
|
|
82
150
|
|
|
151
|
+
class DetectContextWindowTest(unittest.TestCase):
|
|
152
|
+
"""Startup resolution: ask the server first, settings are the fallback."""
|
|
153
|
+
|
|
154
|
+
def tearDown(self):
|
|
155
|
+
os.environ.pop("PROXY_CONTEXT_WINDOW", None)
|
|
156
|
+
os.environ.pop("PROXY_COUNT_TOKENS_SCALE", None)
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def _client(n_ctx=None, fail=False):
|
|
160
|
+
class _Resp:
|
|
161
|
+
status_code = 200
|
|
162
|
+
|
|
163
|
+
def json(self):
|
|
164
|
+
return [{"n_ctx": n_ctx}, {"n_ctx": n_ctx}]
|
|
165
|
+
|
|
166
|
+
class _Client:
|
|
167
|
+
async def get(self, url, timeout=None):
|
|
168
|
+
if fail:
|
|
169
|
+
raise RuntimeError("connection refused")
|
|
170
|
+
return _Resp()
|
|
171
|
+
|
|
172
|
+
return _Client()
|
|
173
|
+
|
|
174
|
+
def test_probes_the_server_even_when_a_fallback_is_configured(self):
|
|
175
|
+
# Previously the configured value short-circuited the probe, so a stale
|
|
176
|
+
# setting governed /v1/models until the first /v1/messages — and SDK
|
|
177
|
+
# clients read /v1/models before sending anything.
|
|
178
|
+
ap = load_proxy(window="65536", measured=False)
|
|
179
|
+
w = asyncio.run(ap.detect_context_window(self._client(n_ctx=199680)))
|
|
180
|
+
self.assertEqual(w, 199680)
|
|
181
|
+
self.assertTrue(ap._context_window_measured)
|
|
182
|
+
|
|
183
|
+
def test_falls_back_to_the_configured_value_when_the_probe_fails(self):
|
|
184
|
+
ap = load_proxy(window="65536", measured=False)
|
|
185
|
+
w = asyncio.run(ap.detect_context_window(self._client(fail=True)))
|
|
186
|
+
self.assertEqual(w, 65536)
|
|
187
|
+
# An operator setting is an assertion, so it may be published.
|
|
188
|
+
self.assertTrue(ap._context_window_measured)
|
|
189
|
+
|
|
190
|
+
def test_last_resort_guess_is_marked_unmeasured(self):
|
|
191
|
+
ap = load_proxy(window="0", measured=True)
|
|
192
|
+
w = asyncio.run(ap.detect_context_window(self._client(fail=True)))
|
|
193
|
+
self.assertEqual(w, 131072)
|
|
194
|
+
self.assertFalse(ap._context_window_measured)
|
|
195
|
+
self.assertEqual(
|
|
196
|
+
ap._model_entry("qwen36-35b-a3b-iq4xs"), {"id": "qwen36-35b-a3b-iq4xs", "object": "model"}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
83
200
|
if __name__ == "__main__":
|
|
84
201
|
unittest.main()
|