fix(llm): make reasoning_effort model-aware (denylist + retry-on-400)#65
Conversation
AsyncLLMClient defaulted reasoning_effort to "medium" and sent it on every OpenAI-compat request unconditionally. Non-reasoning models served over OpenAI-compat endpoints (Llama 3.x/4, Mistral, Mixtral, Qwen2/2.5, Gemma on Groq/Together/Fireworks/etc.) reject the param with HTTP 400, which killed the sourcehunt hunter loop and ranker for those providers. Two layers, both in clearwing/llm/native.py: 1. Model-name denylist with a sentinel default. The constructor default becomes "auto": _auto_resolve_reasoning_effort() maps known non-reasoning model families to None and everything else (incl. Anthropic/Claude) to "medium" — so the default Anthropic path is unchanged. Any explicit value (including None) bypasses auto-resolution. 2. Retry-on-400 safety net. If a provider still rejects reasoning_effort at call time, achat/achat_stream detect that specific 400, drop the param, latch reasoning_effort=None for the session, and retry once. Unrelated 400s/429s/500s propagate untouched. Re-port of #47 onto the refactored native.py (provider routing + codex/ aiohttp streaming fallback landed since the original branch). Tests from the original PR carried over verbatim. Co-authored-by: msyed-01 <288113432+msyed-01@users.noreply.github.com>
|
Thanks for the fast and thorough re-port @ropoctl, just saw this and realizing I missed the rebase window. You were fast lol. The two-layer shape is preserved exactly as designed: denylist + sentinel-default for Layer 1, retry-once with instance-state latch for Layer 2. Putting the streaming retry before the existing aiohttp fallback in One related item that also surfaced during the end-to-end smoke test against Groq Llama 3.3 70B: the Ranker hits a second 400, this time from |
|
Sorry, I probably would have waited a bit longer for your response on this. btw groq llama supports year of our lord 2026 features https://console.groq.com/docs/responses-api https://console.groq.com/docs/structured-outputs |
|
lmao ur so right, my bad, kept it from the reasoning_effort repro (just ended up with it cuz its free tier and hits that bug). moving to gpt-oss / llama-4-scout for json_schema runs. thnx again chief |
|
ah does free tier not allow V1 Responses (openai_resp) api? |
|
honestly never tested it-- was just running openai_compat on groq, never tried the openai_resp path. |
|
actually just tested it, free tier allows /v1/responses on both llama-3.3-70b and gpt-oss-120b. we just hadnt tried the openai_resp path. also, something I noticed: chat-completions says |
Supersedes #47 (closed) — re-ported onto the current refactored
native.py.Problem
AsyncLLMClientdefaultedreasoning_effort="medium"and sent it on every OpenAI-compat request unconditionally. Non-reasoning models served over OpenAI-compat endpoints reject the param with HTTP 400:This kills the sourcehunt hunter loop + ranker against Llama 3.x/4, Mistral, Mixtral, Qwen2/2.5, Gemma on Groq/Together/Fireworks/etc. The default Anthropic/Claude path was never affected.
Fix — two layers, both in
clearwing/llm/native.py"auto";_auto_resolve_reasoning_effort()maps known non-reasoning families (_REASONING_EFFORT_UNSUPPORTED_PATTERNS) toNoneand everything else — including Claude — to"medium". So the default path is byte-for-byte unchanged for Anthropic. Any explicit value (includingNone) bypasses auto-resolution.achat/achat_streamdetect that specific 400 (_is_unsupported_reasoning_effort_error: must mentionreasoning_effortand400/unsupported), drop it, latchreasoning_effort=Nonefor the session, and retry once. Unrelated 400s/429s/500s and the existing OpenAI aiohttp stream fallback are preserved.Notes on the re-port
native.pywas refactored since #47's branch (centralized provider routing, codex/aiohttp streaming fallback), so the original patch no longer applied. Re-implemented against currentmain:achatretry wraps the_with_rate_limit_retriescall.achat_streamretry is layered into the existingexceptalongside (not replacing) the OpenAI HTTP fallback, via a small_consume(opts)helper.Original author credited via
Co-authored-by.Verification
Tests from #47 carried over verbatim (denylist resolution, constructor wiring, error classification, options rebuild, and achat/achat_stream retry-once behavior).