95cf7bc77c
* fix(modelis): declare reasoning_options per model from measurements Follow-up to #3932. That PR landed with the same six-value effort list on all nine models; the review bot was right that this is over-broad, and re-measuring showed it is also incomplete. Measured one control at a time against the live endpoint: - effort kept only where the levels measurably change reasoning (Claude x3, Gemini x2). Dropped on both DeepSeek and both Qwen models, which accept every value and return 200 but do not change behaviour. - toggle added where both states are caller-reachable. The mechanism differs by family: reasoning.enabled for Claude/Gemini/Qwen, and reasoning_effort "none" for DeepSeek, which ignores reasoning.enabled. - budget_tokens added where reasoning_tokens tracks the requested budget (Gemini x2, Qwen x2). No min/max, since no boundary was probed. - claude-fable-5 and gemini-2.5-pro reject disabling with a 400, so neither declares a toggle. Also drops the header comment that claimed all six effort values were reflected in reasoning_tokens: that holds for five models, not nine. Costs are unchanged and re-verified against the live pricing endpoint. * fix(modelis): move wire-path comments to a leading header block Review finding: every declared control needs its exact request syntax in a leading top-of-file comment, not an inline one next to the option. I had put them inline because Modelis has no sync module, so nothing would strip mid-file comments today. That was the wrong call: the sync rewrites provider TOMLs by parsing and re-serializing them and keeps only a leading header, so an inline comment is one sync module away from vanishing with nobody noticing. Each file now opens with the wire path for every control it declares. * fix(modelis): narrow effort values to measured separable levels Review finding: the six-value lists were the gateway's global accept-set minus none, not per-model truth. Re-measured at three task difficulties, asking which ADJACENT levels are actually distinguishable (sample ranges that do not overlap): - minimal collapses into low on every Claude model at every difficulty -> dropped from all three, as the lab baseline predicted. - xhigh never rises above high on opus, sonnet or gemini-2.5-flash -> dropped there; kept on fable, where it does separate. - gemini-2.5-flash keeps minimal: 37 vs 107 with zero scatter across three repeats. - claude-fable-5 returns 145 reasoning tokens at reasoning_effort none, so it has no off switch at all and declares neither toggle nor none. Per-file: opus/sonnet/gemini-2.5-pro low|medium|high|max, fable low|medium|high|xhigh|max, gemini-2.5-flash minimal|low|medium|high|max. DeepSeek and Qwen still declare no effort list: repeats at one setting scatter up to 5x and the ordering inverts at medium on both DeepSeek models. Numbers are in the PR discussion. * fix(modelis): effort-none authored as effort; restore lab-baseline levels Review findings: 1. Off via reasoning_effort "none" must be authored as effort with none in values, not as toggle. Both DeepSeek files had a toggle declaration whose own wire comment named the effort parameter -- self-contradicting. They now declare effort = [none, high, max] per the peer set. Qwen keeps toggle because there the mechanism really is a separate field: reasoning.enabled false -> 0, while reasoning_effort none leaves those models reasoning unchanged. 2. Dropping a level because adjacent reasoning_tokens ranges overlapped was the wrong test -- a level can differ in latency or quality without differing in thinking tokens. Reverted to the lab/peer baseline and restored xhigh on claude-opus-4-8. minimal stays dropped on the Claude models: it is absent from the lab baseline and returned output identical to low at every difficulty tested.