Private
Public Access
0
0
Files
manual_slop/tests/test_ai_client_tool_loop_send_func.py
T
ed 30c8b26381 fix(ai_client): migrate gemini_cli NormalizedResponse callers to Phase 2 dataclass API
Phase 2 deferred t2_6: update src/ai_client.py _send_grok + _send_minimax +
_send_llama + _send_gemini_cli (4 functions) to use the new
dataclass API after NormalizedResponse was refactored to
(text, tool_calls: tuple[ToolCall, ...], usage: UsageStats, raw_response).

These 4 callers were left with the old keyword args
(usage_input_tokens, usage_output_tokens, ...) which broke at
runtime: ai_client.send() raised
TypeError: NormalizedResponse.__init__() got an unexpected keyword
argument 'usage_input_tokens'.

FIXES:
- src/ai_client.py L2054: gemini_cli 'adapter unavailable' branch
- src/ai_client.py L2088: gemini_cli normal response branch
- Added: from src.openai_schemas import UsageStats (module level)
- Added backward-compat in src/openai_compatible.py:
  messages_dicts = [m.to_dict() if hasattr(m, 'to_dict') else m for m in request.messages]
  (accepts both ChatMessage dataclass and dict for backward compat
  with existing tests that pass raw dicts)

TEST FIXES:
- tests/test_ai_client_tool_loop.py: _make_normalized_response helper
  uses UsageStats instead of usage_*_tokens kwargs
- tests/test_ai_client_tool_loop_builder.py: same
- tests/test_ai_client_tool_loop_send_func.py: same
- tests/test_openai_compatible.py: NormalizedResponse(text=..., usage=UsageStats(...))
  + tool_calls[0].function.name (attribute access) instead of ['function']['name']
- tests/test_auto_whitelist.py: use update_session_metadata() instead of
  dict subscript assignment (Session dataclass doesn't support item assignment)

VERIFIED:
  uv run pytest tests/test_ai_client_*.py tests/test_openai_*.py \
               tests/test_auto_whitelist.py --timeout=30
    56 passed in 4.49s (19 previously failing tests now pass)
  uv run python scripts/audit_weak_types.py --strict
    STRICT OK: 115 weak sites <= baseline 115
  uv run python scripts/audit_dataclass_coverage.py --strict
    STRICT OK: 200 weak sites <= baseline 207

This commit closes the t2_6 deferred task. The 41-site Phase 3 call-site
migration remains deferred (separate provider_state_migration track).
2026-06-21 17:42:35 -04:00

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.ai_client import run_with_tool_loop
from src.vendor_capabilities import VendorCapabilities
def _make_normalized_response(text: str = "ok", tool_calls: list[dict[str, Any]] | None = None) -> NormalizedResponse:
from src.openai_schemas import UsageStats
return NormalizedResponse(
text=text, tool_calls=tool_calls or (),
usage=UsageStats(input_tokens=10, output_tokens=5),
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