mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-29 14:48:51 +01:00
* 🔧chore: add internationalization labels for archive feature * ✨ feat: Add function to useArchiveConversationMutation() This commit adds a new mutation function `useArchiveConversationMutation()` for archiving conversations. This function takes the ID string of the conversation to be archived and returns a mutation result object. Upon successful archiving, it removes and refreshes the conversation from the query data cache. While ChatGPT PATCHes the archived status by sending `{is_archived: true}` to the URL `/backend-api/conversation/$conversation_id`, this implementation uses the `dataService.updateConversation(payload)` with a POST method, aligning with the existing code conventions. * ✨ feat(api): add is_archived field to Conversation schema and update getConvosByPage method This commit adds a new field `is_archived` with a default value of false to the Conversation schema. It also modifies the `getConvosByPage` method within the Conversation API to adjust the query to only target conversations where `is_archived` is set to false or where the `is_archived` field does not exist. The function `getConvosQueried`, which returns conversations for a specified Conversation ID, was determined not to require consideration of whether `is_archived` is true or false, and thus was not modified. * ♻️ refactor: add className prop to DotsIcon component To enhance the versatility of the DotsIcon component, this commit introduces the ability to specify a className prop, allowing for greater customization. * ✨ feat(ui): add Edit Button to group Title change and Conversation delete buttons Added a new Edit Button to the conversations, similar to the ChatGPT UI, which groups options for editing the conversation title and deleting conversations. This grouping is accessible through a dialogue that appears when the three-dot icon is clicked. * ♻️ refactor(ui): enhance Delete Button to accept className and label options Enhanced the Delete Button component to accept a `className` for customization and an optional `appendLabel`. The DeleteButton component is used by both `Convo.tsx` and `Conversation.tsx`, but currently only `Convo.tsx` is active and `Conversation.tsx `is apparently not used; removing `Conversation.tsx` may eliminate the need for the `appendLabel` property in the future. * ♻️ refactor(ui): enhance RenameButton to accept label options Added the ability to optionally display labels; the Rename Button component is used by both `Convo.tsx` and `Conversation.tsx`, but currently only `Convo.tsx` is active and `Conversation.tsx `is apparently not used; removing `Conversation.tsx` may eliminate the need for the `appendLabel` property in the future. * 🔧 chors: additional localization labels * ♻️ refactor: change is_archived property of conversation to camelCase * Refactor the is_archived property of conversation to camelCase (isArchived) to adhere to the existing code conventions * Modify the function that retrieves conversations to accept the isArchived parameter * ♻️ refactor: add archiveConversation mutation I thought I could divert dataService.updateConversation, but added a new archiveConversation because the request types are different. It might be better to make them common, but to avoid side effects, I added a new function this time. Added process to deleteConversationMutation to delete archived conversations * ✨ feat: Add the function to hide a cancel button in DialogTemplate component The Cancel button is not needed when displaying the archive list, so I made the Cancel button optional. * ♻️ refactor: Add support for filtering archived conversations in Nav component This commit modifies the Nav component to add the ability to filter out archived conversations when fetching data. This is done by adding `isArchived: false` to the query parameters for both the `useConversationsInfiniteQuery()` and `useSearchInfiniteQuery()` hooks, effectively excluding any archived conversations from the results returned. * ♻️ refactor: add Tooltip to DeleteButton * Add Tooltip to DeleteButton component * Display Tooltip when DeleteButton only shows an Icon without text * ✨ feat(ui): add ArchiveButton component for archiving conversations To be compatible with the ChatGPT UI, no confirmation dialog is displayed when ArchiveButton is clicked. The basic behavior conforms to DeleteButton and RenameButton. * ✨ feat(ui): add Archive button to list of conversations Modify the Nav of the conversation list to include a dropdown that contains the Rename and Delete options, similar to the ChatGPT UI. Additionally, an Archive button has been added adjacent to the dropdown menu. * ✨ feat: Add ArchivedChatsTable component Adds the `ArchivedChatsTable` component, which displays a table of archived chats. It has been implemented to be as compatible with the ChatGPT UI as possible. * 🚑 fix(tooltip): increase z-index to ensure visibility over Dialog Resolve an issue where tooltips were not visible when displayed over a Dialog. The z-index of `DialogPrimitive.Portal` in `Dialog.tsx` is set to 999. Since the rationale for this value is unclear, the z-index of the tooltip has been increased to 1000 to guarantee its visibility above the Dialog component. * 🔧 chors: add internationalization labels
328 lines
10 KiB
TypeScript
328 lines
10 KiB
TypeScript
import * as f from './types/files';
|
|
import * as q from './types/queries';
|
|
import * as m from './types/mutations';
|
|
import * as a from './types/assistants';
|
|
import * as t from './types';
|
|
import * as s from './schemas';
|
|
import request from './request';
|
|
import * as endpoints from './api-endpoints';
|
|
import type { AxiosResponse } from 'axios';
|
|
|
|
export function abortRequestWithMessage(
|
|
endpoint: string,
|
|
abortKey: string,
|
|
message: string,
|
|
): Promise<void> {
|
|
return request.post(endpoints.abortRequest(endpoint), { arg: { abortKey, message } });
|
|
}
|
|
|
|
export function revokeUserKey(name: string): Promise<unknown> {
|
|
return request.delete(endpoints.revokeUserKey(name));
|
|
}
|
|
|
|
export function revokeAllUserKeys(): Promise<unknown> {
|
|
return request.delete(endpoints.revokeAllUserKeys());
|
|
}
|
|
|
|
export function getMessagesByConvoId(conversationId: string): Promise<s.TMessage[]> {
|
|
if (conversationId === 'new') {
|
|
return Promise.resolve([]);
|
|
}
|
|
return request.get(endpoints.messages(conversationId));
|
|
}
|
|
|
|
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 updateUserKey(payload: t.TUpdateUserKeyRequest) {
|
|
const { value } = payload;
|
|
if (!value) {
|
|
throw new Error('value is required');
|
|
}
|
|
|
|
return request.put(endpoints.keys(), payload);
|
|
}
|
|
|
|
export function getPresets(): Promise<s.TPreset[]> {
|
|
return request.get(endpoints.presets());
|
|
}
|
|
|
|
export function createPreset(payload: s.TPreset): Promise<s.TPreset> {
|
|
return request.post(endpoints.presets(), payload);
|
|
}
|
|
|
|
export function updatePreset(payload: s.TPreset): Promise<s.TPreset> {
|
|
return request.post(endpoints.presets(), payload);
|
|
}
|
|
|
|
export function deletePreset(arg: s.TPreset | undefined): Promise<m.PresetDeleteResponse> {
|
|
return request.post(endpoints.deletePreset(), arg);
|
|
}
|
|
|
|
export function getSearchEnabled(): Promise<boolean> {
|
|
return request.get(endpoints.searchEnabled());
|
|
}
|
|
|
|
export function getUser(): Promise<t.TUser> {
|
|
return request.get(endpoints.user());
|
|
}
|
|
|
|
export function getUserBalance(): Promise<string> {
|
|
return request.get(endpoints.balance());
|
|
}
|
|
|
|
export const updateTokenCount = (text: string) => {
|
|
return request.post(endpoints.tokenizer(), { arg: text });
|
|
};
|
|
|
|
export const login = (payload: t.TLoginUser) => {
|
|
return request.post(endpoints.login(), payload);
|
|
};
|
|
|
|
export const logout = () => {
|
|
return request.post(endpoints.logout());
|
|
};
|
|
|
|
export const register = (payload: t.TRegisterUser) => {
|
|
return request.post(endpoints.register(), payload);
|
|
};
|
|
|
|
export const userKeyQuery = (name: string): Promise<t.TCheckUserKeyResponse> =>
|
|
request.get(endpoints.userKeyQuery(name));
|
|
|
|
export const getLoginGoogle = () => {
|
|
return request.get(endpoints.loginGoogle());
|
|
};
|
|
|
|
export const requestPasswordReset = (
|
|
payload: t.TRequestPasswordReset,
|
|
): Promise<t.TRequestPasswordResetResponse> => {
|
|
return request.post(endpoints.requestPasswordReset(), payload);
|
|
};
|
|
|
|
export const resetPassword = (payload: t.TResetPassword) => {
|
|
return request.post(endpoints.resetPassword(), payload);
|
|
};
|
|
|
|
export const getAvailablePlugins = (): Promise<s.TPlugin[]> => {
|
|
return request.get(endpoints.plugins());
|
|
};
|
|
|
|
export const updateUserPlugins = (payload: t.TUpdateUserPlugins) => {
|
|
return request.post(endpoints.userPlugins(), payload);
|
|
};
|
|
|
|
/* Config */
|
|
|
|
export const getStartupConfig = (): Promise<t.TStartupConfig> => {
|
|
return request.get(endpoints.config());
|
|
};
|
|
|
|
export const getAIEndpoints = (): Promise<t.TEndpointsConfig> => {
|
|
return request.get(endpoints.aiEndpoints());
|
|
};
|
|
|
|
export const getModels = async (): Promise<t.TModelsConfig> => {
|
|
return request.get(endpoints.models());
|
|
};
|
|
|
|
export const getEndpointsConfigOverride = (): Promise<unknown | boolean> => {
|
|
return request.get(endpoints.endpointsConfigOverride());
|
|
};
|
|
|
|
/* Assistants */
|
|
|
|
export const createAssistant = (data: a.AssistantCreateParams): Promise<a.Assistant> => {
|
|
return request.post(endpoints.assistants(), data);
|
|
};
|
|
|
|
export const getAssistantById = (assistant_id: string): Promise<a.Assistant> => {
|
|
return request.get(endpoints.assistants(assistant_id));
|
|
};
|
|
|
|
export const updateAssistant = (
|
|
assistant_id: string,
|
|
data: a.AssistantUpdateParams,
|
|
): Promise<a.Assistant> => {
|
|
return request.patch(endpoints.assistants(assistant_id), data);
|
|
};
|
|
|
|
export const deleteAssistant = (assistant_id: string, model: string): Promise<void> => {
|
|
return request.delete(endpoints.assistants(assistant_id, { model }));
|
|
};
|
|
|
|
export const listAssistants = (
|
|
params?: a.AssistantListParams,
|
|
): Promise<a.AssistantListResponse> => {
|
|
return request.get(endpoints.assistants(), { params });
|
|
};
|
|
|
|
export function getAssistantDocs(): Promise<a.AssistantDocument[]> {
|
|
return request.get(endpoints.assistants('documents'));
|
|
}
|
|
|
|
/* Tools */
|
|
|
|
export const getAvailableTools = (): Promise<s.TPlugin[]> => {
|
|
return request.get(`${endpoints.assistants()}/tools`);
|
|
};
|
|
|
|
/* Files */
|
|
|
|
export const getFiles = (): Promise<f.TFile[]> => {
|
|
return request.get(endpoints.files());
|
|
};
|
|
|
|
export const getFileConfig = (): Promise<f.FileConfig> => {
|
|
return request.get(`${endpoints.files()}/config`);
|
|
};
|
|
|
|
export const uploadImage = (data: FormData): Promise<f.TFileUpload> => {
|
|
return request.postMultiPart(endpoints.images(), data);
|
|
};
|
|
|
|
export const uploadFile = (data: FormData): Promise<f.TFileUpload> => {
|
|
return request.postMultiPart(endpoints.files(), data);
|
|
};
|
|
|
|
/**
|
|
* Imports a conversations file.
|
|
*
|
|
* @param data - The FormData containing the file to import.
|
|
* @returns A Promise that resolves to the import start response.
|
|
*/
|
|
export const importConversationsFile = (data: FormData): Promise<t.TImportStartResponse> => {
|
|
return request.postMultiPart(endpoints.importConversation(), data);
|
|
};
|
|
|
|
/**
|
|
* Retrieves the status of an import conversation job.
|
|
*
|
|
* @param jobId - The ID of the import conversation job.
|
|
* @returns A promise that resolves to the import job status.
|
|
*/
|
|
export const queryImportConversationJobStatus = async (
|
|
jobId: string,
|
|
): Promise<t.TImportJobStatus> => {
|
|
return request.get(endpoints.importConversationJobStatus(jobId));
|
|
};
|
|
|
|
export const uploadAvatar = (data: FormData): Promise<f.AvatarUploadResponse> => {
|
|
return request.postMultiPart(endpoints.avatar(), data);
|
|
};
|
|
|
|
export const uploadAssistantAvatar = (data: m.AssistantAvatarVariables): Promise<a.Assistant> => {
|
|
return request.postMultiPart(
|
|
endpoints.assistants(`avatar/${data.assistant_id}`, { model: data.model }),
|
|
data.formData,
|
|
);
|
|
};
|
|
|
|
export const getFileDownload = async (userId: string, file_id: string): Promise<AxiosResponse> => {
|
|
return request.getResponse(`${endpoints.files()}/download/${userId}/${file_id}`, {
|
|
responseType: 'blob',
|
|
headers: {
|
|
Accept: 'application/octet-stream',
|
|
},
|
|
});
|
|
};
|
|
|
|
export const deleteFiles = async (
|
|
files: f.BatchFile[],
|
|
assistant_id?: string,
|
|
): Promise<f.DeleteFilesResponse> =>
|
|
request.deleteWithOptions(endpoints.files(), {
|
|
data: { files, assistant_id },
|
|
});
|
|
|
|
/* actions */
|
|
|
|
export const updateAction = (data: m.UpdateActionVariables): Promise<m.UpdateActionResponse> => {
|
|
const { assistant_id, ...body } = data;
|
|
return request.post(endpoints.assistants(`actions/${assistant_id}`), body);
|
|
};
|
|
|
|
export function getActions(): Promise<a.Action[]> {
|
|
return request.get(endpoints.assistants('actions'));
|
|
}
|
|
|
|
export const deleteAction = async (
|
|
assistant_id: string,
|
|
action_id: string,
|
|
model: string,
|
|
): Promise<void> =>
|
|
request.delete(endpoints.assistants(`actions/${assistant_id}/${action_id}/${model}`));
|
|
|
|
/* conversations */
|
|
|
|
export function forkConversation(payload: t.TForkConvoRequest): Promise<t.TForkConvoResponse> {
|
|
return request.post(endpoints.forkConversation(), payload);
|
|
}
|
|
|
|
export function deleteConversation(payload: t.TDeleteConversationRequest) {
|
|
//todo: this should be a DELETE request
|
|
return request.post(endpoints.deleteConversation(), { arg: payload });
|
|
}
|
|
|
|
export function clearAllConversations(): Promise<unknown> {
|
|
return request.post(endpoints.deleteConversation(), { arg: {} });
|
|
}
|
|
|
|
export const listConversations = (
|
|
params?: q.ConversationListParams,
|
|
): Promise<q.ConversationListResponse> => {
|
|
// Assuming params has a pageNumber property
|
|
const pageNumber = params?.pageNumber || '1'; // Default to page 1 if not provided
|
|
const isArchived = params?.isArchived || false; // Default to false if not provided
|
|
return request.get(endpoints.conversations(pageNumber, isArchived));
|
|
};
|
|
|
|
export const listConversationsByQuery = (
|
|
params?: q.ConversationListParams & { searchQuery?: string },
|
|
): Promise<q.ConversationListResponse> => {
|
|
const pageNumber = params?.pageNumber || '1'; // Default to page 1 if not provided
|
|
const searchQuery = params?.searchQuery || ''; // If no search query is provided, default to an empty string
|
|
// Update the endpoint to handle a search query
|
|
if (searchQuery !== '') {
|
|
return request.get(endpoints.search(searchQuery, pageNumber));
|
|
} else {
|
|
return request.get(endpoints.conversations(pageNumber));
|
|
}
|
|
};
|
|
|
|
export const searchConversations = async (
|
|
q: string,
|
|
pageNumber: string,
|
|
): Promise<t.TSearchResults> => {
|
|
return request.get(endpoints.search(q, pageNumber));
|
|
};
|
|
|
|
export function getConversations(pageNumber: string): Promise<t.TGetConversationsResponse> {
|
|
return request.get(endpoints.conversations(pageNumber));
|
|
}
|
|
|
|
export function getConversationById(id: string): Promise<s.TConversation> {
|
|
return request.get(endpoints.conversationById(id));
|
|
}
|
|
|
|
export function updateConversation(
|
|
payload: t.TUpdateConversationRequest,
|
|
): Promise<t.TUpdateConversationResponse> {
|
|
return request.post(endpoints.updateConversation(), { arg: payload });
|
|
}
|
|
|
|
export function archiveConversation(
|
|
payload: t.TArchiveConversationRequest,
|
|
): Promise<t.TArchiveConversationResponse> {
|
|
return request.post(endpoints.updateConversation(), { arg: payload });
|
|
}
|
|
|
|
export function genTitle(payload: m.TGenTitleRequest): Promise<m.TGenTitleResponse> {
|
|
return request.post(endpoints.genTitle(), payload);
|
|
}
|