fix(reasoning-tags): strip MiniMax mm: tags on silent-reply and strea… · openclaw/openclaw@f8e7d66
Alix-007
·
2026-06-17
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -213,7 +213,7 @@ function resolveAssistantTextChunk(params: {
|
213 | 213 | return ""; |
214 | 214 | } |
215 | 215 | |
216 | | -const REASONING_TAG_RE = /<\s*\/?\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b/i; |
| 216 | +const REASONING_TAG_RE = /<\s*\/?\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\b/i; |
217 | 217 | |
218 | 218 | function resolveStreamVisibleText(params: { |
219 | 219 | previousRawText: string; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -966,6 +966,20 @@ describe("subscribeEmbeddedAgentSession", () => {
|
966 | 966 | expect(payloads[0]?.delta).toBe("Visible answer"); |
967 | 967 | }); |
968 | 968 | |
| 969 | +it("replaces leaked MiniMax reasoning when its orphan close arrives in a later delta", () => { |
| 970 | +const { emit, onAgentEvent } = createAgentEventHarness(); |
| 971 | + |
| 972 | +emit({ type: "message_start", message: { role: "assistant" } }); |
| 973 | +emitAssistantTextDelta(emit, "private chain"); |
| 974 | +emitAssistantTextDelta(emit, "</mm:think>Visible answer"); |
| 975 | + |
| 976 | +const payloads = extractAgentEventPayloads(onAgentEvent.mock.calls); |
| 977 | +expect(payloads).toMatchObject([ |
| 978 | +{ text: "private chain", delta: "private chain" }, |
| 979 | +{ text: "Visible answer", delta: "", replace: true }, |
| 980 | +]); |
| 981 | +}); |
| 982 | + |
969 | 983 | it("replaces malformed streamed reasoning when orphan close tags split across deltas", () => { |
970 | 984 | const { emit, onAgentEvent } = createAgentEventHarness(); |
971 | 985 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -128,6 +128,13 @@ describe("isSilentReplyPayloadText", () => {
|
128 | 128 | expect(isSilentReplyPayloadText("<think>I will stay quiet here.NO_REPLY")).toBe(true); |
129 | 129 | }); |
130 | 130 | |
| 131 | +it.each([ |
| 132 | +["closed", "<mm:think>internal reasoning</mm:think>\nNO_REPLY"], |
| 133 | +["open", "<mm:think>internal reasoning\nNO_REPLY"], |
| 134 | +])("returns true for %s MiniMax reasoning followed by NO_REPLY", (_kind, text) => { |
| 135 | +expect(isSilentReplyPayloadText(text)).toBe(true); |
| 136 | +}); |
| 137 | + |
131 | 138 | it("keeps substantive replies that also contain a trailing NO_REPLY token", () => { |
132 | 139 | expect(isSilentReplyPayloadText("Here is a helpful response.\n\nNO_REPLY")).toBe(false); |
133 | 140 | expect( |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -86,9 +86,9 @@ function isSilentReplyEnvelopeText(
|
86 | 86 | } |
87 | 87 | |
88 | 88 | const taggedReasoningPrefixRe = |
89 | | -/^\s*<\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>[\s\S]*?<\s*\/\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>\s*/i; |
| 89 | +/^\s*<\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>[\s\S]*?<\s*\/\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\s*>\s*/i; |
90 | 90 | const openReasoningPrefixRe = |
91 | | -/^\s*<\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/i; |
| 91 | +/^\s*<\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/i; |
92 | 92 | const plainReasoningPrefixRe = /^\s*(?:think(?:ing)?|thought|analysis|reasoning)\s*:?\s*\r?\n/i; |
93 | 93 | |
94 | 94 | function stripLeadingReasoningBlocks(text: string): string { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。