mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
⌚ fix: Wait for Initial Message Save & Correct Latest Message (#3399)
* chore: assistants, unsupported assistant, better logging * chore: remove unnecessary logger in validateAssistant middleware * fix: resolve initial conversation save/promise before saving response * chore: Import and organize dependencies in Speech component * fix: conversation statefulness - Latest Message (at index 0) should not be reset if existing convo - add debugging context for clearAllLatestMessages - Added logging concerning latest Message updates (dev mode only) - update latest message Set logic, also checks for change in conversation Id - consolidated latest message helpers to client/src/utils/messages.ts
This commit is contained in:
parent
9e7615f832
commit
2ad097647c
25 changed files with 275 additions and 113 deletions
|
|
@ -540,6 +540,9 @@ class BaseClient {
|
||||||
const completionTokens = this.getTokenCount(completion);
|
const completionTokens = this.getTokenCount(completion);
|
||||||
await this.recordTokenUsage({ promptTokens, completionTokens });
|
await this.recordTokenUsage({ promptTokens, completionTokens });
|
||||||
}
|
}
|
||||||
|
if (this.userMessagePromise) {
|
||||||
|
await this.userMessagePromise;
|
||||||
|
}
|
||||||
this.responsePromise = this.saveMessageToDatabase(responseMessage, saveOptions, user);
|
this.responsePromise = this.saveMessageToDatabase(responseMessage, saveOptions, user);
|
||||||
const messageCache = getLogStores(CacheKeys.MESSAGES);
|
const messageCache = getLogStores(CacheKeys.MESSAGES);
|
||||||
messageCache.set(
|
messageCache.set(
|
||||||
|
|
@ -620,18 +623,23 @@ class BaseClient {
|
||||||
unfinished: false,
|
unfinished: false,
|
||||||
user,
|
user,
|
||||||
},
|
},
|
||||||
{ context: 'api/app/clients/BaseClient.js - saveMessageToDatabase' },
|
{ context: 'api/app/clients/BaseClient.js - saveMessageToDatabase #saveMessage' },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.skipSaveConvo) {
|
if (this.skipSaveConvo) {
|
||||||
return { message: savedMessage };
|
return { message: savedMessage };
|
||||||
}
|
}
|
||||||
const conversation = await saveConvo(user, {
|
|
||||||
conversationId: message.conversationId,
|
const conversation = await saveConvo(
|
||||||
endpoint: this.options.endpoint,
|
this.options.req,
|
||||||
endpointType: this.options.endpointType,
|
{
|
||||||
...endpointOptions,
|
conversationId: message.conversationId,
|
||||||
});
|
endpoint: this.options.endpoint,
|
||||||
|
endpointType: this.options.endpointType,
|
||||||
|
...endpointOptions,
|
||||||
|
},
|
||||||
|
{ context: 'api/app/clients/BaseClient.js - saveMessageToDatabase #saveConvo' },
|
||||||
|
);
|
||||||
|
|
||||||
return { message: savedMessage, conversation };
|
return { message: savedMessage, conversation };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const { Constants } = require('librechat-data-provider');
|
const { Constants } = require('librechat-data-provider');
|
||||||
const { initializeFakeClient } = require('./FakeClient');
|
const { initializeFakeClient } = require('./FakeClient');
|
||||||
|
|
||||||
jest.mock('../../../lib/db/connectDb');
|
jest.mock('~/lib/db/connectDb');
|
||||||
jest.mock('~/models', () => ({
|
jest.mock('~/models', () => ({
|
||||||
User: jest.fn(),
|
User: jest.fn(),
|
||||||
Key: jest.fn(),
|
Key: jest.fn(),
|
||||||
|
|
@ -631,5 +631,32 @@ describe('BaseClient', () => {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('userMessagePromise is awaited before saving response message', async () => {
|
||||||
|
// Mock the saveMessageToDatabase method
|
||||||
|
TestClient.saveMessageToDatabase = jest.fn().mockImplementation(() => {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, 100)); // Simulate a delay
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send a message
|
||||||
|
const messagePromise = TestClient.sendMessage('Hello, world!');
|
||||||
|
|
||||||
|
// Wait a short time to ensure the user message save has started
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||||
|
|
||||||
|
// Check that saveMessageToDatabase has been called once (for the user message)
|
||||||
|
expect(TestClient.saveMessageToDatabase).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// Wait for the message to be fully processed
|
||||||
|
await messagePromise;
|
||||||
|
|
||||||
|
// Check that saveMessageToDatabase has been called twice (once for user message, once for response)
|
||||||
|
expect(TestClient.saveMessageToDatabase).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
// Check the order of calls
|
||||||
|
const calls = TestClient.saveMessageToDatabase.mock.calls;
|
||||||
|
expect(calls[0][0].isCreatedByUser).toBe(true); // First call should be for user message
|
||||||
|
expect(calls[1][0].isCreatedByUser).toBe(false); // Second call should be for response message
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,32 @@ const getConvo = async (user, conversationId) => {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Conversation,
|
Conversation,
|
||||||
saveConvo: async (user, { conversationId, newConversationId, ...convo }) => {
|
/**
|
||||||
|
* Saves a conversation to the database.
|
||||||
|
* @param {Object} req - The request object.
|
||||||
|
* @param {string} conversationId - The conversation's ID.
|
||||||
|
* @param {Object} metadata - Additional metadata to log for operation.
|
||||||
|
* @returns {Promise<TConversation>} The conversation object.
|
||||||
|
*/
|
||||||
|
saveConvo: async (req, { conversationId, newConversationId, ...convo }, metadata) => {
|
||||||
try {
|
try {
|
||||||
|
if (metadata && metadata?.context) {
|
||||||
|
logger.info(`[saveConvo] ${metadata.context}`);
|
||||||
|
}
|
||||||
const messages = await getMessages({ conversationId }, '_id');
|
const messages = await getMessages({ conversationId }, '_id');
|
||||||
const update = { ...convo, messages, user };
|
const update = { ...convo, messages, user: req.user.id };
|
||||||
if (newConversationId) {
|
if (newConversationId) {
|
||||||
update.conversationId = newConversationId;
|
update.conversationId = newConversationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversation = await Conversation.findOneAndUpdate({ conversationId, user }, update, {
|
const conversation = await Conversation.findOneAndUpdate(
|
||||||
new: true,
|
{ conversationId, user: req.user.id },
|
||||||
upsert: true,
|
update,
|
||||||
});
|
{
|
||||||
|
new: true,
|
||||||
|
upsert: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return conversation.toObject();
|
return conversation.toObject();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,9 @@ const chatV1 = async (req, res) => {
|
||||||
return files;
|
return files;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @type {Promise<Run>|undefined} */
|
||||||
|
let userMessagePromise;
|
||||||
|
|
||||||
const initializeThread = async () => {
|
const initializeThread = async () => {
|
||||||
/** @type {[ undefined | MongoFile[]]}*/
|
/** @type {[ undefined | MongoFile[]]}*/
|
||||||
const [processedFiles] = await Promise.all([addVisionPrompt(), getRequestFileIds()]);
|
const [processedFiles] = await Promise.all([addVisionPrompt(), getRequestFileIds()]);
|
||||||
|
|
@ -439,7 +442,7 @@ const chatV1 = async (req, res) => {
|
||||||
previousMessages.push(requestMessage);
|
previousMessages.push(requestMessage);
|
||||||
|
|
||||||
/* asynchronous */
|
/* asynchronous */
|
||||||
saveUserMessage({ ...requestMessage, model });
|
userMessagePromise = saveUserMessage(req, { ...requestMessage, model });
|
||||||
|
|
||||||
conversation = {
|
conversation = {
|
||||||
conversationId,
|
conversationId,
|
||||||
|
|
@ -583,7 +586,10 @@ const chatV1 = async (req, res) => {
|
||||||
});
|
});
|
||||||
res.end();
|
res.end();
|
||||||
|
|
||||||
await saveAssistantMessage({ ...responseMessage, model });
|
if (userMessagePromise) {
|
||||||
|
await userMessagePromise;
|
||||||
|
}
|
||||||
|
await saveAssistantMessage(req, { ...responseMessage, model });
|
||||||
|
|
||||||
if (parentMessageId === Constants.NO_PARENT && !_thread_id) {
|
if (parentMessageId === Constants.NO_PARENT && !_thread_id) {
|
||||||
addTitle(req, {
|
addTitle(req, {
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,9 @@ const chatV2 = async (req, res) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @type {Promise<Run>|undefined} */
|
||||||
|
let userMessagePromise;
|
||||||
|
|
||||||
const initializeThread = async () => {
|
const initializeThread = async () => {
|
||||||
await getRequestFileIds();
|
await getRequestFileIds();
|
||||||
|
|
||||||
|
|
@ -288,7 +291,7 @@ const chatV2 = async (req, res) => {
|
||||||
previousMessages.push(requestMessage);
|
previousMessages.push(requestMessage);
|
||||||
|
|
||||||
/* asynchronous */
|
/* asynchronous */
|
||||||
saveUserMessage({ ...requestMessage, model });
|
userMessagePromise = saveUserMessage(req, { ...requestMessage, model });
|
||||||
|
|
||||||
conversation = {
|
conversation = {
|
||||||
conversationId,
|
conversationId,
|
||||||
|
|
@ -449,7 +452,10 @@ const chatV2 = async (req, res) => {
|
||||||
});
|
});
|
||||||
res.end();
|
res.end();
|
||||||
|
|
||||||
await saveAssistantMessage({ ...responseMessage, model });
|
if (userMessagePromise) {
|
||||||
|
await userMessagePromise;
|
||||||
|
}
|
||||||
|
await saveAssistantMessage(req, { ...responseMessage, model });
|
||||||
|
|
||||||
if (parentMessageId === Constants.NO_PARENT && !_thread_id) {
|
if (parentMessageId === Constants.NO_PARENT && !_thread_id) {
|
||||||
addTitle(req, {
|
addTitle(req, {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ const validateAssistant = async (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { supportedIds, excludedIds } = assistantsConfig;
|
const { supportedIds, excludedIds } = assistantsConfig;
|
||||||
const error = { message: 'Assistant not supported' };
|
const error = { message: 'validateAssistant: Assistant not supported' };
|
||||||
|
|
||||||
if (supportedIds?.length && !supportedIds.includes(assistant_id)) {
|
if (supportedIds?.length && !supportedIds.includes(assistant_id)) {
|
||||||
return await handleAbortError(res, req, error, {
|
return await handleAbortError(res, req, error, {
|
||||||
sender: 'System',
|
sender: 'System',
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ router.post('/', setHeaders, async (req, res) => {
|
||||||
|
|
||||||
if (!overrideParentMessageId) {
|
if (!overrideParentMessageId) {
|
||||||
await saveMessage(req, { ...userMessage, user: req.user.id });
|
await saveMessage(req, { ...userMessage, user: req.user.id });
|
||||||
await saveConvo(req.user.id, {
|
await saveConvo(req, {
|
||||||
...userMessage,
|
...userMessage,
|
||||||
...endpointOption,
|
...endpointOption,
|
||||||
conversationId,
|
conversationId,
|
||||||
|
|
@ -183,7 +183,7 @@ const ask = async ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveConvo(user, conversationUpdate);
|
await saveConvo(req, conversationUpdate);
|
||||||
conversationId = newConversationId;
|
conversationId = newConversationId;
|
||||||
|
|
||||||
// STEP3 update the user message
|
// STEP3 update the user message
|
||||||
|
|
@ -213,7 +213,7 @@ const ask = async ({
|
||||||
if (userParentMessageId == Constants.NO_PARENT) {
|
if (userParentMessageId == Constants.NO_PARENT) {
|
||||||
// const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage });
|
// const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage });
|
||||||
const title = await response.details.title;
|
const title = await response.details.title;
|
||||||
await saveConvo(user, {
|
await saveConvo(req, {
|
||||||
conversationId: conversationId,
|
conversationId: conversationId,
|
||||||
title,
|
title,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ router.post('/', setHeaders, async (req, res) => {
|
||||||
|
|
||||||
if (!overrideParentMessageId) {
|
if (!overrideParentMessageId) {
|
||||||
await saveMessage(req, { ...userMessage, user: req.user.id });
|
await saveMessage(req, { ...userMessage, user: req.user.id });
|
||||||
await saveConvo(req.user.id, {
|
await saveConvo(req, {
|
||||||
...userMessage,
|
...userMessage,
|
||||||
...endpointOption,
|
...endpointOption,
|
||||||
conversationId,
|
conversationId,
|
||||||
|
|
@ -216,7 +216,7 @@ const ask = async ({
|
||||||
conversationUpdate.invocationId = response.invocationId;
|
conversationUpdate.invocationId = response.invocationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveConvo(user, conversationUpdate);
|
await saveConvo(req, conversationUpdate);
|
||||||
userMessage.messageId = newUserMessageId;
|
userMessage.messageId = newUserMessageId;
|
||||||
|
|
||||||
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
|
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
|
||||||
|
|
@ -245,7 +245,7 @@ const ask = async ({
|
||||||
response: responseMessage,
|
response: responseMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
await saveConvo(user, {
|
await saveConvo(req, {
|
||||||
conversationId: conversationId,
|
conversationId: conversationId,
|
||||||
title,
|
title,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ router.post('/update', async (req, res) => {
|
||||||
const update = req.body.arg;
|
const update = req.body.arg;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbResponse = await saveConvo(req.user.id, update);
|
const dbResponse = await saveConvo(req, update, { context: 'POST /api/convos/update' });
|
||||||
res.status(201).json(dbResponse);
|
res.status(201).json(dbResponse);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error updating conversation', error);
|
logger.error('Error updating conversation', error);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ router.post('/:conversationId', validateMessageReq, async (req, res) => {
|
||||||
if (!savedMessage) {
|
if (!savedMessage) {
|
||||||
return res.status(400).json({ error: 'Message not saved' });
|
return res.status(400).json({ error: 'Message not saved' });
|
||||||
}
|
}
|
||||||
await saveConvo(req.user.id, savedMessage);
|
await saveConvo(req, savedMessage, { context: 'POST /api/messages/:conversationId' });
|
||||||
res.status(201).json(savedMessage);
|
res.status(201).json(savedMessage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error saving message:', error);
|
logger.error('Error saving message:', error);
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,14 @@ const addTitle = async (req, { text, response, client }) => {
|
||||||
|
|
||||||
const title = await client.titleConvo({ text, responseText: response?.text });
|
const title = await client.titleConvo({ text, responseText: response?.text });
|
||||||
await titleCache.set(key, title, 120000);
|
await titleCache.set(key, title, 120000);
|
||||||
await saveConvo(req.user.id, {
|
await saveConvo(
|
||||||
conversationId: response.conversationId,
|
req,
|
||||||
title,
|
{
|
||||||
});
|
conversationId: response.conversationId,
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
{ context: 'api/server/services/Endpoints/anthropic/addTitle.js' },
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = addTitle;
|
module.exports = addTitle;
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,14 @@ const addTitle = async (req, { text, responseText, conversationId, client }) =>
|
||||||
const title = await client.titleConvo({ text, conversationId, responseText });
|
const title = await client.titleConvo({ text, conversationId, responseText });
|
||||||
await titleCache.set(key, title, 120000);
|
await titleCache.set(key, title, 120000);
|
||||||
|
|
||||||
await saveConvo(req.user.id, {
|
await saveConvo(
|
||||||
conversationId,
|
req,
|
||||||
title,
|
{
|
||||||
});
|
conversationId,
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
{ context: 'api/server/services/Endpoints/assistants/addTitle.js' },
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = addTitle;
|
module.exports = addTitle;
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,14 @@ const addTitle = async (req, { text, response, client }) => {
|
||||||
|
|
||||||
const title = await titleClient.titleConvo({ text, responseText: response?.text });
|
const title = await titleClient.titleConvo({ text, responseText: response?.text });
|
||||||
await titleCache.set(key, title, 120000);
|
await titleCache.set(key, title, 120000);
|
||||||
await saveConvo(req.user.id, {
|
await saveConvo(
|
||||||
conversationId: response.conversationId,
|
req,
|
||||||
title,
|
{
|
||||||
});
|
conversationId: response.conversationId,
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
{ context: 'api/server/services/Endpoints/google/addTitle.js' },
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = addTitle;
|
module.exports = addTitle;
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,14 @@ const addTitle = async (req, { text, response, client }) => {
|
||||||
|
|
||||||
const title = await client.titleConvo({ text, responseText: response?.text });
|
const title = await client.titleConvo({ text, responseText: response?.text });
|
||||||
await titleCache.set(key, title, 120000);
|
await titleCache.set(key, title, 120000);
|
||||||
await saveConvo(req.user.id, {
|
await saveConvo(
|
||||||
conversationId: response.conversationId,
|
req,
|
||||||
title,
|
{
|
||||||
});
|
conversationId: response.conversationId,
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
{ context: 'api/server/services/Endpoints/openAI/addTitle.js' },
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = addTitle;
|
module.exports = addTitle;
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ async function initThread({ openai, body, thread_id: _thread_id }) {
|
||||||
/**
|
/**
|
||||||
* Saves a user message to the DB in the Assistants endpoint format.
|
* Saves a user message to the DB in the Assistants endpoint format.
|
||||||
*
|
*
|
||||||
|
* @param {Object} req - The request object.
|
||||||
* @param {Object} params - The parameters of the user message
|
* @param {Object} params - The parameters of the user message
|
||||||
* @param {string} params.user - The user's ID.
|
* @param {string} params.user - The user's ID.
|
||||||
* @param {string} params.text - The user's prompt.
|
* @param {string} params.text - The user's prompt.
|
||||||
|
|
@ -59,7 +60,7 @@ async function initThread({ openai, body, thread_id: _thread_id }) {
|
||||||
* @param {string[]} [params.file_ids] - Optional. List of File IDs attached to the userMessage.
|
* @param {string[]} [params.file_ids] - Optional. List of File IDs attached to the userMessage.
|
||||||
* @return {Promise<Run>} A promise that resolves to the created run object.
|
* @return {Promise<Run>} A promise that resolves to the created run object.
|
||||||
*/
|
*/
|
||||||
async function saveUserMessage(params) {
|
async function saveUserMessage(req, params) {
|
||||||
const tokenCount = await countTokens(params.text);
|
const tokenCount = await countTokens(params.text);
|
||||||
|
|
||||||
// todo: do this on the frontend
|
// todo: do this on the frontend
|
||||||
|
|
@ -110,14 +111,16 @@ async function saveUserMessage(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = await recordMessage(userMessage);
|
const message = await recordMessage(userMessage);
|
||||||
await saveConvo(params.user, convo);
|
await saveConvo(req, convo, {
|
||||||
|
context: 'api/server/services/Threads/manage.js #saveUserMessage',
|
||||||
|
});
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves an Assistant message to the DB in the Assistants endpoint format.
|
* Saves an Assistant message to the DB in the Assistants endpoint format.
|
||||||
*
|
*
|
||||||
|
* @param {Object} req - The request object.
|
||||||
* @param {Object} params - The parameters of the Assistant message
|
* @param {Object} params - The parameters of the Assistant message
|
||||||
* @param {string} params.user - The user's ID.
|
* @param {string} params.user - The user's ID.
|
||||||
* @param {string} params.messageId - The message Id.
|
* @param {string} params.messageId - The message Id.
|
||||||
|
|
@ -134,7 +137,7 @@ async function saveUserMessage(params) {
|
||||||
* @param {string} [params.promptPrefix] - Optional: from preset for `additional_instructions` field.
|
* @param {string} [params.promptPrefix] - Optional: from preset for `additional_instructions` field.
|
||||||
* @return {Promise<Run>} A promise that resolves to the created run object.
|
* @return {Promise<Run>} A promise that resolves to the created run object.
|
||||||
*/
|
*/
|
||||||
async function saveAssistantMessage(params) {
|
async function saveAssistantMessage(req, params) {
|
||||||
// const tokenCount = // TODO: need to count each content part
|
// const tokenCount = // TODO: need to count each content part
|
||||||
|
|
||||||
const message = await recordMessage({
|
const message = await recordMessage({
|
||||||
|
|
@ -154,14 +157,18 @@ async function saveAssistantMessage(params) {
|
||||||
// tokenCount,
|
// tokenCount,
|
||||||
});
|
});
|
||||||
|
|
||||||
await saveConvo(params.user, {
|
await saveConvo(
|
||||||
endpoint: params.endpoint,
|
req,
|
||||||
conversationId: params.conversationId,
|
{
|
||||||
promptPrefix: params.promptPrefix,
|
endpoint: params.endpoint,
|
||||||
instructions: params.instructions,
|
conversationId: params.conversationId,
|
||||||
assistant_id: params.assistant_id,
|
promptPrefix: params.promptPrefix,
|
||||||
model: params.model,
|
instructions: params.instructions,
|
||||||
});
|
assistant_id: params.assistant_id,
|
||||||
|
model: params.model,
|
||||||
|
},
|
||||||
|
{ context: 'api/server/services/Threads/manage.js #saveAssistantMessage' },
|
||||||
|
);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
@ -338,10 +345,14 @@ async function syncMessages({
|
||||||
await Promise.all(modifyPromises);
|
await Promise.all(modifyPromises);
|
||||||
await Promise.all(recordPromises);
|
await Promise.all(recordPromises);
|
||||||
|
|
||||||
await saveConvo(openai.req.user.id, {
|
await saveConvo(
|
||||||
conversationId,
|
openai.req,
|
||||||
file_ids: attached_file_ids,
|
{
|
||||||
});
|
conversationId,
|
||||||
|
file_ids: attached_file_ids,
|
||||||
|
},
|
||||||
|
{ context: 'api/server/services/Threads/manage.js #syncMessages' },
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useState, useRef, useMemo } from 'react';
|
import { useState, useRef, useMemo } from 'react';
|
||||||
|
import { Constants } from 'librechat-data-provider';
|
||||||
import { useGetEndpointsQuery, useGetStartupConfig } from 'librechat-data-provider/react-query';
|
import { useGetEndpointsQuery, useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||||
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
|
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
|
||||||
import { useUpdateConversationMutation } from '~/data-provider';
|
import { useUpdateConversationMutation } from '~/data-provider';
|
||||||
|
|
@ -9,14 +10,14 @@ import { useConversations, useNavigateToConvo } from '~/hooks';
|
||||||
import { NotificationSeverity } from '~/common';
|
import { NotificationSeverity } from '~/common';
|
||||||
import { ArchiveIcon } from '~/components/svg';
|
import { ArchiveIcon } from '~/components/svg';
|
||||||
import { useToastContext } from '~/Providers';
|
import { useToastContext } from '~/Providers';
|
||||||
import DropDownMenu from './DropDownMenu';
|
|
||||||
import ArchiveButton from './ArchiveButton';
|
import ArchiveButton from './ArchiveButton';
|
||||||
|
import DropDownMenu from './DropDownMenu';
|
||||||
import DeleteButton from './DeleteButton';
|
import DeleteButton from './DeleteButton';
|
||||||
import RenameButton from './RenameButton';
|
import RenameButton from './RenameButton';
|
||||||
import HoverToggle from './HoverToggle';
|
import HoverToggle from './HoverToggle';
|
||||||
|
import ShareButton from './ShareButton';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
import ShareButton from './ShareButton';
|
|
||||||
|
|
||||||
type KeyEvent = KeyboardEvent<HTMLInputElement>;
|
type KeyEvent = KeyboardEvent<HTMLInputElement>;
|
||||||
|
|
||||||
|
|
@ -51,7 +52,8 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
|
||||||
|
|
||||||
// set document title
|
// set document title
|
||||||
document.title = title;
|
document.title = title;
|
||||||
navigateWithLastTools(conversation);
|
/* Note: Latest Message should not be reset if existing convo */
|
||||||
|
navigateWithLastTools(conversation, !conversationId || conversationId === Constants.NEW_CONVO);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renameHandler = (e: MouseEvent<HTMLButtonElement>) => {
|
const renameHandler = (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
import * as Tabs from '@radix-ui/react-tabs';
|
import * as Tabs from '@radix-ui/react-tabs';
|
||||||
|
import { Lightbulb, Cog } from 'lucide-react';
|
||||||
import { SettingsTabValues } from 'librechat-data-provider';
|
import { SettingsTabValues } from 'librechat-data-provider';
|
||||||
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useGetCustomConfigSpeechQuery } from 'librechat-data-provider/react-query';
|
||||||
import { Lightbulb, Cog } from 'lucide-react';
|
|
||||||
import { useOnClickOutside, useMediaQuery } from '~/hooks';
|
|
||||||
import store from '~/store';
|
|
||||||
import { cn } from '~/utils';
|
|
||||||
import ConversationModeSwitch from './ConversationModeSwitch';
|
|
||||||
import {
|
import {
|
||||||
CloudBrowserVoicesSwitch,
|
CloudBrowserVoicesSwitch,
|
||||||
AutomaticPlaybackSwitch,
|
AutomaticPlaybackSwitch,
|
||||||
|
|
@ -24,7 +21,10 @@ import {
|
||||||
EngineSTTDropdown,
|
EngineSTTDropdown,
|
||||||
DecibelSelector,
|
DecibelSelector,
|
||||||
} from './STT';
|
} from './STT';
|
||||||
import { useGetCustomConfigSpeechQuery } from 'librechat-data-provider/react-query';
|
import ConversationModeSwitch from './ConversationModeSwitch';
|
||||||
|
import { useOnClickOutside, useMediaQuery } from '~/hooks';
|
||||||
|
import { cn, logger } from '~/utils';
|
||||||
|
import store from '~/store';
|
||||||
|
|
||||||
function Speech() {
|
function Speech() {
|
||||||
const [confirmClear, setConfirmClear] = useState(false);
|
const [confirmClear, setConfirmClear] = useState(false);
|
||||||
|
|
@ -131,8 +131,7 @@ function Speech() {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
console.log(sttExternal);
|
logger.log({ sttExternal, ttsExternal });
|
||||||
console.log(ttsExternal);
|
|
||||||
|
|
||||||
const contentRef = useRef(null);
|
const contentRef = useRef(null);
|
||||||
useOnClickOutside(contentRef, () => confirmClear && setConfirmClear(false), []);
|
useOnClickOutside(contentRef, () => confirmClear && setConfirmClear(false), []);
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,16 @@ export default function useChatFunctions({
|
||||||
const intermediateId = overrideUserMessageId ?? v4();
|
const intermediateId = overrideUserMessageId ?? v4();
|
||||||
parentMessageId = parentMessageId || latestMessage?.messageId || Constants.NO_PARENT;
|
parentMessageId = parentMessageId || latestMessage?.messageId || Constants.NO_PARENT;
|
||||||
|
|
||||||
|
logger.dir('Ask function called with:', {
|
||||||
|
index,
|
||||||
|
latestMessage,
|
||||||
|
conversationId,
|
||||||
|
intermediateId,
|
||||||
|
parentMessageId,
|
||||||
|
currentMessages,
|
||||||
|
});
|
||||||
|
logger.log('=====================================');
|
||||||
|
|
||||||
if (conversationId == Constants.NEW_CONVO) {
|
if (conversationId == Constants.NEW_CONVO) {
|
||||||
parentMessageId = Constants.NO_PARENT;
|
parentMessageId = Constants.NO_PARENT;
|
||||||
currentMessages = [];
|
currentMessages = [];
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import type {
|
||||||
TModelsConfig,
|
TModelsConfig,
|
||||||
TEndpointsConfig,
|
TEndpointsConfig,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField } from '~/utils';
|
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField, logger } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
const useConversation = () => {
|
const useConversation = () => {
|
||||||
|
|
@ -60,6 +60,10 @@ const useConversation = () => {
|
||||||
setMessages(messages);
|
setMessages(messages);
|
||||||
setSubmission({} as TSubmission);
|
setSubmission({} as TSubmission);
|
||||||
resetLatestMessage();
|
resetLatestMessage();
|
||||||
|
logger.log(
|
||||||
|
'[useConversation] Switched to conversation and reset Latest Message',
|
||||||
|
conversation,
|
||||||
|
);
|
||||||
|
|
||||||
if (conversation.conversationId === 'new' && !modelsData) {
|
if (conversation.conversationId === 'new' && !modelsData) {
|
||||||
queryClient.invalidateQueries([QueryKeys.messages, 'new']);
|
queryClient.invalidateQueries([QueryKeys.messages, 'new']);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { QueryKeys, EModelEndpoint, LocalStorageKeys } from 'librechat-data-provider';
|
import { QueryKeys, EModelEndpoint, LocalStorageKeys, Constants } from 'librechat-data-provider';
|
||||||
import type { TConversation, TEndpointsConfig, TModelsConfig } from 'librechat-data-provider';
|
import type { TConversation, TEndpointsConfig, TModelsConfig } from 'librechat-data-provider';
|
||||||
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField } from '~/utils';
|
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
@ -10,7 +10,7 @@ const useNavigateToConvo = (index = 0) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const clearAllConversations = store.useClearConvoState();
|
const clearAllConversations = store.useClearConvoState();
|
||||||
const clearAllLatestMessages = store.useClearLatestMessages();
|
const clearAllLatestMessages = store.useClearLatestMessages(`useNavigateToConvo ${index}`);
|
||||||
const setSubmission = useSetRecoilState(store.submissionByIndex(index));
|
const setSubmission = useSetRecoilState(store.submissionByIndex(index));
|
||||||
const { setConversation } = store.useCreateConversationAtom(index);
|
const { setConversation } = store.useCreateConversationAtom(index);
|
||||||
|
|
||||||
|
|
@ -50,10 +50,10 @@ const useNavigateToConvo = (index = 0) => {
|
||||||
}
|
}
|
||||||
clearAllConversations(true);
|
clearAllConversations(true);
|
||||||
setConversation(convo);
|
setConversation(convo);
|
||||||
navigate(`/c/${convo.conversationId ?? 'new'}`);
|
navigate(`/c/${convo.conversationId ?? Constants.NEW_CONVO}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateWithLastTools = (conversation: TConversation) => {
|
const navigateWithLastTools = (conversation: TConversation, _resetLatestMessage?: boolean) => {
|
||||||
// set conversation to the new conversation
|
// set conversation to the new conversation
|
||||||
if (conversation?.endpoint === EModelEndpoint.gptPlugins) {
|
if (conversation?.endpoint === EModelEndpoint.gptPlugins) {
|
||||||
let lastSelectedTools = [];
|
let lastSelectedTools = [];
|
||||||
|
|
@ -63,12 +63,15 @@ const useNavigateToConvo = (index = 0) => {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// console.error(e);
|
// console.error(e);
|
||||||
}
|
}
|
||||||
navigateToConvo({
|
navigateToConvo(
|
||||||
...conversation,
|
{
|
||||||
tools: conversation?.tools?.length ? conversation?.tools : lastSelectedTools,
|
...conversation,
|
||||||
});
|
tools: conversation?.tools?.length ? conversation?.tools : lastSelectedTools,
|
||||||
|
},
|
||||||
|
_resetLatestMessage,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
navigateToConvo(conversation);
|
navigateToConvo(conversation, _resetLatestMessage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { useEffect, useRef, useCallback } from 'react';
|
import { useEffect, useRef, useCallback } from 'react';
|
||||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
import { Constants, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||||
import type { TMessageProps } from '~/common';
|
import type { TMessageProps } from '~/common';
|
||||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||||
import { getLatestText, getLengthAndFirstFiveChars } from '~/utils';
|
|
||||||
import useCopyToClipboard from './useCopyToClipboard';
|
import useCopyToClipboard from './useCopyToClipboard';
|
||||||
|
import { getTextKey, logger } from '~/utils';
|
||||||
|
|
||||||
export default function useMessageHelpers(props: TMessageProps) {
|
export default function useMessageHelpers(props: TMessageProps) {
|
||||||
const latestText = useRef<string | number>('');
|
const latestText = useRef<string | number>('');
|
||||||
|
|
@ -27,7 +27,8 @@ export default function useMessageHelpers(props: TMessageProps) {
|
||||||
const isLast = !children?.length;
|
const isLast = !children?.length;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (conversation?.conversationId === 'new') {
|
const convoId = conversation?.conversationId;
|
||||||
|
if (convoId === Constants.NEW_CONVO) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!message) {
|
if (!message) {
|
||||||
|
|
@ -37,15 +38,25 @@ export default function useMessageHelpers(props: TMessageProps) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = getLatestText(message);
|
const textKey = getTextKey(message, convoId);
|
||||||
const textKey = `${message?.messageId ?? ''}${getLengthAndFirstFiveChars(text)}`;
|
|
||||||
|
|
||||||
if (textKey === latestText.current) {
|
// Check for text/conversation change
|
||||||
return;
|
const logInfo = {
|
||||||
|
textKey,
|
||||||
|
'latestText.current': latestText.current,
|
||||||
|
messageId: message?.messageId,
|
||||||
|
convoId,
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
textKey !== latestText.current ||
|
||||||
|
(latestText.current && convoId !== latestText.current.split(Constants.COMMON_DIVIDER)[2])
|
||||||
|
) {
|
||||||
|
logger.log('[useMessageHelpers] Setting latest message: ', logInfo);
|
||||||
|
latestText.current = textKey;
|
||||||
|
setLatestMessage({ ...message });
|
||||||
|
} else {
|
||||||
|
logger.log('No change in latest message', logInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
latestText.current = textKey;
|
|
||||||
setLatestMessage({ ...message });
|
|
||||||
}, [isLast, message, setLatestMessage, conversation?.conversationId]);
|
}, [isLast, message, setLatestMessage, conversation?.conversationId]);
|
||||||
|
|
||||||
const enterEdit = useCallback(
|
const enterEdit = useCallback(
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import { Constants } from 'librechat-data-provider';
|
||||||
import { useEffect, useRef, useCallback, useMemo, useState } from 'react';
|
import { useEffect, useRef, useCallback, useMemo, useState } from 'react';
|
||||||
import type { TMessage } from 'librechat-data-provider';
|
import type { TMessage } from 'librechat-data-provider';
|
||||||
import { useChatContext, useAddedChatContext } from '~/Providers';
|
import { useChatContext, useAddedChatContext } from '~/Providers';
|
||||||
import { getLatestText, getLengthAndFirstFiveChars } from '~/utils';
|
import { getTextKey, logger } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
export default function useMessageProcess({ message }: { message?: TMessage | null }) {
|
export default function useMessageProcess({ message }: { message?: TMessage | null }) {
|
||||||
|
|
@ -26,7 +27,8 @@ export default function useMessageProcess({ message }: { message?: TMessage | nu
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (conversation?.conversationId === 'new') {
|
const convoId = conversation?.conversationId;
|
||||||
|
if (convoId === Constants.NEW_CONVO) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!message) {
|
if (!message) {
|
||||||
|
|
@ -36,15 +38,27 @@ export default function useMessageProcess({ message }: { message?: TMessage | nu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = getLatestText(message);
|
const textKey = getTextKey(message, convoId);
|
||||||
const textKey = `${message?.messageId ?? ''}${getLengthAndFirstFiveChars(text)}`;
|
|
||||||
|
|
||||||
if (textKey === latestText.current) {
|
// Check for text/conversation change
|
||||||
return;
|
const logInfo = {
|
||||||
|
textKey,
|
||||||
|
'latestText.current': latestText.current,
|
||||||
|
messageId: message?.messageId,
|
||||||
|
convoId,
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
textKey !== latestText.current ||
|
||||||
|
(convoId &&
|
||||||
|
latestText.current &&
|
||||||
|
convoId !== latestText.current.split(Constants.COMMON_DIVIDER)[2])
|
||||||
|
) {
|
||||||
|
logger.log('[useMessageProcess] Setting latest message: ', logInfo);
|
||||||
|
latestText.current = textKey;
|
||||||
|
setLatestMessage({ ...message });
|
||||||
|
} else {
|
||||||
|
logger.log('No change in latest message', logInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
latestText.current = textKey;
|
|
||||||
setLatestMessage({ ...message });
|
|
||||||
}, [hasNoChildren, message, setLatestMessage, conversation?.conversationId]);
|
}, [hasNoChildren, message, setLatestMessage, conversation?.conversationId]);
|
||||||
|
|
||||||
const handleScroll = useCallback(() => {
|
const handleScroll = useCallback(() => {
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ const useNewConvo = (index = 0) => {
|
||||||
const { data: startupConfig } = useGetStartupConfig();
|
const { data: startupConfig } = useGetStartupConfig();
|
||||||
const clearAllConversations = store.useClearConvoState();
|
const clearAllConversations = store.useClearConvoState();
|
||||||
const defaultPreset = useRecoilValue(store.defaultPreset);
|
const defaultPreset = useRecoilValue(store.defaultPreset);
|
||||||
const clearAllLatestMessages = store.useClearLatestMessages();
|
|
||||||
const { setConversation } = store.useCreateConversationAtom(index);
|
const { setConversation } = store.useCreateConversationAtom(index);
|
||||||
const [files, setFiles] = useRecoilState(store.filesByIndex(index));
|
const [files, setFiles] = useRecoilState(store.filesByIndex(index));
|
||||||
|
const clearAllLatestMessages = store.useClearLatestMessages(`useNewConvo ${index}`);
|
||||||
const setSubmission = useSetRecoilState<TSubmission | null>(store.submissionByIndex(index));
|
const setSubmission = useSetRecoilState<TSubmission | null>(store.submissionByIndex(index));
|
||||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
useSetRecoilState,
|
useSetRecoilState,
|
||||||
useRecoilCallback,
|
useRecoilCallback,
|
||||||
} from 'recoil';
|
} from 'recoil';
|
||||||
import { LocalStorageKeys } from 'librechat-data-provider';
|
import { LocalStorageKeys, Constants } from 'librechat-data-provider';
|
||||||
import type { TMessage, TPreset, TConversation, TSubmission } from 'librechat-data-provider';
|
import type { TMessage, TPreset, TConversation, TSubmission } from 'librechat-data-provider';
|
||||||
import type { TOptionSettings, ExtendedFile } from '~/common';
|
import type { TOptionSettings, ExtendedFile } from '~/common';
|
||||||
import { storeEndpointSettings, logger } from '~/utils';
|
import { storeEndpointSettings, logger } from '~/utils';
|
||||||
|
|
@ -27,6 +27,14 @@ const submissionKeysAtom = atom<(string | number)[]>({
|
||||||
const latestMessageFamily = atomFamily<TMessage | null, string | number | null>({
|
const latestMessageFamily = atomFamily<TMessage | null, string | number | null>({
|
||||||
key: 'latestMessageByIndex',
|
key: 'latestMessageByIndex',
|
||||||
default: null,
|
default: null,
|
||||||
|
effects: [
|
||||||
|
({ onSet, node }) => {
|
||||||
|
onSet(async (newValue) => {
|
||||||
|
const key = Number(node.key.split(Constants.COMMON_DIVIDER)[1]);
|
||||||
|
logger.log('Recoil Effect: Setting latestMessage', { key, newValue });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
] as const,
|
||||||
});
|
});
|
||||||
|
|
||||||
const submissionByIndex = atomFamily<TSubmission | null, string | number>({
|
const submissionByIndex = atomFamily<TSubmission | null, string | number>({
|
||||||
|
|
@ -41,7 +49,7 @@ const latestMessageKeysSelector = selector<(string | number)[]>({
|
||||||
return keys.filter((key) => get(latestMessageFamily(key)) !== null);
|
return keys.filter((key) => get(latestMessageFamily(key)) !== null);
|
||||||
},
|
},
|
||||||
set: ({ set }, newKeys) => {
|
set: ({ set }, newKeys) => {
|
||||||
logger.log('setting latestMessageKeys', newKeys);
|
logger.log('setting latestMessageKeys', { newKeys });
|
||||||
set(latestMessageKeysAtom, newKeys);
|
set(latestMessageKeysAtom, newKeys);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -279,19 +287,22 @@ function useClearSubmissionState() {
|
||||||
return clearAllSubmissions;
|
return clearAllSubmissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function useClearLatestMessages() {
|
function useClearLatestMessages(context?: string) {
|
||||||
const clearAllLatestMessages = useRecoilCallback(
|
const clearAllLatestMessages = useRecoilCallback(
|
||||||
({ reset, set, snapshot }) =>
|
({ reset, set, snapshot }) =>
|
||||||
async (skipFirst?: boolean) => {
|
async (skipFirst?: boolean) => {
|
||||||
const latestMessageKeys = await snapshot.getPromise(latestMessageKeysSelector);
|
const latestMessageKeys = await snapshot.getPromise(latestMessageKeysSelector);
|
||||||
logger.log('latestMessageKeys', latestMessageKeys);
|
logger.log('[clearAllLatestMessages] latestMessageKeys', latestMessageKeys);
|
||||||
|
if (context) {
|
||||||
|
logger.log(`[clearAllLatestMessages] context: ${context}`);
|
||||||
|
}
|
||||||
|
|
||||||
for (const key of latestMessageKeys) {
|
for (const key of latestMessageKeys) {
|
||||||
if (skipFirst && key == 0) {
|
if (skipFirst && key == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log('resetting latest message', key);
|
logger.log(`[clearAllLatestMessages] resetting latest message; key: ${key}`);
|
||||||
reset(latestMessageFamily(key));
|
reset(latestMessageFamily(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import { ContentTypes } from 'librechat-data-provider';
|
import { ContentTypes, Constants } from 'librechat-data-provider';
|
||||||
import type { TMessage } from 'librechat-data-provider';
|
import type { TMessage } from 'librechat-data-provider';
|
||||||
|
|
||||||
export const getLengthAndFirstFiveChars = (str?: string) => {
|
export const getLengthAndLastTenChars = (str?: string): string => {
|
||||||
const length = str ? str.length : 0;
|
if (!str) {
|
||||||
const firstFiveChars = str ? str.substring(0, 5) : '';
|
return '0';
|
||||||
return `${length}${firstFiveChars}`;
|
}
|
||||||
|
|
||||||
|
const length = str.length;
|
||||||
|
const lastTenChars = str.slice(-10);
|
||||||
|
return `${length}${lastTenChars}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLatestText = (message?: TMessage | null) => {
|
export const getLatestText = (message?: TMessage | null, includeIndex?: boolean) => {
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
@ -18,9 +22,24 @@ export const getLatestText = (message?: TMessage | null) => {
|
||||||
for (let i = message.content.length - 1; i >= 0; i--) {
|
for (let i = message.content.length - 1; i >= 0; i--) {
|
||||||
const part = message.content[i];
|
const part = message.content[i];
|
||||||
if (part.type === ContentTypes.TEXT && part[ContentTypes.TEXT]?.value?.length > 0) {
|
if (part.type === ContentTypes.TEXT && part[ContentTypes.TEXT]?.value?.length > 0) {
|
||||||
return part[ContentTypes.TEXT].value;
|
const text = part[ContentTypes.TEXT].value;
|
||||||
|
if (includeIndex) {
|
||||||
|
return `${text}-${i}`;
|
||||||
|
} else {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getTextKey = (message?: TMessage | null, convoId?: string | null) => {
|
||||||
|
if (!message) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const text = getLatestText(message, true);
|
||||||
|
return `${message.messageId ?? ''}${Constants.COMMON_DIVIDER}${getLengthAndLastTenChars(text)}${
|
||||||
|
Constants.COMMON_DIVIDER
|
||||||
|
}${message.conversationId ?? convoId}`;
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue