feat(model): 按 provider 形状独立适配推理开关 + per-persona 提供商链真接线(裁决 03.4.2/08.4.1/03.4.1 余项)
- 新增 reasoning_shape.go:**每个 provider 形状一个 resolver,只发自己形状的字段**
· deepseek:thinking{type} + reasoning_effort(依据 DevDocs/reference/deepseek 官方本地副本)
· vllm:chat_template_kwargs.enable_thinking · dashscope:顶层 enable_thinking(**只允许显式声明**)
· openai:仅 reasoning_effort · anthropic:自己的 thinking{budget_tokens}(未改)
· 未知 kind → WARN 且一个字段都不发(不许静默失效)
- 形状载体:models.json 组级 `reasoning.kind`(可热更新,未声明则不写该键)+ env `LLM_REASONING_KIND`
- 红线:`effort:none` 在只有布尔开关的形状上翻成**显式关思考**,绝不降成任何强度档
- 03.4.1 余项:`Request.PersonaID` + `RuntimeLLM.ForPersona(pid)` 视图;
每 persona 协议一条链并消费 `RouteProviderForPersona`;`agent/request.go` 非流式路径同一钩子
- **生产装配(主会话补)**:默认 agent 与 personaAgentPool 都改用 `ForPersona(pid)` 视图——
否则角色级模型/提供商路由在生产里永不被选中(形同虚设)
- 复核:build/vet 绿 + model/agent/config/cmd 包测试绿;5 条变异反证先红后还原
(其中一条「红但没钉住」已改用例:通道读取加 2s 超时,避免整包 panic 掩盖信号)
- 未落:DashScope 的 auto 推断(缺官方依据,只允许显式声明);Qwen `/think` 前导消息
(design/01 §2.1 与 design/11 §3 口径冲突,停手报告)
This commit is contained in:
1 parent
71fefe5fc3
commit
8890c30bb5
17 files changed
+1496
-134
No files matched your search
@@ -25,6 +25,15 @@ type openAIChatRequest struct {
|
||||
// only after validation against the detected vendor's whitelist (design/04
|
||||
// §3.2); an unrecognized value is dropped, never turned into a different tier.
|
||||
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
||||
// ChatTemplateKwargs carries the vLLM-style thinking switch
|
||||
// (chat_template_kwargs.enable_thinking, design/04 §3.2 Qwen3 行). It is sent
|
||||
// only to an endpoint whose provider declared reasoning.kind=vllm.
|
||||
ChatTemplateKwargs *openAIChatTemplateKwargs `json:"chat_template_kwargs,omitempty"`
|
||||
// EnableThinking carries the DashScope compatible-mode thinking switch (the
|
||||
// top-level enable_thinking). It is sent only to an endpoint whose provider
|
||||
// declared reasoning.kind=dashscope — the shape is never inferred, because a
|
||||
// wrong shape 400s the request.
|
||||
EnableThinking *bool `json:"enable_thinking,omitempty"`
|
||||
}
|
||||
|
||||
// openAIThinking is the wire form of DeepSeek's thinking switch: enabled or
|
||||
@@ -33,6 +42,12 @@ type openAIThinking struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// openAIChatTemplateKwargs is the wire form of the vLLM (Qwen3) thinking switch:
|
||||
// the boolean lives nested under chat_template_kwargs.
|
||||
type openAIChatTemplateKwargs struct {
|
||||
EnableThinking *bool `json:"enable_thinking,omitempty"`
|
||||
}
|
||||
|
||||
// openAIStreamOpts asks the provider to include usage in the final stream chunk.
|
||||
type openAIStreamOpts struct {
|
||||
IncludeUsage bool `json:"include_usage"`
|
||||
@@ -85,20 +100,23 @@ func (a *OpenAIChatAdapter) buildRequest(req Request, stream bool) ([]byte, erro
|
||||
// The vendor behind an OpenAI-compatible endpoint decides which
|
||||
// reasoning_effort tiers are legal and whether it has a thinking switch;
|
||||
// classify it from the resolved model (and the base URL as a fallback) before
|
||||
// touching the request (design/04 §3.2).
|
||||
// touching the request. The endpoint's configured shape (models.json
|
||||
// reasoning.kind, 裁决 03.4.2/08.4.1) wins over that classification, because
|
||||
// the same wire protocol carries different switch shapes (vLLM vs DashScope).
|
||||
vendor := reasoningVendorFor(req.Model, a.baseURL)
|
||||
effort := resolveReasoningEffort(a.log, vendor, req.ReasoningEffort, req.Model)
|
||||
thinking, effort := resolveOpenAIReasoning(a.log, vendor, req.Model, req.Thinking, effort)
|
||||
reportForcedToolChoiceWithThinking(a.log, req.Model, req.ToolChoice, openAIThinkingOn(thinking, effort))
|
||||
plan := resolveOpenAIReasoning(a.log, a.reasoningKind, vendor, req.Model, req.Thinking, req.ReasoningEffort)
|
||||
reportForcedToolChoiceWithThinking(a.log, plan.provider, req.Model, req.ToolChoice, plan.thinkingOn())
|
||||
wire := openAIChatRequest{
|
||||
Model: req.Model,
|
||||
Messages: msgs,
|
||||
Temperature: req.Temperature,
|
||||
MaxTokens: req.MaxTokens,
|
||||
Stream: stream,
|
||||
Tools: buildTools(req.Tools),
|
||||
Thinking: thinking,
|
||||
ReasoningEffort: effort,
|
||||
Model: req.Model,
|
||||
Messages: msgs,
|
||||
Temperature: req.Temperature,
|
||||
MaxTokens: req.MaxTokens,
|
||||
Stream: stream,
|
||||
Tools: buildTools(req.Tools),
|
||||
Thinking: plan.thinking,
|
||||
ReasoningEffort: plan.effort,
|
||||
ChatTemplateKwargs: plan.template,
|
||||
EnableThinking: plan.enable,
|
||||
}
|
||||
if stream {
|
||||
wire.StreamOptions = &openAIStreamOpts{IncludeUsage: true}
|
||||
|
||||
Reference in new issue
Block a user