diff --git a/client/src/data-provider/data-service.ts b/client/src/data-provider/data-service.ts new file mode 100644 index 0000000000..621831a0ee --- /dev/null +++ b/client/src/data-provider/data-service.ts @@ -0,0 +1,81 @@ +import * as t from './types'; +import request from './request'; +import * as endpoints from './endpoints'; + +export function getOpenAIModels(): Promise { + return request.get(endpoints.openAiModels()); +} + +export function postAICompletion(payload: t.TAICompletionRequest) { + return request.post(endpoints.getAICompletion(), payload); +} + +export function getConversations(pageNumber: string): Promise { + return request.get(endpoints.getConversations(pageNumber)); +} + +export function deleteConversation(payload: t.TDeleteConversationRequest) { + //todo: this should be a DELETE request + return request.post(endpoints.deleteConversation(), payload); +} + +export function clearAllConversations() { + return request.post(endpoints.deleteConversation()); +} + +export function getMessagesByConvoId(id: string): Promise { + return request.get(endpoints.getMessages(id)); +} + +export function getConversationById(id: string): Promise { + return request.get(endpoints.getConversationById(id)); +} + +export function updateConversation( + payload: t.TUpdateConversationRequest +): Promise { + return request.post(endpoints.updateConversation(), payload); +} + +export function updateCustomGpt(payload: t.TUpdateCustomGptRequest) { + return request.post(endpoints.customGpts(), payload); +} + +export function getCustomGpts(): Promise { + return request.get(endpoints.customGpts()); +} + +export function deleteCustomGpt(payload: t.TDeleteCustomGptRequest): Promise { + return request.post(endpoints.deleteCustomGpt(), payload); +} + +export function getModels(): Promise { + return request.get(endpoints.getModels()); +} + +export function getSearchEnabled(): Promise { + return request.get(endpoints.searchEnabled()); +} + +export function getSearchResults(q: string, pageNumber: string): Promise { + return request.get(endpoints.search(q, pageNumber)); +} + +export function getUser(): Promise { + return request.get(endpoints.user()); +} + +type TSearchFetcherProps = { + pre: () => void, + q: string, + pageNumber: string, + callback: (data: any) => void +}; + +export const searchFetcher = async ({ pre, q, pageNumber, callback }: TSearchFetcherProps) => { + pre(); + //@ts-ignore + const { data } = await request.get(endpoints.search(q, pageNumber)); + console.log('search data', data); + callback(data); +}; diff --git a/client/src/data-provider/endpoints.ts b/client/src/data-provider/endpoints.ts new file mode 100644 index 0000000000..72f2ac608b --- /dev/null +++ b/client/src/data-provider/endpoints.ts @@ -0,0 +1,60 @@ +export const openAiModels = () => { + return `/api/open-ai-models`; +}; + +export const getModels = () => { + return `/api/models`; +}; + +export const getAICompletion = () => { + return `/api/ask `; +}; + +export const user = () => { + return `/api/me`; +}; + +export const getMessages = (id: string) => { + return `/api/messages/${id}`; +}; + +export const getConversations = (pageNumber: string) => { + return `/api/convos?pageNumber=${pageNumber}`; +}; + +export const getConversationById = (id: string) => { + return `/api/convos/${id}`; +}; + +export const updateConversation = () => { + return `/api/convos/update`; +}; + +export const deleteConversation = () => { + return `/api/convos/clear`; +}; + +export const prompts = () => { + return `/api/prompts`; +}; + +export const customGpts = () => { + return `/api/customGpts`; +}; + +// TODO: turn this into a DELETE instead of POST on the backend +export const deleteCustomGpt = () => { + return `/api/customGpts/delete`; +}; + +export const generateTitle = () => { + return `/api/convos/gen_title`; +}; + +export const search = (q: string, pageNumber: string) => { + return `/api/search?q=${q}&pageNumber=${pageNumber}`; +} + +export const searchEnabled = () => { + return `/api/search/enable`; +} diff --git a/client/src/data-provider/headers-helpers.ts b/client/src/data-provider/headers-helpers.ts new file mode 100644 index 0000000000..195f7c7912 --- /dev/null +++ b/client/src/data-provider/headers-helpers.ts @@ -0,0 +1,9 @@ +import axios from 'axios'; + +export function setAcceptLanguageHeader(value: string): void { + axios.defaults.headers.common['Accept-Language'] = value; +} + +export function setTokenHeader(token: string) { + axios.defaults.headers.common['Authorization'] = 'Bearer ' + token; +} diff --git a/client/src/data-provider/index.ts b/client/src/data-provider/index.ts new file mode 100644 index 0000000000..ea66903770 --- /dev/null +++ b/client/src/data-provider/index.ts @@ -0,0 +1,7 @@ +export * from './data-service'; +// export * from './endpoints'; +export * from './request'; +export * from './types'; +export * from './react-query-service'; +export * from './headers-helpers'; +// export * from './events'; \ No newline at end of file diff --git a/client/src/data-provider/react-query-service.ts b/client/src/data-provider/react-query-service.ts new file mode 100644 index 0000000000..fa7b99c3d8 --- /dev/null +++ b/client/src/data-provider/react-query-service.ts @@ -0,0 +1,172 @@ +import { + UseQueryOptions, + useQuery, + useMutation, + useQueryClient, + UseMutationResult, + QueryObserverResult, +} from "@tanstack/react-query"; +import * as t from "./types"; +import * as dataService from "./data-service"; + +export enum QueryKeys { + messages = "messsages", + allConversations = "allConversations", + conversation = "conversation", + models = "models", + customGpts = "customGpts", + searchEnabled = "searchEnabled", + user = "user", +} + +export const useGetUserQuery = (): QueryObserverResult => { + return useQuery([QueryKeys.user], () => dataService.getUser(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useGetMessagesByConvoId = ( + id: string, + config?: UseQueryOptions +): QueryObserverResult => { + return useQuery([QueryKeys.messages, id], () => + dataService.getMessagesByConvoId(id), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + } + ); +}; + +export const useGetConversationByIdQuery = ( + id: string, + config?: UseQueryOptions + ): QueryObserverResult => { + return useQuery([QueryKeys.conversation, id], () => + dataService.getConversationById(id), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + enabled: false, + ...config + } + ); +} + +export const useUpdateConversationMutation = ( + id: string +): UseMutationResult< + t.TUpdateConversationResponse, + unknown, + t.TUpdateConversationRequest, + unknown +> => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TUpdateConversationRequest) => + dataService.updateConversation(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.conversation, id]); + }, + } + ); +}; + +// export const useDeleteConversationMutation = ( +// id: string +// ): UseMutationResult< + + +export const useUpdateCustomGptMutation = (): UseMutationResult< + t.TUpdateCustomGptResponse, + unknown, + t.TUpdateCustomGptRequest, + unknown +> => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TUpdateCustomGptRequest) => + dataService.updateCustomGpt(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.customGpts]); + }, + } + ); +}; + +export const useGetCustomGptsQuery = (): QueryObserverResult< + t.TCustomGpt[], + unknown +> => { + return useQuery([QueryKeys.customGpts], () => dataService.getCustomGpts(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useDeleteCustomGptMutation = (): UseMutationResult< + t.TDeleteCustomGptResponse, + unknown, + t.TDeleteCustomGptRequest, + unknown +> => { + const queryClient = useQueryClient(); + return useMutation( + (payload: t.TDeleteCustomGptRequest) => + dataService.deleteCustomGpt(payload), + { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.customGpts]); + }, + } + ); +}; + +export const useGetModelsQuery = (): QueryObserverResult< + t.TGetModelsResponse, + unknown +> => { + return useQuery([QueryKeys.models], () => dataService.getModels(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + }); +}; + +export const useClearConversationsMutation = (): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation(() => dataService.clearAllConversations(), { + onSuccess: () => { + queryClient.invalidateQueries([QueryKeys.allConversations]); + }, + }); +}; + +export const useGetConversationsQuery = (pageNumber: string): QueryObserverResult => { + return useQuery([QueryKeys.allConversations, pageNumber], () => + dataService.getConversations(pageNumber), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + } + ); +} + +export const useGetSearchEnabledQuery = (config?: UseQueryOptions): QueryObserverResult => { + return useQuery([QueryKeys.searchEnabled], () => + dataService.getSearchEnabled(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + } + ); +} diff --git a/client/src/data-provider/request.ts b/client/src/data-provider/request.ts new file mode 100644 index 0000000000..dcfb818d08 --- /dev/null +++ b/client/src/data-provider/request.ts @@ -0,0 +1,66 @@ +import axios, { AxiosRequestConfig } from "axios"; + +async function _get(url: string, options?: AxiosRequestConfig): Promise { + const response = await axios.get(url, { withCredentials: true, ...options}); + return response.data; +} + +async function _post(url: string, data?: any) { + const response = await axios.post(url, JSON.stringify(data), { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +async function _postMultiPart( + url: string, + formData: FormData, + options?: AxiosRequestConfig +) { + const response = await axios.post(url, formData, { + withCredentials: true, + ...options, + headers: { "Content-Type": "multipart/form-data" }, + }); + return response.data; +} + +async function _put(url: string, data?: any) { + const response = await axios.put(url, JSON.stringify(data), { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +async function _delete(url: string): Promise { + const response = await axios.delete(url, { withCredentials: true }); + return response.data; +} + +async function _deleteWithOptions( + url: string, + options?: AxiosRequestConfig +): Promise { + const response = await axios.delete(url, { withCredentials: true, ...options}); + return response.data; +} + +async function _patch(url: string, data?: any) { + const response = await axios.patch(url, JSON.stringify(data), { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + }); + return response.data; +} + +export default { + get: _get, + post: _post, + postMultiPart: _postMultiPart, + put: _put, + delete: _delete, + deleteWithOptions: _deleteWithOptions, + patch: _patch, +}; diff --git a/client/src/data-provider/types.ts b/client/src/data-provider/types.ts new file mode 100644 index 0000000000..fb60f4e37f --- /dev/null +++ b/client/src/data-provider/types.ts @@ -0,0 +1,183 @@ +export type TMessage = { + messageId: string, + conversationId: string, + conversationSignature: string | null, + clientId: string, + invocationId: string, + parentMessageId: string, + sender: string, + text: string, + isCreatedByUser: boolean, + error: boolean, + createdAt: string, + updatedAt: string, + searchResult: string[], + submitting: boolean, + children?: any[] | undefined, + bgColor?: string, + model?: string, + cancelled?: boolean +}; + +export type TMessageTreeNode = {} + +export type TSearchMessage = {} + +export type TSearchMessageTreeNode = {} + +export type TMessageToAsk = {} + + +export enum EModelEndpoint { + azureOpenAI = 'azureOpenAI', + openAI = 'openAI', + bingAI = 'bingAI', + chatGPTBrowser = 'chatGPTBrowser' +} + +export type TConversation = { + conversationId: string; + title: string; + user: string | null; + endpoint: EModelEndpoint; + model: string; // for azureOpenAI, openAI, chatGPTBrowser only, eg. gpt-3.5-turbo + // for azureOpenAI, openAI only + chatGptLabel?: string; + promptPrefix?: string; + temperature?: number; + top_p?: number; + presence_penalty?: number; + // for bingAI only + jailbreak?: boolean; + jailbreakConversationId?: string; + conversationSignature?: string; + clientId?: string; + invocationId?: string; + toneStyle?: string; + suggestions?: string[]; + messages?: TMessage[]; + createdAt: string; + updatedAt: string; +} + +export type TPrompt = { + title: string, + prompt: string, + category: string, + createdAt: string, + updatedAt: string +}; + +export type TCustomGpt = { + chatGptLabel: string, + promptPrefix: string, + value: string, + createdAt: string, + updatedAt: string, + _id: string +}; + +export type TModel = { + _id: string, + name: string, + value: string, + model: string, + chatGptLabel?: string, + promptPrefix?: string +}; + +export type TUser = { + username: string, + display: string +}; + +export type TGetConversationsResponse = { + conversations: TConversation[], + pageNumber: string, + pageSize: string | number, + pages: string | number +}; + +export type TGetConversationResponse = { + data: TConversation +}; + +export type TGetMessagesResponse = { + data: TMessage[] +}; + +export type TDeleteConversationRequest = { + conversationId: string +}; + +export type TAICompletionRequest = { + chatGptLabel?: string, + conversationId: string, + current: boolean, + isCreatedByUser: boolean, + model: string, + messageId: string, + parentMessageId: string, + overrideParentMessageId?: boolean, + promptPrefix?: string, + sender: string, + text: string +}; + +export type TGetModelsResponse = { + hasOpenAI: boolean, + hasChatGpt: boolean, + hasBing: boolean +}; + +export type TOpenAIModel = { + object: string, + id: string, + ready: boolean, + owner: string, + created: string | null, + permissions: string[] | null +}; + +export type TOpenAIModels = { + models: { + object: string, + data: TOpenAIModel[] + } +}; + +export type TConversationUpdate = { + conversationId: string, + title?: string +}; +export type TUpdateConversationRequest = { + arg: {}, + withCredentials?: boolean +}; + +export type TUpdateConversationResponse = { + data: TConversation +}; + +export type TUpdateCustomGptRequest = { + value: string, + chatGptLabel: string, + promptPrefix?: string, + prevLabel?: string +}; + +export type TUpdateCustomGptResponse = {}; + +export type TDeleteCustomGptRequest = { + id: string +}; + +export type TDeleteCustomGptResponse = {}; + +export type TClearConversationsRequest = {}; + +export type TClearConversationsResponse = {}; + +export type TGetCustomGptsResponse = {}; + +export type TSearchResults = {}; \ No newline at end of file