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:
parent
4ce940e590
commit
f1dd9ea6a2
@ -3,6 +3,54 @@ import { stripReasoningTagsFromText } from "../shared/text/reasoning-tags.js";
|
|||||||
import { sanitizeUserFacingText } from "./pi-embedded-helpers.js";
|
import { sanitizeUserFacingText } from "./pi-embedded-helpers.js";
|
||||||
import { formatToolDetail, resolveToolDisplay } from "./tool-display.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.
|
* Strip malformed Minimax tool invocations that leak into text content.
|
||||||
* Minimax sometimes embeds tool calls as XML in text blocks instead of
|
* Minimax sometimes embeds tool calls as XML in text blocks instead of
|
||||||
@ -181,8 +229,10 @@ export function extractAssistantText(msg: AssistantMessage): string {
|
|||||||
? msg.content
|
? msg.content
|
||||||
.filter(isTextBlock)
|
.filter(isTextBlock)
|
||||||
.map((c) =>
|
.map((c) =>
|
||||||
stripThinkingTagsFromText(
|
stripGlmNumberedReasoning(
|
||||||
stripDowngradedToolCallText(stripMinimaxToolCallXml(c.text)),
|
stripThinkingTagsFromText(
|
||||||
|
stripDowngradedToolCallText(stripMinimaxToolCallXml(c.text)),
|
||||||
|
),
|
||||||
).trim(),
|
).trim(),
|
||||||
)
|
)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user