feat: refuse an impossible tier with the cheapest one that model does have
resolve_thinking now takes an Effort instead of a tri-state bool, and the four gates become five. The new one sits ahead of the generic tier check on purpose: asking for `none` on GLM-5.3 used to fall through to "none is not supported, pick low/high/max", which loses both the fact that the model cannot stop reasoning and the one tier the caller could switch to right now. Without that alternative, downstream goes looking for extra_body — which is how issue #20 happened in the first place. The return type is a ThinkingResolution rather than the payload alone. Under fallback="nearest" the tier that goes out is not the tier that was asked for, and telemetry has to record the one that ran, or task 10 files a call under a tier it never used. Ties in that mapping go to the weaker side: a silent medium -> max is a multiple of the bill, and the library does not raise a caller's price on its own. Two readings the design left implicit, both settled the way its own compatibility promise requires: - `auto` is exempt from the tier list. It means "on, no tier named", which in the body is the absence of the effort key, not a value of it. Checking it against the list would break every existing source that sets ENABLE_THINKING=true against deepseek-v4 or glm-5.3. - `none` is never a mapping target. Turning "think less" into "do not think" reverses the decision instead of cheapening it; a switch-only model maps to `auto` and a model that only has `none` still errors. Both call sites convert enable_thinking in place for now; task 5 folds that into effective_effort along with the source- and call-level tiers.
This commit is contained in:
@@ -34,7 +34,12 @@ from polygateway.thinking import (
|
||||
resolve_thinking,
|
||||
)
|
||||
from polygateway.transports._http_errors import compose_message, summarize_body
|
||||
from polygateway.types import EmbeddingTransportResult, SourceConfig, TransportResult
|
||||
from polygateway.types import (
|
||||
Effort,
|
||||
EmbeddingTransportResult,
|
||||
SourceConfig,
|
||||
TransportResult,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import AsyncIterator, Callable, Mapping
|
||||
@@ -355,14 +360,19 @@ class OpenAICompatTransport:
|
||||
capability = get_capability(source.model, table=self._capabilities)
|
||||
first_time = source.model not in self._warned_models
|
||||
self._warned_models.add(source.model)
|
||||
# `enable_thinking` 的档位语法糖(True → auto,False → none,None 不表态);
|
||||
# 就地转换是过渡形态,T5 起由 thinking.effective_effort() 统一收口并接上
|
||||
# 源级/请求级档位(设计 §4.2)
|
||||
enabled = source.enable_thinking
|
||||
effort = None if enabled is None else (Effort.AUTO if enabled else Effort.NONE)
|
||||
payload.update(
|
||||
resolve_thinking(
|
||||
profile,
|
||||
capability,
|
||||
source.enable_thinking,
|
||||
effort,
|
||||
model=source.model,
|
||||
warn_unregistered=first_time,
|
||||
)
|
||||
).payload
|
||||
)
|
||||
# 顺序即优先级(issue #4 设计决策 A): 配置级 extra_body 在前,调用级
|
||||
# overlay(含结构化注入)在后覆盖之。两行不可调换
|
||||
|
||||
Reference in New Issue
Block a user