2023-03-08 21:30:11 +00:00
|
|
|
import { useDebouncedValue } from "@mantine/hooks";
|
2023-03-06 13:30:58 +00:00
|
|
|
import React, { useState, useRef, useMemo, useEffect, useCallback } from "react";
|
2023-03-08 21:30:11 +00:00
|
|
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
2023-03-06 13:30:58 +00:00
|
|
|
import { backend } from "./backend";
|
|
|
|
import ChatManagerInstance, { ChatManager } from "./chat-manager";
|
|
|
|
import { defaultElevenLabsVoiceID } from "./elevenlabs";
|
2023-03-08 21:30:11 +00:00
|
|
|
import { loadParameters, saveParameters } from "./parameters";
|
2023-03-09 09:38:59 +00:00
|
|
|
import { Message, Parameters } from "./types";
|
2023-03-08 21:30:11 +00:00
|
|
|
import { useChat, UseChatResult } from "./use-chat";
|
2023-03-06 13:30:58 +00:00
|
|
|
|
|
|
|
export interface Context {
|
|
|
|
authenticated: boolean;
|
|
|
|
chat: ChatManager;
|
2023-03-08 21:30:11 +00:00
|
|
|
id: string | undefined | null;
|
|
|
|
currentChat: UseChatResult;
|
|
|
|
isShare: boolean;
|
2023-03-06 13:30:58 +00:00
|
|
|
apiKeys: {
|
|
|
|
openai: string | undefined | null;
|
|
|
|
setOpenAIApiKey: (apiKey: string | null) => void;
|
|
|
|
elevenlabs: string | undefined | null;
|
|
|
|
setElevenLabsApiKey: (apiKey: string | null) => void;
|
|
|
|
};
|
|
|
|
settings: {
|
|
|
|
tab: string | undefined | null;
|
|
|
|
option: string | undefined | null;
|
|
|
|
open: (tab: string, option?: string | undefined | null) => void;
|
|
|
|
close: () => void;
|
|
|
|
};
|
|
|
|
voice: {
|
|
|
|
id: string;
|
|
|
|
setVoiceID: (id: string) => void;
|
2023-03-08 21:30:11 +00:00
|
|
|
};
|
|
|
|
generating: boolean;
|
|
|
|
message: string;
|
|
|
|
parameters: Parameters;
|
2023-03-09 09:38:59 +00:00
|
|
|
setMessage: (message: string, parentID?: string) => void;
|
2023-03-08 21:30:11 +00:00
|
|
|
setParameters: (parameters: Parameters) => void;
|
|
|
|
onNewMessage: (message?: string) => Promise<boolean>;
|
2023-03-09 09:38:59 +00:00
|
|
|
regenerateMessage: (message: Message) => Promise<boolean>;
|
|
|
|
editMessage: (message: Message, content: string) => Promise<boolean>;
|
2023-03-06 13:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const AppContext = React.createContext<Context>({} as any);
|
|
|
|
|
|
|
|
export function useCreateAppContext(): Context {
|
2023-03-08 21:30:11 +00:00
|
|
|
const { id } = useParams();
|
|
|
|
const pathname = useLocation().pathname;
|
|
|
|
const isShare = pathname.startsWith('/s/');
|
|
|
|
const navigate = useNavigate();
|
2023-03-09 09:38:59 +00:00
|
|
|
|
2023-03-08 21:30:11 +00:00
|
|
|
const chatManager = useRef(ChatManagerInstance);
|
|
|
|
const currentChat = useChat(chatManager.current, id, isShare);
|
2023-03-09 19:50:57 +00:00
|
|
|
const [authenticated, setAuthenticated] = useState(backend.current?.isAuthenticated || false);
|
2023-03-06 13:30:58 +00:00
|
|
|
|
|
|
|
const updateAuth = useCallback((authenticated: boolean) => setAuthenticated(authenticated), []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-03-09 19:50:57 +00:00
|
|
|
backend.current?.on('authenticated', updateAuth);
|
2023-03-06 13:30:58 +00:00
|
|
|
return () => {
|
2023-03-09 19:50:57 +00:00
|
|
|
backend.current?.off('authenticated', updateAuth)
|
2023-03-06 13:30:58 +00:00
|
|
|
};
|
2023-03-09 19:50:57 +00:00
|
|
|
}, [updateAuth]);
|
2023-03-06 13:30:58 +00:00
|
|
|
|
|
|
|
const [openaiApiKey, setOpenAIApiKey] = useState<string | null>(
|
|
|
|
localStorage.getItem('openai-api-key') || ''
|
|
|
|
);
|
|
|
|
const [elevenLabsApiKey, setElevenLabsApiKey] = useState<string | null>(
|
|
|
|
localStorage.getItem('elevenlabs-api-key') || ''
|
|
|
|
);
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-03-08 21:30:11 +00:00
|
|
|
if (openaiApiKey) {
|
|
|
|
localStorage.setItem('openai-api-key', openaiApiKey || '');
|
|
|
|
}
|
2023-03-06 13:30:58 +00:00
|
|
|
}, [openaiApiKey]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-03-08 21:30:11 +00:00
|
|
|
if (elevenLabsApiKey) {
|
|
|
|
localStorage.setItem('elevenlabs-api-key', elevenLabsApiKey || '');
|
|
|
|
}
|
2023-03-06 13:30:58 +00:00
|
|
|
}, [elevenLabsApiKey]);
|
|
|
|
|
|
|
|
const [settingsTab, setSettingsTab] = useState<string | null | undefined>();
|
|
|
|
const [option, setOption] = useState<string | null | undefined>();
|
|
|
|
|
|
|
|
const [voiceID, setVoiceID] = useState(localStorage.getItem('voice-id') || defaultElevenLabsVoiceID);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
localStorage.setItem('voice-id', voiceID);
|
|
|
|
}, [voiceID]);
|
|
|
|
|
2023-03-08 21:30:11 +00:00
|
|
|
const [generating, setGenerating] = useState(false);
|
|
|
|
|
|
|
|
const [message, setMessage] = useState('');
|
|
|
|
|
|
|
|
const [_parameters, setParameters] = useState<Parameters>(loadParameters(id));
|
|
|
|
useEffect(() => {
|
|
|
|
setParameters(loadParameters(id));
|
|
|
|
}, [id]);
|
|
|
|
|
|
|
|
const [parameters] = useDebouncedValue(_parameters, 2000);
|
|
|
|
useEffect(() => {
|
|
|
|
if (id) {
|
|
|
|
saveParameters(id, parameters);
|
|
|
|
}
|
|
|
|
saveParameters('', parameters);
|
|
|
|
}, [id, parameters]);
|
|
|
|
|
|
|
|
const onNewMessage = useCallback(async (message?: string) => {
|
|
|
|
if (isShare) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!message?.trim().length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!openaiApiKey) {
|
|
|
|
setSettingsTab('user');
|
|
|
|
setOption('openai-api-key');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setGenerating(true);
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
await chatManager.current.sendMessage({
|
|
|
|
chatID: id,
|
|
|
|
content: message.trim(),
|
|
|
|
requestedParameters: {
|
|
|
|
...parameters,
|
|
|
|
apiKey: openaiApiKey,
|
|
|
|
},
|
|
|
|
parentID: currentChat.leaf?.id,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const id = await chatManager.current.createChat();
|
|
|
|
await chatManager.current.sendMessage({
|
|
|
|
chatID: id,
|
|
|
|
content: message.trim(),
|
|
|
|
requestedParameters: {
|
|
|
|
...parameters,
|
|
|
|
apiKey: openaiApiKey,
|
|
|
|
},
|
|
|
|
parentID: currentChat.leaf?.id,
|
|
|
|
});
|
|
|
|
navigate('/chat/' + id);
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(() => setGenerating(false), 4000);
|
|
|
|
|
|
|
|
return true;
|
2023-03-09 19:50:57 +00:00
|
|
|
}, [chatManager, openaiApiKey, id, parameters, currentChat.leaf, navigate, isShare]);
|
2023-03-08 21:30:11 +00:00
|
|
|
|
2023-03-09 09:38:59 +00:00
|
|
|
const regenerateMessage = useCallback(async (message: Message) => {
|
|
|
|
if (isShare) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!openaiApiKey) {
|
|
|
|
setSettingsTab('user');
|
|
|
|
setOption('openai-api-key');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setGenerating(true);
|
|
|
|
|
|
|
|
await chatManager.current.regenerate(message, {
|
|
|
|
...parameters,
|
|
|
|
apiKey: openaiApiKey,
|
|
|
|
});
|
|
|
|
|
|
|
|
setTimeout(() => setGenerating(false), 4000);
|
|
|
|
|
|
|
|
return true;
|
2023-03-09 19:50:57 +00:00
|
|
|
}, [chatManager, openaiApiKey, parameters, isShare]);
|
2023-03-09 09:38:59 +00:00
|
|
|
|
|
|
|
const editMessage = useCallback(async (message: Message, content: string) => {
|
|
|
|
if (isShare) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!content?.trim().length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!openaiApiKey) {
|
|
|
|
setSettingsTab('user');
|
|
|
|
setOption('openai-api-key');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setGenerating(true);
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
await chatManager.current.sendMessage({
|
|
|
|
chatID: id,
|
|
|
|
content: content.trim(),
|
|
|
|
requestedParameters: {
|
|
|
|
...parameters,
|
|
|
|
apiKey: openaiApiKey,
|
|
|
|
},
|
|
|
|
parentID: message.parentID,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const id = await chatManager.current.createChat();
|
|
|
|
await chatManager.current.sendMessage({
|
|
|
|
chatID: id,
|
|
|
|
content: content.trim(),
|
|
|
|
requestedParameters: {
|
|
|
|
...parameters,
|
|
|
|
apiKey: openaiApiKey,
|
|
|
|
},
|
|
|
|
parentID: message.parentID,
|
|
|
|
});
|
|
|
|
navigate('/chat/' + id);
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(() => setGenerating(false), 4000);
|
|
|
|
|
|
|
|
return true;
|
2023-03-09 19:50:57 +00:00
|
|
|
}, [chatManager, openaiApiKey, id, parameters, isShare, navigate]);
|
2023-03-09 09:38:59 +00:00
|
|
|
|
2023-03-06 13:30:58 +00:00
|
|
|
const context = useMemo<Context>(() => ({
|
|
|
|
authenticated,
|
2023-03-08 21:30:11 +00:00
|
|
|
id,
|
|
|
|
chat: chatManager.current,
|
|
|
|
currentChat,
|
|
|
|
isShare,
|
2023-03-06 13:30:58 +00:00
|
|
|
apiKeys: {
|
|
|
|
openai: openaiApiKey,
|
|
|
|
elevenlabs: elevenLabsApiKey,
|
|
|
|
setOpenAIApiKey,
|
|
|
|
setElevenLabsApiKey,
|
|
|
|
},
|
|
|
|
settings: {
|
|
|
|
tab: settingsTab,
|
|
|
|
option: option,
|
|
|
|
open: (tab: string, option?: string | undefined | null) => {
|
|
|
|
setSettingsTab(tab);
|
|
|
|
setOption(option);
|
|
|
|
},
|
|
|
|
close: () => {
|
|
|
|
setSettingsTab(null);
|
|
|
|
setOption(null);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
voice: {
|
|
|
|
id: voiceID,
|
|
|
|
setVoiceID,
|
|
|
|
},
|
2023-03-08 21:30:11 +00:00
|
|
|
generating,
|
|
|
|
message,
|
|
|
|
parameters,
|
|
|
|
setMessage,
|
|
|
|
setParameters,
|
|
|
|
onNewMessage,
|
2023-03-09 09:38:59 +00:00
|
|
|
regenerateMessage,
|
|
|
|
editMessage,
|
2023-03-08 21:30:11 +00:00
|
|
|
}), [chatManager, authenticated, openaiApiKey, elevenLabsApiKey, settingsTab, option, voiceID,
|
2023-03-09 19:50:57 +00:00
|
|
|
generating, message, parameters, onNewMessage, regenerateMessage, editMessage, currentChat,
|
|
|
|
id, isShare]);
|
2023-03-06 13:30:58 +00:00
|
|
|
|
|
|
|
return context;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useAppContext() {
|
|
|
|
return React.useContext(AppContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function AppContextProvider(props: { children: React.ReactNode }) {
|
|
|
|
const context = useCreateAppContext();
|
|
|
|
return <AppContext.Provider value={context}>{props.children}</AppContext.Provider>;
|
|
|
|
}
|