mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-20 17:13:44 +00:00
🪢 fix: Preserve Response Identity and Branch During Resumable SSE Sync (#14788)
* fix(client): preserve resumable response identity Fixes #14787 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(client): align resumable sync regressions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(client): clarify resumable response ownership Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(client): preserve resumed regeneration ordering Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(client): cover missing resumed response row Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(client): preserve resume identity on page reload * fix(client): replace reassigned resume placeholder * fix(client): preserve content during response id handoff * fix(client): limit resume placeholder handoff * fix(client): preserve resume display metadata * fix(client): reconcile resume metadata in one pass * fix(client): reconcile preliminary resume user * fix(client): restore regenerated branch on early abort * test(client): cover external regeneration resume * fix(client): preserve regeneration history on errors * fix(client): replace reused regeneration error ids * fix(client): preserve exact-id regeneration rollback --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
b399ad8370
commit
9f8d71a3c5
16 changed files with 822 additions and 81 deletions
|
|
@ -1080,6 +1080,48 @@ describe('ResumableAgentController resume metadata', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('records regeneration ownership for exact-ID resume reconstruction', async () => {
|
||||
const conversationId = 'conversation-123';
|
||||
const initializeClient = jest.fn().mockRejectedValue(new Error('stop before tool loading'));
|
||||
const req = {
|
||||
user: { id: 'user-123' },
|
||||
body: {
|
||||
text: 'Regenerate the edited response.',
|
||||
messageId: 'user-message',
|
||||
parentMessageId: 'parent-message',
|
||||
responseMessageId: 'edited-response',
|
||||
isRegenerate: true,
|
||||
conversationId,
|
||||
endpointOption: {
|
||||
endpoint: 'agents',
|
||||
modelOptions: { model: 'gpt-4.1' },
|
||||
},
|
||||
},
|
||||
config: {},
|
||||
};
|
||||
const res = {
|
||||
headersSent: true,
|
||||
json: jest.fn(() => {
|
||||
res.headersSent = true;
|
||||
}),
|
||||
status: jest.fn(() => res),
|
||||
};
|
||||
|
||||
await AgentController(req, res, jest.fn(), initializeClient, null);
|
||||
|
||||
expect(mockGenerationJobManager.createJob).toHaveBeenCalledWith(
|
||||
conversationId,
|
||||
'user-123',
|
||||
conversationId,
|
||||
expect.objectContaining({
|
||||
initialMetadata: expect.objectContaining({
|
||||
responseMessageId: 'edited-response',
|
||||
isRegenerate: true,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('falls back to the model spec preset endpoint when no icon URL is configured', async () => {
|
||||
const conversationId = 'conversation-123';
|
||||
const initializeClient = jest.fn().mockRejectedValue(new Error('stop before tool loading'));
|
||||
|
|
|
|||
|
|
@ -1050,6 +1050,7 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit
|
|||
// Persist temporary-chat state so a HITL resume keeps the resumed response
|
||||
// non-persisted instead of trusting the resume request to re-send the flag.
|
||||
isTemporary: req.body?.isTemporary,
|
||||
...(isRegenerate && { isRegenerate: true }),
|
||||
...(scheduleId
|
||||
? {
|
||||
scheduleId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue