fix: hide stale and effort-preset models without losing checks
Build and deploy / build-deploy (push) Waiting to run

This commit is contained in:
2026-10-27 09:44:23 +00:00
parent 3ac835ab7e
commit 631b44666f
4 changed files with 93 additions and 13 deletions
+39 -12
View File
@@ -6,6 +6,7 @@ from __future__ import annotations
import json import json
import os import os
import re
import sqlite3 import sqlite3
import time import time
import urllib.request import urllib.request
@@ -95,6 +96,7 @@ def init_db():
text_model_match TEXT, text_model_match TEXT,
agent_model_match TEXT, agent_model_match TEXT,
updated_at TEXT, updated_at TEXT,
active INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY(text_model_match) REFERENCES arena_text_leaderboard(model_name), FOREIGN KEY(text_model_match) REFERENCES arena_text_leaderboard(model_name),
FOREIGN KEY(agent_model_match) REFERENCES arena_agent_leaderboard(model_name) FOREIGN KEY(agent_model_match) REFERENCES arena_agent_leaderboard(model_name)
); );
@@ -133,6 +135,11 @@ def init_db():
if col not in cols: if col not in cols:
db.execute(f"ALTER TABLE checks ADD COLUMN {col} {decl}") db.execute(f"ALTER TABLE checks ADD COLUMN {col} {decl}")
# Историю checks не трогаем; видимость модели живёт отдельно.
model_cols = [r[1] for r in db.execute("PRAGMA table_info(models)").fetchall()]
if "active" not in model_cols:
db.execute("ALTER TABLE models ADD COLUMN active INTEGER NOT NULL DEFAULT 1")
db.commit() db.commit()
db.close() db.close()
@@ -232,7 +239,9 @@ def fetch_omni_models() -> list[dict]:
) )
with urllib.request.urlopen(req, timeout=15) as r: with urllib.request.urlopen(req, timeout=15) as r:
data = json.loads(r.read()) data = json.loads(r.read())
return data.get("data", []) if not isinstance(data, dict) or not isinstance(data.get("data"), list):
raise ValueError("OmniRoute returned an invalid models catalog")
return data["data"]
def is_skip(model_id: str) -> bool: def is_skip(model_id: str) -> bool:
@@ -242,11 +251,17 @@ def is_skip(model_id: str) -> bool:
]) ])
def is_effort_variant(model_id: str, catalog_ids: set[str]) -> bool:
"""Пресет только при наличии базового ID на том же маршруте.
Так не скрываем самостоятельные модели вроде qwen3.8-max.
"""
base = re.sub(r"-(?:low|medium|high|xhigh|max|ultra|none)(?=(:free)?$)", "", model_id)
return base != model_id and base in catalog_ids
# ─── Arena matching (simple) ───────────────────────────────────────────────── # ─── Arena matching (simple) ─────────────────────────────────────────────────
import re
def _norm(s: str) -> str: def _norm(s: str) -> str:
return re.sub(r"[^a-z0-9]", "", s.lower()) return re.sub(r"[^a-z0-9]", "", s.lower())
@@ -332,7 +347,12 @@ def sync_models():
return None return None
now = datetime.now(timezone.utc).isoformat() now = datetime.now(timezone.utc).isoformat()
if not omni:
print("[sync] OmniRoute returned an empty catalog; keeping previous visibility")
db.close()
return
catalog_ids = {m["id"] for m in omni}
seen = set()
for m in omni: for m in omni:
mid = m["id"] mid = m["id"]
if is_skip(mid): if is_skip(mid):
@@ -340,6 +360,9 @@ def sync_models():
# openrouter — только :free # openrouter — только :free
if mid.startswith("openrouter/") and not mid.endswith(":free"): if mid.startswith("openrouter/") and not mid.endswith(":free"):
continue continue
if is_effort_variant(mid, catalog_ids):
continue
seen.add(mid)
provider = mid.split("/")[0] provider = mid.split("/")[0]
raw = mid.split("/")[-1].lower() raw = mid.split("/")[-1].lower()
@@ -353,20 +376,24 @@ def sync_models():
db.execute(""" db.execute("""
INSERT INTO models (id, provider, name, raw_base, norm_base, clean_base, INSERT INTO models (id, provider, name, raw_base, norm_base, clean_base,
text_model_match, agent_model_match, updated_at) text_model_match, agent_model_match, updated_at, active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1)
ON CONFLICT(id) DO UPDATE SET ON CONFLICT(id) DO UPDATE SET
text_model_match = excluded.text_model_match, text_model_match = excluded.text_model_match,
agent_model_match = excluded.agent_model_match, agent_model_match = excluded.agent_model_match,
updated_at = excluded.updated_at updated_at = excluded.updated_at,
active = 1
""", ( """, (
mid, provider, mid, base, _norm(base), _norm(cleaned), mid, provider, mid, base, _norm(base), _norm(cleaned),
text_match, agent_match, now text_match, agent_match, now
)) ))
# Снимок каталога целиком: устаревшие/пресетные строки сохраняются для
# истории checks, но не попадают в список и следующий cron-прогон.
db.execute("UPDATE models SET active = 0 WHERE updated_at IS NULL OR updated_at != ?", (now,))
db.commit() db.commit()
db.close() db.close()
print(f"[sync] synced {len(omni)} models at {now} with SQL foreign keys") print(f"[sync] active {len(seen)} models from {len(omni)} OmniRoute entries at {now}")
# ─── Stats helpers ──────────────────────────────────────────────────────────── # ─── Stats helpers ────────────────────────────────────────────────────────────
@@ -538,7 +565,7 @@ def api_models(
FROM models m FROM models m
LEFT JOIN arena_text_leaderboard t ON m.text_model_match = t.model_name LEFT JOIN arena_text_leaderboard t ON m.text_model_match = t.model_name
LEFT JOIN arena_agent_leaderboard a ON m.agent_model_match = a.model_name LEFT JOIN arena_agent_leaderboard a ON m.agent_model_match = a.model_name
WHERE 1=1 WHERE m.active = 1
""" """
params: list = [] params: list = []
if provider: if provider:
@@ -585,7 +612,7 @@ def api_models(
@app.get("/api/providers") @app.get("/api/providers")
def api_providers(): def api_providers():
db = get_db() db = get_db()
rows = db.execute("SELECT DISTINCT provider FROM models ORDER BY provider").fetchall() rows = db.execute("SELECT DISTINCT provider FROM models WHERE active = 1 ORDER BY provider").fetchall()
db.close() db.close()
return [r["provider"] for r in rows] return [r["provider"] for r in rows]
+5 -1
View File
@@ -81,7 +81,11 @@ def _ensure_usage_columns(db: sqlite3.Connection):
def run(): def run():
db = sqlite3.connect(DB_PATH, check_same_thread=False) db = sqlite3.connect(DB_PATH, check_same_thread=False)
_ensure_usage_columns(db) _ensure_usage_columns(db)
models = [r[0] for r in db.execute("SELECT id FROM models").fetchall()] model_cols = [r[1] for r in db.execute("PRAGMA table_info(models)")]
if "active" not in model_cols:
db.execute("ALTER TABLE models ADD COLUMN active INTEGER NOT NULL DEFAULT 1")
db.commit()
models = [r[0] for r in db.execute("SELECT id FROM models WHERE active = 1").fetchall()]
db.close() db.close()
print(f"[probe] checking {len(models)} models @ {datetime.now(timezone.utc).isoformat()}") print(f"[probe] checking {len(models)} models @ {datetime.now(timezone.utc).isoformat()}")
+3
View File
@@ -125,6 +125,9 @@ def _reset_omni(omni_server):
"""Изолировать тесты: состояние мока не должно протекать между тестами.""" """Изолировать тесты: состояние мока не должно протекать между тестами."""
with omni_server.lock: with omni_server.lock:
omni_server.state["per_model"] = {} omni_server.state["per_model"] = {}
omni_server.state["models"] = {"data": [
{"id": "zai/test-model-a"}, {"id": "agy/test-model-b"},
]}
omni_server.state["global_delay"] = 0.0 omni_server.state["global_delay"] = 0.0
omni_server.state["completions"] = {"status": 200, "usage": None, "content": "4"} omni_server.state["completions"] = {"status": 200, "usage": None, "content": "4"}
omni_server.requests.clear() omni_server.requests.clear()
+46
View File
@@ -0,0 +1,46 @@
"""Каталог OmniRoute: пропавшие ID и effort-пресеты не должны загрязнять витрину."""
import sqlite3
def test_sync_hides_stale_and_effort_but_preserves_history(core, omni_server):
db = sqlite3.connect(core.db_path)
db.execute("INSERT INTO checks (model_id,checked_at,ok) VALUES ('zai/test-model-a','2026-10-26T00:00:00+00:00',1)")
db.commit()
db.close()
omni_server.state["models"] = {"data": [
{"id": "agy/test-model-b"},
{"id": "agy/test-model-b-high"},
{"id": "cx/gpt-5.6-terra"},
{"id": "cx/gpt-5.6-terra-low"},
{"id": "openrouter/foo/model-high:free"},
{"id": "openrouter/foo/model:free"},
{"id": "agy/kimi-highway"},
{"id": "freellmapi/qwen3.8-max"},
]}
assert core.client.post("/api/sync").status_code == 200
ids = {m["id"] for m in core.client.get("/api/models").json()}
assert ids == {"agy/test-model-b", "cx/gpt-5.6-terra", "openrouter/foo/model:free", "agy/kimi-highway", "freellmapi/qwen3.8-max"}
assert "zai" not in core.client.get("/api/providers").json()
db = sqlite3.connect(core.db_path)
assert db.execute("SELECT active FROM models WHERE id='zai/test-model-a'").fetchone()[0] == 0
assert db.execute("SELECT COUNT(*) FROM checks WHERE model_id='zai/test-model-a'").fetchone()[0] == 1
db.close()
omni_server.requests.clear()
core.probe.run()
requested = {x["body"].get("model") for x in omni_server.requests if x["path"] == "/v1/chat/completions"}
assert requested == ids
# ID возвращается в каталог — снова активен, история на месте.
omni_server.state["models"]["data"].append({"id": "zai/test-model-a"})
core.client.post("/api/sync")
assert "zai/test-model-a" in {m["id"] for m in core.client.get("/api/models").json()}
db = sqlite3.connect(core.db_path)
assert db.execute("SELECT active FROM models WHERE id='zai/test-model-a'").fetchone()[0] == 1
assert db.execute("SELECT COUNT(*) FROM checks WHERE model_id='zai/test-model-a'").fetchone()[0] == 1
db.close()
def test_empty_catalog_does_not_hide_everything(core, omni_server):
original = {m["id"] for m in core.client.get("/api/models").json()}
omni_server.state["models"] = {"data": []}
core.client.post("/api/sync")
assert {m["id"] for m in core.client.get("/api/models").json()} == original