feat: customise free api endpoint

This commit is contained in:
Jing Hua 2023-03-07 08:15:28 +08:00
parent 98729351e0
commit 8ba2e473cd
5 changed files with 91 additions and 38 deletions

View file

@ -1,8 +1,7 @@
import { ConfigInterface, MessageInterface } from '@type/chat';
export const endpoint = 'https://chatgpt-api.shn.hk/v1/';
export const getChatCompletion = async (
endpoint: string,
messages: MessageInterface[],
config: ConfigInterface
) => {
@ -24,6 +23,7 @@ export const getChatCompletion = async (
};
export const getChatCompletionStream = async (
endpoint: string,
messages: MessageInterface[],
config: ConfigInterface
) => {
@ -39,6 +39,11 @@ export const getChatCompletionStream = async (
stream: true,
}),
});
if (response.status === 404)
throw new Error(
'Message from freechatgpt.chat:\nInvalid API endpoint! We recommend you to check your free API endpoint.'
);
const text = await response.text();
if (response.status === 429 && text.includes('insufficient_quota'))
throw new Error(

View file

@ -15,26 +15,34 @@ const ApiMenu = ({
const setApiKey = useStore((state) => state.setApiKey);
const apiFree = useStore((state) => state.apiFree);
const setApiFree = useStore((state) => state.setApiFree);
const apiFreeEndpoint = useStore((state) => state.apiFreeEndpoint);
const setApiFreeEndpoint = useStore((state) => state.setApiFreeEndpoint);
const [_apiFree, _setApiFree] = useState<boolean>(apiFree);
const [_apiKey, _setApiKey] = useState<string>(apiKey || '');
const [error, setError] = useState<boolean>(false);
const [_apiFreeEndpoint, _setApiFreeEndpoint] =
useState<string>(apiFreeEndpoint);
const [error, setError] = useState<string>('');
const handleSave = async () => {
if (_apiFree === true) {
setApiFreeEndpoint(_apiFreeEndpoint);
setApiFree(true);
setError('');
setIsModalOpen(false);
} else {
const valid = await validateApiKey(_apiKey);
if (valid) {
setApiKey(_apiKey);
setApiFree(false);
setError(false);
setError('');
setIsModalOpen(false);
} else {
setError(true);
setError(
'Error: Invalid API key or network blocked. Please check your API key and network settings for OpenAI API.'
);
setTimeout(() => {
setError(false);
setError('');
}, 10000);
}
}
@ -42,17 +50,24 @@ const ApiMenu = ({
useEffect(() => {
if (apiKey) {
setApiFree(false);
_setApiFree(false);
_setApiKey(apiKey);
}
setApiFree(false);
_setApiFree(false);
_setApiKey(apiKey);
}
}, []);
const handleClose = () => {
_setApiFree(apiFree);
_setApiFreeEndpoint(apiFreeEndpoint);
apiKey && _setApiKey(apiKey);
};
return isModalOpen ? (
<PopupModal
title='API'
setIsModalOpen={setIsModalOpen}
handleConfirm={handleSave}
handleClose={handleClose}
>
<div className='p-6 border-b border-gray-200 dark:border-gray-600'>
<div className='flex items-center mb-2'>
@ -63,16 +78,40 @@ const ApiMenu = ({
onChange={() => _setApiFree(true)}
/>
<label className='ml-2 text-sm font-medium text-gray-900 dark:text-gray-300'>
Use free API from{' '}
<a
href='https://github.com/ayaka14732/ChatGPTAPIFree'
className='underline dark:hover:text-white hover:text-black'
target='_blank'
>
Ayaka
</a>
Use free API endpoint
</label>
</div>
{_apiFree && (
<div className='mt-2 mb-6'>
<div className='text-sm font-medium text-gray-900 dark:text-gray-300 mb-2'>
Use free API endpoint from{' '}
<a
href='https://github.com/ayaka14732/ChatGPTAPIFree'
className='underline dark:hover:text-white hover:text-black'
target='_blank'
>
Ayaka
</a>
: https://chatgpt-api.shn.hk/v1/ or enter your own API endpoint
</div>
<div className='flex gap-2 items-center justify-center'>
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm'>
Free API Endpoint
</div>
<input
type='text'
className='text-gray-800 dark:text-white p-3 text-sm border-none bg-gray-200 dark:bg-gray-600 rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
value={_apiFreeEndpoint}
placeholder='https://chatgpt-api.shn.hk/v1/'
onChange={(e) => {
_setApiFreeEndpoint(e.target.value);
}}
/>
</div>
</div>
)}
<div className='flex items-center'>
<input
type='radio'
@ -86,27 +125,19 @@ const ApiMenu = ({
</div>
{_apiFree === false && (
<>
<div className='flex gap-2 items-center justify-center mt-2'>
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm'>
API Key
</div>
<input
type='text'
className='text-gray-800 dark:text-white p-3 text-sm border-none bg-gray-200 dark:bg-gray-600 rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
value={_apiKey}
onChange={(e) => {
_setApiKey(e.target.value);
}}
/>
<div className='flex gap-2 items-center justify-center mt-2'>
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm'>
API Key
</div>
{error && (
<div className='bg-red-600/50 p-2 rounded-sm mt-3 text-gray-900 dark:text-gray-300 text-sm'>
Error: Invalid API key or network blocked. Please check your API
key and network settings for OpenAI API.
</div>
)}
</>
<input
type='text'
className='text-gray-800 dark:text-white p-3 text-sm border-none bg-gray-200 dark:bg-gray-600 rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
value={_apiKey}
onChange={(e) => {
_setApiKey(e.target.value);
}}
/>
</div>
)}
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm mt-4 text-center'>
@ -126,6 +157,11 @@ const ApiMenu = ({
purpose of accessing the OpenAI API and not for any other unauthorised
use.
</div>
{error !== '' && (
<div className='bg-red-600/50 p-2 rounded-sm mt-3 text-gray-900 dark:text-gray-300 text-sm'>
{error}
</div>
)}
</div>
</PopupModal>
) : (

View file

@ -40,6 +40,7 @@ const useSubmit = () => {
if (apiFree) {
stream = await getChatCompletionStreamFree(
useStore.getState().apiFreeEndpoint,
messages,
chats[currentChatIndex].config
);

View file

@ -3,12 +3,15 @@ import { StoreSlice } from './store';
export interface AuthSlice {
apiKey?: string;
apiFree: boolean;
apiFreeEndpoint: string;
setApiKey: (apiKey: string) => void;
setApiFree: (apiFree: boolean) => void;
setApiFreeEndpoint: (apiFreeEndpoint: string) => void;
}
export const createAuthSlice: StoreSlice<AuthSlice> = (set, get) => ({
apiFree: true,
apiFreeEndpoint: 'https://chatgpt-api.shn.hk/v1/',
setApiKey: (apiKey: string) => {
set((prev: AuthSlice) => ({
...prev,
@ -21,4 +24,10 @@ export const createAuthSlice: StoreSlice<AuthSlice> = (set, get) => ({
apiFree: apiFree,
}));
},
setApiFreeEndpoint: (apiFreeEndpoint: string) => {
set((prev: AuthSlice) => ({
...prev,
apiFreeEndpoint: apiFreeEndpoint,
}));
},
});

View file

@ -26,6 +26,8 @@ const useStore = create<StoreState>()(
chats: state.chats,
currentChatIndex: state.currentChatIndex,
apiKey: state.apiKey,
apiFree: state.apiFree,
apiFreeEndpoint: state.apiFreeEndpoint,
theme: state.theme,
}),
}