fix(models): preserve native catalog contracts

This commit is contained in:
RaresKeY 2026-07-18 12:43:05 +00:00
parent a71661208a
commit d3d4d03c0f
12 changed files with 333 additions and 26 deletions

View file

@ -160,7 +160,11 @@ def records_from_payload(
normalized = tuple(normalized_records)
else:
normalized_records: list[ModelCapabilityRecord] = []
for item in shape.items(payload):
catalog_items = shape.items(payload)
select_catalog_items = getattr(reader, "select_catalog_items", None)
if callable(select_catalog_items):
catalog_items = tuple(select_catalog_items(catalog_items))
for item in catalog_items:
item_payload = shape.payload_for_item(payload, item)
if shape.item_matches(item):
if reader is generic_openai:

View file

@ -9,6 +9,7 @@ labels.
from __future__ import annotations
import hashlib
import math
from collections.abc import Iterable, Mapping
from dataclasses import dataclass, field
from typing import Any, Protocol
@ -139,6 +140,12 @@ def compact_str(value: Any) -> str:
return str(value or "").strip()
def identity_str(value: Any) -> str:
"""Return a provider identity only when the payload supplied a string."""
return value.strip() if isinstance(value, str) else ""
def _identity_part(value: Any) -> str:
text = compact_str(value).lower()
out = []
@ -171,10 +178,7 @@ def stable_model_id_for(vendor: Any, model_id: Any, *, endpoint_id: Any = "", ba
def model_id_from(raw: Mapping[str, Any], *keys: str) -> str:
for key in keys:
raw_value = raw.get(key)
if not isinstance(raw_value, str):
continue
value = raw_value.strip()
value = identity_str(raw.get(key))
if value:
return value.removeprefix("models/")
return ""
@ -183,6 +187,10 @@ def model_id_from(raw: Mapping[str, Any], *keys: str) -> str:
def int_limit(value: Any) -> int | None:
if isinstance(value, bool):
return None
if isinstance(value, float) and (
not math.isfinite(value) or not value.is_integer()
):
return None
try:
limit = int(value)
except (OverflowError, TypeError, ValueError):

View file

@ -2,6 +2,7 @@
from __future__ import annotations
import math
from collections.abc import Mapping
from typing import Any
@ -12,11 +13,59 @@ from src.model_capability_readers.base import (
as_list,
as_mapping,
compact_str,
identity_str,
stable_model_id_for,
)
vendor = VENDOR_CHATGPT_SUBSCRIPTION
_DEFAULT_PRIORITY = 10_000
def _priority_rank(raw: Mapping[str, Any]) -> int | float:
value = raw.get("priority")
if isinstance(value, bool):
return _DEFAULT_PRIORITY
if isinstance(value, int):
return value
if isinstance(value, float) and math.isfinite(value):
return value
return _DEFAULT_PRIORITY
def _is_hidden(raw: Mapping[str, Any]) -> bool:
visibility = raw.get("visibility")
return (
isinstance(visibility, str)
and visibility.strip().lower() in {"hide", "hidden"}
)
def select_catalog_items(
items: tuple[Mapping[str, Any], ...],
) -> tuple[Mapping[str, Any], ...]:
"""Apply the provider's visibility, priority, and slug de-duplication."""
sortable: list[tuple[int | float, str, int, Mapping[str, Any]]] = []
passthrough: list[Mapping[str, Any]] = []
for position, item in enumerate(items):
if _is_hidden(item):
continue
slug = identity_str(item.get("slug"))
if not slug:
passthrough.append(item)
continue
sortable.append((_priority_rank(item), slug, position, item))
sortable.sort(key=lambda entry: (entry[0], entry[1], entry[2]))
selected: list[Mapping[str, Any]] = []
seen: set[str] = set()
for _, slug, _, item in sortable:
if slug not in seen:
selected.append(item)
seen.add(slug)
selected.extend(passthrough)
return tuple(selected)
def record_from_model(
@ -25,7 +74,7 @@ def record_from_model(
endpoint_id: Any = "",
base_url: Any = "",
) -> ModelCapabilityRecord | None:
model_id = compact_str(raw.get("slug"))
model_id = identity_str(raw.get("slug"))
if not model_id:
return None
return ModelCapabilityRecord(
@ -55,7 +104,8 @@ def records_from_payload(
values = as_mapping(payload).get("models")
return tuple(
record
for item in as_list(values)
if isinstance(item, Mapping)
for item in select_catalog_items(
tuple(item for item in as_list(values) if isinstance(item, Mapping))
)
if (record := record_from_model(item, endpoint_id=endpoint_id, base_url=base_url))
)

View file

@ -19,6 +19,7 @@ from src.model_capability_readers.base import (
build_capability,
compact_str,
deterministic_controls_from_supported_parameters,
identity_str,
int_limit,
stable_model_id_for,
)
@ -60,7 +61,7 @@ def record_from_model(
endpoint_id: Any = "",
base_url: Any = "",
) -> ModelCapabilityRecord | None:
model_id = compact_str(raw.get("name"))
model_id = identity_str(raw.get("name"))
if not model_id:
return None
family = _family(raw)

View file

@ -28,6 +28,18 @@ _SUPPORT_CAPABILITIES = {
}
def select_catalog_items(
items: tuple[Mapping[str, Any], ...],
) -> tuple[Mapping[str, Any], ...]:
"""Keep picker-enabled models when the catalog advertises any of them."""
if any(item.get("model_picker_enabled") is True for item in items):
return tuple(
item for item in items if item.get("model_picker_enabled") is True
)
return items
def _supports(raw: Mapping[str, Any]) -> Mapping[str, Any]:
return as_mapping(as_mapping(raw.get("capabilities")).get("supports"))
@ -102,7 +114,7 @@ def records_from_payload(
base_url: Any = "",
) -> tuple[ModelCapabilityRecord, ...]:
records: list[ModelCapabilityRecord] = []
for item in openai_model_items(payload):
for item in select_catalog_items(openai_model_items(payload)):
record = record_from_model(item, endpoint_id=endpoint_id, base_url=base_url)
if record:
records.append(record)

View file

@ -13,7 +13,12 @@ from collections.abc import Mapping
from typing import Any
from src import model_capabilities as mc
from src.model_capability_readers.base import as_list, compact_str, int_limit
from src.model_capability_readers.base import (
as_list,
compact_str,
identity_str,
int_limit,
)
METHOD_GENERATE_CONTENT = "generateContent"
@ -55,7 +60,7 @@ MODEL_FIELD_MAP = {
def google_model_id(raw: Mapping[str, Any]) -> str:
value = compact_str(raw.get("baseModelId")) or compact_str(raw.get("name"))
value = identity_str(raw.get("baseModelId")) or identity_str(raw.get("name"))
return value.removeprefix("models/")

View file

@ -11,7 +11,7 @@ from src.model_capability_readers.base import (
VENDOR_HUGGINGFACE,
build_capability,
compact_str,
openai_model_items,
model_id_from,
stable_model_id_for,
)
@ -105,7 +105,7 @@ def record_from_model(
endpoint_id: Any = "",
base_url: Any = "",
) -> ModelCapabilityRecord | None:
model_id = compact_str(raw.get("modelId") or raw.get("id"))
model_id = model_id_from(raw, "modelId", "id")
if not model_id:
return None
return ModelCapabilityRecord(
@ -136,11 +136,15 @@ def records_from_payload(
endpoint_id: Any = "",
base_url: Any = "",
) -> tuple[ModelCapabilityRecord, ...]:
if isinstance(payload, Mapping) and (payload.get("modelId") or payload.get("pipeline_tag")):
if isinstance(payload, Mapping) and "pipeline_tag" in payload:
record = record_from_model(payload, endpoint_id=endpoint_id, base_url=base_url)
return (record,) if record else ()
if not isinstance(payload, (list, tuple)):
return ()
records: list[ModelCapabilityRecord] = []
for item in openai_model_items(payload):
for item in payload:
if not isinstance(item, Mapping):
continue
record = record_from_model(item, endpoint_id=endpoint_id, base_url=base_url)
if record:
records.append(record)

View file

@ -22,6 +22,7 @@ from src.model_capability_readers.base import (
build_capability,
compact_str,
deterministic_controls_from_supported_parameters,
identity_str,
int_limit,
merge_unique,
model_id_from,
@ -53,10 +54,10 @@ def _server_model_entries(payload: Mapping[str, Any]) -> tuple[Mapping[str, Any]
def _model_id_from_props(payload: Mapping[str, Any]) -> str:
payload = as_mapping(payload)
model_alias = compact_str(payload.get("model_alias"))
model_alias = identity_str(payload.get("model_alias"))
if model_alias:
return model_alias
model_path = compact_str(payload.get("model_path"))
model_path = identity_str(payload.get("model_path"))
if model_path:
return PurePosixPath(model_path).name
return ""

View file

@ -13,6 +13,7 @@ from src.model_capability_readers.base import (
as_mapping,
build_capability,
compact_str,
identity_str,
int_limit,
merge_unique,
model_id_from,
@ -116,7 +117,7 @@ def record_from_show_payload(
endpoint_id: Any = "",
base_url: Any = "",
) -> ModelCapabilityRecord | None:
model_id = compact_str(model_id) or model_id_from(payload, "model", "name")
model_id = identity_str(model_id) or model_id_from(payload, "model", "name")
if not model_id:
return None
capability_values = payload.get("capabilities")

View file

@ -14,8 +14,8 @@ from src.model_capability_readers.base import (
VENDOR_SGLANG,
as_mapping,
build_capability,
compact_str,
deterministic_controls_from_supported_parameters,
identity_str,
int_limit,
openai_model_items,
stable_model_id_for,
@ -26,7 +26,9 @@ vendor = VENDOR_SGLANG
def _model_id(payload: Mapping[str, Any]) -> str:
value = compact_str(payload.get("served_model_name") or payload.get("model_path"))
value = identity_str(payload.get("served_model_name")) or identity_str(
payload.get("model_path")
)
if not value:
return ""
return PurePosixPath(value).name if value.startswith("/") else value

View file

@ -229,6 +229,15 @@ GOOGLE_MODELS_SHAPE = ProviderCatalogShape(
item_types=(("supportedGenerationMethods", (list, tuple)),),
detection_priority=100,
)
GOOGLE_MODEL_SHAPE = ProviderCatalogShape(
shape_id="google.generative-language.model.v1beta",
provider_id="google",
envelope=ENVELOPE_SINGLE,
identity_paths=("baseModelId", "name"),
required_item_paths=("supportedGenerationMethods",),
item_types=(("supportedGenerationMethods", (list, tuple)),),
detection_priority=100,
)
OLLAMA_TAGS_SHAPE = ProviderCatalogShape(
shape_id="ollama.tags.v1",
provider_id="ollama",
@ -290,6 +299,17 @@ LLAMACPP_PROPS_SHAPE = ProviderCatalogShape(
item_types=(("default_generation_settings", (Mapping,)),),
detection_priority=100,
)
LLAMACPP_MODELS_SHAPE = ProviderCatalogShape(
shape_id="llamacpp.models.native.v1",
provider_id="llamacpp",
envelope=ENVELOPE_MODELS,
identity_paths=("id", "name", "model"),
required_item_paths=("capabilities",),
item_types=(("capabilities", (list, tuple)),),
# Model/capability fields are not globally provider-specific. Interpret
# them only after explicit llama.cpp endpoint/provider selection.
detection_priority=0,
)
MISTRAL_MODELS_SHAPE = ProviderCatalogShape(
shape_id="mistral.models.rich.v1",
provider_id="mistral",
@ -325,7 +345,9 @@ ANTHROPIC_MODELS_SHAPE = ProviderCatalogShape(
identity_paths=("id",),
required_item_paths=("type", "display_name", "created_at"),
item_values=(("type", ("model",)),),
detection_priority=70,
# These model-resource fields are not globally provider-specific. Require
# explicit Anthropic endpoint/provider context before assigning identity.
detection_priority=0,
)
CHATGPT_MODELS_SHAPE = ProviderCatalogShape(
shape_id="chatgpt-subscription.codex-models.v1",
@ -376,6 +398,15 @@ HUGGINGFACE_MODEL_SHAPE = ProviderCatalogShape(
item_types=(("pipeline_tag", (str,)),),
detection_priority=0,
)
HUGGINGFACE_MODELS_LIST_SHAPE = ProviderCatalogShape(
shape_id="huggingface.hub.model-info-list.v1",
provider_id="huggingface",
envelope=ENVELOPE_BARE_LIST,
identity_paths=("modelId", "id"),
required_item_paths=("pipeline_tag",),
item_types=(("pipeline_tag", (str,)),),
detection_priority=0,
)
COHERE_MODELS_SHAPE = ProviderCatalogShape(
shape_id="cohere.models.rich.v1",
provider_id="cohere",
@ -432,7 +463,7 @@ PROVIDER_SCHEMAS = {
"google",
aliases=("gemini", "google_ai_studio"),
hosts=("generativelanguage.googleapis.com",),
shapes=(GOOGLE_MODELS_SHAPE,),
shapes=(GOOGLE_MODELS_SHAPE, GOOGLE_MODEL_SHAPE),
),
"anthropic": _provider(
"anthropic",
@ -452,7 +483,7 @@ PROVIDER_SCHEMAS = {
"llamacpp": _provider(
"llamacpp",
aliases=("llama.cpp", "llama_cpp", "llama_server"),
shapes=(LLAMACPP_PROPS_SHAPE,),
shapes=(LLAMACPP_PROPS_SHAPE, LLAMACPP_MODELS_SHAPE),
),
"mistral": _provider(
"mistral",
@ -480,7 +511,7 @@ PROVIDER_SCHEMAS = {
"huggingface",
aliases=("hf", "hugging_face"),
hosts=("huggingface.co",),
shapes=(HUGGINGFACE_MODEL_SHAPE,),
shapes=(HUGGINGFACE_MODEL_SHAPE, HUGGINGFACE_MODELS_LIST_SHAPE),
),
"cohere": _provider(
"cohere",

View file

@ -241,6 +241,7 @@ def test_native_catalog_shapes_resolve_with_required_provider_context():
)
explicit_context_providers = {
"anthropic",
"chatgpt_subscription",
"cohere",
"huggingface",
@ -608,6 +609,139 @@ def test_structured_identity_values_are_not_stringified_into_fallback_records():
assert records_from_payload(payload, vendor="future-provider") == ()
def test_native_readers_skip_structured_identity_candidates():
google_record = records_from_payload(
{
"models": [
{
"baseModelId": {"nested": "bad"},
"name": "models/good-google-id",
"supportedGenerationMethods": ["embedContent"],
}
]
}
)[0]
huggingface_record = records_from_payload(
{
"modelId": {"nested": "bad"},
"id": "org/good-hf-id",
"pipeline_tag": "text-to-image",
},
vendor="huggingface",
)[0]
llamacpp_record = records_from_payload(
{
"model_alias": {"nested": "bad"},
"model_path": "/models/good-llama.gguf",
"default_generation_settings": {},
"chat_template_caps": {"supports_vision": True},
},
vendor="llamacpp",
)[0]
sglang_record = records_from_payload(
{
"served_model_name": {"nested": "bad"},
"model_path": "/models/good-sglang",
"is_generation": True,
"has_image_understanding": True,
},
vendor="sglang",
)[0]
assert google_record.model_id == "good-google-id"
assert huggingface_record.model_id == "org/good-hf-id"
assert llamacpp_record.model_id == "good-llama.gguf"
assert sglang_record.model_id == "good-sglang"
assert chatgpt_subscription.record_from_model({"slug": {"nested": "bad"}}) is None
assert cohere.record_from_model(
{"name": {"nested": "bad"}, "endpoints": ["chat"]}
) is None
def test_native_singleton_and_bare_list_shapes_reach_their_readers():
google_records = records_from_payload(
{
"name": "models/gemini-embed",
"supportedGenerationMethods": ["embedContent"],
},
vendor="google",
)
huggingface_records = records_from_payload(
[{"modelId": "org/model", "pipeline_tag": "text-generation"}],
vendor="huggingface",
)
llamacpp_records = records_from_payload(
{
"models": [
{
"id": "served-model",
"capabilities": ["chat", "tools"],
}
]
},
vendor="llamacpp",
)
assert google_records[0].model_id == "gemini-embed"
assert google_records[0].capability.family == mc.FAMILY_EMBEDDING
assert google_records[0].catalog_shape_id == (
"google.generative-language.model.v1beta"
)
assert huggingface_records[0].model_id == "org/model"
assert huggingface_records[0].capability.family == mc.FAMILY_CHAT
assert huggingface_records[0].catalog_shape_id == (
"huggingface.hub.model-info-list.v1"
)
assert llamacpp_records[0].model_id == "served-model"
assert llamacpp_records[0].capability.family == mc.FAMILY_CHAT
assert llamacpp_records[0].capability.capabilities == (mc.CAP_TOOL_CALL,)
assert llamacpp_records[0].catalog_shape_id == "llamacpp.models.native.v1"
def test_huggingface_openai_serving_envelope_stays_identity_only():
payload = {
"data": [
{
"id": "served-model",
"pipeline_tag": "text-to-image",
}
]
}
direct = huggingface.records_from_payload(payload)
wrapped = records_from_payload(payload, vendor="huggingface")
assert direct == ()
assert wrapped[0].model_id == "served-model"
assert wrapped[0].capability.family == mc.FAMILY_UNKNOWN
assert wrapped[0].capability.capabilities == ()
assert wrapped[0].fallback is True
def test_generic_model_resource_fields_do_not_infer_anthropic_identity():
payload = {
"data": [
{
"id": "foreign-model",
"type": "model",
"display_name": "Foreign Model",
"created_at": "2026-01-01T00:00:00Z",
"capabilities": {"tools": True},
}
]
}
resolution = pcs.resolve_provider(payload)
records = records_from_payload(payload)
assert resolution.provider_id == pcs.PROVIDER_UNKNOWN
assert resolution.shape_id == "fallback.models.data.v1"
assert resolution.fallback is True
assert records[0].vendor == pcs.PROVIDER_UNKNOWN
assert records[0].fallback is True
assert records[0].capability.capabilities == ()
def test_mistral_reader_maps_per_model_capabilities_without_provider_inheritance():
records = mistral.records_from_payload(
{
@ -686,6 +820,60 @@ def test_copilot_reader_ignores_unverified_support_aliases():
assert record.capability.capabilities == ()
def test_copilot_catalog_uses_picker_selection_with_no_picker_fallback():
def payload(*picker_values):
return {
"data": [
{
"id": f"model-{index}",
"model_picker_enabled": picker_enabled,
"capabilities": {"supports": {}},
}
for index, picker_enabled in enumerate(picker_values)
]
}
selected = records_from_payload(payload(False, True, False), vendor="copilot")
fallback = records_from_payload(payload(False, False), vendor="copilot")
assert [record.model_id for record in selected] == ["model-1"]
assert [record.model_id for record in fallback] == ["model-0", "model-1"]
def test_chatgpt_catalog_applies_visibility_priority_and_slug_deduplication():
records = records_from_payload(
{
"models": [
{"slug": "hidden", "visibility": "hidden", "priority": 0},
{"slug": "later", "visibility": "list", "priority": 20},
{
"slug": "duplicate",
"visibility": "list",
"priority": 30,
"title": "lower-precedence",
},
{"slug": "first", "visibility": "list", "priority": 1},
{
"slug": "duplicate",
"visibility": "list",
"priority": 5,
"title": "selected",
},
{"slug": "unranked", "visibility": "list", "priority": float("inf")},
]
},
vendor="chatgpt_subscription",
)
assert [record.model_id for record in records] == [
"first",
"duplicate",
"later",
"unranked",
]
assert records[1].display_name == "selected"
def test_sglang_model_info_maps_native_generation_flags_only():
generation = sglang.records_from_payload(
{
@ -746,7 +934,7 @@ def test_sglang_openai_catalog_preserves_only_valid_native_context_limit():
]
}
)[0]
for value in (0, True, float("inf"))
for value in (0, True, 1.5, float("inf"))
]
assert valid.vendor == "sglang"