From 35d3f9b89d563f53e236984e9e4c3de293d84049 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Tue, 7 Jul 2026 15:52:54 +0200 Subject: [PATCH] fix(ai-chat): sort message parts by orderIndex on reload (#22629) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem When an AI chat conversation is reloaded from the DB (page refresh or initial load), message parts are returned without guaranteed ordering. The renderer groups reasoning/thinking steps only when they are **contiguous** — so if `reasoning` parts land after the `text` part, thinking blocks appear below the final answer, and can appear duplicated or split. This only surfaces with reasoning models (OpenRouter, etc.) because those produce multiple reasoning/tool/text parts per message, making ordering observable. Simple text-only messages aren't affected. ## Root cause `AgentChatService.getMessagesForThread()` fetches `parts` via a TypeORM relation with no ORDER BY on `orderIndex`. The DB can return parts in any order. `mapDBMessagesToUIMessages()` then calls `dbMessage.parts.map(...)` directly, without sorting. A parallel server-side utility (`mapDBPartsToUIMessageParts.ts`) already sorts by `orderIndex` — this fix makes the frontend fetch path consistent with it. ## Fix Sort parts by `orderIndex` before mapping to UI parts in `mapDBMessagesToUIMessages.ts`. ```ts parts: [...dbMessage.parts] .sort((a, b) => a.orderIndex - b.orderIndex) .map(mapDBPartToUIMessagePart), ``` `orderIndex` is already included in `GetChatMessagesDocument` — no schema or query changes needed. ## Test 1. Open Ask AI with a reasoning model (e.g. via OpenRouter). 2. Run a prompt that produces thinking steps. 3. Hard-refresh the page. 4. Thinking blocks should appear collapsed above the final answer, not below it. Closes #22386 Review in cubic --- .../src/modules/ai/utils/mapDBMessagesToUIMessages.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/twenty-front/src/modules/ai/utils/mapDBMessagesToUIMessages.ts b/packages/twenty-front/src/modules/ai/utils/mapDBMessagesToUIMessages.ts index c568998da0..bb1cbe227a 100644 --- a/packages/twenty-front/src/modules/ai/utils/mapDBMessagesToUIMessages.ts +++ b/packages/twenty-front/src/modules/ai/utils/mapDBMessagesToUIMessages.ts @@ -9,7 +9,9 @@ export const mapDBMessagesToUIMessages = ( id: dbMessage.id, role: dbMessage.role as ExtendedUIMessage['role'], status: dbMessage.status as 'queued' | 'sent', - parts: dbMessage.parts.map(mapDBPartToUIMessagePart), + parts: [...dbMessage.parts] + .sort((a, b) => a.orderIndex - b.orderIndex) + .map(mapDBPartToUIMessagePart), metadata: { createdAt: dbMessage.createdAt, },