fix(ai-chat): sort message parts by orderIndex on reload (#22629)
## 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 <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22629?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -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,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user