From f6300c72b790c6017a08ceacc425f9863907493d Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:21:50 +0800 Subject: [PATCH] fix(runtime): validate thinking config in iflow and qwen --- internal/runtime/executor/iflow_executor.go | 14 ++++++++++++-- internal/runtime/executor/qwen_executor.go | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/internal/runtime/executor/iflow_executor.go b/internal/runtime/executor/iflow_executor.go index a445e47d..d1a69812 100644 --- a/internal/runtime/executor/iflow_executor.go +++ b/internal/runtime/executor/iflow_executor.go @@ -58,9 +58,14 @@ func (e *IFlowExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re to := sdktranslator.FromString("openai") body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false) body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort") - if upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata); upstreamModel != "" { + upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata) + if upstreamModel != "" { body, _ = sjson.SetBytes(body, "model", upstreamModel) } + body = normalizeThinkingConfig(body, upstreamModel) + if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil { + return resp, errValidate + } body = applyPayloadConfig(e.cfg, req.Model, body) endpoint := strings.TrimSuffix(baseURL, "/") + iflowDefaultEndpoint @@ -144,9 +149,14 @@ func (e *IFlowExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true) body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort") - if upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata); upstreamModel != "" { + upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata) + if upstreamModel != "" { body, _ = sjson.SetBytes(body, "model", upstreamModel) } + body = normalizeThinkingConfig(body, upstreamModel) + if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil { + return nil, errValidate + } // Ensure tools array exists to avoid provider quirks similar to Qwen's behaviour. toolsResult := gjson.GetBytes(body, "tools") if toolsResult.Exists() && toolsResult.IsArray() && len(toolsResult.Array()) == 0 { diff --git a/internal/runtime/executor/qwen_executor.go b/internal/runtime/executor/qwen_executor.go index d25ed5da..2b8d0e50 100644 --- a/internal/runtime/executor/qwen_executor.go +++ b/internal/runtime/executor/qwen_executor.go @@ -52,9 +52,14 @@ func (e *QwenExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req to := sdktranslator.FromString("openai") body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false) body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort") - if upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata); upstreamModel != "" { + upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata) + if upstreamModel != "" { body, _ = sjson.SetBytes(body, "model", upstreamModel) } + body = normalizeThinkingConfig(body, upstreamModel) + if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil { + return resp, errValidate + } body = applyPayloadConfig(e.cfg, req.Model, body) url := strings.TrimSuffix(baseURL, "/") + "/chat/completions" @@ -127,9 +132,14 @@ func (e *QwenExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Aut body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true) body = applyReasoningEffortMetadata(body, req.Metadata, req.Model, "reasoning_effort") - if upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata); upstreamModel != "" { + upstreamModel := util.ResolveOriginalModel(req.Model, req.Metadata) + if upstreamModel != "" { body, _ = sjson.SetBytes(body, "model", upstreamModel) } + body = normalizeThinkingConfig(body, upstreamModel) + if errValidate := validateThinkingConfig(body, upstreamModel); errValidate != nil { + return nil, errValidate + } toolsResult := gjson.GetBytes(body, "tools") // I'm addressing the Qwen3 "poisoning" issue, which is caused by the model needing a tool to be defined. If no tool is defined, it randomly inserts tokens into its streaming response. // This will have no real consequences. It's just to scare Qwen3.