🔍 refactor: Search & Message Retrieval (#6903)

* refactor: conversation search fetch

* refactor: Message and Convo fetch with paramters and search

* refactor: update search states and cleanup old store states

* refactor: re-enable search API; fix: search conversation

* fix: message's convo fetch

* fix: redirect when searching

* chore: use logger instead of console

* fix: search message loading

* feat: small optimizations

* feat(Message): remove cache for search path

* fix: handle delete of all archivedConversation and sharedLinks

* chore: cleanup

* fix: search messages

* style: update ConvoOptions styles

* refactor(SearchButtons): streamline conversation fetching and remove unused state

* fix: ensure messages are invalidated after fetching conversation data

* fix: add iconURL to conversation query selection

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Marco Beretta 2025-04-17 03:07:43 +02:00 committed by GitHub
parent 851938e7a6
commit 88f4ad7c47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 489 additions and 576 deletions

View file

@ -1,4 +1,5 @@
import type { AssistantsEndpoint } from './schemas';
import * as q from './types/queries';
// Testing this buildQuery function
const buildQuery = (params: Record<string, unknown>): string => {
@ -28,8 +29,19 @@ export const userPlugins = () => '/api/user/plugins';
export const deleteUser = () => '/api/user/delete';
export const messages = (conversationId: string, messageId?: string) =>
`/api/messages/${conversationId}${messageId != null && messageId ? `/${messageId}` : ''}`;
export const messages = (params: q.MessagesListParams) => {
const { conversationId, messageId, ...rest } = params;
if (conversationId && messageId) {
return `/api/messages/${conversationId}/${messageId}`;
}
if (conversationId) {
return `/api/messages/${conversationId}`;
}
return `/api/messages${buildQuery(rest)}`;
};
const shareRoot = '/api/share';
export const shareMessages = (shareId: string) => `${shareRoot}/${shareId}`;
@ -62,22 +74,7 @@ export const abortRequest = (endpoint: string) => `/api/ask/${endpoint}/abort`;
export const conversationsRoot = '/api/convos';
export const conversations = (
isArchived?: boolean,
sortBy?: 'title' | 'createdAt' | 'updatedAt',
sortDirection?: 'asc' | 'desc',
tags?: string[],
search?: string,
cursor?: string,
) => {
const params = {
isArchived,
sortBy,
sortDirection,
tags,
search,
cursor,
};
export const conversations = (params: q.ConversationListParams) => {
return `${conversationsRoot}${buildQuery(params)}`;
};

View file

@ -30,16 +30,6 @@ export function deleteUser(): Promise<s.TPreset> {
return request.delete(endpoints.deleteUser());
}
export function getMessagesByConvoId(conversationId: string): Promise<s.TMessage[]> {
if (
conversationId === config.Constants.NEW_CONVO ||
conversationId === config.Constants.PENDING_CONVO
) {
return Promise.resolve([]);
}
return request.get(endpoints.messages(conversationId));
}
export function getSharedMessages(shareId: string): Promise<t.TSharedMessagesResponse> {
return request.get(endpoints.shareMessages(shareId));
}
@ -70,31 +60,6 @@ export function deleteSharedLink(shareId: string): Promise<m.TDeleteSharedLinkRe
return request.delete(endpoints.shareMessages(shareId));
}
export function updateMessage(payload: t.TUpdateMessageRequest): Promise<unknown> {
const { conversationId, messageId, text } = payload;
if (!conversationId) {
throw new Error('conversationId is required');
}
return request.put(endpoints.messages(conversationId, messageId), { text });
}
export const editArtifact = async ({
messageId,
...params
}: m.TEditArtifactRequest): Promise<m.TEditArtifactResponse> => {
return request.post(`/api/messages/artifact/${messageId}`, params);
};
export function updateMessageContent(payload: t.TUpdateMessageContent): Promise<unknown> {
const { conversationId, messageId, index, text } = payload;
if (!conversationId) {
throw new Error('conversationId is required');
}
return request.put(endpoints.messages(conversationId, messageId), { text, index });
}
export function updateUserKey(payload: t.TUpdateUserKeyRequest) {
const { value } = payload;
if (!value) {
@ -602,24 +567,11 @@ export function clearAllConversations(): Promise<unknown> {
export const listConversations = (
params?: q.ConversationListParams,
): Promise<q.ConversationListResponse> => {
const isArchived = params?.isArchived ?? false;
const sortBy = params?.sortBy;
const sortDirection = params?.sortDirection;
const tags = params?.tags || [];
const search = params?.search || '';
const cursor = params?.cursor;
if (search !== '' && isArchived === false) {
return request.get(endpoints.search(search, cursor));
} else {
return request.get(
endpoints.conversations(isArchived, sortBy, sortDirection, tags, search, cursor),
);
}
return request.get(endpoints.conversations(params ?? {}));
};
export function getConversations(cursor: string): Promise<t.TGetConversationsResponse> {
return request.get(endpoints.conversations(undefined, undefined, undefined, [], '', cursor));
return request.get(endpoints.conversations({ cursor }));
}
export function getConversationById(id: string): Promise<s.TConversation> {
@ -642,6 +594,45 @@ export function genTitle(payload: m.TGenTitleRequest): Promise<m.TGenTitleRespon
return request.post(endpoints.genTitle(), payload);
}
export const listMessages = (params?: q.MessagesListParams): Promise<q.MessagesListResponse> => {
return request.get(endpoints.messages(params ?? {}));
};
export function updateMessage(payload: t.TUpdateMessageRequest): Promise<unknown> {
const { conversationId, messageId, text } = payload;
if (!conversationId) {
throw new Error('conversationId is required');
}
return request.put(endpoints.messages({ conversationId, messageId }), { text });
}
export function updateMessageContent(payload: t.TUpdateMessageContent): Promise<unknown> {
const { conversationId, messageId, index, text } = payload;
if (!conversationId) {
throw new Error('conversationId is required');
}
return request.put(endpoints.messages({ conversationId, messageId }), { text, index });
}
export const editArtifact = async ({
messageId,
...params
}: m.TEditArtifactRequest): Promise<m.TEditArtifactResponse> => {
return request.post(`/api/messages/artifact/${messageId}`, params);
};
export function getMessagesByConvoId(conversationId: string): Promise<s.TMessage[]> {
if (
conversationId === config.Constants.NEW_CONVO ||
conversationId === config.Constants.PENDING_CONVO
) {
return Promise.resolve([]);
}
return request.get(endpoints.messages({ conversationId }));
}
export function getPrompt(id: string): Promise<{ prompt: t.TPrompt }> {
return request.get(endpoints.getPrompt(id));
}

View file

@ -27,21 +27,6 @@ export type MinimalConversation = Pick<
export type ConversationListResponse = {
conversations: MinimalConversation[];
mwssages?: s.TMessage[];
nextCursor: string | null;
};
export type SearchConversationListParams = {
nextCursor?: string | null;
pageSize?: number;
search: string;
};
export type SearchConversation = Pick<s.TConversation, 'conversationId' | 'title' | 'user'>;
export type SearchConversationListResponse = {
conversations: SearchConversation[];
messages: s.TMessage[];
nextCursor: string | null;
};
@ -51,6 +36,23 @@ export type ConversationUpdater = (
conversation: s.TConversation,
) => ConversationData;
/* Messages */
export type MessagesListParams = {
cursor?: string | null;
sortBy?: 'endpoint' | 'createdAt' | 'updatedAt';
sortDirection?: 'asc' | 'desc';
pageSize?: number;
conversationId?: string;
messageId?: string;
search?: string;
};
export type MessagesListResponse = {
messages: s.TMessage[];
nextCursor: string | null;
};
/* Shared Links */
export type SharedMessagesResponse = Omit<s.TSharedLink, 'messages'> & {
messages: s.TMessage[];
};