diff --git a/src/model_capability_readers/base.py b/src/model_capability_readers/base.py index c2651a823..813f79754 100644 --- a/src/model_capability_readers/base.py +++ b/src/model_capability_readers/base.py @@ -178,9 +178,11 @@ def model_id_from(raw: Mapping[str, Any], *keys: str) -> str: def int_limit(value: Any) -> int | None: + if isinstance(value, bool): + return None try: limit = int(value) - except (TypeError, ValueError): + except (OverflowError, TypeError, ValueError): return None return limit if limit > 0 else None diff --git a/src/provider_capability_schemas.py b/src/provider_capability_schemas.py index 0e7593fbc..3fb679c28 100644 --- a/src/provider_capability_schemas.py +++ b/src/provider_capability_schemas.py @@ -322,7 +322,7 @@ CHATGPT_MODELS_SHAPE = ProviderCatalogShape( envelope=ENVELOPE_MODELS, identity_paths=("slug",), required_item_any_paths=("visibility", "priority"), - detection_priority=90, + detection_priority=0, ) SGLANG_MODEL_INFO_SHAPE = ProviderCatalogShape( shape_id="sglang.model-info.v2", @@ -605,11 +605,11 @@ def native_shape_for_payload( matches = [shape for shape in shapes if shape.matches(payload)] if not matches: return None - priority = max(shape.detection_priority for shape in matches) - best = [shape for shape in matches if shape.detection_priority == priority] - providers = {shape.provider_id for shape in best} + providers = {shape.provider_id for shape in matches} if len(providers) != 1: return None + priority = max(shape.detection_priority for shape in matches) + best = [shape for shape in matches if shape.detection_priority == priority] return sorted(best, key=lambda shape: shape.shape_id)[0] diff --git a/tests/test_provider_capability_schemas.py b/tests/test_provider_capability_schemas.py index 4e61087b5..4a533155c 100644 --- a/tests/test_provider_capability_schemas.py +++ b/tests/test_provider_capability_schemas.py @@ -240,7 +240,12 @@ def test_native_catalog_shapes_resolve_with_required_provider_context(): ), ) - explicit_context_providers = {"cohere", "lmstudio", "mistral"} + explicit_context_providers = { + "chatgpt_subscription", + "cohere", + "lmstudio", + "mistral", + } for payload, expected_provider, expected_shape in cases: explicit_provider = ( expected_provider if expected_provider in explicit_context_providers else None @@ -273,6 +278,10 @@ def test_ambiguous_common_fields_do_not_infer_provider_from_payload_alone(): {"models": [{"name": "generic", "endpoints": ["chat"], "context_length": 4096}]}, "cohere", ), + ( + {"models": [{"slug": "generic", "visibility": "list", "priority": 1}]}, + "chatgpt_subscription", + ), ) for payload, provider_id in cases: @@ -284,6 +293,26 @@ def test_ambiguous_common_fields_do_not_infer_provider_from_payload_alone(): assert contextual.provider_id == provider_id +def test_payload_matching_multiple_providers_degrades_to_fallback(): + payload = _openrouter_payload( + { + **_openrouter_payload()["data"][0], + "model_picker_enabled": True, + "capabilities": {"supports": {"tool_calls": True}}, + } + ) + resolution = pcs.resolve_provider(payload) + record = records_from_payload(payload)[0] + + assert resolution.provider_id == pcs.PROVIDER_UNKNOWN + assert resolution.shape_id == "fallback.models.data.v1" + assert resolution.fallback is True + assert record.vendor == pcs.PROVIDER_UNKNOWN + assert record.capability.family == mc.FAMILY_UNKNOWN + assert record.capability.capabilities == () + assert record.fallback is True + + def test_openrouter_payload_detection_requires_the_compound_official_shape(): complete = _openrouter_payload() assert pcs.resolve_provider(complete).shape_id == "openrouter.models.rich.v1" @@ -574,18 +603,21 @@ def test_sglang_openai_catalog_preserves_only_valid_native_context_limit(): ] } )[0] - nonpositive = sglang.records_from_payload( - { - "data": [ - { - "id": "served-model", - "owned_by": "sglang", - "root": "org/model", - "max_model_len": 0, - } - ] - } - )[0] + malformed = [ + records_from_payload( + { + "data": [ + { + "id": "served-model", + "owned_by": "sglang", + "root": "org/model", + "max_model_len": value, + } + ] + } + )[0] + for value in (0, True, float("inf")) + ] assert valid.vendor == "sglang" assert valid.capability.family == mc.FAMILY_UNKNOWN @@ -593,7 +625,7 @@ def test_sglang_openai_catalog_preserves_only_valid_native_context_limit(): assert dict(valid.capability.limits) == {"context_tokens": 131072} assert valid.catalog_shape_id == "sglang.models.openai.v1" assert valid.fallback is False - assert dict(nonpositive.capability.limits) == {} + assert all(dict(record.capability.limits) == {} for record in malformed) def test_identity_only_native_catalogs_remain_unknown():