mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
🔀 fix: Endpoint Type Mismatch when Switching Conversations (#1834)
* refactor(useUpdateUserKeysMutation): only invalidate the endpoint whose key is being updated by user * fix(assistants): await `getUserKeyExpiry` call * chore: fix spinner loading color * refactor(initializeClient): make known which endpoint api Key is missing * fix: prevent an `endpointType` mismatch by making it impossible to assign when the `endpointsConfig` doesn't have a `type` defined, also prefer `getQueryData` call to useQuery in useChatHelpers
This commit is contained in:
parent
d1eb7fcfc7
commit
5291d18f38
10 changed files with 28 additions and 18 deletions
|
|
@ -32,7 +32,10 @@ const initializeClient = async ({ req, res, endpointOption, initAppClient = fals
|
|||
|
||||
let userKey = null;
|
||||
if (isUserProvided) {
|
||||
const expiresAt = getUserKeyExpiry({ userId: req.user.id, name: EModelEndpoint.assistants });
|
||||
const expiresAt = await getUserKeyExpiry({
|
||||
userId: req.user.id,
|
||||
name: EModelEndpoint.assistants,
|
||||
});
|
||||
checkUserKeyExpiry(
|
||||
expiresAt,
|
||||
'Your Assistants API key has expired. Please provide your API key again.',
|
||||
|
|
@ -43,7 +46,7 @@ const initializeClient = async ({ req, res, endpointOption, initAppClient = fals
|
|||
let apiKey = isUserProvided ? userKey : credentials;
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('API key not provided.');
|
||||
throw new Error(`${EModelEndpoint.assistants} API key not provided.`);
|
||||
}
|
||||
|
||||
/** @type {OpenAIClient} */
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
}
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('API key not provided.');
|
||||
throw new Error(`${endpoint} API key not provided.`);
|
||||
}
|
||||
|
||||
const client = new PluginsClient(apiKey, clientOptions);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// gptPlugins/initializeClient.spec.js
|
||||
const { PluginsClient } = require('~/app');
|
||||
const { EModelEndpoint } = require('librechat-data-provider');
|
||||
const { getUserKey } = require('~/server/services/UserService');
|
||||
const initializeClient = require('./initializeClient');
|
||||
const { getUserKey } = require('../../UserService');
|
||||
const { PluginsClient } = require('~/app');
|
||||
|
||||
// Mock getUserKey since it's the only function we want to mock
|
||||
jest.mock('~/server/services/UserService', () => ({
|
||||
|
|
@ -112,7 +113,7 @@ describe('gptPlugins/initializeClient', () => {
|
|||
const endpointOption = { modelOptions: { model: 'default-model' } };
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
'API key not provided.',
|
||||
`${EModelEndpoint.openAI} API key not provided.`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const initializeClient = async ({ req, res, endpointOption }) => {
|
|||
}
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('API key not provided.');
|
||||
throw new Error(`${endpoint} API key not provided.`);
|
||||
}
|
||||
|
||||
const client = new OpenAIClient(apiKey, clientOptions);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
const { OpenAIClient } = require('~/app');
|
||||
const initializeClient = require('./initializeClient');
|
||||
const { EModelEndpoint } = require('librechat-data-provider');
|
||||
const { getUserKey } = require('~/server/services/UserService');
|
||||
const initializeClient = require('./initializeClient');
|
||||
const { OpenAIClient } = require('~/app');
|
||||
|
||||
// Mock getUserKey since it's the only function we want to mock
|
||||
jest.mock('~/server/services/UserService', () => ({
|
||||
|
|
@ -145,7 +146,7 @@ describe('initializeClient', () => {
|
|||
const endpointOption = {};
|
||||
|
||||
await expect(initializeClient({ req, res, endpointOption })).rejects.toThrow(
|
||||
'API key not provided.',
|
||||
`${EModelEndpoint.openAI} API key not provided.`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { cn } from '~/utils/';
|
|||
export default function Spinner({ className = 'm-auto', size = '1em' }) {
|
||||
return (
|
||||
<svg
|
||||
stroke="#ffffff"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
ContentTypes,
|
||||
} from 'librechat-data-provider';
|
||||
import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { useGetMessagesByConvoId, useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import { useGetMessagesByConvoId } from 'librechat-data-provider/react-query';
|
||||
import type {
|
||||
TMessage,
|
||||
TSubmission,
|
||||
|
|
@ -21,12 +21,12 @@ import useSetFilesToDelete from './Files/useSetFilesToDelete';
|
|||
import useGetSender from './Conversations/useGetSender';
|
||||
import { useAuthContext } from './AuthContext';
|
||||
import useUserKey from './Input/useUserKey';
|
||||
import { getEndpointField } from '~/utils';
|
||||
import useNewConvo from './useNewConvo';
|
||||
import store from '~/store';
|
||||
|
||||
// this to be set somewhere else
|
||||
export default function useChatHelpers(index = 0, paramId: string | undefined) {
|
||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
const setShowStopButton = useSetRecoilState(store.showStopButtonByIndex(index));
|
||||
const [files, setFiles] = useRecoilState(store.filesByIndex(index));
|
||||
const [filesLoading, setFilesLoading] = useState(false);
|
||||
|
|
@ -39,7 +39,7 @@ export default function useChatHelpers(index = 0, paramId: string | undefined) {
|
|||
const { newConversation } = useNewConvo(index);
|
||||
const { useCreateConversationAtom } = store;
|
||||
const { conversation, setConversation } = useCreateConversationAtom(index);
|
||||
const { conversationId, endpoint, endpointType } = conversation ?? {};
|
||||
const { conversationId, endpoint } = conversation ?? {};
|
||||
|
||||
const queryParam = paramId === 'new' ? paramId : conversationId ?? paramId ?? '';
|
||||
|
||||
|
|
@ -142,6 +142,9 @@ export default function useChatHelpers(index = 0, paramId: string | undefined) {
|
|||
|
||||
const thread_id = parentMessage?.thread_id ?? latestMessage?.thread_id;
|
||||
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const endpointType = getEndpointField(endpointsConfig, endpoint, 'type');
|
||||
|
||||
// set the endpoint option
|
||||
const convo = parseCompactConvo({
|
||||
endpoint,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ const useNewConvo = (index = 0) => {
|
|||
const endpointType = getEndpointField(endpointsConfig, defaultEndpoint, 'type');
|
||||
if (!conversation.endpointType && endpointType) {
|
||||
conversation.endpointType = endpointType;
|
||||
} else if (conversation.endpointType && !endpointType) {
|
||||
conversation.endpointType = undefined;
|
||||
}
|
||||
|
||||
if (!conversation.assistant_id && defaultEndpoint === EModelEndpoint.assistants) {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export default function ChatRoute() {
|
|||
}, [initialConvoQuery.data, modelsQuery.data, endpointsQuery.data]);
|
||||
|
||||
if (endpointsQuery.isLoading || modelsQuery.isLoading) {
|
||||
return <Spinner className="m-auto dark:text-white" />;
|
||||
return <Spinner className="m-auto text-black dark:text-white" />;
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
|
|
|
|||
|
|
@ -117,8 +117,8 @@ export const useUpdateUserKeysMutation = (): UseMutationResult<
|
|||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation((payload: t.TUpdateUserKeyRequest) => dataService.updateUserKey(payload), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries([QueryKeys.name]);
|
||||
onSuccess: (data, variables) => {
|
||||
queryClient.invalidateQueries([QueryKeys.name, variables.name]);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -136,7 +136,7 @@ export const useRevokeUserKeyMutation = (name: string): UseMutationResult<unknow
|
|||
const queryClient = useQueryClient();
|
||||
return useMutation(() => dataService.revokeUserKey(name), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries([QueryKeys.name]);
|
||||
queryClient.invalidateQueries([QueryKeys.name, name]);
|
||||
if (name === s.EModelEndpoint.assistants) {
|
||||
queryClient.invalidateQueries([QueryKeys.assistants, defaultOrderQuery]);
|
||||
queryClient.invalidateQueries([QueryKeys.assistantDocs]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue