65 lines
3.1 KiB
Python
65 lines
3.1 KiB
Python
"""Regression check for #7228 — model-picker search vs OpenRouter display names.
|
|
|
|
Composer model-picker search was a literal substring on the rendered name and
|
|
id. OpenRouter overflow rows were labeled with the raw id (``stealth/ox-alpha``)
|
|
instead of the provider display name (``Ox Alpha``), and spaces were not
|
|
normalized to hyphens — so typing the name users see in Hermes Desktop
|
|
(``Ox Alpha``) always yielded "No models found".
|
|
|
|
Fixed in three layers (issue requirement):
|
|
1. backend: /api/models ships the friendly display name from the local
|
|
OpenRouter metadata disk cache for curated catalog rows;
|
|
2. frontend: getModelLabel() resolves via the dynamic label map, which is
|
|
now hydrated with the display name;
|
|
3. frontend: search folds whitespace/hyphens/dots on both sides so
|
|
``ox alpha`` == ``ox-alpha`` == ``ox.alpha``.
|
|
|
|
Verified at the source level so this stays fast.
|
|
"""
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).parent.parent
|
|
UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8")
|
|
CONFIG_PY = (REPO / "api" / "config.py").read_text(encoding="utf-8")
|
|
|
|
|
|
def test_backend_has_openrouter_display_name_helper():
|
|
# The helper must read only the local disk cache (no network).
|
|
assert "def _openrouter_model_display_name(model_id: str) -> str:" in CONFIG_PY
|
|
assert "_load_model_metadata_disk_cache" in CONFIG_PY
|
|
|
|
|
|
def test_backend_ships_display_name_not_raw_id():
|
|
# Curated OpenRouter catalog rows must use the friendly display name
|
|
# instead of the raw id as the picker label (#7228).
|
|
snippet = CONFIG_PY[CONFIG_PY.index('fetch_openrouter_models as _fetch_or_models'):]
|
|
snippet = snippet[:snippet.index('except Exception')]
|
|
assert "_openrouter_model_display_name(mid)" in snippet
|
|
assert '{"id": mid, "label": mid}' not in snippet
|
|
|
|
|
|
def test_filter_models_folds_space_hyphen_dot():
|
|
# Search must fold whitespace/hyphens/dots on both sides so display names
|
|
# with spaces match ids with hyphens ("ox alpha" == "ox-alpha").
|
|
assert "replace(/[\\s._-]+/g,'')" in UI_JS
|
|
assert "_foldModelSearch(name).includes(foldTerm)" in UI_JS
|
|
assert "_foldModelSearch(id).includes(foldTerm)" in UI_JS
|
|
|
|
|
|
def test_get_model_label_uses_dynamic_map_first():
|
|
# getModelLabel() must resolve through the dynamic label map, which is
|
|
# hydrated from m.label / overflowModel.label (now the display name).
|
|
idx = UI_JS.index("function getModelLabel(modelId){")
|
|
# The dynamic-map lookup must appear before the static-label fallback,
|
|
# so backend display names win over the hardcoded table.
|
|
assert "_dynamicModelLabels[modelId]" in UI_JS[idx:idx + 900]
|
|
assert UI_JS[idx:idx + 900].index("_dynamicModelLabels[modelId]") < \
|
|
(UI_JS[idx:idx + 900].index("STATIC_LABELS") if "STATIC_LABELS" in UI_JS[idx:idx + 900] else 10**9)
|
|
# The dynamic map must be populated from the backend label, not the raw id.
|
|
assert "_dynamicModelLabels[m.id]=m.label||m.id" in UI_JS
|
|
|
|
|
|
def test_literal_substring_match_still_present():
|
|
# Raw-id search ("stealth", "stealth/ox-alpha") must keep working.
|
|
assert "name.includes(term)||id.includes(term)" in UI_JS
|