refactor(ai_client): merge vendor_capabilities into ai_client; git rm src/vendor_capabilities.py

Per spec FR2 + Phase 2.1: VendorCapabilities + register + get_capabilities +
list_models_for_vendor + the ~40 vendor registrations move into ai_client.py
as a region block. Renamed internal _REGISTRY to _VENDOR_REGISTRY to avoid
collision with mcp_tool_specs._REGISTRY.

Importers (in src/) updated:
- src/ai_client.py: removed top-level import; removed 4 local imports of
  list_models_for_vendor/get_capabilities (symbol now in module namespace)
- src/app_controller.py: 2 sites updated to 'from src.ai_client import get_capabilities'
- src/gui_2.py: 1 site updated to 'from src.ai_client import VendorCapabilities, get_capabilities'

Tests updated:
- 8 test_*.py files: changed 'from src.vendor_capabilities import' to
  'from src.ai_client import'
- tests/test_vendor_capabilities.py: _clean_registry fixture updated to
  reference src.ai_client._VENDOR_REGISTRY (was src.vendor_capabilities._REGISTRY)

Verification: 157 tests pass across the affected files (vendor_capabilities,
ai_client_tool_loop variants, openai_compatible, command_palette,
diff_viewer, patch_modal, app_controller_result, app_controller_sigint,
handle_reset_session, ai_loop_regressions, grok/llama/minimax provider tests).
This commit is contained in:
ed
2026-06-26 07:07:12 -04:00
parent ac2a5ac3bd
commit 81d8bce419
13 changed files with 114 additions and 117 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ from src.result_types import Result
from src.openai_compatible import NormalizedResponse, OpenAICompatibleRequest
from src.openai_schemas import UsageStats
from src.ai_client import run_with_tool_loop
from src.vendor_capabilities import VendorCapabilities
from src.ai_client import VendorCapabilities
@pytest.fixture
def caps() -> VendorCapabilities:
+1 -1
View File
@@ -11,7 +11,7 @@ from src.openai_compatible import NormalizedResponse, OpenAICompatibleRequest
from src.openai_schemas import UsageStats
from src.ai_client import run_with_tool_loop
from src.result_types import Result
from src.vendor_capabilities import VendorCapabilities
from src.ai_client import VendorCapabilities
def _make_normalized_response(text: str = "ok", tool_calls: list[dict[str, Any]] | None = None) -> NormalizedResponse:
return NormalizedResponse(
+1 -1
View File
@@ -9,7 +9,7 @@ from unittest.mock import MagicMock, patch
from src.openai_compatible import NormalizedResponse
from src.openai_schemas import UsageStats
from src.ai_client import run_with_tool_loop
from src.vendor_capabilities import VendorCapabilities
from src.ai_client import VendorCapabilities
def _make_normalized_response(text: str = "ok", tool_calls: list[dict[str, Any]] | None = None) -> NormalizedResponse:
return NormalizedResponse(
+1 -1
View File
@@ -214,7 +214,7 @@ def test_fr3_minimax_thinking_in_returned_text() -> None:
from src import openai_compatible as oc
from src import provider_state
from src.provider_state import ProviderHistory
from src.vendor_capabilities import register, VendorCapabilities
from src.ai_client import register, VendorCapabilities
register(VendorCapabilities(vendor="minimax", model="MiniMax-M2.7", reasoning=True))
ai_client._model = "MiniMax-M2.7"
+1 -1
View File
@@ -23,7 +23,7 @@ def test_send_grok_uses_xai_endpoint(monkeypatch: pytest.MonkeyPatch) -> None:
assert mock_client.chat.completions.create.called
def test_grok_2_vision_supports_image() -> None:
from src.vendor_capabilities import get_capabilities
from src.ai_client import get_capabilities
caps = get_capabilities("grok", "grok-2-vision")
assert caps.vision is True
+1 -1
View File
@@ -62,7 +62,7 @@ def test_llama_model_discovery_unions_ollama_and_openrouter() -> None:
assert "llama-3.3-70b-specdec" in models
def test_llama_3_2_vision_vision_capability() -> None:
from src.vendor_capabilities import get_capabilities
from src.ai_client import get_capabilities
caps = get_capabilities("llama", "llama-3.2-11b-vision-preview")
assert caps.vision is True
+2 -2
View File
@@ -42,7 +42,7 @@ def test_minimax_reasoning_extractor_used_when_caps_reasoning_true() -> None:
def _fake_send(client, request, *, capabilities):
captured_kwargs.append({"model": request.model})
return MagicMock(text="ok", tool_calls=[], usage=UsageStats(input_tokens=0, output_tokens=0, cache_read_tokens=0, cache_creation_tokens=0), raw_response=None)
from src.vendor_capabilities import register, VendorCapabilities
from src.ai_client import register, VendorCapabilities
register(VendorCapabilities(vendor='minimax', model='MiniMax-M2.5', reasoning=True))
with patch.object(oc, "send_openai_compatible", side_effect=_fake_send), \
patch("src.ai_client._ensure_minimax_client", return_value=MagicMock()), \
@@ -54,7 +54,7 @@ def test_minimax_reasoning_extractor_omitted_when_caps_reasoning_false() -> None
"""caps.reasoning=False (M2/M2.1) should NOT pass the reasoning_extractor (avoid useless getattr)."""
from src import openai_compatible as oc
from src.openai_schemas import UsageStats
from src.vendor_capabilities import register, VendorCapabilities
from src.ai_client import register, VendorCapabilities
register(VendorCapabilities(vendor='minimax', model='MiniMax-M2', reasoning=False))
captured_kwargs: list[dict] = []
def _fake_send(client, request, *, capabilities):
+1 -1
View File
@@ -6,7 +6,7 @@ from src.openai_compatible import (
send_openai_compatible,
)
from src.openai_schemas import ChatMessage
from src.vendor_capabilities import VendorCapabilities, register
from src.ai_client import VendorCapabilities, register
@pytest.fixture
def caps() -> VendorCapabilities:
+6 -6
View File
@@ -1,13 +1,13 @@
import pytest
from src.vendor_capabilities import VendorCapabilities, get_capabilities, register
from src.ai_client import VendorCapabilities, get_capabilities, register
@pytest.fixture(autouse=True)
def _clean_registry():
import src.vendor_capabilities
snapshot = src.vendor_capabilities._REGISTRY.copy()
import src.ai_client as _ai
snapshot = _ai._VENDOR_REGISTRY.copy()
yield
src.vendor_capabilities._REGISTRY.clear()
src.vendor_capabilities._REGISTRY.update(snapshot)
_ai._VENDOR_REGISTRY.clear()
_ai._VENDOR_REGISTRY.update(snapshot)
def test_registry_lookup_known_model():
caps = VendorCapabilities(
@@ -217,6 +217,6 @@ def test_v2_capability_badge_helper_skips_disabled_fields() -> None:
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
from src.ai_client import VendorCapabilities
empty_caps = VendorCapabilities(vendor='test', model='empty')
_render_v2_capability_badges(empty_caps)