fix: persist modelIdx so duplicate side-by-side models don't collapse on reload (#26980)

When the same model is selected multiple times in a side-by-side chat,
each response is created with a distinct modelIdx (0,1,2,3) that
identifies its column. The backend now owns message persistence, but it
built the assistant placeholders without modelIdx, so the field was
never saved. On reload MultiResponseMessages groups responses by
modelIdx and falls back to grouping by model id when modelIdx is
missing; with duplicate models that fallback lumps every response into
each column, so all columns render the first response (and show a bogus
'1/N' pager).

Send modelIdx with each message_ids entry from the frontend and persist
it on the assistant placeholders in both the new-chat and existing-chat
paths. The message_ids list is now forwarded for every send (not just
multi-model ones) so single-column regenerations in a duplicate-model
chat also keep their column identity across reloads.
This commit is contained in:
G30
2026-07-26 18:44:03 -04:00
committed by GitHub
parent 86bf927d08
commit 79695a1d14
2 changed files with 35 additions and 15 deletions
+21 -11
View File
@@ -1259,7 +1259,7 @@ async def chat_completion(
target_model_id = entry['model_id']
assistant_message_id = entry['message_id']
if assistant_message_id:
history_messages[assistant_message_id] = {
assistant_message = {
'id': assistant_message_id,
'parentId': user_message_id,
'childrenIds': [],
@@ -1269,6 +1269,11 @@ async def chat_completion(
'model': target_model_id,
'timestamp': int(time.time()),
}
# Preserve the side-by-side column index so duplicate
# models don't collapse into one another on reload.
if entry.get('modelIdx') is not None:
assistant_message['modelIdx'] = entry['modelIdx']
history_messages[assistant_message_id] = assistant_message
await Chats.insert_new_chat(
chat_id,
@@ -1481,19 +1486,24 @@ async def chat_completion(
target_model_id = entry['model_id']
assistant_message_id = entry['message_id']
if assistant_message_id:
assistant_message = {
'id': assistant_message_id,
'parentId': user_message_id,
'childrenIds': [],
'role': 'assistant',
'content': '',
'done': False,
'model': target_model_id,
'timestamp': int(time.time()),
}
# Preserve the side-by-side column index so duplicate
# models don't collapse into one another on reload.
if entry.get('modelIdx') is not None:
assistant_message['modelIdx'] = entry['modelIdx']
await Chats.upsert_message_to_chat_by_id_and_message_id(
chat_id,
assistant_message_id,
{
'id': assistant_message_id,
'parentId': user_message_id,
'childrenIds': [],
'role': 'assistant',
'content': '',
'done': False,
'model': target_model_id,
'timestamp': int(time.time()),
},
assistant_message,
)
await publish_event(
request,
+14 -4
View File
@@ -2777,9 +2777,11 @@
: selectedModels;
// Create response messages for each selected model
// Build message_ids list: [{model_id, message_id}, ...]
// Build message_ids list: [{model_id, message_id, modelIdx}, ...]
// Uses an array instead of a dict to support duplicate model IDs in side-by-side chat.
const messageIdsList: Array<{ model_id: string; message_id: string }> = [];
// modelIdx identifies each side-by-side column so the backend can persist it; without
// it, duplicate models collapse into one another when the chat is reloaded.
const messageIdsList: Array<{ model_id: string; message_id: string; modelIdx: number }> = [];
for (const [_modelIdx, modelId] of selectedModelIds.entries()) {
const model = $models.filter((m) => m.id === modelId).at(0);
@@ -2811,7 +2813,11 @@
}
responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`] = responseMessageId;
messageIdsList.push({ model_id: modelId, message_id: responseMessageId });
messageIdsList.push({
model_id: modelId,
message_id: responseMessageId,
modelIdx: modelIdx ? modelIdx : _modelIdx
});
}
}
history = history;
@@ -2890,7 +2896,11 @@
primaryResponseMessageId,
_chatId,
{
messageIdsList: selectedModelIds.length > 1 ? messageIdsList : undefined,
// Always forward the message_ids list (not just for multi-model sends) so the
// backend persists each response's modelIdx — including single-column
// regenerations in a duplicate-model chat, which would otherwise lose their
// column identity and collapse on reload.
messageIdsList: messageIdsList.length > 0 ? messageIdsList : undefined,
regenerationPrompt
}
);