You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(llm): OpenAI provider compatibility for caching, temperature, and tool calls (#98)
* fix(llm): OpenAI provider compatibility for caching, temperature, and tool calls
Three request-shape bugs surfaced when running against the real OpenAI
API (DeepSeek tolerates all three, which is why they went unnoticed):
1. Anthropic prompt-caching shape sent to every provider. With
prompt_caching enabled, the loop moved the system prompt into the
Anthropic-only top-level "system" field and added cache_control
markers; OpenAI rejects this with 400 unknown_parameter. Markers are
now only applied when the endpoint is Anthropic (new
Client.IsAnthropic). OpenAI and DeepSeek cache automatically.
2. temperature:0 sent to reasoning models. odek defaults to
temperature 0 for determinism, but OpenAI reasoning models
(o1/o3/o4, gpt-5 series) only accept the default (1) and answer 400
unsupported_value. The field is now omitted for those models;
behavior for all other providers/models is unchanged.
3. Function tools + reasoning_effort rejected by gpt-5.6 models.
gpt-5.6-luna/sol reject tools combined with any reasoning_effort
other than "none" — and their default effort is not "none", so
omitting the field still 400s. The client now learns the constraint
from the 400 (atomic flag), retries once with effort "none", and
pins it for subsequent tool-bearing calls; tool-less calls keep the
configured effort.
Verified live against api.openai.com (gpt-4o-mini, gpt-5-nano,
gpt-5.6-luna, gpt-5.6-sol — basic runs, tool round-trips, automatic
prompt caching) and regression-checked against DeepSeek v4 Flash
(native cache metrics and temperature:0 default intact).
Regression tests: TestClient_IsAnthropic,
TestCall_OmitsTemperatureForReasoningModels,
TestCall_LearnsReasoningEffortNoneWithTools,
TestEngine_PromptCaching_NonAnthropicSkipsMarkers.
* fix(llm): map thinking enabled/disabled per provider instead of always sending the Anthropic object
The thinking object ({"type":"enabled"/"disabled"}) was sent
unconditionally; OpenAI rejects unknown top-level parameters with a
400, so thinking: disabled (and enabled) was broken against OpenAI —
same family as the system-field bug fixed earlier in this branch.
The shaping logic now lives in an extracted, directly testable
buildCallParams method:
- Anthropic/DeepSeek (new sendsThinkingObject gate): unchanged,
thinking object sent as before.
- OpenAI reasoning models (o1/o3/o4, gpt-5 series): enabled maps to
reasoning_effort "high", disabled to "none" (verified accepted on
the gpt-5 series); temperature stays omitted per the forbidlist.
- Non-reasoning models (e.g. gpt-4o): no thinking capability exists,
so both fields are omitted and temperature is honored as configured.
The two existing thinking tests asserted the old unconditional
behavior through an httptest server whose URL is never a recognized
provider; they now exercise buildCallParams directly. New table test
TestBuildCallParams_ThinkingMapping covers all provider/model/thinking
combinations.
0 commit comments