@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.210.3",
3
+ "version": "1.210.4",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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 Override context window size (auto-detected from
58
- upstream /slots endpoint if not set)
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
- if PROXY_CONTEXT_WINDOW > 0:
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
- # Safe default: 128K (common for modern models)
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
- default_context_window if default_context_window > 0 else PROXY_CONTEXT_WINDOW,
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 = default_context_window if default_context_window > 0 else PROXY_CONTEXT_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
- if PROXY_CONTEXT_WINDOW > 0 and not _should_passthrough_model(model_id):
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] = PROXY_CONTEXT_WINDOW
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"), 130048, mid)
80
+ self.assertEqual(ap._model_entry(mid).get("context_length"), 199680, mid)
67
81
 
68
- def test_unset_window_advertises_nothing(self):
69
- # Better to say nothing than to assert a wrong number.
70
- ap = load_proxy(window="0")
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()