fix(picker): catalog is authority for @custom labels; keep plain-lane colon models whole

The #7240 fix peeled the leading @custom: segment on first-colon split, but a
plain-lane custom id whose model carries a tag/variant colon
(@custom:ollamacloud/qwen3.5:397b) was still misread as slug=ollamacloud/qwen3.5,
rendering only "397b". Two changes:

- _dynamicModelLabels (the backend-populated catalog) is now consulted BEFORE
  the string peel: the backend knows the true provider/model split and ships an
  exact m.label per routing id, so catalogued ids — including plain-lane colon
  models and collision cases — render verbatim.
- The legacy string fallback now treats a slash-bearing first segment as the
  model itself (a provider slug is a config key or host:port authority and
  never contains '/'), mirroring the '/' means routable rule api/config.py
  applies when building ids.

Tests: add plain-lane @custom:ollamacloud/qwen3.5:397b controls, a
post-catalog dynamic-label control, and a collision case proving labels stay
distinct by full routing id. Driver now accepts an optional pre-hydrated
catalog map.
This commit is contained in:
webtecnica authored and n committed 2026-09-20 06:33:58 +00:00
1 parent e71c3e1f97
commit da4e4ef316
2 files changed
+73 -8

No files matched your search

+15 -2
View File
@@ -7386,6 +7386,15 @@ function _stripDottedModelPrefix(bare){
function getModelLabel(modelId){ function getModelLabel(modelId){
if(!modelId) return 'Unknown'; if(!modelId) return 'Unknown';
const rawId=String(modelId||''); const rawId=String(modelId||'');
// The catalog is the authority on model identity: the backend knows the real
// provider/model split and ships an exact `m.label` per routing id, so a
// catalogued id — including a plain-lane `@custom:` id whose model contains
// colons (`@custom:ollamacloud/qwen3.5:397b`) — renders verbatim. The
// string parsing below is only a legacy fallback for ids the catalog has
// never seen (pre-hydration, stale sessions, removed providers), where a
// first-colon split alone cannot tell a `@custom:<slug>:<model>` from a
// plain-lane `@custom:<model-with-colon>` (#7240).
if(_dynamicModelLabels[modelId]) return _dynamicModelLabels[modelId];
// Preserve custom gateway model IDs exactly as configured. A custom id is // Preserve custom gateway model IDs exactly as configured. A custom id is
// `@custom:<model>` in the plain custom lane or `@custom:<slug>:<model>` for // `@custom:<model>` in the plain custom lane or `@custom:<slug>:<model>` for
// a named custom provider; the provider slug may itself be an endpoint // a named custom provider; the provider slug may itself be an endpoint
@@ -7400,6 +7409,12 @@ function getModelLabel(modelId){
const rest=rawId.slice('@custom:'.length); const rest=rawId.slice('@custom:'.length);
const sep=rest.indexOf(':'); const sep=rest.indexOf(':');
if(sep<0) return rest||rawId; if(sep<0) return rest||rawId;
// A provider slug is a config key or a host:port authority — it never
// contains a `/`. A slash-bearing first segment is therefore the model
// itself in the plain custom lane (`@custom:ollamacloud/qwen3.5:397b`
// must render the whole remainder, not just `397b`), mirroring the
// `/`-means-routable rule api/config.py applies when building ids.
if(rest.slice(0,sep).includes('/')) return rest||rawId;
let model=rest.slice(sep+1); let model=rest.slice(sep+1);
// Endpoint-style slug (`custom:10.8.71.41:8080:model`): the `:port` belongs // Endpoint-style slug (`custom:10.8.71.41:8080:model`): the `:port` belongs
// to the provider segment, mirroring the host:port slug check in // to the provider segment, mirroring the host:port slug check in
@@ -7416,8 +7431,6 @@ function getModelLabel(modelId){
} }
return model||rawId; return model||rawId;
} }
// Check dynamic labels first, then fall back to splitting the ID
if(_dynamicModelLabels[modelId]) return _dynamicModelLabels[modelId];
// Static fallback for common models // Static fallback for common models
const STATIC_LABELS={'openai/gpt-5.4-mini':'GPT-5.4 Mini','openai/gpt-4o':'GPT-4o','openai/o3':'o3','openai/o4-mini':'o4-mini','anthropic/claude-sonnet-4.6':'Sonnet 4.6','anthropic/claude-sonnet-4-5':'Sonnet 4.5','anthropic/claude-haiku-3-5':'Haiku 3.5','google/gemini-3.1-pro-preview':'Gemini 3.1 Pro','google/gemini-3-flash-preview':'Gemini 3 Flash','google/gemini-3.1-flash-lite-preview':'Gemini 3.1 Flash Lite','google/gemini-2.5-pro':'Gemini 2.5 Pro','google/gemini-2.5-flash':'Gemini 2.5 Flash','deepseek/deepseek-v4-flash':'DeepSeek V4 Flash','deepseek/deepseek-v4-pro':'DeepSeek V4 Pro','deepseek/deepseek-chat-v3-0324':'DeepSeek V3 (legacy)','meta-llama/llama-4-scout':'Llama 4 Scout'}; const STATIC_LABELS={'openai/gpt-5.4-mini':'GPT-5.4 Mini','openai/gpt-4o':'GPT-4o','openai/o3':'o3','openai/o4-mini':'o4-mini','anthropic/claude-sonnet-4.6':'Sonnet 4.6','anthropic/claude-sonnet-4-5':'Sonnet 4.5','anthropic/claude-haiku-3-5':'Haiku 3.5','google/gemini-3.1-pro-preview':'Gemini 3.1 Pro','google/gemini-3-flash-preview':'Gemini 3 Flash','google/gemini-3.1-flash-lite-preview':'Gemini 3.1 Flash Lite','google/gemini-2.5-pro':'Gemini 2.5 Pro','google/gemini-2.5-flash':'Gemini 2.5 Flash','deepseek/deepseek-v4-flash':'DeepSeek V4 Flash','deepseek/deepseek-v4-pro':'DeepSeek V4 Pro','deepseek/deepseek-chat-v3-0324':'DeepSeek V3 (legacy)','meta-llama/llama-4-scout':'Llama 4 Scout'};
if(STATIC_LABELS[modelId]) return STATIC_LABELS[modelId]; if(STATIC_LABELS[modelId]) return STATIC_LABELS[modelId];
@@ -6,9 +6,11 @@ following the LAST ``:``, so a tag/variant suffix like ``:free``/``:31b``/
``:397b`` inside the model's own name became the whole label ``:397b`` inside the model's own name became the whole label
(``@custom:omni:kg/stepfun/step-3.7-flash:free`` rendered as ``free``). (``@custom:omni:kg/stepfun/step-3.7-flash:free`` rendered as ``free``).
The fix peels only the leading provider segment (``@custom:<slug>:`` — the The fix makes the catalog the authority — a catalogued routing id renders its
``<slug>`` is itself optional in the plain custom lane, and may be an endpoint exact `m.label` (the backend knows the real provider/model split). The string
authority ``host:port``), leaving the full configured model id as the label. peel is only a legacy fallback for ids the catalog has never seen, where a
provider slug (a config key or host:port authority) is distinguished from a
plain-lane model by its lack of a `/`.
Runs the live getModelLabel() via Node so drift between the test and the real Runs the live getModelLabel() via Node so drift between the test and the real
code is caught immediately. code is caught immediately.
@@ -36,7 +38,9 @@ if (start < 0) throw new Error('getModelLabel not found');
const after = ui.indexOf('\nfunction _gatewayProviderName(', start); const after = ui.indexOf('\nfunction _gatewayProviderName(', start);
if (after < 0) throw new Error('getModelLabel end boundary not found'); if (after < 0) throw new Error('getModelLabel end boundary not found');
const fnSrc = ui.slice(start, after); const fnSrc = ui.slice(start, after);
const _dynamicModelLabels = {}; // Optional pre-hydrated catalog: {routingId: label}. Absent -> empty, which
// exercises the string-only legacy fallback.
const _dynamicModelLabels = process.argv[3] ? JSON.parse(process.argv[3]) : {};
function _fmtOllamaLabel(s){ return s; } function _fmtOllamaLabel(s){ return s; }
// getModelLabel() calls the dotted Bedrock/Vertex prefix normalizer, which lives // getModelLabel() calls the dotted Bedrock/Vertex prefix normalizer, which lives
// just above it with two Sets it closes over. Pull all three in, or the eval'd // just above it with two Sets it closes over. Pull all three in, or the eval'd
@@ -52,9 +56,12 @@ process.stdout.write(JSON.stringify(out));
""" """
def _labels(model_ids): def _labels(model_ids, catalog=None):
argv = [NODE, "-e", _DRIVER, str(UI_JS_PATH), json.dumps(model_ids)]
if catalog is not None:
argv.append(json.dumps(catalog))
proc = subprocess.run( proc = subprocess.run(
[NODE, "-e", _DRIVER, str(UI_JS_PATH), json.dumps(model_ids)], argv,
capture_output=True, text=True, timeout=30, capture_output=True, text=True, timeout=30,
) )
assert proc.returncode == 0, f"node driver failed: {proc.stderr}" assert proc.returncode == 0, f"node driver failed: {proc.stderr}"
@@ -100,3 +107,48 @@ def test_custom_model_label_plain_lane_and_host_port_slugs():
assert out["@custom:10.8.71.41:8080:Qwen3-235B"] == "Qwen3-235B" assert out["@custom:10.8.71.41:8080:Qwen3-235B"] == "Qwen3-235B"
assert out["@custom:localhost:11434:llama3"] == "llama3" assert out["@custom:localhost:11434:llama3"] == "llama3"
assert out["@custom:omni:11434:Qwen3"] == "11434:Qwen3" assert out["@custom:omni:11434:Qwen3"] == "11434:Qwen3"
def test_plain_custom_lane_colon_bearing_model_slash_segment():
"""#7240: a plain-lane ``@custom:<model-with-colon>`` whose first segment
is slash-bearing (`ollamacloud/qwen3.5:397b`) is the model itself — a
provider slug never contains a `/`, so it must not be peeled as one."""
out = _labels([
"@custom:ollamacloud/qwen3.5:397b",
"@custom:ollamacloud/gemma4:31b",
"@custom:some-vendor/step-3.7-flash:free",
])
assert out["@custom:ollamacloud/qwen3.5:397b"] == "ollamacloud/qwen3.5:397b"
assert out["@custom:ollamacloud/gemma4:31b"] == "ollamacloud/gemma4:31b"
assert out["@custom:some-vendor/step-3.7-flash:free"] == "some-vendor/step-3.7-flash:free"
def test_catalog_label_wins_over_legacy_peel():
"""Post-hydration: the catalog ships the exact backend label per routing
id, so a catalogued id renders verbatim even where the string peel alone
would be ambiguous or lossy."""
catalog = {
# Plain lane, colon-bearing model: the backend knows this is ONE model.
"@custom:ollamacloud/qwen3.5:397b": "ollamacloud/qwen3.5:397b",
# Named lane, same native model: distinct routing id, distinct entry.
"@custom:omni:ollamacloud/qwen3.5:397b": "ollamacloud/qwen3.5:397b",
# A custom display label is preserved exactly.
"@custom:ai_gateway:Qwen3.6-35B-A3B": "My Qwen Gateway",
}
out = _labels(list(catalog), catalog=catalog)
assert out["@custom:ollamacloud/qwen3.5:397b"] == "ollamacloud/qwen3.5:397b"
assert out["@custom:omni:ollamacloud/qwen3.5:397b"] == "ollamacloud/qwen3.5:397b"
assert out["@custom:ai_gateway:Qwen3.6-35B-A3B"] == "My Qwen Gateway"
def test_collision_labels_stay_distinct_by_full_routing_id():
"""Two providers exposing the same native model stay distinguishable: the
catalog keys labels by the FULL routing id, never by the bare model."""
catalog = {
"@custom:omni:ollamacloud/qwen3.5:397b": "ollamacloud/qwen3.5:397b (omni)",
"@custom:ai_gateway:ollamacloud/qwen3.5:397b": "ollamacloud/qwen3.5:397b (gateway)",
}
out = _labels(list(catalog), catalog=catalog)
assert out["@custom:omni:ollamacloud/qwen3.5:397b"] == "ollamacloud/qwen3.5:397b (omni)"
assert out["@custom:ai_gateway:ollamacloud/qwen3.5:397b"] == "ollamacloud/qwen3.5:397b (gateway)"
assert out["@custom:omni:ollamacloud/qwen3.5:397b"] != out["@custom:ai_gateway:ollamacloud/qwen3.5:397b"]