The 5 tests in tests/test_openai_compatible.py used the LEGACY dict-based
API. Updated to use the canonical typed API:
- test_send_non_streaming_returns_text_in_result
- test_send_streaming_aggregates_chunks
- test_tool_call_detection_in_blocking_response
- test_vision_multimodal_message
- test_error_classification_429_to_rate_limit
Changes per test:
- messages=[{...}] -> messages=[ChatMessage(role=..., content=...)]
- tool_calls[0]['function']['name'] -> tool_calls[0].function.name
- tool_calls[0]['id'] -> tool_calls[0].id
The dict messages in test_tool_call_detection_in_blocking_response's kwargs
are CORRECT - that test calls _send_blocking(client, kwargs) directly with
raw OpenAI kwargs (which expect dicts because they go to the OpenAI client),
bypassing OpenAICompatibleRequest.
Verification:
- uv run pytest tests/test_openai_compatible.py -v -> 6 of 6 pass
- tier-1-unit-core in batched suite now PASS (was FAIL)
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).
This resolves the issue where calling 'send_openai_compatible' discarded the NormalizedResponse details, resulting in an AttributeError when accessing 'raw_response' inside the tool loop.
6 failing tests in tests/test_openai_compatible.py that establish the
core behaviors of the new send_openai_compatible() shared helper:
1. test_send_non_streaming_returns_normalized_response: blocking call
returns text, empty tool_calls, and correct usage token counts
2. test_send_streaming_aggregates_chunks: streaming call aggregates
deltas into final text and fires stream_callback per chunk
3. test_tool_call_detection_in_response: tool_calls from the response
are converted to dicts with id/type/function/arguments fields
4. test_vision_multimodal_message: messages with multimodal content
(text + image_url) are passed through unchanged to the client
5. test_error_classification_429_to_rate_limit: RateLimitError from
openai SDK is caught and re-raised as ProviderError(kind='rate_limit')
6. test_normalized_response_is_frozen_dataclass: NormalizedResponse is
a frozen dataclass (FrozenInstanceError on attribute assignment)
All 6 tests fail with ModuleNotFoundError: No module named
'src.openai_compatible' (confirmed via pytest). The implementation file
will be created in the next commit (Green phase).
ProviderError confirmed importable from src.ai_client (no stub needed).