• v1.3.3 a716f12483

    iomgaa 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: boolsupported_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 保留,降为它的语法糖(trueautofalsenone、缺省 ≡ 不表态);两键语义矛盾(如 true + none)在装配期报错,不做「后者赢」的静默兜底
    {SCOPE}__{PROVIDER}__{N}__EFFORT_FALLBACK error(缺省,报错)或 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_effort

    llm_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-5claude-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-5glm-5.1glm-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_CAPABILITIESMiniMax-M3supported_efforts 不含 auto(实测结论——它的默认档不推理),而 minimax 段的 wire 会为 auto 注入 {"reasoning_effort":"medium"} 并被放行。resolve_thinking 的 Phase 5 对 auto 无条件放行(auto 不是写进 effort_key 的取值,而是「不写 effort_key」),能力表拦不住这条路;当前是由 wire 侧的权宜之计兜住的。别把它读成「能力表能挡住 auto」。

    Downloads