feat: model the thinking switch as shape plus capability (issue #5)
enable_thinking=False was a no-op for minimax and openai sources: both profiles had empty dicts on each side, so the payload update injected nothing while the caller believed reasoning had been turned off. A downstream project was blocked on exactly this. The root cause is that an empty dict meant two different things -- "no injection needed" and "we do not know how this provider spells it" -- and that a provider-level table cannot express what turned out to be a per-model property. Live testing showed MiniMax-M3 can disable reasoning via reasoning_effort while M2.7 and M2.5 cannot be disabled at all, which two external registries independently confirm. So the shape stays at provider level and a capability table joins it at model level. Unknown, unsupported and no-opinion are now three distinct values, and resolve_thinking is the single place they meet: it raises at assembly time when a model cannot honour the request, warns and injects for unregistered models, and injects silently otherwise. Every registered capability carries the evidence it was derived from. enable_thinking also joins the cache fingerprint, since it now really does change the request body.
This commit is contained in:
@@ -21,7 +21,13 @@ from polygateway.errors import (
|
||||
SourceDeadError,
|
||||
TransientError,
|
||||
)
|
||||
from polygateway.providers import ProviderProfile, get_provider
|
||||
from polygateway.providers import (
|
||||
ProviderProfile,
|
||||
ThinkingCapability,
|
||||
get_capability,
|
||||
get_provider,
|
||||
resolve_thinking,
|
||||
)
|
||||
from polygateway.streaming import StreamLivenessTimeout, stream_with_liveness_timeouts
|
||||
from polygateway.types import EmbeddingTransportResult, SourceConfig, TransportResult
|
||||
|
||||
@@ -284,9 +290,11 @@ class OpenAICompatTransport:
|
||||
self,
|
||||
*,
|
||||
registry: Mapping[str, ProviderProfile] | None = None,
|
||||
capabilities: Mapping[str, ThinkingCapability] | None = None,
|
||||
client_factory: Callable[[SourceConfig], httpx.AsyncClient] | None = None,
|
||||
) -> None:
|
||||
self._registry = registry
|
||||
self._capabilities = capabilities
|
||||
self._client_factory = client_factory or _default_client_factory
|
||||
self._clients: dict[str, httpx.AsyncClient] = {}
|
||||
|
||||
@@ -309,10 +317,12 @@ class OpenAICompatTransport:
|
||||
payload: dict[str, Any] = {"model": source.model, "messages": messages, "stream": stream}
|
||||
if stream:
|
||||
payload["stream_options"] = {"include_usage": True} # 强制 usage 帧(三项目同款)
|
||||
if source.enable_thinking is True:
|
||||
payload.update(profile.thinking_on)
|
||||
elif source.enable_thinking is False:
|
||||
payload.update(profile.thinking_off)
|
||||
# 形态(provider 级)与能力(model 级)在此相遇;不可满足时 ValueError,
|
||||
# 由 complete() 翻译为四分类之一(issue #5)
|
||||
capability = get_capability(source.model, table=self._capabilities)
|
||||
payload.update(
|
||||
resolve_thinking(profile, capability, source.enable_thinking, model=source.model)
|
||||
)
|
||||
# 顺序即优先级(issue #4 设计决策 A): 配置级 extra_body 在前,调用级
|
||||
# overlay(含结构化注入)在后覆盖之。两行不可调换
|
||||
payload.update(source.extra_body)
|
||||
@@ -330,9 +340,17 @@ class OpenAICompatTransport:
|
||||
) -> TransportResult:
|
||||
"""一次原始调用;HTTP/线路/流式异常按 ARCH §6.2 翻译为领域错误。"""
|
||||
profile = get_provider(source.provider, registry=self._registry)
|
||||
payload = self._build_payload(
|
||||
messages=messages, source=source, profile=profile, stream=stream, overlay=overlay
|
||||
)
|
||||
try:
|
||||
payload = self._build_payload(
|
||||
messages=messages, source=source, profile=profile, stream=stream, overlay=overlay
|
||||
)
|
||||
except ValueError as exc:
|
||||
# 推理开关不可满足是**请求本身**的问题: 换源重试都救不了它
|
||||
raise RequestRejectedError(
|
||||
f"{source.name} 推理开关无法满足: {exc}",
|
||||
source_name=source.name,
|
||||
operation="chat",
|
||||
) from exc
|
||||
url = source.base_url.rstrip("/") + "/chat/completions"
|
||||
client = self._client_for(source)
|
||||
ctx: dict[str, Any] = {"source_name": source.name, "operation": "chat"}
|
||||
|
||||
Reference in New Issue
Block a user