From 2940264045858dd80132531d410d5f590fb2eb2b Mon Sep 17 00:00:00 2001 From: hobostay Date: Thu, 12 Mar 2026 17:05:32 +0800 Subject: [PATCH] fix: strip leaked Thinking/Final tags from OpenAI-compatible responses Some OpenAI-compatible providers return chain-of-thought content mixed into the response field using XML-style tags. This internal reasoning text leaks to end users. This commit adds a sanitizer that: 1. Handles escaped Unicode forms (\u003c/\u003e) 2. Removes thinking blocks entirely (tags + content) 3. Removes tag wrappers while preserving the final content 4. Cleans up leftover whitespace-only lines Uses direct regex calls instead of pattern array to avoid linter issues. Fixes #1235 Co-Authored-By: Claude Opus 4.6 --- pkg/providers/openai_compat/provider.go | 47 ++++++++++++++----------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index f887202f3..4721f9c03 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -526,7 +526,7 @@ func supportsPromptCacheKey(apiBase string) bool { // stripThinkingTags removes chain-of-thought tags from content that may be // leaked by some OpenAI-compatible providers (e.g., MiniMax-style responses). // Handles both normal tags and escaped Unicode forms. -// Tags removed: , , , , +// Tags removed: , , , , func stripThinkingTags(content string) string { if content == "" { return content @@ -539,26 +539,33 @@ func stripThinkingTags(content string) string { content = strings.ReplaceAll(content, `\\u003c`, "<") content = strings.ReplaceAll(content, `\\u003e`, ">") - // Pattern to match thinking/reasoning tags (with optional inner content) - // Matches: content, , - patterns := []string{ - `(?i)]*>.*?`, // ... - `(?i)]*>.*?`, // ... - `(?i)]*>.*?`, // ... - `(?i)]*>.*?`, // ... - `(?i)*>(?:/>|>.*?)`, // or ... (remove tag, keep content) - } + // Remove tags and content (case-insensitive, multiline) + content = regexp.MustCompile(`(?is)]*>.*? - for _, pattern := range patterns { - re := regexp.MustCompile(pattern) - if strings.HasPrefix(pattern, `(?i) tags, remove the tags but keep the inner content - content = re.ReplaceAllString(content, "$1") - } else { - // For thinking tags, remove both tags and content - content = re.ReplaceAllString(content, "") - } - } + + + + + + + + + +`).ReplaceAllString(content, "") + + // Remove tags and content + content = regexp.MustCompile(`(?is)]*>.*?`).ReplaceAllString(content, "") + + // Remove tags and content + content = regexp.MustCompile(`(?is)]*>.*?`).ReplaceAllString(content, "") + + // Remove tags and content + content = regexp.MustCompile(`(?is)]*>.*?`).ReplaceAllString(content, "") + + // For tags, remove the tags but keep the inner content + // Handle both and content + content = regexp.MustCompile(`(?i)]*/>`).ReplaceAllString(content, "") + content = regexp.MustCompile(`(?is)]*>(.*?)`).ReplaceAllString(content, "$1") // Clean up any whitespace-only lines left after removing blocks lines := strings.Split(content, "\n")