import pytest from src.vendor_capabilities import VendorCapabilities, get_capabilities, register @pytest.fixture(autouse=True) def _clean_registry(): import src.vendor_capabilities snapshot = src.vendor_capabilities._REGISTRY.copy() yield src.vendor_capabilities._REGISTRY.clear() src.vendor_capabilities._REGISTRY.update(snapshot) def test_registry_lookup_known_model(): caps = VendorCapabilities( vendor='qwen', model='qwen-max', vision=False, context_window=32768 ) register(caps) retrieved = get_capabilities('qwen', 'qwen-max') assert retrieved.vendor == 'qwen' assert retrieved.model == 'qwen-max' assert retrieved.context_window == 32768 assert retrieved.vision is False def test_fallback_to_vendor_default(): caps = VendorCapabilities( vendor='llama', model='*', context_window=131072, cost_tracking=False ) register(caps) retrieved = get_capabilities('llama', 'llama-3.3-future-unregistered') assert retrieved.context_window == 131072 assert retrieved.cost_tracking is False def test_unknown_vendor_raises(): with pytest.raises(KeyError, match='No capabilities registered'): get_capabilities('nonexistent_vendor', 'anymodel') V2_FIELDS: list[str] = [ 'local', 'reasoning', 'structured_output', 'code_execution', 'web_search', 'x_search', 'file_search', 'mcp_support', 'audio', 'video', 'grounding', 'computer_use', ] @pytest.mark.parametrize('field_name', V2_FIELDS) def test_v2_field_default_is_false(field_name: str) -> None: caps = VendorCapabilities(vendor='test', model='m') assert getattr(caps, field_name) is False, f'{field_name} should default to False' @pytest.mark.parametrize('field_name', V2_FIELDS) def test_v2_field_round_trip(field_name: str) -> None: caps = VendorCapabilities(vendor='test', model='m', **{field_name: True}) assert getattr(caps, field_name) is True, f'{field_name} should round-trip to True' def test_v2_local_flag_works_for_local_vendor() -> None: register(VendorCapabilities(vendor='llama', model='llama-local-test-3.1', local=True)) caps = get_capabilities('llama', 'llama-local-test-3.1') assert caps.local is True def test_v2_local_flag_falls_back_to_wildcard() -> None: register(VendorCapabilities(vendor='llama', model='*', local=True)) caps = get_capabilities('llama', 'some-unregistered-model-3.1-future') assert caps.local is True def test_v2_local_flag_does_not_affect_other_vendors() -> None: register(VendorCapabilities(vendor='llama', model='*', local=True)) register(VendorCapabilities(vendor='qwen', model='*')) caps = get_capabilities('qwen', 'qwen-turbo') assert caps.local is False def test_runtime_caps_override_sets_local_for_llama_localhost() -> None: from dataclasses import replace base = VendorCapabilities(vendor='llama', model='llama-3.1-70b-versatile') assert base.local is False overridden = replace(base, local=True) assert overridden.local is True overridden2 = replace(overridden, local=False) assert overridden2.local is False def test_v2_per_model_population() -> None: caps = get_capabilities('minimax', 'MiniMax-M2.5') assert caps.reasoning is True caps_old = get_capabilities('minimax', 'MiniMax-M2') assert caps_old.reasoning is False caps_grok_v = get_capabilities('grok', 'grok-2-vision') assert caps_grok_v.web_search is True assert caps_grok_v.x_search is True assert caps_grok_v.vision is True caps_qwen_audio = get_capabilities('qwen', 'qwen-audio') assert caps_qwen_audio.audio is True caps_qwen_long = get_capabilities('qwen', 'qwen-long') assert caps_qwen_long.caching is True caps_llama_reasoning = get_capabilities('llama', 'llama-3.1-405b-reasoning') assert caps_llama_reasoning.reasoning is True caps_llama_plain = get_capabilities('llama', 'llama-3.1-8b-instant') assert caps_llama_plain.reasoning is False def test_runtime_caps_override_helper_for_llama_localhost() -> None: from src import gui_2 from src import ai_client original_url = ai_client._llama_base_url try: class MockApp: current_provider = 'llama' mock = MockApp() caps = VendorCapabilities(vendor='llama', model='llama-3.1-70b-versatile') ai_client._llama_base_url = 'https://openrouter.ai/api/v1' result = gui_2._apply_runtime_caps_override(mock, caps) assert result.local is False ai_client._llama_base_url = 'http://localhost:11434/v1' result = gui_2._apply_runtime_caps_override(mock, caps) assert result.local is True finally: ai_client._llama_base_url = original_url def test_runtime_caps_override_helper_does_not_touch_other_vendors() -> None: from src import gui_2 from src import ai_client original_url = ai_client._llama_base_url try: class MockApp: current_provider = 'qwen' mock = MockApp() caps = VendorCapabilities(vendor='qwen', model='qwen-turbo') ai_client._llama_base_url = 'http://localhost:11434/v1' result = gui_2._apply_runtime_caps_override(mock, caps) assert result.local is False finally: ai_client._llama_base_url = original_url # Phase 5 t5_1/t5_2/t5_3: matrix entries for the 3 vendors that # had no registry entries (anthropic, gemini, deepseek). # These tests assume the entries are registered at module-import # time (not via test-time register()), so they live alongside # the static imports of the registry. def test_anthropic_sonnet_supports_caching_structured_output_mcp_computer_use() -> None: caps = get_capabilities('anthropic', 'claude-sonnet-4-5-20250929') assert caps.caching is True assert caps.structured_output is True assert caps.mcp_support is True assert caps.computer_use is True assert caps.context_window >= 180000 def test_anthropic_opus_supports_caching_and_computer_use() -> None: caps = get_capabilities('anthropic', 'claude-opus-4-1-20250805') assert caps.caching is True assert caps.computer_use is True assert caps.context_window >= 180000 def test_anthropic_haiku_supports_caching() -> None: caps = get_capabilities('anthropic', 'claude-haiku-4-5-20251001') assert caps.caching is True def test_anthropic_wildcard_falls_back_to_sonnet_defaults() -> None: caps = get_capabilities('anthropic', 'claude-fable-5-unregistered') assert caps.caching is True assert caps.structured_output is True assert caps.mcp_support is True assert caps.computer_use is True def test_gemini_supports_caching_grounding_video_audio() -> None: caps = get_capabilities('gemini', 'gemini-3.1-pro-preview') assert caps.caching is True assert caps.grounding is True assert caps.video is True assert caps.audio is True assert caps.structured_output is True assert caps.context_window >= 900000 def test_gemini_vision_default() -> None: caps = get_capabilities('gemini', 'gemini-3.1-pro-preview') assert caps.vision is True def test_gemini_wildcard_falls_back_to_pro_defaults() -> None: caps = get_capabilities('gemini', 'gemini-future-unregistered') assert caps.caching is True assert caps.grounding is True assert caps.video is True assert caps.audio is True assert caps.vision is True assert caps.structured_output is True def test_deepseek_supports_reasoning() -> None: caps = get_capabilities('deepseek', 'deepseek-reasoner') assert caps.reasoning is True assert caps.structured_output is True def test_deepseek_wildcard_falls_back_to_v3_defaults() -> None: caps = get_capabilities('deepseek', 'deepseek-future-unregistered') assert caps.reasoning is True assert caps.structured_output is True def test_v2_capability_badge_helper_contains_all_11_v2_fields() -> None: """The GUI's v2 capability badges should render badges for all 11 v2 fields. This test ensures the helper stays in sync with the v2 matrix.""" import src.gui_2 import inspect src_lines: str = inspect.getsource(src.gui_2._render_v2_capability_badges) known_v2_fields: list[str] = [ "reasoning", "structured_output", "code_execution", "web_search", "x_search", "file_search", "mcp_support", "audio", "video", "grounding", "computer_use", ] for field in known_v2_fields: assert f'"{field}"' in src_lines, f'v2 field {field!r} missing from _render_v2_capability_badges helper' def test_v2_capability_badge_helper_skips_disabled_fields() -> None: """Sanity: a caps with all v2 fields False should produce no badges. We can verify this by passing a default-constructed VendorCapabilities and asserting the helper returns without erroring. (We can't easily verify the ImGui output without a live context, but we can verify the helper is a no-op on the no-cap case.)""" from src.gui_2 import _render_v2_capability_badges from src.vendor_capabilities import VendorCapabilities empty_caps = VendorCapabilities(vendor='test', model='empty') _render_v2_capability_badges(empty_caps)