refactor(aibox): make profile reasoning_enabled a constructor param (M3)

Profile-level reasoning_enabled was attached as an instance attribute
after construction only when a profile matched; the base class never
declared it and consumers probed with double getattr. Any code that
accidentally set client.reasoning_enabled would silently override the
global switch.

The flag is now an explicit LLMClient constructor parameter with
three-state semantics: True/False (profile-level explicit, or the
global api.reasoning_enabled value concretized when the profile omits
the field) and None (no profile -> consumer falls back to global).
create_llm_client passes it at construction and no longer mutates the
client afterwards; DeepSeekChat._reasoning_enabled simplifies to a
single getattr probe that still tolerates clients without the attr.

Tests: TestM3ReasoningConstructor (4 cases) covering profile True,
profile-omitted -> global concretized, base default None, and the
None -> global fallback at the consumer; one legacy assertion updated
to the new contract (attribute exists, value None). Full suite 299
passed.
This commit is contained in:
qing committed 2026-08-17 23:58:45 +08:00
1 parent 5f3ca9a246
commit a583f44d5d
4 files changed
+71 -17

No files matched your search

+4 -4
View File
@@ -976,10 +976,10 @@ class DeepSeekChat:
pass
def _reasoning_enabled(self) -> bool:
"""思考模式开关:客户端带档案级 reasoning_enabled 属性时优先(多模型档案),否则用全局配置"""
return getattr(self.client, "reasoning_enabled", None) \
if getattr(self.client, "reasoning_enabled", None) is not None \
else constants.REASONING_ENABLED
"""思考模式开关:client.reasoning_enabled 非 None 时优先(档案级构造参数),
否则回退全局配置;getattr 兼容未实现该契约的自定义 client"""
val = getattr(self.client, "reasoning_enabled", None)
return val if val is not None else constants.REASONING_ENABLED
def _is_cancelled(self) -> bool:
"""检查是否已被取消。"""