Files
hermes-webui/tests/test_dotted_model_label.py
T
747b5a6913 Batch release exp-v0.52.236 — dotted model labels (#6628) + MCP SDK pin (#6616) (#7103)
* fix(models): normalize dotted Bedrock/Vertex model IDs in labels

Split out of #6607 at reviewer request — it was unrelated to that PR's MEDIA
path handling and needed its own tests.

`us.anthropic.claude-opus-5` carries a cross-region routing prefix plus a vendor
namespace, and `mistral.mistral-large-2407-v1:0` adds a provisioned-revision
suffix. None of it belongs in a human label, so the turn footer rendered
"Us.anthropic.claude Opus 5".

Strip only the two shapes these hosts actually publish, against a CLOSED
provider allow-list:

  <region>.<vendor>.<model>   us.anthropic.claude-opus-5
  <vendor>.<model>            mistral.mistral-large-2407-v1:0

A generic "drop leading letters-only dot segments" loop was rejected because it
rewrites arbitrary uncatalogued IDs: `deepseek.v3` rendered as "V3" (vendor name
silently deleted) and `foo.bar.baz` as "BAZ". Dropping a vendor is additionally
gated on the remainder still naming the model, so `deepseek.v3` — where the
vendor IS the name — is left byte-intact.

Backend and frontend are paired: tests/test_dotted_model_label.py drives one
table through both `_get_label_for_model()` and `_stripDottedModelPrefix()` and
fails on divergence, including version dots (`gpt-4.1`, `qwen3.6-35b`),
URI-scheme IDs, and unknown vendors.

The Python half is inlined inside `_get_label_for_model` on purpose: the #3429
harnesses extract that function's source and eval it in isolation, so a
module-level helper NameErrors there. The #3429 JS driver is updated to pull in
the new helper for the same reason.

* fix(models): recognize `global` as a Bedrock routing prefix

Re-review catch, and a real gap: the catalog ships six
`global.anthropic.claude-*` IDs (api/config.py:1901-1909) and the first-party
routing notes use `global.anthropic.claude-…` as the canonical Bedrock shape,
but `global` was missing from the region allow-list. All six therefore kept the
noise this change exists to remove:

    global.anthropic.claude-opus-4-7  ->  "Global.anthropic.claude Opus 4 7"

Added to the region set in both implementations, which now read identically.

The root cause is two lists that must agree — the region allow-list and the
shipped catalog — so the new guard is catalog-driven rather than another
hardcoded region list: it scrapes every dotted `<head>.<vendor>.<model>` ID out
of api/config.py and asserts none of them keeps a routing/vendor namespace in
its label. A future routing prefix added to the catalog without updating the
region set fails there, instead of shipping mislabeled.

Also pins all six `global.*` IDs plus `us-gov` in the shared strip table (no
suite covered `us-gov` before either), and verified by mutation: dropping
`global` from the Python set fails 3 tests, dropping it from only the JS set
fails 2 — so backend/frontend parity drift is caught, not just a total absence.

Note: the review cited tests/test_provider_prefix_label_normalization.py and
tests/test_ui_model_label_parity.py as the suites to extend; neither exists in
this tree (nothing under tests/ referenced `us-gov` at all), so the cases live
in tests/test_dotted_model_label.py alongside the existing paired coverage.

* fix(models): add missing Bedrock vendors; make the catalog guard actually broad

Two self-review findings (hostile critic pass). Both are the same class as the
`global` gap the reviewer already caught, which means the guard I added for that
gap was too weak to prevent a recurrence.

1. Missing vendors. `luma`, `twelvelabs` and `ibm` are real Bedrock
   foundation-model vendors and were absent from the allow-list, so genuine IDs
   shipped with the namespace intact:

       luma.ray-2                       -> "Luma.ray 2"
       us.twelvelabs.marengo-embed-2-7  -> "Us.twelvelabs.marengo Embed 2 7"
       us.ibm.granite-3-8b              -> "Us.ibm.granite 3 8B"

   Added those plus `nvidia` and `snowflake` to both implementations.

2. The catalog guard inspected 6 of 75 dotted IDs. Its scrape regex only matched
   three-segment `"id": "<region>.<vendor>.<model>"` literals with double quotes,
   so the entire two-segment `<vendor>.<model>` shape -- the OTHER documented
   shape this PR handles -- was invisible to it. A test that reads 8% of the
   corpus while claiming to cover the catalog is worse than no test, because it
   reads as proof.

   Rewritten to scrape any quoted `id` value regardless of quote style or segment
   count (75 dotted IDs now inspected), and to DERIVE the namespace heads from the
   production `_regions`/`_vendors` literals instead of retyping them, so a set
   that grows without a test update is still covered. Version dots (`qwen3.6-plus`,
   `gpt-5.4`) are correctly skipped as non-namespaces.

Also added an explicit per-vendor round-trip test. It asserts no dotted NAMESPACE
survives rather than that the vendor word is absent, because a vendor legitimately
reappears inside some model names (`mistral.mistral-large-2407` -> "Mistral Large
2407") -- my first version of that assertion was wrong for exactly that reason.

Verified by mutation: removing the new vendors fails the round-trip test; deleting
the strip entirely fails 6 tests.

* test(models): drive real getModelLabel() in the parity oracle

The paired test compared _get_label_for_model() against itself, so JS
getModelLabel() never ran and nothing asserted the two sides agreed. It
stayed green when the JS dotted strip was reverted to a no-op AND when the
whole post-strip retry chain was deleted -- blind to both.

Replaced with two tests driven through the real getModelLabel() under Node,
reusing the driver already proven in test_issue3429_uri_scheme_model_label.
Only sinks are stubbed (_dynamicModelLabels empty as it is pre-fetch,
_fmtOllamaLabel identity); every decision function is the shipped source.

The oracle asserts the actual contract -- no routing/vendor namespace leaks
into either label -- rather than string equality of the two labels. Label
divergence is pre-existing and by design: at base dd7f6ac3, claude-opus-5
(no dot, untouched here) already labels 'claude-opus-5' in JS vs
'Claude Opus 5' in Python, and openai/gpt-4o 'GPT-4o' vs 'GPT 4O'.
getModelLabel() checks _dynamicModelLabels first (ui.js:6991), populated
from the server label (ui.js:3483,3494,3606), so the backend wins once a
catalog loads; the JS formatter is the pre-fetch fallback.

Dropping the JS post-normalization instead would regress the picker to raw
ids before catalog load (us.anthropic.claude-sonnet-4-5 -> 'claude-sonnet-4-5'
instead of 'Sonnet 4.5').

Mutation-checked: JS strip no-op fails 2, retry chain deleted fails 1,
'global' dropped from backend _regions fails 1. Region set derived from
api/config.py source rather than retyped.

No production code changed.

* fix(test): pin mcp SDK to compatible 1.x range with bootstrap guard (#6602)

Change  to  in requirements-dev.txt and the
mcp_server.py docstring, and add a bootstrap guard in mcp_server.py
that checks Server.list_tools existence at module load time.

The guard fails fast with a clear error message if an incompatible
mcp SDK (2.x) is installed, instead of producing dozens of
secondary errors at test collection/setup. This completes the
mitigation started in PR #6564 by adding a minimum version floor
and an import-time compatibility check.

* docs(changelog): note #6628 dotted model labels, #6616 mcp SDK pin

---------

Co-authored-by: Sam Painter <samfp@amazon.com>
Co-authored-by: webtecnica <webtecnica@gmail.com>
Co-authored-by: n <a@n>
2026-08-17 06:37:52 +00:00

450 lines
20 KiB
Python

"""Dotted Bedrock/Vertex model-label normalization (split out of PR #6607).
``us.anthropic.claude-opus-5`` carries a cross-region routing prefix and a vendor
namespace. Left intact both survive into the human label, so the turn footer
rendered "Us.anthropic.claude Opus 5".
The normalization is implemented twice — inlined in ``_get_label_for_model()``
(api/config.py) and as ``_stripDottedModelPrefix()`` (static/ui.js) — so these
tests are PAIRED: one table drives both sides, and a divergence fails.
The allow-list is closed on purpose. A generic "drop leading letters-only dot
segments" loop rewrote arbitrary uncatalogued IDs (``deepseek.v3`` → "V3",
``foo.bar.baz`` → "BAZ"); those cases are pinned below.
"""
from __future__ import annotations
import json
import shutil
import subprocess
import sys
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).parent.parent.resolve()
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from api.config import _get_label_for_model # noqa: E402
UI_JS = (REPO_ROOT / "static" / "ui.js").read_text(encoding="utf-8")
def _derive_set_from_config(marker: str) -> set[str]:
"""Read an allow-list out of PRODUCTION source instead of retyping it.
A retyped copy silently stops matching when the real set grows -- which is
exactly how ``global`` and then ``luma``/``twelvelabs``/``ibm`` shipped
mislabeled. Same technique as test_every_catalog_dotted_id_loses_its_routing_prefix.
"""
import re as _re
config_src = (REPO_ROOT / "api" / "config.py").read_text(encoding="utf-8")
start = config_src.index(marker)
body = config_src[start:config_src.index("}", start)]
found = {m.lower() for m in _re.findall(r'"([a-z0-9-]+)"', body)}
assert found, f"could not derive {marker!r} from api/config.py"
return found
_PRODUCTION_REGIONS = _derive_set_from_config("_regions = {")
# (model_id, expected_normalized_id) — what the dotted-prefix step must leave
# behind, BEFORE the shared cosmetic title-casing that both sides apply after.
STRIP_CASES = [
# --- documented Bedrock/Vertex shapes: prefix is plumbing --------------
("us.anthropic.claude-opus-5", "claude-opus-5"),
("eu.anthropic.claude-sonnet-4-5-20250929-v1:0",
"claude-sonnet-4-5-20250929-v1"),
("apac.anthropic.claude-haiku-4", "claude-haiku-4"),
# ``global`` is a routing head the catalog actually ships (six
# ``global.anthropic.claude-*`` IDs at api/config.py:1901-1909, and the
# first-party routing notes use it as the canonical Bedrock shape). Omitting
# it from the region set left every one of those labels reading
# "Global.anthropic.claude Opus 4 7".
("global.anthropic.claude-opus-4-7", "claude-opus-4-7"),
("global.anthropic.claude-opus-4-6-v1", "claude-opus-4-6-v1"),
("global.anthropic.claude-sonnet-4-6", "claude-sonnet-4-6"),
("global.anthropic.claude-opus-4-5-20251101-v1:0",
"claude-opus-4-5-20251101-v1"),
("global.anthropic.claude-sonnet-4-5-20250929-v1:0",
"claude-sonnet-4-5-20250929-v1"),
("global.anthropic.claude-haiku-4-5-20251001-v1:0",
"claude-haiku-4-5-20251001-v1"),
("us-gov.anthropic.claude-opus-5", "claude-opus-5"),
("mistral.mistral-large-2407-v1:0", "mistral-large-2407-v1"),
("amazon.nova-pro-v1:0", "nova-pro-v1"),
("meta.llama3-70b-instruct-v1:0", "llama3-70b-instruct-v1"),
# --- must be left BYTE-INTACT ------------------------------------------
# Vendor is the whole name: stripping it would render the model as "V3".
("deepseek.v3", "deepseek.v3"),
# Uncatalogued vendor: not our shape, do not touch.
("foo.bar.baz", "foo.bar.baz"),
("acme.super-model-9", "acme.super-model-9"),
# A known region head with an unknown vendor is not our shape either.
("us.foo.bar", "us.foo.bar"),
# Version dots must survive.
("gpt-4.1", "gpt-4.1"),
("qwen3.6-35b", "qwen3.6-35b"),
("o1.5-preview", "o1.5-preview"),
# URI-scheme IDs are paths, not dotted namespaces.
("https://host/v1.2/model", "https://host/v1.2/model"),
# No dot at all.
("claude-opus-5", "claude-opus-5"),
]
# End-to-end labels through the real backend function.
LABEL_CASES = [
("us.anthropic.claude-opus-5", "Claude Opus 5"),
("mistral.mistral-large-2407-v1:0", "Mistral Large 2407 V1"),
("gpt-4.1", "GPT 4.1"),
("qwen3.6-35b", "Qwen3.6 35B"),
]
@pytest.mark.parametrize("model_id,expected", LABEL_CASES)
def test_backend_label_drops_dotted_plumbing(model_id, expected):
assert _get_label_for_model(model_id, []) == expected
@pytest.mark.parametrize("model_id", [
"deepseek.v3", "foo.bar.baz", "acme.super-model-9", "us.foo.bar",
])
def test_backend_label_keeps_uncatalogued_vendor_name(model_id):
"""The vendor word must not be silently deleted from an unknown ID."""
vendor = model_id.split(".")[0]
label = _get_label_for_model(model_id, [])
assert vendor.lower() in label.lower(), (
f"{model_id!r} lost its vendor: label={label!r}"
)
def _js_strip(model_ids: list[str]) -> list[str]:
node = shutil.which("node")
if not node:
pytest.skip("node not available")
def extract(src: str, name: str) -> str:
start = src.index(f"function {name}(")
depth = 0
started = False
for i in range(start, len(src)):
if src[i] == "{":
depth += 1
started = True
elif src[i] == "}":
depth -= 1
if started and depth == 0:
return src[start:i + 1]
raise AssertionError(f"unbalanced braces extracting {name}")
# The two Sets the helper closes over are declared immediately above it.
sets_start = UI_JS.index("const _BEDROCK_REGION_PREFIXES")
sets_end = UI_JS.index("function _stripDottedModelPrefix")
script = "\n".join([
UI_JS[sets_start:sets_end],
extract(UI_JS, "_stripDottedModelPrefix"),
"const ids = JSON.parse(process.argv[1]);",
"console.log(JSON.stringify(ids.map(_stripDottedModelPrefix)));",
])
proc = subprocess.run(
[node, "--input-type=module", "-e", script, json.dumps(model_ids)],
capture_output=True, text=True, timeout=30, check=True,
)
return json.loads(proc.stdout)
def test_frontend_strip_matches_the_table():
"""The JS half must normalize exactly as specified — same table."""
ids = [c[0] for c in STRIP_CASES]
js = _js_strip(ids)
for (model_id, expected), js_out in zip(STRIP_CASES, js, strict=True):
assert js_out == expected, (
f"js drifted for {model_id!r}: js={js_out!r} expected={expected!r}"
)
def _js_model_labels(model_ids: list[str]) -> dict[str, str]:
"""Drive the REAL ``getModelLabel()`` from static/ui.js under Node.
``_js_strip()`` above exercises only ``_stripDottedModelPrefix()``. That is
the wrong unit for a parity claim: ``getModelLabel()`` applies a whole retry
chain AFTER the strip (``_dynamicModelLabels`` → ``STATIC_LABELS`` →
``'anthropic/' + id`` → a ``claude-`` prettifier). A test that stops at the
strip cannot observe any of it.
Only SINKS are stubbed (``_dynamicModelLabels`` starts empty, exactly as it
is before a catalog fetch; ``_fmtOllamaLabel`` is identity). Every decision
function is the shipped source, eval'd — so drift fails here instead of
rendering wrong in the picker.
"""
node = shutil.which("node")
if not node:
pytest.skip("node not available")
proc = subprocess.run(
[node, "-e", _GET_MODEL_LABEL_DRIVER, str(REPO_ROOT / "static" / "ui.js"),
json.dumps(model_ids)],
capture_output=True, text=True, timeout=30,
)
assert proc.returncode == 0, f"node driver failed: {proc.stderr}"
return json.loads(proc.stdout)
# Reuses the boundary-slicing approach already proven in
# tests/test_issue3429_uri_scheme_model_label.py: regex literals inside
# getModelLabel() defeat a naive brace counter, so bound it by the next
# top-level function instead.
_GET_MODEL_LABEL_DRIVER = r"""
const fs = require('fs');
const ui = fs.readFileSync(process.argv[1], 'utf8');
const start = ui.indexOf('function getModelLabel(');
if (start < 0) throw new Error('getModelLabel not found');
const after = ui.indexOf('\nfunction _gatewayProviderName(', start);
if (after < 0) throw new Error('getModelLabel end boundary not found');
// Sinks only: no catalog has been fetched yet, so dynamic labels are empty.
const _dynamicModelLabels = {};
function _fmtOllamaLabel(s){ return s; }
// The dotted normalizer and the two Sets it closes over sit just above
// getModelLabel(); without them the eval'd function ReferenceErrors.
const _stripStart = ui.indexOf('const _BEDROCK_REGION_PREFIXES');
if (_stripStart < 0 || _stripStart > start) throw new Error('strip block not found');
eval(ui.slice(_stripStart, start));
eval(ui.slice(start, after));
const out = {};
for (const m of JSON.parse(process.argv[2])) out[m] = getModelLabel(m);
process.stdout.write(JSON.stringify(out));
"""
def test_frontend_and_backend_agree_that_no_namespace_survives():
"""The real contract, driven through the real ``getModelLabel()``.
NOT string equality of the two labels. The frontend and the backend are
deliberately different formatters and always have been: on the unmodified
base commit ``claude-opus-5`` (no dot, untouched by this change) already
labels as ``'claude-opus-5'`` in JS and ``'Claude Opus 5'`` in Python, and
``openai/gpt-4o`` as ``'GPT-4o'`` vs ``'GPT 4O'``. Asserting equality would
fail on pre-existing behavior this PR does not touch.
The reason that divergence is not a bug: ``getModelLabel()`` consults
``_dynamicModelLabels`` FIRST (static/ui.js:6991), and that map is populated
from the server's own ``label`` field (static/ui.js:3483, 3494, 3606). Once a
catalog is loaded the backend label wins in the UI. The JS formatter is the
pre-fetch fallback, so its job is to be no WORSE than the backend, not
byte-identical to it.
What must hold on both sides — the actual defect this PR fixes — is that no
routing/vendor namespace leaks into a human label.
"""
ids = [c[0] for c in STRIP_CASES]
js_labels = _js_model_labels(ids)
for model_id, expected_stripped in STRIP_CASES:
js_label = js_labels[model_id]
py_label = _get_label_for_model(model_id, [])
# A namespace head is only "leaked" if it was supposed to be stripped.
# Cases pinned as byte-intact (deepseek.v3, foo.bar.baz, gpt-4.1) must
# KEEP their dotted head, so they are checked in the opposite direction:
# the head must still be present rather than silently deleted.
if expected_stripped == model_id:
if "." not in model_id:
# No dot at all (``claude-opus-5``): the dotted-prefix step is
# not involved. Nothing for this test to assert beyond the strip
# being a no-op.
assert _js_strip([model_id]) == [model_id], (
f"dotted strip must be a no-op for {model_id!r}"
)
continue
if "://" in model_id:
# URI-scheme IDs are paths, not dotted namespaces.
# getModelLabel() deliberately renders the last meaningful path
# segment (the #3429 fix, pinned in
# tests/test_issue3429_uri_scheme_model_label.py), so a full-URL
# assertion here would contradict that test. What matters for
# THIS PR is only that the dotted-prefix strip left it alone.
assert _js_strip([model_id]) == [model_id], (
f"dotted strip must not touch the URI-scheme id {model_id!r}"
)
continue
head = model_id.split(".", 1)[0].lower()
# Only a LETTERS-ONLY head is a namespace candidate. ``gpt-4.1``,
# ``qwen3.6-35b`` and ``o1.5-preview`` carry a VERSION dot, so their
# "head" (``gpt-4``) is not a namespace and title-casing legitimately
# reshapes it ("GPT 4.1"). What must hold for those is that the dot
# itself survives -- asserted directly below.
if head.replace("-", "").isalpha():
assert head in js_label.lower(), (
f"{model_id!r} is pinned byte-intact but the frontend deleted "
f"its head {head!r}: {js_label!r}"
)
assert head in py_label.lower(), (
f"{model_id!r} is pinned byte-intact but the backend deleted "
f"its head {head!r}: {py_label!r}"
)
else:
assert "." in js_label and "." in py_label, (
f"{model_id!r} lost its version dot: js={js_label!r} "
f"py={py_label!r}"
)
continue
head = model_id.split(".", 1)[0].lower()
for side, label in (("frontend", js_label), ("backend", py_label)):
assert f"{head}." not in label.lower(), (
f"{side} leaked the routing namespace for {model_id!r}: {label!r}"
)
# A REGION head must never survive as a leading word either
# ("Us anthropic claude Opus 5"). This check does NOT apply to a
# vendor head, because a vendor name legitimately reappears inside
# some model names -- ``mistral.mistral-large-2407-v1:0`` correctly
# labels as "Mistral Large 2407 V1". Same rule as
# test_known_bedrock_vendors_are_all_covered below.
if head in _PRODUCTION_REGIONS:
assert not label.lower().startswith(f"{head} "), (
f"{side} kept the region head as a word for {model_id!r}: {label!r}"
)
def test_frontend_retry_chain_reaches_the_static_label_table():
"""Pin the behavior that makes the JS fallback WORTH having.
This is the post-strip chain the previous test could not see. Removing the
retry block at static/ui.js:7053-7071 leaves these rendering as raw IDs
(``'claude-sonnet-4-5'`` instead of ``'Sonnet 4.5'``) — verified by excising
the block and re-running this driver.
"""
cases = {
# Lands on STATIC_LABELS['anthropic/claude-sonnet-4-5'] after the strip.
"us.anthropic.claude-sonnet-4-5": "Sonnet 4.5",
"eu.anthropic.claude-haiku-3-5": "Haiku 3.5",
# No table entry: the claude- prettifier drops the vendor word, the
# -YYYYMMDD date pin and the -v1 revision.
"global.anthropic.claude-opus-4-5-20251101-v1:0": "Opus 4 5",
"us-gov.anthropic.claude-opus-5": "Opus 5",
}
got = _js_model_labels(list(cases))
for model_id, expected in cases.items():
assert got[model_id] == expected, (
f"frontend label drift for {model_id!r}: {got[model_id]!r} != {expected!r}"
)
for label in got.values():
assert "claude-" not in label.lower(), f"vendor word survived: {label!r}"
def test_every_catalog_dotted_id_loses_its_routing_prefix():
"""Catalog-driven guard against region/vendor-set drift.
The allow-lists and the shipped catalog are two lists that must agree. They
didn't, twice: first ``global`` was missing (six IDs mislabeled), then
``luma``/``twelvelabs``/``ibm`` were missing (real Bedrock vendors rendering
as "Us.luma.ray 2").
An earlier version of this test scraped only three-segment
``<region>.<vendor>.<model>`` literals and therefore inspected **6** of the
**75** dotted catalog IDs — reassuring, but nearly blind. This version:
- scrapes ANY quoted ``id`` value (single or double quotes, any segment count);
- derives the offending prefixes from the PRODUCTION allow-lists rather than a
retyped copy, so a set that grows without test updates is still covered;
- skips version dots (``qwen3.6-plus``, ``gpt-5.4``), which are not namespaces.
"""
import re as _re
config_src = (REPO_ROOT / "api" / "config.py").read_text(encoding="utf-8")
# Derive the real allow-lists out of production source, don't retype them.
def _set_literal(marker: str) -> set[str]:
start = config_src.index(marker)
body = config_src[start:config_src.index("}", start)]
return {m.lower() for m in _re.findall(r'"([a-z0-9-]+)"', body)}
regions = _set_literal("_regions = {")
vendors = _set_literal("_vendors = {")
assert regions and vendors, "could not derive allow-lists from api/config.py"
namespace_heads = regions | vendors
ids = {
i for i in _re.findall(r"""['"]id['"]\s*:\s*['"]([^'"]+)['"]""", config_src)
if "." in i
}
assert len(ids) > 20, f"catalog scrape found only {len(ids)} dotted ids"
offenders = []
for model_id in sorted(ids):
head = model_id.split(".")[0].lower()
# Only IDs whose head is a KNOWN namespace should be stripped; a version
# dot such as `qwen3.6-plus` has no namespace head and is left alone.
if head not in namespace_heads:
continue
label = _get_label_for_model(model_id, [])
if head in label.lower().replace(" ", "."):
offenders.append((model_id, label))
assert not offenders, (
"catalog IDs still carry a routing/vendor prefix in their label — add the "
f"missing head to the region/vendor sets: {offenders}"
)
def test_known_bedrock_vendors_are_all_covered():
"""Real Bedrock foundation-model vendors must all be in the allow-list.
These were shipping mislabeled: `luma.ray-2` rendered as "Luma.ray 2",
`us.twelvelabs.marengo-embed-2-7` as "Us.twelvelabs.marengo Embed 2 7".
The assertion is that no DOTTED NAMESPACE survives — not that the vendor word
is absent, because a vendor legitimately reappears inside some model names
(``mistral.mistral-large-2407`` → "Mistral Large 2407").
"""
for model in [
"luma.ray-2",
"twelvelabs.marengo-embed-2-7",
"ibm.granite-3-8b-instruct",
"anthropic.claude-opus-5",
"amazon.nova-pro-v1:0",
"mistral.mistral-large-2407-v1:0",
]:
head = model.split(".")[0]
label = _get_label_for_model(model, [])
assert f"{head}." not in label.lower(), (
f"{model!r} kept its vendor namespace: {label!r}"
)
# And with a region prefix in front.
regional = f"us.{model}"
rlabel = _get_label_for_model(regional, [])
assert "us." not in rlabel.lower() and f"{head}." not in rlabel.lower(), (
f"{regional!r} kept its namespace: {rlabel!r}"
)
def test_global_region_is_recognized_in_both_implementations():
"""Explicit pin for the reported gap, both sides."""
label = _get_label_for_model("global.anthropic.claude-opus-4-7", [])
assert "global" not in label.lower(), label
assert "anthropic" not in label.lower(), label
assert _js_strip(["global.anthropic.claude-opus-4-7"]) == [
"claude-opus-4-7"
]
def test_frontend_helper_exists_and_is_used():
assert "function _stripDottedModelPrefix" in UI_JS
assert "_stripDottedModelPrefix(_last)" in UI_JS
# The generic letters-only loop must be gone.
assert "/^[a-z]+$/i.test(_segs[_i]" not in UI_JS
def test_backend_uses_a_closed_allow_list():
"""Guard the design: the fix must not regress to a generic prefix loop."""
config_src = (REPO_ROOT / "api" / "config.py").read_text(encoding="utf-8")
idx = config_src.index("def _get_label_for_model")
block = config_src[idx:idx + 4000]
assert "_regions" in block and "_vendors" in block, (
"the dotted-prefix strip must be gated on explicit provider allow-lists"
)
assert "while _i < len(_segs) - 1 and _segs[_i].isalpha()" not in block, (
"the generic letters-only loop rewrites uncatalogued IDs"
)