81d8bce419
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).
48 lines
1.9 KiB
Python
48 lines
1.9 KiB
Python
"""Verify run_with_tool_loop supports a custom send_func for vendors
|
|
that don't use send_openai_compatible (gemini_cli, gemini, anthropic,
|
|
deepseek). The vendor provides a send_func that returns a
|
|
NormalizedResponse, and the helper handles history + dispatch.
|
|
"""
|
|
from __future__ import annotations
|
|
from typing import Any
|
|
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.ai_client import VendorCapabilities
|
|
|
|
def _make_normalized_response(text: str = "ok", tool_calls: list[dict[str, Any]] | None = None) -> NormalizedResponse:
|
|
return NormalizedResponse(
|
|
text=text, tool_calls=tool_calls or [],
|
|
usage=UsageStats(input_tokens=10, output_tokens=5, cache_read_tokens=0, cache_creation_tokens=0),
|
|
raw_response=None,
|
|
)
|
|
|
|
def test_run_with_tool_loop_uses_send_func_when_provided() -> None:
|
|
client = MagicMock()
|
|
def send_func(_round_idx: int) -> NormalizedResponse:
|
|
return _make_normalized_response(f"from-send-func-{_round_idx}")
|
|
result = run_with_tool_loop(
|
|
client, request=lambda _i: MagicMock(), # should be IGNORED
|
|
base_dir=".", vendor_name="custom",
|
|
send_func=send_func,
|
|
)
|
|
assert result == "from-send-func-0"
|
|
|
|
def test_run_with_tool_loop_dispatches_via_send_func() -> None:
|
|
client = MagicMock()
|
|
tool_resp = _make_normalized_response(
|
|
"first", tool_calls=[{"id": "c1", "type": "function", "function": {"name": "t", "arguments": "{}"}}]
|
|
)
|
|
final = _make_normalized_response("done")
|
|
def send_func(round_idx: int) -> NormalizedResponse:
|
|
return [tool_resp, final][round_idx]
|
|
with patch("src.ai_client._execute_tool_calls_concurrently", return_value=[("t", "c1", "r", "")]) as dispatch:
|
|
result = run_with_tool_loop(
|
|
client, request=lambda _i: MagicMock(),
|
|
base_dir=".", vendor_name="custom",
|
|
send_func=send_func,
|
|
)
|
|
assert result == "done"
|
|
assert dispatch.call_count == 1
|