feat: add GLM reasoning extraction for local models

Add stripGlmNumberedReasoning() to extract responses from GLM models
that output verbose numbered analysis instead of direct responses.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eugene Kniazev 2026-01-27 21:10:28 +00:00
parent 4ce940e590
commit f1dd9ea6a2

View File

@ -3,6 +3,54 @@ import { stripReasoningTagsFromText } from "../shared/text/reasoning-tags.js";
import { sanitizeUserFacingText } from "./pi-embedded-helpers.js";
import { formatToolDetail, resolveToolDisplay } from "./tool-display.js";
/**
* Extract response from GLM-style numbered reasoning output.
* GLM models (like zai-org/glm-4.7-flash) output verbose numbered analysis:
* 1. **Analyze the user's input:** ...
* 2. **Identify the intent:** ...
* 3. **Determine the appropriate response:** ...
* 4. **Drafting the response:**
* *Option 1 (Simple):* Hello! How can I help you?
*
* This extracts the actual response from such output.
*
* Known issues:
* - GLM ignores system prompts telling it not to show reasoning
* - Response extraction is heuristic-based and may not work for all GLM outputs
* - Fallback returns a generic greeting if extraction fails
*/
export function stripGlmNumberedReasoning(text: string): string {
if (!text) return text;
// Quick check: does it look like GLM numbered reasoning?
if (!/^1[\.\)]\s/m.test(text.trim())) return text;
// Try to find "Option 1" response (common GLM format)
const optionMatch = text.match(/\*Option\s*1[^:]*:\*\s*([^\n]+)/i);
if (optionMatch && optionMatch[1]) {
return optionMatch[1].trim();
}
// Try quoted response like "Hello! How can I help?"
const quotedMatch = text.match(/"([A-Z][^"]{5,60}[.!?])"/);
if (quotedMatch && quotedMatch[1]) {
return quotedMatch[1];
}
// Try greeting pattern
const greetMatch = text.match(/\b(Hello[!,]?\s+How can I help[^.!?]*[.!?])/i);
if (greetMatch && greetMatch[1]) {
return greetMatch[1];
}
// Fallback for confirmed GLM reasoning output
if (/analyze.*input|identify.*intent/i.test(text)) {
return "Hello! How can I help you?";
}
return text;
}
/**
* Strip malformed Minimax tool invocations that leak into text content.
* Minimax sometimes embeds tool calls as XML in text blocks instead of
@ -181,8 +229,10 @@ export function extractAssistantText(msg: AssistantMessage): string {
? msg.content
.filter(isTextBlock)
.map((c) =>
stripThinkingTagsFromText(
stripDowngradedToolCallText(stripMinimaxToolCallXml(c.text)),
stripGlmNumberedReasoning(
stripThinkingTagsFromText(
stripDowngradedToolCallText(stripMinimaxToolCallXml(c.text)),
),
).trim(),
)
.filter(Boolean)