mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
♻️ fix: Correct Message ID Assignment Logic (#8439)
* fix: Add `isRegenerate` flag to chat payload to avoid saving temporary response IDs * fix: Remove unused `isResubmission` flag * ci: Add tests for responseMessageId regeneration logic in BaseClient
This commit is contained in:
parent
170cc340d8
commit
e370a87ebe
8 changed files with 61 additions and 15 deletions
|
@ -197,6 +197,10 @@ class BaseClient {
|
|||
this.currentMessages[this.currentMessages.length - 1].messageId = head;
|
||||
}
|
||||
|
||||
if (opts.isRegenerate && responseMessageId.endsWith('_')) {
|
||||
responseMessageId = crypto.randomUUID();
|
||||
}
|
||||
|
||||
this.responseMessageId = responseMessageId;
|
||||
|
||||
return {
|
||||
|
|
|
@ -422,6 +422,46 @@ describe('BaseClient', () => {
|
|||
expect(response).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
test('should replace responseMessageId with new UUID when isRegenerate is true and messageId ends with underscore', async () => {
|
||||
const mockCrypto = require('crypto');
|
||||
const newUUID = 'new-uuid-1234';
|
||||
jest.spyOn(mockCrypto, 'randomUUID').mockReturnValue(newUUID);
|
||||
|
||||
const opts = {
|
||||
isRegenerate: true,
|
||||
responseMessageId: 'existing-message-id_',
|
||||
};
|
||||
|
||||
await TestClient.setMessageOptions(opts);
|
||||
|
||||
expect(TestClient.responseMessageId).toBe(newUUID);
|
||||
expect(TestClient.responseMessageId).not.toBe('existing-message-id_');
|
||||
|
||||
mockCrypto.randomUUID.mockRestore();
|
||||
});
|
||||
|
||||
test('should not replace responseMessageId when isRegenerate is false', async () => {
|
||||
const opts = {
|
||||
isRegenerate: false,
|
||||
responseMessageId: 'existing-message-id_',
|
||||
};
|
||||
|
||||
await TestClient.setMessageOptions(opts);
|
||||
|
||||
expect(TestClient.responseMessageId).toBe('existing-message-id_');
|
||||
});
|
||||
|
||||
test('should not replace responseMessageId when it does not end with underscore', async () => {
|
||||
const opts = {
|
||||
isRegenerate: true,
|
||||
responseMessageId: 'existing-message-id',
|
||||
};
|
||||
|
||||
await TestClient.setMessageOptions(opts);
|
||||
|
||||
expect(TestClient.responseMessageId).toBe('existing-message-id');
|
||||
});
|
||||
|
||||
test('sendMessage should work with provided conversationId and parentMessageId', async () => {
|
||||
const userMessage = 'Second message in the conversation';
|
||||
const opts = {
|
||||
|
|
|
@ -12,6 +12,7 @@ const { saveMessage } = require('~/models');
|
|||
const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
||||
let {
|
||||
text,
|
||||
isRegenerate,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
isContinued = false,
|
||||
|
@ -167,6 +168,7 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
onStart,
|
||||
getReqData,
|
||||
isContinued,
|
||||
isRegenerate,
|
||||
editedContent,
|
||||
conversationId,
|
||||
parentMessageId,
|
||||
|
|
|
@ -345,9 +345,7 @@ export type TOptions = {
|
|||
isContinued?: boolean;
|
||||
isEdited?: boolean;
|
||||
overrideMessages?: t.TMessage[];
|
||||
/** This value is only true when the user submits a message with "Save & Submit" for a user-created message */
|
||||
isResubmission?: boolean;
|
||||
/** Currently only utilized when `isResubmission === true`, uses that message's currently attached files */
|
||||
/** Currently only utilized when resubmitting user-created message, uses that message's currently attached files */
|
||||
overrideFiles?: t.TMessage['files'];
|
||||
};
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ const EditMessage = ({
|
|||
conversationId,
|
||||
},
|
||||
{
|
||||
isResubmission: true,
|
||||
overrideFiles: message.files,
|
||||
},
|
||||
);
|
||||
|
|
|
@ -83,7 +83,6 @@ export default function useChatFunctions({
|
|||
{
|
||||
editedContent = null,
|
||||
editedMessageId = null,
|
||||
isResubmission = false,
|
||||
isRegenerate = false,
|
||||
isContinued = false,
|
||||
isEdited = false,
|
||||
|
@ -230,7 +229,10 @@ export default function useChatFunctions({
|
|||
}
|
||||
|
||||
const responseMessageId =
|
||||
editedMessageId ?? (latestMessage?.messageId ? latestMessage?.messageId + '_' : null) ?? null;
|
||||
editedMessageId ??
|
||||
(latestMessage?.messageId && isRegenerate ? latestMessage?.messageId + '_' : null) ??
|
||||
null;
|
||||
|
||||
const initialResponse: TMessage = {
|
||||
sender: responseSender,
|
||||
text: '',
|
||||
|
@ -307,7 +309,6 @@ export default function useChatFunctions({
|
|||
isEdited: isEditOrContinue,
|
||||
isContinued,
|
||||
isRegenerate,
|
||||
isResubmission,
|
||||
initialResponse,
|
||||
isTemporary,
|
||||
ephemeralAgent,
|
||||
|
|
|
@ -4,14 +4,15 @@ import * as s from './schemas';
|
|||
|
||||
export default function createPayload(submission: t.TSubmission) {
|
||||
const {
|
||||
conversation,
|
||||
userMessage,
|
||||
endpointOption,
|
||||
isEdited,
|
||||
userMessage,
|
||||
isContinued,
|
||||
isTemporary,
|
||||
ephemeralAgent,
|
||||
isRegenerate,
|
||||
conversation,
|
||||
editedContent,
|
||||
ephemeralAgent,
|
||||
endpointOption,
|
||||
} = submission;
|
||||
const { conversationId } = s.tConvoUpdateSchema.parse(conversation);
|
||||
const { endpoint: _e, endpointType } = endpointOption as {
|
||||
|
@ -31,11 +32,12 @@ export default function createPayload(submission: t.TSubmission) {
|
|||
...userMessage,
|
||||
...endpointOption,
|
||||
endpoint,
|
||||
ephemeralAgent: s.isAssistantsEndpoint(endpoint) ? undefined : ephemeralAgent,
|
||||
isContinued: !!(isEdited && isContinued),
|
||||
conversationId,
|
||||
isTemporary,
|
||||
isRegenerate,
|
||||
editedContent,
|
||||
conversationId,
|
||||
isContinued: !!(isEdited && isContinued),
|
||||
ephemeralAgent: s.isAssistantsEndpoint(endpoint) ? undefined : ephemeralAgent,
|
||||
};
|
||||
|
||||
return { server, payload };
|
||||
|
|
|
@ -105,6 +105,7 @@ export type TEphemeralAgent = {
|
|||
export type TPayload = Partial<TMessage> &
|
||||
Partial<TEndpointOption> & {
|
||||
isContinued: boolean;
|
||||
isRegenerate?: boolean;
|
||||
conversationId: string | null;
|
||||
messages?: TMessages;
|
||||
isTemporary: boolean;
|
||||
|
@ -125,7 +126,6 @@ export type TSubmission = {
|
|||
isTemporary: boolean;
|
||||
messages: TMessage[];
|
||||
isRegenerate?: boolean;
|
||||
isResubmission?: boolean;
|
||||
initialResponse?: TMessage;
|
||||
conversation: Partial<TConversation>;
|
||||
endpointOption: TEndpointOption;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue