feat: let a source name its reasoning tier, and say so when it contradicts itself
This commit is contained in:
@@ -18,6 +18,9 @@ from loguru import logger
|
||||
|
||||
_MISSING_DONE_DOMAIN = frozenset({"retry", "salvage"})
|
||||
|
||||
_EFFORT_FALLBACK_DOMAIN = frozenset({"error", "nearest"})
|
||||
"""`SourceConfig.effort_fallback` 的值域: 请求档打空时报错,还是映射到最近的档。"""
|
||||
|
||||
_PROTECTED_OVERLAY_KEYS: Mapping[str, str] = MappingProxyType(
|
||||
{
|
||||
"model": "会让遥测记录的 model 与实际请求分叉,成本按错单价换算",
|
||||
@@ -395,6 +398,10 @@ class SourceConfig:
|
||||
|
||||
限额闸 0 表示不启用;`enable_thinking` 三态: None=不注入(模型默认)、
|
||||
True=注入开启参数、False=注入关闭参数(统一 VT 与 CHS 相反的现状)。
|
||||
|
||||
2026-09-04 起 `enable_thinking` 降级为 `reasoning_effort` 的语法糖
|
||||
(`True`→`AUTO`、`False`→`NONE`),保留不删是因为它已被三项目消费
|
||||
(迁移兼容约束,ARCH §5.1)。两个字段说的是同一件事,故矛盾即报错。
|
||||
"""
|
||||
|
||||
name: str
|
||||
@@ -419,10 +426,25 @@ class SourceConfig:
|
||||
(加任何 mapping 字段的固有代价,裸 dict 亦然),库内无以源作 key 的写法;
|
||||
要可变副本用 `dict(source.extra_body)`,要改字段用 `dataclasses.replace`。"""
|
||||
|
||||
reasoning_effort: Effort | None = None
|
||||
"""本源默认的推理档位;None = 不表态(与 `Effort.NONE`「要求不推理」不同)。
|
||||
|
||||
**追加在末尾**是硬要求: 三项目的测试按位置构造 fake,插在中间会静默错位
|
||||
(本模块头部 docstring 的字段保序约定)。"""
|
||||
|
||||
effort_fallback: str = "error"
|
||||
"""请求档打空时的处置: `error`(默认,报错)或 `nearest`(映射到最近的档)。
|
||||
|
||||
默认报错的理由是钱: 一次静默的 `medium → max` 在 GLM-5.3 上是数倍账单
|
||||
(P5「严禁默认值掩盖错误」)。值域在此把关而非交给 `resolve_thinking`——
|
||||
后者对未知值是 fail-closed(按 `error` 处理),不会替配置兜错,漏判的结果
|
||||
就是 `EFFORT_FALLBAK` 这种拼写错误静默失效。"""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self._validate_identity()
|
||||
self._validate_gates()
|
||||
self._validate_watchdog()
|
||||
self._validate_thinking()
|
||||
self._freeze_extra_body()
|
||||
|
||||
def effective_est_tokens(self) -> int:
|
||||
@@ -460,6 +482,33 @@ class SourceConfig:
|
||||
):
|
||||
raise ValueError("看门狗不变式要求 0 < inter_token < ttft < timeout_s")
|
||||
|
||||
def _validate_thinking(self) -> None:
|
||||
"""推理两键的值域与互不矛盾(issue #20 设计 §4.2)。
|
||||
|
||||
矛盾**报错而非「后者赢」**: `enable_thinking` 与 `reasoning_effort` 表达的是
|
||||
同一件事,静默取其一等于替下游猜它到底想要哪个,而猜错的代价是账单——
|
||||
猜成开启就是白花钱,猜成关闭就是拿到一个没推理过的答案。
|
||||
|
||||
判据是「二者是否都在说关闭」: `enable_thinking is False` 与
|
||||
`reasoning_effort is NONE` 必须同真同假。`True` + 某个开启档(如 `low`)
|
||||
不算矛盾,那只是把同一件事说了两遍,且后者更精确。
|
||||
"""
|
||||
if self.effort_fallback not in _EFFORT_FALLBACK_DOMAIN:
|
||||
raise ValueError(
|
||||
f"SourceConfig.effort_fallback(EFFORT_FALLBACK)非法值 "
|
||||
f"{self.effort_fallback!r};允许: {sorted(_EFFORT_FALLBACK_DOMAIN)}"
|
||||
)
|
||||
if self.enable_thinking is None or self.reasoning_effort is None:
|
||||
return
|
||||
if (self.enable_thinking is False) != (self.reasoning_effort is Effort.NONE):
|
||||
raise ValueError(
|
||||
f"源 {self.name!r} 的 enable_thinking={self.enable_thinking} 与 "
|
||||
f"reasoning_effort={self.reasoning_effort.value!r} 相互矛盾: "
|
||||
f"enable_thinking 已是 reasoning_effort 的语法糖"
|
||||
f"(True={Effort.AUTO.value}、False={Effort.NONE.value})。"
|
||||
f"请只保留其中一个,或让两者语义一致"
|
||||
)
|
||||
|
||||
def _freeze_extra_body(self) -> None:
|
||||
"""校验后转只读视图: 装配完成的源不应再被就地改采样参数(设计决策 E)。"""
|
||||
validated = validate_request_overlay(
|
||||
|
||||
Reference in New Issue
Block a user