fix: read a tier the way every config path actually spells it

Both public assembly paths took the tier on trust: a bare "none" from
JSON or a hand-built SourceConfig stayed a str, and `is Effort.NONE`
then read it as a contradiction and crashed on `.value` while wording
the error -- the caller got an AttributeError where a ValueError was
promised, and on the request side that unclassified exception walked
straight through the transport's ThinkingUnsupportedError catch and the
retry classifier.

Normalize at the two entrances instead, matching what the .env path has
always done, and let EFFORT_FALLBACK be spelled with the same freedom as
its neighbour.
This commit is contained in:
2026-09-05 04:07:06 -04:00
parent 701a8a6841
commit 1a35d515d9
6 changed files with 173 additions and 26 deletions
+14 -3
View File
@@ -41,6 +41,7 @@ from polygateway.types import (
Effort,
LLMResponse,
TelemetryStatus,
coerce_effort,
validate_caller_dimensions,
validate_request_overlay,
)
@@ -315,7 +316,7 @@ class GatewayClient:
structured: type[BaseModel] | Literal["json"] | None = None,
stream: bool = True,
overlay: Mapping[str, Any] | None = None,
reasoning_effort: Effort | None = None,
reasoning_effort: Effort | str | None = None,
tenant_id: str | None = None,
meta: Mapping[str, Any] | None = None,
) -> LLMResponse:
@@ -327,7 +328,8 @@ class GatewayClient:
`reasoning_effort` 是本次调用的推理档位,优先级高于源级 `REASONING_EFFORT`
与 `ENABLE_THINKING`(设计 §4.2)。`None` 是**不表态**(随源级配置),与
`Effort.NONE`("要求不推理")严格区分。
`Effort.NONE`("要求不推理")严格区分。裸字符串(`"low"`)也收,在此归一成
`Effort`,非法值当场 `ValueError`——与 `SourceConfig` 那条装配路同口径。
`tenant_id` 与 `meta` 是调用方自定义维度,只进遥测、**不进缓存 key**
(租户隔离由 `cache_namespace` 负责,ARCH §7.5);前者享有真实列待遇
@@ -348,6 +350,15 @@ class GatewayClient:
dimension_tenant_id, dimensions = validate_caller_dimensions(
tenant_id, meta, origin="chat(tenant_id=..., meta=...)"
)
# 同样必须在洋葱之外归一: 档位一路要被 `is Effort.NONE` 身份比较,裸字符串
# 进去会在 transport 的错误路径上抛 `AttributeError`——那不属错误四分类,
# 会穿透 `except ThinkingUnsupportedError` 与 RetryMW 的分类捕获(库铁律
# 「错误分类驱动」)。归一失败是调用方编程错误,抛裸 ValueError 不进洋葱
effort = (
None
if reasoning_effort is None
else coerce_effort(reasoning_effort, origin="chat(reasoning_effort=...)")
)
request = ChatRequest(
messages=messages,
session_id=session_id,
@@ -358,7 +369,7 @@ class GatewayClient:
stream=stream,
overlay=sampling,
sampling=sampling,
reasoning_effort=reasoning_effort,
reasoning_effort=effort,
tenant_id=dimension_tenant_id,
meta=dimensions,
)