Skip to content

Commit f6ed185

Browse files
regnullclaude
andcommitted
Never skip the last upstream; judge warm calls on what could be cached, per bot
Review fixes for the cache learning: - Only skip an upstream when the model's endpoint list is known and another upstream remains, so the ignore list can never cover every upstream and fail all calls with 'no endpoints'. - Judge a warm call against min(this prompt, previous prompt): a big new tool result can only be cached from the call after it, so it no longer counts as a miss. - Miss streaks are per bot, so one bot's hit does not hide another's misses and ordinary misses from several bots do not add up to a skip. - A pin to a skipped upstream (re-pinned by a call in flight) is dropped from the request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 879fe08 commit f6ed185

2 files changed

Lines changed: 114 additions & 17 deletions

File tree

‎backend/openbot/runtime/upstream.py‎

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def __init__(self, fetch: Callable[[str], Awaitable[dict[str, str]]] = fetch_end
112112
self._slugs: dict[str, tuple[float, dict[str, str]]] = {}
113113
# One lock per model: a slow fetch for one model must not hold up the others' runs.
114114
self._locks: dict[str, asyncio.Lock] = {}
115-
self._misses: dict[tuple[str, str], int] = {} # (model, slug) -> consecutive warm misses
115+
self._misses: dict[tuple[str, str, str], int] = {} # (bot, model, slug) -> consecutive warm misses
116116
self._ignored: dict[str, dict[str, float]] = {} # model -> {slug: skipped until (monotonic)}
117117

118118
def pinned(self, bot_id: str, model: str) -> str | None:
@@ -134,6 +134,14 @@ async def slug_for(self, model: str, name: str) -> str:
134134
self._slugs[model] = (time.monotonic(), slugs)
135135
return slugs.get(name.lower()) or guess_slug(name)
136136

137+
def _others_remain(self, model: str, slug: str) -> bool:
138+
"""Whether skipping `slug` still leaves `model` an upstream to route to. Unknown when the
139+
endpoint list could not be fetched; then nothing is skipped, since an ignore list covering
140+
every upstream would fail every call to the model with "no endpoints"."""
141+
cached = self._slugs.get(model)
142+
known = set(cached[1].values()) if cached else set()
143+
return bool(known - set(self.ignored(model)) - {slug})
144+
137145
def ignored(self, model: str) -> list[str]:
138146
"""Upstreams currently skipped for `model`, expired skips dropped."""
139147
entries = self._ignored.get(model, {})
@@ -142,17 +150,24 @@ def ignored(self, model: str) -> list[str]:
142150
del entries[slug]
143151
return sorted(entries)
144152

145-
def observe_cache(self, model: str, slug: str, prompt_tokens: int, cached_tokens: int) -> None:
146-
"""Judge one warm call: a hit clears the upstream's miss streak; MISS_LIMIT misses in a row
147-
skip it for this model and unpin every bot pinned to it."""
148-
key = (model, slug)
149-
if prompt_tokens <= 0 or cached_tokens >= CACHE_HIT_SHARE * prompt_tokens:
153+
def observe_cache(self, bot_id: str, model: str, slug: str, cacheable_tokens: int, cached_tokens: int) -> None:
154+
"""Judge one warm call by one bot. `cacheable_tokens` is what the upstream could have had
155+
cached: the part of this prompt the previous call already sent, not the whole prompt (a 30k
156+
tool result can only be cached from the call after it). A hit clears the bot's miss streak;
157+
MISS_LIMIT misses in a row skip the upstream for this model and unpin every bot pinned to it.
158+
Streaks are per bot, so one bot's hit does not hide another's misses, nor do ordinary misses
159+
from several bots add up to a skip."""
160+
key = (bot_id, model, slug)
161+
if cacheable_tokens <= 0 or cached_tokens >= CACHE_HIT_SHARE * cacheable_tokens:
150162
self._misses.pop(key, None)
151163
return
152164
self._misses[key] = self._misses.get(key, 0) + 1
153165
if self._misses[key] < MISS_LIMIT:
154166
return
155167
del self._misses[key]
168+
if not self._others_remain(model, slug):
169+
log.info("OpenRouter upstream %s barely caches %s, but it is the last one left: keeping it", slug, model)
170+
return
156171
entries = self._ignored.setdefault(model, {})
157172
entries[slug] = time.monotonic() + IGNORE_TTL
158173
while len(entries) > MAX_IGNORED:
@@ -191,6 +206,7 @@ def __init__(self, pins: UpstreamPins, bot_id: str, model: str, settings) -> Non
191206
self.pins, self.bot_id, self.model, self.settings = pins, bot_id, model, settings
192207
# One middleware per run: the upstream of this run's previous call, to tell warm calls apart.
193208
self._last_slug: str | None = None
209+
self._last_prompt = 0 # input tokens of that call: the most a warm call can have cached
194210

195211
def _explicit(self) -> bool:
196212
return bool(getattr(self.settings, "openrouter_provider_order", None))
@@ -200,6 +216,9 @@ def _pinned(self, request: ModelRequest) -> ModelRequest:
200216
return request
201217
slug = self.pins.pinned(self.bot_id, self.model)
202218
ignored = self.pins.ignored(self.model)
219+
if slug in ignored:
220+
# A call still in flight when its upstream was skipped re-pinned the bot to it.
221+
slug = None
203222
provider: dict[str, Any] = {}
204223
if slug is not None:
205224
provider.update(order=[slug], allow_fallbacks=True)
@@ -228,8 +247,9 @@ async def awrap_model_call(
228247
if name and not self._explicit():
229248
slug = await self.pins.record(self.bot_id, self.model, name)
230249
usage = getattr(reply, "usage_metadata", None) or {}
250+
prompt = int(usage.get("input_tokens") or 0)
231251
if slug == self._last_slug and usage:
232252
cached = int((usage.get("input_token_details") or {}).get("cache_read") or 0)
233-
self.pins.observe_cache(self.model, slug, int(usage.get("input_tokens") or 0), cached)
234-
self._last_slug = slug
253+
self.pins.observe_cache(self.bot_id, self.model, slug, min(prompt, self._last_prompt), cached)
254+
self._last_slug, self._last_prompt = slug, prompt
235255
return response

‎backend/tests/test_upstream.py‎

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,28 +128,33 @@ async def test_an_upstream_that_keeps_missing_warm_calls_is_skipped_and_unpinned
128128
pins = UpstreamPins(_fetch)
129129
await pins.record("bot1", "glm", "Z.AI")
130130
await pins.record("bot2", "glm", "Z.AI")
131-
pins.observe_cache("glm", "z-ai", 20_000, 0)
132-
pins.observe_cache("glm", "z-ai", 20_000, 0)
133-
pins.observe_cache("glm", "z-ai", 20_000, 15_000) # a hit resets the streak
134-
pins.observe_cache("glm", "z-ai", 20_000, 700)
135-
pins.observe_cache("glm", "z-ai", 20_000, 0)
131+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 0)
132+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 0)
133+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 15_000) # a hit resets the streak
134+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 700)
135+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 0)
136136
assert pins.ignored("glm") == [] and pins.pinned("bot1", "glm") == "z-ai"
137-
pins.observe_cache("glm", "z-ai", 20_000, 0)
137+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 0)
138138
assert pins.ignored("glm") == ["z-ai"]
139139
assert pins.pinned("bot1", "glm") is None and pins.pinned("bot2", "glm") is None
140140
assert pins.ignored("other-model") == [] # skips are per model
141141
clock[0] += 6 * 3600 + 1
142142
assert pins.ignored("glm") == [] # and lapse, so the upstream gets another chance
143143

144144

145-
def test_the_skip_list_is_capped_so_routing_never_runs_out(monkeypatch):
145+
async def test_the_skip_list_is_capped_so_routing_never_runs_out(monkeypatch):
146146
clock = [1000.0]
147147
monkeypatch.setattr("openbot.runtime.upstream.time.monotonic", lambda: clock[0])
148-
pins = UpstreamPins(_fetch)
148+
149+
async def many(m):
150+
return {f"host {i}": f"host{i}" for i in range(20)}
151+
152+
pins = UpstreamPins(many)
153+
await pins.slug_for("glm", "host 0") # loads the endpoint list
149154
for i in range(12):
150155
clock[0] += 1
151156
for _ in range(3):
152-
pins.observe_cache("glm", f"host{i}", 20_000, 0)
157+
pins.observe_cache("bot1", "glm", f"host{i}", 20_000, 0)
153158
skipped = pins.ignored("glm")
154159
assert len(skipped) == 10 and "host0" not in skipped and "host11" in skipped
155160

@@ -188,3 +193,75 @@ async def handler(request):
188193
for _ in range(8):
189194
await mw.awrap_model_call(_request(model()), handler)
190195
assert pins.ignored("z-ai/glm-5.3-flash") == []
196+
197+
198+
async def test_the_last_upstream_left_is_never_skipped():
199+
"""An ignore list naming every upstream would fail every call to the model with "no endpoints"."""
200+
201+
async def two(m):
202+
return {"z.ai": "z-ai", "deepinfra": "deepinfra"}
203+
204+
pins = UpstreamPins(two)
205+
await pins.slug_for("glm", "Z.AI")
206+
for slug in ("z-ai", "deepinfra"):
207+
for _ in range(3):
208+
pins.observe_cache("bot1", "glm", slug, 20_000, 0)
209+
assert pins.ignored("glm") == ["z-ai"] # deepinfra is all that is left, so it stays
210+
211+
async def offline(m):
212+
raise OSError("down")
213+
214+
blind = UpstreamPins(offline)
215+
await blind.slug_for("glm", "Z.AI")
216+
for _ in range(3):
217+
blind.observe_cache("bot1", "glm", "z-ai", 20_000, 0)
218+
assert blind.ignored("glm") == [] # without the endpoint list it cannot tell, so it skips nothing
219+
220+
221+
async def test_warm_calls_are_judged_on_what_could_have_been_cached(settings):
222+
"""A big tool result makes the next prompt much longer; only the previous call's part of it can be
223+
cached, so 8k cached of a 38k prompt that followed an 8k one is a full hit, not a miss."""
224+
settings.openrouter_provider_order = []
225+
pins = UpstreamPins(_fetch)
226+
mw = StickyUpstreamMiddleware(pins, "bot1", "z-ai/glm-5.3-flash", settings)
227+
# Each warm call has under 25% of its whole prompt cached, but all of the previous call's.
228+
calls = iter([(8_000, 0), (38_000, 8_000), (160_000, 38_000), (700_000, 160_000)])
229+
230+
async def handler(request):
231+
prompt, cached = next(calls)
232+
return AIMessage("ok", response_metadata={UPSTREAM_KEY: ["Z.AI"]}, usage_metadata=_usage(prompt, cached))
233+
234+
for _ in range(4):
235+
await mw.awrap_model_call(_request(model()), handler)
236+
assert pins.ignored("z-ai/glm-5.3-flash") == []
237+
238+
239+
async def test_miss_streaks_are_per_bot():
240+
pins = UpstreamPins(_fetch)
241+
await pins.slug_for("glm", "Z.AI")
242+
for bot in ("bot1", "bot2", "bot3"): # one ordinary miss each does not add up to a skip
243+
pins.observe_cache(bot, "glm", "z-ai", 20_000, 0)
244+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 0)
245+
pins.observe_cache("bot2", "glm", "z-ai", 20_000, 15_000) # bot2's hit does not clear bot1's streak
246+
pins.observe_cache("bot1", "glm", "z-ai", 20_000, 0)
247+
assert pins.ignored("glm") == ["z-ai"]
248+
249+
250+
async def test_a_pin_to_a_skipped_upstream_is_not_sent(settings):
251+
"""A call in flight when its upstream was skipped re-pins the bot to it; the request must not then
252+
name the same upstream in both order and ignore."""
253+
settings.openrouter_provider_order = []
254+
pins = UpstreamPins(_fetch)
255+
await pins.slug_for("z-ai/glm-5.3-flash", "Z.AI")
256+
for _ in range(3):
257+
pins.observe_cache("bot2", "z-ai/glm-5.3-flash", "z-ai", 20_000, 0)
258+
await pins.record("bot1", "z-ai/glm-5.3-flash", "Z.AI")
259+
mw = StickyUpstreamMiddleware(pins, "bot1", "z-ai/glm-5.3-flash", settings)
260+
sent = []
261+
262+
async def handler(request):
263+
sent.append(request.model_settings["extra_body"]["provider"])
264+
return AIMessage("ok")
265+
266+
await mw.awrap_model_call(_request(model()), handler)
267+
assert sent == [{"ignore": ["z-ai"]}]

0 commit comments

Comments
 (0)