Skip to content

Commit 151af5b

Browse files
capitanfeederedelauna
authored andcommitted
Fold text into last tool message to preserve reasoning continuity
1 parent 31cde34 commit 151af5b

2 files changed

Lines changed: 31 additions & 10 deletions

File tree

src/api/providers/__tests__/mimo.spec.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ describe("MimoHandler", () => {
376376
expect(result[0].content).toBe("(empty)")
377377
})
378378

379-
it("should separate tool_results from text in user messages", () => {
379+
it("should merge text into last tool message when both exist in same turn", () => {
380380
const messages: Anthropic.Messages.MessageParam[] = [
381381
{
382382
role: "user",
@@ -386,15 +386,28 @@ describe("MimoHandler", () => {
386386
tool_use_id: "call_1",
387387
content: "result",
388388
},
389-
{ type: "text" as const, text: "Here are the results" },
389+
{ type: "text" as const, text: "<environment_details>..." },
390390
],
391391
},
392392
]
393393
const result = (handler as any).convertMessagesForMiMo(messages)
394-
expect(result).toHaveLength(2)
394+
expect(result).toHaveLength(1)
395395
expect(result[0].role).toBe("tool")
396-
expect(result[1].role).toBe("user")
397-
expect(result[1].content).toBe("Here are the results")
396+
expect(result[0].content).toContain("result")
397+
expect(result[0].content).toContain("<environment_details>...")
398+
})
399+
400+
it("should keep text as separate user message when no tool_results present", () => {
401+
const messages: Anthropic.Messages.MessageParam[] = [
402+
{
403+
role: "user",
404+
content: [{ type: "text" as const, text: "Hello" }],
405+
},
406+
]
407+
const result = (handler as any).convertMessagesForMiMo(messages)
408+
expect(result).toHaveLength(1)
409+
expect(result[0].role).toBe("user")
410+
expect(result[0].content).toBe("Hello")
398411
})
399412

400413
it("should handle user message with string content", () => {

src/api/providers/mimo.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ export class MimoHandler extends OpenAiHandler {
181181
} else if (block.type === "text") {
182182
textBlocks.push(block.text)
183183
} else if (block.type === "image") {
184-
// Convert Anthropic image block to OpenAI image_url format
185184
const src = (block as any).source
186185
if (src?.type === "base64" && src?.media_type) {
187186
mediaParts.push({
@@ -195,10 +194,9 @@ export class MimoHandler extends OpenAiHandler {
195194
})
196195
}
197196
}
198-
// audio/video blocks are not supported in OpenAI chat format — skip silently
199197
}
200198

201-
// Add tool results as role:"tool" messages (MiMo supports this)
199+
// Add tool results as role:"tool" messages
202200
for (const tr of toolResults) {
203201
let content: string
204202
if (typeof tr.content === "string") {
@@ -216,8 +214,18 @@ export class MimoHandler extends OpenAiHandler {
216214
})
217215
}
218216

219-
// Build user message content — plain string or multimodal array
220-
if (mediaParts.length > 0) {
217+
// When both tool_results and text exist in the same user turn (happens
218+
// during resume/delegate flows where <environment_details> is appended),
219+
// fold the text into the last tool message instead of creating a separate
220+
// user message. A fresh role:"user" after tool results drops reasoning
221+
// continuity for thinking models.
222+
if (toolResults.length > 0 && textBlocks.length > 0 && mediaParts.length === 0) {
223+
const lastToolIdx = converted.length - 1
224+
const lastTool = converted[lastToolIdx] as any
225+
if (lastTool?.role === "tool") {
226+
lastTool.content += "\n" + textBlocks.join("\n")
227+
}
228+
} else if (mediaParts.length > 0) {
221229
const content: any[] = []
222230
if (textBlocks.length > 0) {
223231
content.push({ type: "text", text: textBlocks.join("\n") })

0 commit comments

Comments
 (0)