fix(providers): self-heal temperature rejection on the OpenAI-compatible path - #1246
Conversation
…ble path Next-generation Claude models reject any request carrying `temperature` with HTTP 400 "`temperature` is deprecated for this model." (issue HKUDS#1223). The native Anthropic Messages path already classifies the error, retries once without the field and remembers the model, but the generic OpenAI-compatible branch still sent `temperature` unconditionally, so LANGCHAIN_PROVIDER=openai with OPENAI_BASE_URL pointed at Anthropic's compatibility endpoint failed on every call. Extend the same self-heal to ChatOpenAIWithReasoning: reuse _is_anthropic_temperature_unsupported_error and the process-wide _ANTHROPIC_TEMPERATURE_UNSUPPORTED set, retry sync/async generate and both stream paths once without the field, and omit temperature up front for remembered models in _get_request_payload. _astream's stream_usage fallback moves to a named _astream_with_usage_fallback to mirror the sync layering; behavior is unchanged. Models that accept `temperature` are unaffected. Regression tests feed the verbatim 400 body through mocked endpoints and fail on the pre-fix code. Signed-off-by: lorenzozanee <wyz0707@proton.me>
…se the two #1223 side fixes Hardens the retry #1246 adds: it restarted the whole stream on the temperature 400 because that error arrives before the first chunk. True, but that is a property of the provider, not of this code — the retry now re-raises once a chunk has been emitted, so a mid-stream error matching the detector can never replay already-emitted output. Also lands the two fixes promised on #1223 and not covered by that PR: - ANTHROPIC_API_KEY joins env_schema. It was read through a bare os.getenv, so provider doctor and preflight could not tell a missing key from a working one and it surfaced as an opaque 401 at the first call. - .env.example states that pointing OPENAI_BASE_URL at Anthropic's compatibility endpoint is not a supported route.
|
Merged, and I was wrong to decline this direction on #1223. My argument there was that the generic OpenAI-compatible branch is "deliberately provider-agnostic" and teaching it Anthropic-specific error prose would couple it to every provider quirk it currently stays clean of. That is not true of the branch as it stands: One hardening pushed on top ( The same commit lands the two things I promised on #1223 and had not done: |
Summary
ChatOpenAIWithReasoning), so requests to Anthropic models throughOPENAI_BASE_URLrecover from "temperatureis deprecated for this model." automatically.temperature— the configured value keeps being sent.Why
Next-generation Claude models (claude-opus-4-8, claude-sonnet-5, ...) reject any request carrying
temperaturewith HTTP 400. At v0.1.14 the native Anthropic Messages path detects the error, retries without the field and remembers the model, but the generic OpenAI-compatible branch still sendstemperatureunconditionally, so runningLANGCHAIN_PROVIDER=openaiwithOPENAI_BASE_URL=https://api.anthropic.com/v1/fails on every call.Closes #1223
Changes
ChatOpenAIWithReasoning._remember_temperature_unsupportedclassifies the 400 with the existing_is_anthropic_temperature_unsupported_errordetector, logs once, and records the model in_ANTHROPIC_TEMPERATURE_UNSUPPORTED— the same set the native path uses._generate/_agenerateretry once without the field;_stream/_astreamdo the same (the 400 is raised before the first chunk, so the retry cannot duplicate output). The async stream_usage fallback moves to a named_astream_with_usage_fallbackto mirror the sync_streamlayering — no behavior change._get_request_payloadomitstemperatureup front for remembered models, so later calls skip the doomed request.Test Plan
pytest --ignore=agent/tests/e2e_backtest --tb=short -q— verified for the touched components:agent/tests/test_llm.py,agent/tests/test_provider_header_isolation.py,agent/tests/test_env_schema.py)agent/tests/test_llm.pycovering invoke self-heal and the remembered-model fast path, supported-model preservation, stream self-heal, and async invoke; all fail on the pre-fix code with the verbatim 400httpx.MockTransport, no live credentials)Checklist
src/agent/,src/session/,src/providers/) without prior discussion — direction raised and left open in temperature sent unconditionally on the OpenAI-compatible path to Anthropic (native-path fix at v0.1.14 doesn't cover it) #1223; follows the self-heal precedent from Swarm run.json under-reports output tokens ~18-36x: streamed calls never request usage, and the fallback ignores tool-call arguments #1224 on the same file