@wendellresearch-ai/benchwarmers 0.2.0 → 0.2.2

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.
Files changed (103) hide show
  1. package/AGENTS.md +152 -147
  2. package/BENCHMARKS.md +262 -262
  3. package/GATEWAY.md +415 -272
  4. package/NOTICE +28 -28
  5. package/PRIVACY.md +46 -46
  6. package/README.md +408 -314
  7. package/SECURITY.md +83 -83
  8. package/TERMS.md +22 -22
  9. package/USAGE.md +186 -163
  10. package/VERSION +1 -1
  11. package/bin/benchwarmers.js +16 -74
  12. package/cli.py +674 -0
  13. package/examples/config/gateway.json +5 -5
  14. package/examples/config/model-roles.json +11 -11
  15. package/gateway.py +939 -755
  16. package/launcher.py +120 -0
  17. package/mcp_server.py +266 -266
  18. package/metrics/alerts.py +116 -116
  19. package/metrics/aliases.py +41 -41
  20. package/metrics/bench_collections.py +141 -141
  21. package/metrics/catalog.py +304 -295
  22. package/metrics/client.py +804 -394
  23. package/metrics/cliproxy.py +191 -191
  24. package/metrics/connect.py +691 -691
  25. package/metrics/cost.py +171 -152
  26. package/metrics/decide.py +227 -227
  27. package/metrics/discover.py +318 -318
  28. package/metrics/endpoints.py +436 -436
  29. package/metrics/formats.py +1021 -0
  30. package/metrics/guard.py +647 -592
  31. package/metrics/learn.py +234 -221
  32. package/metrics/ledger.py +148 -0
  33. package/metrics/litellm_hub.py +206 -206
  34. package/metrics/model_roles.py +94 -84
  35. package/metrics/offload.py +166 -166
  36. package/metrics/owui.py +205 -205
  37. package/metrics/pacing.py +248 -0
  38. package/metrics/paths.py +60 -60
  39. package/metrics/quota.py +383 -0
  40. package/metrics/route.py +581 -579
  41. package/metrics/scheduler.py +275 -275
  42. package/metrics/sources.py +167 -167
  43. package/metrics/wrong.py +144 -144
  44. package/package.json +69 -65
  45. package/perf/README.md +89 -89
  46. package/perf/run.py +284 -261
  47. package/scripts/auto_retire.py +367 -367
  48. package/scripts/check.py +113 -106
  49. package/scripts/check_deprecations.py +378 -378
  50. package/scripts/check_themes.py +103 -103
  51. package/scripts/init.py +124 -124
  52. package/scripts/list_served.py +78 -78
  53. package/scripts/notice.py +78 -78
  54. package/scripts/onboard.py +410 -331
  55. package/scripts/provider_tags.py +129 -129
  56. package/scripts/reprefix.py +136 -136
  57. package/scripts/sync_config.py +105 -105
  58. package/scripts/test_auth.py +255 -255
  59. package/scripts/test_cli.py +189 -0
  60. package/scripts/test_client.py +277 -233
  61. package/scripts/test_connect.py +222 -222
  62. package/scripts/test_first_run.py +180 -180
  63. package/scripts/test_formats.py +549 -0
  64. package/scripts/test_hubs.py +328 -328
  65. package/scripts/test_launcher.py +67 -0
  66. package/scripts/test_llm_log.py +85 -81
  67. package/scripts/test_mcp.py +178 -178
  68. package/scripts/test_merged.py +215 -214
  69. package/scripts/test_no_owui.py +291 -291
  70. package/scripts/test_owui.py +90 -90
  71. package/scripts/test_quota.py +334 -0
  72. package/scripts/test_serve_edit.py +453 -438
  73. package/scripts/test_server_json.py +79 -0
  74. package/scripts/test_verdict.py +102 -98
  75. package/scripts/test_vision.py +231 -228
  76. package/serve.py +3972 -3764
  77. package/suite/mine.py +154 -154
  78. package/suite/pace.py +145 -145
  79. package/suite/run.py +1046 -1042
  80. package/suite/seed.py +421 -421
  81. package/suite/tasks/claim-alias-unbound/fixtures/onboard.py +286 -286
  82. package/suite/tasks/claim-error-text-string/fixtures/decide.py +187 -187
  83. package/suite/tasks/claim-list-body/fixtures/gateway.py +235 -235
  84. package/suite/tasks/claim-member-nominated-twice/fixtures/gateway.py +284 -284
  85. package/suite/tasks/claim-nominee-uses-slot/fixtures/gateway.py +284 -284
  86. package/suite/tasks/claim-silent-exit/fixtures/gateway.py +235 -235
  87. package/suite/tasks/claim-sse-hang/fixtures/gateway.py +235 -235
  88. package/suite/tasks/claim-unknown-nominee-crash/fixtures/gateway.py +284 -284
  89. package/suite/tasks/rebench-matchms-809-bug/task.json +23 -23
  90. package/suite/tasks/rebench-matchms-809-control/task.json +27 -27
  91. package/suite/tasks/rebench-more-itertools-1028-bug/task.json +23 -23
  92. package/suite/tasks/rebench-more-itertools-1028-control/task.json +27 -27
  93. package/suite/tasks/rebench-narwhals-2864-bug/task.json +23 -23
  94. package/suite/tasks/rebench-narwhals-2864-control/task.json +27 -27
  95. package/suite/tasks/rebench-quantecon-py-769-bug/task.json +23 -23
  96. package/suite/tasks/rebench-quantecon-py-769-control/task.json +27 -27
  97. package/suite/tasks/rebench-scrapy-6867-bug/task.json +23 -23
  98. package/suite/tasks/rebench-scrapy-6867-control/task.json +27 -27
  99. package/ui/bench.css +430 -425
  100. package/ui/bench.js +1757 -1576
  101. package/ui/themes/dark.css +35 -35
  102. package/ui/themes/hardwood.css +52 -52
  103. package/ui/themes/rainforest.css +32 -32
@@ -1,291 +1,291 @@
1
- """Benchwarmers without Open WebUI, and the owner's install with it, against a fake server.
2
-
3
- python scripts/test_no_owui.py
4
-
5
- Two installs in one temp data folder (BENCHWARMERS_HOME and LLM_USER_PATH are
6
- set before anything is imported, so no real file is read or written):
7
-
8
- 1. A stranger whose only endpoint is an OpenRouter-like aggregator, the
9
- default. Registry, connections, onboarding candidates, the served check
10
- and auto-retire's plan all work, and no Open WebUI path is ever requested:
11
- owui.py, provider_tags.py and reprefix.py refuse or skip cleanly.
12
- 2. The owner's shape: endpoints.json with no `kind` anywhere, an `openwebui`
13
- default at a bare /api root with a `{path}` decisions passthrough, an
14
- OpenRouter endpoint and a local Ollama. Every Open WebUI path still runs.
15
- """
16
-
17
- from __future__ import annotations
18
-
19
- import contextlib
20
- import io
21
- import json
22
- import os
23
- import pathlib
24
- import shutil
25
- import sys
26
- import tempfile
27
- import threading
28
- from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
29
-
30
- ROOT = pathlib.Path(__file__).resolve().parent.parent
31
- TMP = pathlib.Path(tempfile.mkdtemp(prefix="bench-no-owui-"))
32
- os.environ["BENCHWARMERS_HOME"] = str(TMP)
33
- os.environ["LLM_USER_PATH"] = str(TMP / "llm")
34
- (TMP / "config").mkdir()
35
- (TMP / "llm").mkdir()
36
- sys.path[:0] = [str(ROOT), str(ROOT / "metrics"), str(ROOT / "suite"), str(ROOT / "scripts")]
37
-
38
- SEEN: list[str] = []
39
- LISTS = {
40
- "/v1/models": ["vendor/a:free", "vendor/b:free", "vendor/c", "vendor/d", "vendor/e", "vendor/f"],
41
- "/or/api/v1/models": ["vendor/x:free", "vendor/y"],
42
- "/ol/v1/models": ["llama-local"],
43
- }
44
- OWUI_MODELS = [{"id": "fast-1", "urlIdx": 0}, {"id": "llm7.codestral", "urlIdx": 1},
45
- {"id": "fresh-2", "urlIdx": 0}, {"id": "mercury-decide", "urlIdx": 1}]
46
- OWUI_CONFIG = {"OPENAI_API_BASE_URLS": ["https://api.groq.com/openai/v1", "https://api.llm7.io/v1"],
47
- "OPENAI_API_KEYS": ["never-read-1", "never-read-2"]}
48
-
49
-
50
- class Fake(BaseHTTPRequestHandler):
51
- def do_GET(self): # noqa: N802
52
- SEEN.append(self.path)
53
- if self.path in LISTS:
54
- return self._send({"data": [{"id": i} for i in LISTS[self.path]]})
55
- if self.path == "/api/models":
56
- return self._send({"data": OWUI_MODELS})
57
- if self.path == "/openai/config":
58
- return self._send(OWUI_CONFIG)
59
- self._send({}, 404)
60
-
61
- def do_POST(self): # noqa: N802
62
- SEEN.append(self.path)
63
- self.rfile.read(int(self.headers.get("Content-Length") or 0))
64
- if self.path == "/openai/decisions": # Open WebUI's passthrough
65
- return self._send({"answers": {"probe": {"answer": True}}})
66
- self._send({}, 404)
67
-
68
- def _send(self, data, status=200):
69
- body = json.dumps(data).encode()
70
- self.send_response(status)
71
- self.send_header("Content-Type", "application/json")
72
- self.send_header("Content-Length", str(len(body)))
73
- self.end_headers()
74
- self.wfile.write(body)
75
-
76
- def log_message(self, *_a):
77
- pass
78
-
79
-
80
- def write(name: str, data) -> None:
81
- path = TMP / "config" / name
82
- path.write_text(data if isinstance(data, str) else json.dumps(data), encoding="utf-8")
83
-
84
-
85
- def quietly(fn, *args):
86
- with contextlib.redirect_stdout(io.StringIO()) as out:
87
- result = fn(*args)
88
- return result, out.getvalue()
89
-
90
-
91
- def run_main(module, argv: list[str]):
92
- """A script's main() as its command line would run it: (exit, printed)."""
93
- saved, sys.argv = sys.argv, argv
94
- try:
95
- return quietly(module.main)
96
- except SystemExit as exc:
97
- return exc.code, ""
98
- finally:
99
- sys.argv = saved
100
-
101
-
102
- def owui_calls() -> list[str]:
103
- return [p for p in SEEN if p.startswith(("/api/", "/openai/"))]
104
-
105
-
106
- def stranger(fake: str) -> None:
107
- write("endpoints.json", {"openrouter": {"base": fake + "/v1", "key": "openrouter", "default": True,
108
- "kind": "aggregator"}})
109
- write("alias-history.json", "{}")
110
- import endpoints
111
- write("extra-openai-models.yaml", endpoints.yaml_block("a-free", "vendor/a:free")
112
- + endpoints.yaml_block("b-free", "vendor/b:free"))
113
- import route
114
- import serve
115
- import check_deprecations
116
- import auto_retire
117
- import owui
118
- import provider_tags
119
- import reprefix
120
-
121
- assert (endpoints.kind("openrouter"), endpoints.hub("openrouter")) == ("aggregator", "")
122
- assert endpoints.find_hub("openwebui") is None and not endpoints.recorded_whole("openrouter")
123
- assert {r["alias"]: r["endpoint"] for r in endpoints.registry()} == {"a-free": "openrouter", "b-free": "openrouter"}
124
- # No tags file at all: the endpoint is the connection.
125
- assert not (TMP / "config" / "provider-tags.json").exists()
126
- assert route.connection_of("a-free") == route.full_tag_of("b-free") == "openrouter"
127
- assert serve.tag_for("a-free") == "openrouter"
128
-
129
- # Onboarding candidates: what it lists minus what is registered; nothing badged new,
130
- # because the served check records only registered models here.
131
- c = serve.onboard_candidates()
132
- assert c["ok"] and not c["errors"], c
133
- assert sorted(x["served"] for x in c["candidates"]) == ["vendor/c", "vendor/d", "vendor/e", "vendor/f"], c
134
- assert {x["group"] for x in c["candidates"]} == {"openrouter (direct)"} and not any(x["new"] for x in c["candidates"])
135
-
136
- # The served check: the registered models only, not the aggregator's catalogue.
137
- assert check_deprecations.served() == {"vendor/a:free", "vendor/b:free"}
138
- code, out = run_main(check_deprecations, ["check_deprecations.py", "--dry-run"])
139
- assert code == 0 and "2 served, 2 registered" in out, (code, out)
140
- assert not (TMP / "config" / "served-snapshot.json").exists() # a dry run writes nothing
141
-
142
- # Auto-retire groups by connection without tags: both models on the one
143
- # connection failing at once is held as the connection, not retired.
144
- import datetime as dt
145
- models = {"a-free": "vendor/a:free", "b-free": "vendor/b:free"}
146
- tags, on_owui = auto_retire.connections(models)
147
- assert tags == {"vendor/a:free": "openrouter", "vendor/b:free": "openrouter"} and on_owui == set(), tags
148
- today = dt.date(2026, 10, 21)
149
- rows = [{"alias": a, "date": today, "answered": False, "failure": "entitlement", "detail": "402"} for a in models]
150
- p = auto_retire.plan(today, models, set(models.values()), {}, rows, {}, {}, tags, on_owui)
151
- assert set(p["held"]) == {"openrouter"} and not p["confirm"], p
152
- gone = auto_retire.plan(today, {"a-free": "vendor/a:free"}, {"x.vendor/a:free"},
153
- {"vendor/a:free": "2026-10-15"}, [], {}, {}, tags, on_owui)
154
- assert dict(gone["retire"])["a-free"].startswith("not served by its endpoint"), gone # no prefix guess
155
-
156
- # Every Open WebUI-only path refuses or skips, and says why.
157
- try:
158
- owui.endpoint()
159
- raise AssertionError("owui found a hub that is not there")
160
- except ValueError as exc:
161
- assert "Open WebUI hub" in str(exc)
162
- r = serve.discover_add("groq", "vendor/z")
163
- assert not r["ok"] and "Open WebUI hub" in r["errors"][0], r
164
- _, out = quietly(provider_tags.main)
165
- assert "no Open WebUI hub" in out and not (TMP / "config" / "provider-tags.json").exists(), out
166
- code, _ = run_main(reprefix, ["reprefix.py", "groq", "--prefix", "g."])
167
- assert "Open WebUI" in str(code), code
168
-
169
- # Adding an endpoint: an explicit kind, a hub only with its type.
170
- assert not serve.save_endpoint("odd", fake + "/v1", "k", new=True, kind="cloud")["ok"]
171
- assert not serve.save_endpoint("odd", fake + "/v1", "k", new=True, kind="hub")["ok"]
172
- # Test (the form's button before Save) answers as Save would and writes nothing, the key included.
173
- t = serve.save_endpoint("tried", fake + "/v1", "k-tried", new=True, test_only=True)
174
- assert t["ok"] and "served" in t and "tried" not in endpoints.load() and "tried" not in endpoints.keys(), t
175
- assert serve.save_endpoint("plain", fake + "/v1", "k", new=True)["ok"]
176
- assert not serve.save_endpoint("plain", fake + "/v1", "k", new=True, test_only=True)["ok"] # names checked in Test too
177
- assert serve.save_endpoint("mine", fake + "/v1", "k", new=True, kind="hub", hub="openwebui")["ok"]
178
- data = endpoints.load()
179
- assert data["plain"]["kind"] == "provider" and "hub" not in data["plain"], data["plain"]
180
- assert (data["mine"]["kind"], data["mine"]["hub"]) == ("hub", "openwebui"), data["mine"]
181
- assert serve.save_endpoint("mine", fake + "/v1", "k2")["ok"] and endpoints.hub("mine") == "openwebui" # Change key keeps it
182
- rows = {e["name"]: e for e in serve.endpoint_list()["endpoints"]}
183
- assert (rows["openrouter"]["kind"], rows["mine"]["hub"]) == ("aggregator", "openwebui"), rows
184
-
185
- assert owui_calls() == [], owui_calls()
186
-
187
-
188
- def owner(fake: str) -> None:
189
- """The owner's endpoints.json shape: no `kind` anywhere."""
190
- write("endpoints.json", {
191
- "openwebui": {"base": fake + "/api", "key": "openwebui", "default": True,
192
- "decisions": fake + "/openai/{path}"},
193
- "openrouter": {"base": fake + "/or/api/v1", "key": "openrouter",
194
- "headers": {"HTTP-Referer": "https://example.com", "X-Title": "Example"},
195
- "unlisted_models": ["vendor/decide:free"]},
196
- "ollama": {"key": "ollama", "connect": {"provider": "ollama", "at": "2026-10-20T22:30:52-05:00"},
197
- "base": fake + "/ol/v1"}})
198
- import endpoints
199
- write("extra-openai-models.yaml", endpoints.yaml_block("fast-1", "fast-1")
200
- + endpoints.yaml_block("codestral", "codestral")
201
- + endpoints.yaml_block("x-free", "vendor/x:free", "openrouter"))
202
- write("served-snapshot.json", {"last_seen": {"fast-1": "2026-10-20", "codestral": "2026-10-18"},
203
- "first_missing": {"codestral": "2026-10-19"}})
204
- import route
205
- import serve
206
- import check_deprecations
207
- import auto_retire
208
- import owui
209
- import provider_tags
210
- import reprefix
211
- import decide
212
- SEEN.clear()
213
-
214
- # Inferred from his real bases (a LAN address stands in as hub.example)...
215
- real = {"openwebui": {"base": "http://hub.example:4141/api"}, "openrouter": {"base": "https://openrouter.ai/api/v1"},
216
- "ollama": {"base": "http://localhost:11434/v1"}}
217
- kinds = {n: (endpoints.kind(n, real), endpoints.hub(n, real)) for n in real}
218
- assert kinds == {"openwebui": ("hub", "openwebui"), "openrouter": ("provider", ""), "ollama": ("local", "")}, kinds
219
- # ...and here, where every base is the fake on 127.0.0.1, the /api root still makes the one hub.
220
- assert [n for n in endpoints.load() if endpoints.hub(n)] == ["openwebui"]
221
- assert endpoints.find_hub("openwebui") == "openwebui" and endpoints.recorded_whole("openwebui")
222
- assert owui.endpoint() == "openwebui" and owui._call("/openai/config") == OWUI_CONFIG
223
-
224
- # Provider tags: Open WebUI's connections by host, the direct endpoint by name.
225
- _, out = quietly(provider_tags.main)
226
- tags = json.loads((TMP / "config" / "provider-tags.json").read_text(encoding="utf-8"))
227
- assert tags["fast-1"] == "groq" and tags["llm7.codestral"] == "llm7-api" and tags["vendor/x:free"] == "openrouter", tags
228
- assert "never-read" not in json.dumps(tags)
229
- assert route.connection_of("fast-1") == "groq" and route.connection_of("x-free") == "openrouter"
230
-
231
- # The default hub is recorded whole; the direct endpoint, its registered models only.
232
- assert check_deprecations.served() == {"fast-1", "llm7.codestral", "fresh-2", "mercury-decide",
233
- "vendor/x:free@openrouter"}
234
- c = serve.onboard_candidates()
235
- groups = {x["served"]: (x["group"], x["new"]) for x in c["candidates"]}
236
- assert groups["fresh-2"] == ("groq", True) and groups["vendor/y"] == ("openrouter (direct)", False), groups
237
- assert "never-read" not in json.dumps(c), c
238
- # Added to Open WebUI after provider_tags.py ran: named live from its urlIdx,
239
- # not "unknown connection"; the tags file answers when the connections
240
- # cannot be read (a key that is not an admin's), and the rest still list.
241
- OWUI_MODELS.append({"id": "just-added", "urlIdx": 1})
242
- real_names = provider_tags.connection_names
243
- try:
244
- live = {x["served"]: x["group"] for x in serve.onboard_candidates()["candidates"]}
245
-
246
- def refused(hub):
247
- raise OSError("401")
248
- provider_tags.connection_names = refused
249
- c = serve.onboard_candidates()
250
- finally:
251
- provider_tags.connection_names = real_names
252
- OWUI_MODELS.pop()
253
- fallback = {x["served"]: x["group"] for x in c["candidates"]}
254
- assert live["just-added"] == "llm7-api" and live["fresh-2"] == "groq", live
255
- assert c["ok"] and not c["errors"] and fallback["just-added"] == "unknown connection", c
256
- assert fallback["fresh-2"] == "groq" and fallback["vendor/y"] == "openrouter (direct)", fallback
257
-
258
- # Auto-retire: a prefix added in Open WebUI is a rename, said in Open WebUI's words.
259
- models = {"fast-1": "fast-1", "codestral": "codestral", "x-free": "vendor/x:free@openrouter"}
260
- conns, on_owui = auto_retire.connections(models)
261
- assert on_owui == {"fast-1", "codestral"} and conns["vendor/x:free@openrouter"] == "openrouter", (conns, on_owui)
262
- import datetime as dt
263
- p = auto_retire.plan(dt.date(2026, 10, 25), models, check_deprecations.served(),
264
- {"codestral": "2026-10-19"}, [], {}, {}, conns, on_owui)
265
- assert "reprefix" in dict(p["pending"])["codestral"] and not p["retire"], p
266
- assert serve.model_status()["codestral"][2] == "missing from Open WebUI since 2026-10-19"
267
- code, out = run_main(reprefix, ["reprefix.py", "llm7-api", "--prefix", "llm7."])
268
- assert code is None and "codestral" in out and "llm7.codestral" in out, (code, out)
269
-
270
- # Decisions through Open WebUI's passthrough.
271
- assert decide.probe("openwebui", "mercury-decide") == (None, "decisions")
272
- hit = set(owui_calls())
273
- assert {"/openai/config", "/api/models", "/openai/decisions"} <= hit, hit
274
-
275
-
276
- def main() -> int:
277
- server = ThreadingHTTPServer(("127.0.0.1", 0), Fake)
278
- threading.Thread(target=server.serve_forever, daemon=True).start()
279
- fake = f"http://127.0.0.1:{server.server_port}"
280
- try:
281
- stranger(fake)
282
- owner(fake)
283
- finally:
284
- server.shutdown()
285
- shutil.rmtree(TMP, ignore_errors=True)
286
- print("ok")
287
- return 0
288
-
289
-
290
- if __name__ == "__main__":
291
- sys.exit(main())
1
+ """Benchwarmers without Open WebUI, and the owner's install with it, against a fake server.
2
+
3
+ python scripts/test_no_owui.py
4
+
5
+ Two installs in one temp data folder (BENCHWARMERS_HOME and LLM_USER_PATH are
6
+ set before anything is imported, so no real file is read or written):
7
+
8
+ 1. A stranger whose only endpoint is an OpenRouter-like aggregator, the
9
+ default. Registry, connections, onboarding candidates, the served check
10
+ and auto-retire's plan all work, and no Open WebUI path is ever requested:
11
+ owui.py, provider_tags.py and reprefix.py refuse or skip cleanly.
12
+ 2. The owner's shape: endpoints.json with no `kind` anywhere, an `openwebui`
13
+ default at a bare /api root with a `{path}` decisions passthrough, an
14
+ OpenRouter endpoint and a local Ollama. Every Open WebUI path still runs.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import contextlib
20
+ import io
21
+ import json
22
+ import os
23
+ import pathlib
24
+ import shutil
25
+ import sys
26
+ import tempfile
27
+ import threading
28
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
29
+
30
+ ROOT = pathlib.Path(__file__).resolve().parent.parent
31
+ TMP = pathlib.Path(tempfile.mkdtemp(prefix="bench-no-owui-"))
32
+ os.environ["BENCHWARMERS_HOME"] = str(TMP)
33
+ os.environ["LLM_USER_PATH"] = str(TMP / "llm")
34
+ (TMP / "config").mkdir()
35
+ (TMP / "llm").mkdir()
36
+ sys.path[:0] = [str(ROOT), str(ROOT / "metrics"), str(ROOT / "suite"), str(ROOT / "scripts")]
37
+
38
+ SEEN: list[str] = []
39
+ LISTS = {
40
+ "/v1/models": ["vendor/a:free", "vendor/b:free", "vendor/c", "vendor/d", "vendor/e", "vendor/f"],
41
+ "/or/api/v1/models": ["vendor/x:free", "vendor/y"],
42
+ "/ol/v1/models": ["llama-local"],
43
+ }
44
+ OWUI_MODELS = [{"id": "fast-1", "urlIdx": 0}, {"id": "llm7.codestral", "urlIdx": 1},
45
+ {"id": "fresh-2", "urlIdx": 0}, {"id": "mercury-decide", "urlIdx": 1}]
46
+ OWUI_CONFIG = {"OPENAI_API_BASE_URLS": ["https://api.groq.com/openai/v1", "https://api.llm7.io/v1"],
47
+ "OPENAI_API_KEYS": ["never-read-1", "never-read-2"]}
48
+
49
+
50
+ class Fake(BaseHTTPRequestHandler):
51
+ def do_GET(self): # noqa: N802
52
+ SEEN.append(self.path)
53
+ if self.path in LISTS:
54
+ return self._send({"data": [{"id": i} for i in LISTS[self.path]]})
55
+ if self.path == "/api/models":
56
+ return self._send({"data": OWUI_MODELS})
57
+ if self.path == "/openai/config":
58
+ return self._send(OWUI_CONFIG)
59
+ self._send({}, 404)
60
+
61
+ def do_POST(self): # noqa: N802
62
+ SEEN.append(self.path)
63
+ self.rfile.read(int(self.headers.get("Content-Length") or 0))
64
+ if self.path == "/openai/decisions": # Open WebUI's passthrough
65
+ return self._send({"answers": {"probe": {"answer": True}}})
66
+ self._send({}, 404)
67
+
68
+ def _send(self, data, status=200):
69
+ body = json.dumps(data).encode()
70
+ self.send_response(status)
71
+ self.send_header("Content-Type", "application/json")
72
+ self.send_header("Content-Length", str(len(body)))
73
+ self.end_headers()
74
+ self.wfile.write(body)
75
+
76
+ def log_message(self, *_a):
77
+ pass
78
+
79
+
80
+ def write(name: str, data) -> None:
81
+ path = TMP / "config" / name
82
+ path.write_text(data if isinstance(data, str) else json.dumps(data), encoding="utf-8")
83
+
84
+
85
+ def quietly(fn, *args):
86
+ with contextlib.redirect_stdout(io.StringIO()) as out:
87
+ result = fn(*args)
88
+ return result, out.getvalue()
89
+
90
+
91
+ def run_main(module, argv: list[str]):
92
+ """A script's main() as its command line would run it: (exit, printed)."""
93
+ saved, sys.argv = sys.argv, argv
94
+ try:
95
+ return quietly(module.main)
96
+ except SystemExit as exc:
97
+ return exc.code, ""
98
+ finally:
99
+ sys.argv = saved
100
+
101
+
102
+ def owui_calls() -> list[str]:
103
+ return [p for p in SEEN if p.startswith(("/api/", "/openai/"))]
104
+
105
+
106
+ def stranger(fake: str) -> None:
107
+ write("endpoints.json", {"openrouter": {"base": fake + "/v1", "key": "openrouter", "default": True,
108
+ "kind": "aggregator"}})
109
+ write("alias-history.json", "{}")
110
+ import endpoints
111
+ write("extra-openai-models.yaml", endpoints.yaml_block("a-free", "vendor/a:free")
112
+ + endpoints.yaml_block("b-free", "vendor/b:free"))
113
+ import route
114
+ import serve
115
+ import check_deprecations
116
+ import auto_retire
117
+ import owui
118
+ import provider_tags
119
+ import reprefix
120
+
121
+ assert (endpoints.kind("openrouter"), endpoints.hub("openrouter")) == ("aggregator", "")
122
+ assert endpoints.find_hub("openwebui") is None and not endpoints.recorded_whole("openrouter")
123
+ assert {r["alias"]: r["endpoint"] for r in endpoints.registry()} == {"a-free": "openrouter", "b-free": "openrouter"}
124
+ # No tags file at all: the endpoint is the connection.
125
+ assert not (TMP / "config" / "provider-tags.json").exists()
126
+ assert route.connection_of("a-free") == route.full_tag_of("b-free") == "openrouter"
127
+ assert serve.tag_for("a-free") == "openrouter"
128
+
129
+ # Onboarding candidates: what it lists minus what is registered; nothing badged new,
130
+ # because the served check records only registered models here.
131
+ c = serve.onboard_candidates()
132
+ assert c["ok"] and not c["errors"], c
133
+ assert sorted(x["served"] for x in c["candidates"]) == ["vendor/c", "vendor/d", "vendor/e", "vendor/f"], c
134
+ assert {x["group"] for x in c["candidates"]} == {"openrouter (direct)"} and not any(x["new"] for x in c["candidates"])
135
+
136
+ # The served check: the registered models only, not the aggregator's catalogue.
137
+ assert check_deprecations.served() == {"vendor/a:free", "vendor/b:free"}
138
+ code, out = run_main(check_deprecations, ["check_deprecations.py", "--dry-run"])
139
+ assert code == 0 and "2 served, 2 registered" in out, (code, out)
140
+ assert not (TMP / "config" / "served-snapshot.json").exists() # a dry run writes nothing
141
+
142
+ # Auto-retire groups by connection without tags: both models on the one
143
+ # connection failing at once is held as the connection, not retired.
144
+ import datetime as dt
145
+ models = {"a-free": "vendor/a:free", "b-free": "vendor/b:free"}
146
+ tags, on_owui = auto_retire.connections(models)
147
+ assert tags == {"vendor/a:free": "openrouter", "vendor/b:free": "openrouter"} and on_owui == set(), tags
148
+ today = dt.date(2026, 10, 21)
149
+ rows = [{"alias": a, "date": today, "answered": False, "failure": "entitlement", "detail": "402"} for a in models]
150
+ p = auto_retire.plan(today, models, set(models.values()), {}, rows, {}, {}, tags, on_owui)
151
+ assert set(p["held"]) == {"openrouter"} and not p["confirm"], p
152
+ gone = auto_retire.plan(today, {"a-free": "vendor/a:free"}, {"x.vendor/a:free"},
153
+ {"vendor/a:free": "2026-10-15"}, [], {}, {}, tags, on_owui)
154
+ assert dict(gone["retire"])["a-free"].startswith("not served by its endpoint"), gone # no prefix guess
155
+
156
+ # Every Open WebUI-only path refuses or skips, and says why.
157
+ try:
158
+ owui.endpoint()
159
+ raise AssertionError("owui found a hub that is not there")
160
+ except ValueError as exc:
161
+ assert "Open WebUI hub" in str(exc)
162
+ r = serve.discover_add("groq", "vendor/z")
163
+ assert not r["ok"] and "Open WebUI hub" in r["errors"][0], r
164
+ _, out = quietly(provider_tags.main)
165
+ assert "no Open WebUI hub" in out and not (TMP / "config" / "provider-tags.json").exists(), out
166
+ code, _ = run_main(reprefix, ["reprefix.py", "groq", "--prefix", "g."])
167
+ assert "Open WebUI" in str(code), code
168
+
169
+ # Adding an endpoint: an explicit kind, a hub only with its type.
170
+ assert not serve.save_endpoint("odd", fake + "/v1", "k", new=True, kind="cloud")["ok"]
171
+ assert not serve.save_endpoint("odd", fake + "/v1", "k", new=True, kind="hub")["ok"]
172
+ # Test (the form's button before Save) answers as Save would and writes nothing, the key included.
173
+ t = serve.save_endpoint("tried", fake + "/v1", "k-tried", new=True, test_only=True)
174
+ assert t["ok"] and "served" in t and "tried" not in endpoints.load() and "tried" not in endpoints.keys(), t
175
+ assert serve.save_endpoint("plain", fake + "/v1", "k", new=True)["ok"]
176
+ assert not serve.save_endpoint("plain", fake + "/v1", "k", new=True, test_only=True)["ok"] # names checked in Test too
177
+ assert serve.save_endpoint("mine", fake + "/v1", "k", new=True, kind="hub", hub="openwebui")["ok"]
178
+ data = endpoints.load()
179
+ assert data["plain"]["kind"] == "provider" and "hub" not in data["plain"], data["plain"]
180
+ assert (data["mine"]["kind"], data["mine"]["hub"]) == ("hub", "openwebui"), data["mine"]
181
+ assert serve.save_endpoint("mine", fake + "/v1", "k2")["ok"] and endpoints.hub("mine") == "openwebui" # Change key keeps it
182
+ rows = {e["name"]: e for e in serve.endpoint_list()["endpoints"]}
183
+ assert (rows["openrouter"]["kind"], rows["mine"]["hub"]) == ("aggregator", "openwebui"), rows
184
+
185
+ assert owui_calls() == [], owui_calls()
186
+
187
+
188
+ def owner(fake: str) -> None:
189
+ """The owner's endpoints.json shape: no `kind` anywhere."""
190
+ write("endpoints.json", {
191
+ "openwebui": {"base": fake + "/api", "key": "openwebui", "default": True,
192
+ "decisions": fake + "/openai/{path}"},
193
+ "openrouter": {"base": fake + "/or/api/v1", "key": "openrouter",
194
+ "headers": {"HTTP-Referer": "https://example.com", "X-Title": "Example"},
195
+ "unlisted_models": ["vendor/decide:free"]},
196
+ "ollama": {"key": "ollama", "connect": {"provider": "ollama", "at": "2026-10-20T22:30:52-05:00"},
197
+ "base": fake + "/ol/v1"}})
198
+ import endpoints
199
+ write("extra-openai-models.yaml", endpoints.yaml_block("fast-1", "fast-1")
200
+ + endpoints.yaml_block("codestral", "codestral")
201
+ + endpoints.yaml_block("x-free", "vendor/x:free", "openrouter"))
202
+ write("served-snapshot.json", {"last_seen": {"fast-1": "2026-10-20", "codestral": "2026-10-18"},
203
+ "first_missing": {"codestral": "2026-10-19"}})
204
+ import route
205
+ import serve
206
+ import check_deprecations
207
+ import auto_retire
208
+ import owui
209
+ import provider_tags
210
+ import reprefix
211
+ import decide
212
+ SEEN.clear()
213
+
214
+ # Inferred from his real bases (a LAN address stands in as hub.example)...
215
+ real = {"openwebui": {"base": "http://hub.example:4141/api"}, "openrouter": {"base": "https://openrouter.ai/api/v1"},
216
+ "ollama": {"base": "http://localhost:11434/v1"}}
217
+ kinds = {n: (endpoints.kind(n, real), endpoints.hub(n, real)) for n in real}
218
+ assert kinds == {"openwebui": ("hub", "openwebui"), "openrouter": ("provider", ""), "ollama": ("local", "")}, kinds
219
+ # ...and here, where every base is the fake on 127.0.0.1, the /api root still makes the one hub.
220
+ assert [n for n in endpoints.load() if endpoints.hub(n)] == ["openwebui"]
221
+ assert endpoints.find_hub("openwebui") == "openwebui" and endpoints.recorded_whole("openwebui")
222
+ assert owui.endpoint() == "openwebui" and owui._call("/openai/config") == OWUI_CONFIG
223
+
224
+ # Provider tags: Open WebUI's connections by host, the direct endpoint by name.
225
+ _, out = quietly(provider_tags.main)
226
+ tags = json.loads((TMP / "config" / "provider-tags.json").read_text(encoding="utf-8"))
227
+ assert tags["fast-1"] == "groq" and tags["llm7.codestral"] == "llm7-api" and tags["vendor/x:free"] == "openrouter", tags
228
+ assert "never-read" not in json.dumps(tags)
229
+ assert route.connection_of("fast-1") == "groq" and route.connection_of("x-free") == "openrouter"
230
+
231
+ # The default hub is recorded whole; the direct endpoint, its registered models only.
232
+ assert check_deprecations.served() == {"fast-1", "llm7.codestral", "fresh-2", "mercury-decide",
233
+ "vendor/x:free@openrouter"}
234
+ c = serve.onboard_candidates()
235
+ groups = {x["served"]: (x["group"], x["new"]) for x in c["candidates"]}
236
+ assert groups["fresh-2"] == ("groq", True) and groups["vendor/y"] == ("openrouter (direct)", False), groups
237
+ assert "never-read" not in json.dumps(c), c
238
+ # Added to Open WebUI after provider_tags.py ran: named live from its urlIdx,
239
+ # not "unknown connection"; the tags file answers when the connections
240
+ # cannot be read (a key that is not an admin's), and the rest still list.
241
+ OWUI_MODELS.append({"id": "just-added", "urlIdx": 1})
242
+ real_names = provider_tags.connection_names
243
+ try:
244
+ live = {x["served"]: x["group"] for x in serve.onboard_candidates()["candidates"]}
245
+
246
+ def refused(hub):
247
+ raise OSError("401")
248
+ provider_tags.connection_names = refused
249
+ c = serve.onboard_candidates()
250
+ finally:
251
+ provider_tags.connection_names = real_names
252
+ OWUI_MODELS.pop()
253
+ fallback = {x["served"]: x["group"] for x in c["candidates"]}
254
+ assert live["just-added"] == "llm7-api" and live["fresh-2"] == "groq", live
255
+ assert c["ok"] and not c["errors"] and fallback["just-added"] == "unknown connection", c
256
+ assert fallback["fresh-2"] == "groq" and fallback["vendor/y"] == "openrouter (direct)", fallback
257
+
258
+ # Auto-retire: a prefix added in Open WebUI is a rename, said in Open WebUI's words.
259
+ models = {"fast-1": "fast-1", "codestral": "codestral", "x-free": "vendor/x:free@openrouter"}
260
+ conns, on_owui = auto_retire.connections(models)
261
+ assert on_owui == {"fast-1", "codestral"} and conns["vendor/x:free@openrouter"] == "openrouter", (conns, on_owui)
262
+ import datetime as dt
263
+ p = auto_retire.plan(dt.date(2026, 10, 25), models, check_deprecations.served(),
264
+ {"codestral": "2026-10-19"}, [], {}, {}, conns, on_owui)
265
+ assert "reprefix" in dict(p["pending"])["codestral"] and not p["retire"], p
266
+ assert serve.model_status()["codestral"][2] == "missing from Open WebUI since 2026-10-19"
267
+ code, out = run_main(reprefix, ["reprefix.py", "llm7-api", "--prefix", "llm7."])
268
+ assert code is None and "codestral" in out and "llm7.codestral" in out, (code, out)
269
+
270
+ # Decisions through Open WebUI's passthrough.
271
+ assert decide.probe("openwebui", "mercury-decide") == (None, "decisions")
272
+ hit = set(owui_calls())
273
+ assert {"/openai/config", "/api/models", "/openai/decisions"} <= hit, hit
274
+
275
+
276
+ def main() -> int:
277
+ server = ThreadingHTTPServer(("127.0.0.1", 0), Fake)
278
+ threading.Thread(target=server.serve_forever, daemon=True).start()
279
+ fake = f"http://127.0.0.1:{server.server_port}"
280
+ try:
281
+ stranger(fake)
282
+ owner(fake)
283
+ finally:
284
+ server.shutdown()
285
+ shutil.rmtree(TMP, ignore_errors=True)
286
+ print("ok")
287
+ return 0
288
+
289
+
290
+ if __name__ == "__main__":
291
+ sys.exit(main())