-
1.3.3 — 推理从开关升级为档位 Stable
released this
2026-09-06 00:43:43 +08:00 | 0 commits to main since this release推理从「开 / 关」升级为档位(issue #20)。
enable_thinking: bool | None表达不了新一代模型:GLM-5.3 官方强制推理、只接受low/high/max,none不是它的档位——二态布尔在它上面无档可填,下游只能手写extra_body,而那条路会静默绕过本库为推理准备的三道机制。本版把档位做成一等公民:八档封闭词汇、源级与请求级两个入口、能力表按档位登记、缓存 key 与遥测各加一维。版号是 patch(2026-09-05 人类指令,不因破坏性变更走 minor),但本版含五处破坏性变更与四条行为变更。 patch 版号从设计上就不承担预警职责,预警只能由这份 CHANGELOG 扛,故全部置于最前。
请先读这一条(一):五处破坏性变更
# 位置 变更 谁会当场断 1 ThinkingCapability构造签名 can_disable: bool→supported_efforts: tuple[Effort, ...]自建能力表的调用方(关键字与位置两种构造都断) 2 ports.Transport.complete()新增无默认值参数 reasoning_effort任何自建 transport 实现 3 ports.TelemetryRecorder.record_llm_call()新增无默认值参数 reasoning_effort(25 → 26 参)任何自建 recorder 实现 4 thinking.resolve_thinking()第三参数由 bool换成Effort,返回类型由Mapping改为ThinkingResolution直调它的读侧代码一律断 5 providers.ProviderProfile两个字段 thinking_on/thinking_off→ 单字段thinking: ThinkingWire自建 profile 的调用方 第 1 条的
can_disable保留为只读派生属性(Effort.NONE in supported_efforts),只读它的代码一行不用改;构造则两种写法都断:1.3.2 的写法 升级后 ThinkingCapability(can_disable=True, evidence="…")(库自己那张表用的就是它)TypeError: ... got an unexpected keyword argument 'can_disable'ThinkingCapability(True, "…")TypeError: 'bool' object is not iterable——断在__post_init__的去重校验里,错误信息看不出真实原因迁移写法 ThinkingCapability(supported_efforts=(Effort.NONE, Effort.AUTO), evidence="…")第 2、3 条按这两个端口的既有纪律不设默认值:库外没有第三方实现者,带默认值只会让漏传时静默落一个默认值。第 4 条的新返回值是
ThinkingResolution(payload, applied_effort)——原来那个 mapping 现在是.payload,多出来的.applied_effort是开了nearest映射后真正发出去的那一档。请先读这一条(二):不改一行代码也会变的四条行为
# 变更 影响 1 glm-5.3/glm-5.3-flash/gemini-3.1-pro首次进入能力表,且三者都登记为关不掉推理本版唯一会打断存量配置的一条。 1.3.2 里这三个型号未登记,给它们配 ENABLE_THINKING=false会按 provider 形态尽力注入并放行(只发一条 warning);本版在装配期抛ThinkingUnsupportedError。并排实测:deepseek/glm-5.3 + ENABLE_THINKING=false在 1.3.2 返回{"thinking": {"type": "disabled"}},在本版当场报错2 openai段的开启方向由「形态未知即装配期报错」放宽为on_base={}把任意兼容厂商挂在 openai段下并配ENABLE_THINKING=true的下游:1.3.2 在装配期报错,本版放行且一个字节都不注入——走模型自己的默认档。若该模型默认不推理,这个配置既不报错也不开推理(见下方「已知限制」)3 openai段的关闭方向由「形态未知即装配期报错」放宽为{"reasoning_effort": "none"}同上但配 ENABLE_THINKING=false的下游:1.3.2 在装配期报错,本版下发这个片段。放宽的依据是reasoning_effort是 OpenAI 官方字段而非厂商方言,经网关的兼容端点不会把它打到不认识它的厂商4 缓存 key 加入 reasoning_effort只有新配 REASONING_EFFORT的源冷启动一次;只配ENABLE_THINKING或什么都没配的源,key 字面量逐字不变(已按 1.3.2 的实现逐字比对)第 1 条是设计上有意为之:调用方要的是「不推理」的语义保证,给不了就必须说,而不是让它继续静默烧推理 token——升级后当场失败,正是这三个型号本来就关不掉推理的证据。报错文案带一条能立刻照做的替代(该模型最省的那一档 + 该配的 env 键名),不把人推回
extra_body那条绕过库的路。qwen/deepseek/minimax三段的注入形态逐字未变。 全量比对(4 个 1.3.2 已有的 provider 段 × 25 个模型 ×ENABLE_THINKING三态 = 300 种组合)显示,本版与 1.3.2 的差异只有上表第 1、2、3 条。minimax的「开」尤其值得点名:它维持{"reasoning_effort": "medium"}逐字不变,因为真实网关实测显示 MiniMax-M3 在不带任何推理参数时不推理(5/5 轮),把它改成「不注入即为开」会让存量ENABLE_THINKING=true的调用静默停止推理。新增能力
新增 说明 八档 Effort:none/auto/minimal/low/medium/high/xhigh/max封闭词汇,取四家参考实现共同收敛的那一套。 none= 要求不推理(与「不表态」是两回事),auto= 要求推理但不指定强度{SCOPE}__{PROVIDER}__{N}__REASONING_EFFORT源级默认档。 ENABLE_THINKING保留,降为它的语法糖(true≡auto、false≡none、缺省 ≡ 不表态);两键语义矛盾(如true+none)在装配期报错,不做「后者赢」的静默兜底{SCOPE}__{PROVIDER}__{N}__EFFORT_FALLBACKerror(缺省,报错)或nearest(映射到最近档并 warning)。默认报错的理由是钱:一次静默的medium → max在部分模型上是数倍账单chat(reasoning_effort=...)请求级覆盖,优先级高于源级;裸字符串会在入口归一 LLMResponse.applied_effort本次实际跑在哪一档(开了 nearest时与请求档分叉)。字段追加在末尾,既有字段只增不改名四个新 provider 段 zhipu/moonshot/anthropic/google连同 1.3.2 已有的 qwen/deepseek/minimax/openai共八段。四段都是新增,不改变任何存量配置的行为能力表由 5 条扩到 24 条 1.3.2 只登记 5 个型号,其余一律走「按 provider 形态尽力注入 + warning」。本版新登记 19 个:qwen 4 款、deepseek 2 款、GLM 6 款、kimi 2 款、gpt 2 款、claude 2 款、gemini 1 款 kimi-k3首次登记为可关闭它在 1.3.2 未登记(配 false走尽力注入 + warning,不报错)。本版实测坐实可关:请求none后短提示词 5/5 轮 + 长上下文 3/3 轮无任何推理信号、completion 恒 9 token,与同模型 max 档(rt 33-146)的锚点可分。两源分歧由此了结——OpenRouter 的mandatory:false是对的,官方档位表没列none只是没列包根新增导出 Effort/EFFORT_ORDER/ThinkingWire/ThinkingResolution深路径 import 会被内部重组打断,一律从 polygateway包根取档位不支持时报错必带可执行替代:模型关不掉推理时,错误文案直接给出该模型最省的那一档和该配的 env 键名。只报错不给出路,下游只会退回
extra_body——而那正是 issue #20 的成因。遥测:第 26 个 INSERT 字段
reasoning_effortllm_calls新增一列reasoning_effort TEXT(INSERT 字段 25 → 26,物理列 26 → 27)。列可空,NULL表示调用方没表态;它与'none'(明确要求不推理)是两回事,折叠成任一档都等于替上游声称了一件它没说过的事。加这一列是为了让「不同档位是不是真有用」这类压测在数据侧能分组——此前 25 列里没有任何一列能回答「这一行跑在哪档」。成功行与失败行不是同一把尺子。 开了
EFFORT_FALLBACK=nearest的源上,成功行记的是映射后的实发档(读response.applied_effort);失败尝试没有响应、实发档无从得知,记的是请求档。故GROUP BY reasoning_effort不带error IS NULL时,两种尺子会混进同一个分组。缓存命中行与终态失败行同样只记请求档——它们手上没有选中源,源级档位与nearest映射都无从谈起。embedding / OCR 两条路径没有推理语义,该列恒NULL。补列走既有的
PGW_TELEMETRY_SCHEMA_MODE,两端 DDL 与COLUMNS同源。manual 档的下游会看到一处文案变化:旧表的缺列告警会多点名reasoning_effort这个维度,并附上对应的ALTER TABLE ADD COLUMN语句。能力表口径:24 条里 17 条经 new-api 实测、7 条仍是文档推定
DEFAULT_CAPABILITIES共 24 条,每条evidence自报家门(实测日期、轮数 N、判据、锚点,或「文档推定」及其四方出处)。读能力表请以逐条 evidence 为准,本版不存在「能力表已全部实测」这回事。 未能实测的 7 条与原因:模型 未覆盖的原因 claude-opus-5、claude-sonnet-5该渠道 claude 全系返回 429「api key 7 天限额已用完」,5/5 轮失败; none档还额外依赖网关把reasoning_effort=none转成thinking关闭形态,同样未经验证gemini-3.1-pro该渠道本型号上游报错( bad_response_status_code/openai_error),5/5 轮失败,连默认档基线都没取到。默认档「官方文档说 high、OpenRouter 说 medium」两源打架仍未决,本版不选边gpt-5.4全账号限流(429 All available accounts are currently rate-limited),5/5 轮失败。同代的 gpt-5.5已实测且与清单逐字相符,可作旁证但不是本型号的证据glm-5、glm-5.1、glm-5.2请求这三个型号时,渠道 5/5 轮把流量路由到 glm-5.3(issue #20 记录的 6/6 复现);拿到的行为不属于本型号,整组数据作废glm-5.2的下游风险要单独说:在这条渠道上给它配none,库会照文档推定放行,而真正服务请求的glm-5.3关不掉推理;运行期对账会喊,但那是事后。另有两条与实测相关的收获值得下游知道:同一批实测发现
zhipu/moonshot这条渠道不校验档位值(未登记的medium也照单收下并返回 200),故「网关没报错」在这两家上不构成「该档受支持」的证据;而openai那条会校验(清单外的max/minimal被上游 400 拒)。已知限制:
auto不等于「强制开推理」(issue #21)reasoning_effort=auto(含它的语法糖ENABLE_THINKING=true)在on_base={}的三个 provider 段(openai/anthropic/google)上表达的是「用模型自己的默认档」,库不注入任何字节。若某模型默认就不推理,这个配置既不报错也不开推理。正解是让auto受能力表约束——模型不支持「由模型自定」时报错并指路显式档位,属公共行为变更,留到下一版(gitea issue #21)。与之相连有一处刻意的不一致,请勿误读:
DEFAULT_CAPABILITIES里MiniMax-M3的supported_efforts不含auto(实测结论——它的默认档不推理),而minimax段的 wire 会为auto注入{"reasoning_effort":"medium"}并被放行。resolve_thinking的 Phase 5 对auto无条件放行(auto不是写进effort_key的取值,而是「不写effort_key」),能力表拦不住这条路;当前是由 wire 侧的权宜之计兜住的。别把它读成「能力表能挡住 auto」。Downloads