mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-10 11:34:23 +01:00
🤖 feat: Model Specs & Save Tools per Convo/Preset (#2578)
* WIP: first pass ModelSpecs * refactor(onSelectEndpoint): use `getConvoSwitchLogic` * feat: introduce iconURL, greeting, frontend fields for conversations/presets/messages * feat: conversation.iconURL & greeting in Landing * feat: conversation.iconURL & greeting in New Chat button * feat: message.iconURL * refactor: ConversationIcon -> ConvoIconURL * WIP: add spec as a conversation field * refactor: useAppStartup, set spec on initial load for new chat, allow undefined spec, add localStorage keys enum, additional type fields for spec * feat: handle `showIconInMenu`, `showIconInHeader`, undefined `iconURL` and no specs on initial load * chore: handle undefined or empty modelSpecs * WIP: first pass, modelSpec schema for custom config * refactor: move default filtered tools definition to ToolService * feat: pass modelSpecs from backend via startupConfig * refactor: modelSpecs config, return and define list * fix: react error and include iconURL in responseMessage * refactor: add iconURL to responseMessage only * refactor: getIconEndpoint * refactor: pass TSpecsConfig * fix(assistants): differentiate compactAssistantSchema, correctly resets shared conversation state with other endpoints * refactor: assistant id prefix localStorage key * refactor: add more LocalStorageKeys and replace hardcoded values * feat: prioritize spec on new chat behavior: last selected modelSpec behavior (localStorage) * feat: first pass, interface config * chore: WIP, todo: add warnings based on config.modelSpecs settings. * feat: enforce modelSpecs if configured * feat: show config file yaml errors * chore: delete unused legacy Plugins component * refactor: set tools to localStorage from recoil store * chore: add stable recoil setter to useEffect deps * refactor: save tools to conversation documents * style(MultiSelectPop): dynamic height, remove unused import * refactor(react-query): use localstorage keys and pass config to useAvailablePluginsQuery * feat(utils): add mapPlugins * refactor(Convo): use conversation.tools if defined, lastSelectedTools if not * refactor: remove unused legacy code using `useSetOptions`, remove conditional flag `isMultiChat` for using legacy settings * refactor(PluginStoreDialog): add exhaustive-deps which are stable react state setters * fix(HeaderOptions): pass `popover` as true * refactor(useSetStorage): use project enums * refactor: use LocalStorageKeys enum * fix: prevent setConversation from setting falsy values in lastSelectedTools * refactor: use map for availableTools state and available Plugins query * refactor(updateLastSelectedModel): organize logic better and add note on purpose * fix(setAgentOption): prevent reseting last model to secondary model for gptPlugins * refactor(buildDefaultConvo): use enum * refactor: remove `useSetStorage` and consolidate areas where conversation state is saved to localStorage * fix: conversations retain tools on refresh * fix(gptPlugins): prevent nullish tools from being saved * chore: delete useServerStream * refactor: move initial plugins logic to useAppStartup * refactor(MultiSelectDropDown): add more pass-in className props * feat: use tools in presets * chore: delete unused usePresetOptions * refactor: new agentOptions default handling * chore: note * feat: add label and custom instructions to agents * chore: remove 'disabled with tools' message * style: move plugins to 2nd column in parameters * fix: TPreset type for agentOptions * fix: interface controls * refactor: add interfaceConfig, use Separator within Switcher * refactor: hide Assistants panel if interface.parameters are disabled * fix(Header): only modelSpecs if list is greater than 0 * refactor: separate MessageIcon logic from useMessageHelpers for better react rule-following * fix(AppService): don't use reserved keyword 'interface' * feat: set existing Icon for custom endpoints through iconURL * fix(ci): tests passing for App Service * docs: refactor custom_config.md for readability and better organization, also include missing values * docs: interface section and re-organize docs * docs: update modelSpecs info * chore: remove unused files * chore: remove unused files * chore: move useSetIndexOptions * chore: remove unused file * chore: move useConversation(s) * chore: move useDefaultConvo * chore: move useNavigateToConvo * refactor: use plugin install hook so it can be used elsewhere * chore: import order * update docs * refactor(OpenAI/Plugins): allow modelLabel as an initial value for chatGptLabel * chore: remove unused EndpointOptionsPopover and hide 'Save as Preset' button if preset UI visibility disabled * feat(loadDefaultInterface): issue warnings based on values * feat: changelog for custom config file * docs: add additional changelog note * fix: prevent unavailable tool selection from preset and update availableTools on Plugin installations * feat: add `filteredTools` option in custom config * chore: changelog * fix(MessageIcon): always overwrite conversation.iconURL in messageSettings * fix(ModelSpecsMenu): icon edge cases * fix(NewChat): dynamic icon * fix(PluginsClient): always include endpoint in responseMessage * fix: always include endpoint and iconURL in responseMessage across different response methods * feat: interchangeable keys for modelSpec enforcing
This commit is contained in:
parent
a5cac03fa4
commit
0e50c07e3f
130 changed files with 3934 additions and 2973 deletions
|
|
@ -1,4 +1,9 @@
|
|||
export { default as usePresets } from './usePresets';
|
||||
export { default as useGetSender } from './useGetSender';
|
||||
export { default as useDefaultConvo } from './useDefaultConvo';
|
||||
export { default as useConversation } from './useConversation';
|
||||
export { default as useConversations } from './useConversations';
|
||||
export { default as useDebouncedInput } from './useDebouncedInput';
|
||||
export { default as useNavigateToConvo } from './useNavigateToConvo';
|
||||
export { default as useSetIndexOptions } from './useSetIndexOptions';
|
||||
export { default as useParameterEffects } from './useParameterEffects';
|
||||
|
|
|
|||
107
client/src/hooks/Conversations/useConversation.ts
Normal file
107
client/src/hooks/Conversations/useConversation.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState, useResetRecoilState, useRecoilCallback } from 'recoil';
|
||||
import { useGetEndpointsQuery, useGetModelsQuery } from 'librechat-data-provider/react-query';
|
||||
import type {
|
||||
TConversation,
|
||||
TMessagesAtom,
|
||||
TSubmission,
|
||||
TPreset,
|
||||
TModelsConfig,
|
||||
TEndpointsConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField } from '~/utils';
|
||||
import useOriginNavigate from '../useOriginNavigate';
|
||||
import store from '~/store';
|
||||
|
||||
const useConversation = () => {
|
||||
const navigate = useOriginNavigate();
|
||||
const setConversation = useSetRecoilState(store.conversation);
|
||||
const resetLatestMessage = useResetRecoilState(store.latestMessage);
|
||||
const setMessages = useSetRecoilState<TMessagesAtom>(store.messages);
|
||||
const setSubmission = useSetRecoilState<TSubmission | null>(store.submission);
|
||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
const modelsQuery = useGetModelsQuery();
|
||||
|
||||
const switchToConversation = useRecoilCallback(
|
||||
() =>
|
||||
async (
|
||||
conversation: TConversation,
|
||||
messages: TMessagesAtom = null,
|
||||
preset: TPreset | null = null,
|
||||
modelsData?: TModelsConfig,
|
||||
) => {
|
||||
const modelsConfig = modelsData ?? modelsQuery.data;
|
||||
const { endpoint = null } = conversation;
|
||||
|
||||
if (endpoint === null) {
|
||||
const defaultEndpoint = getDefaultEndpoint({
|
||||
convoSetup: preset ?? conversation,
|
||||
endpointsConfig,
|
||||
});
|
||||
|
||||
const endpointType = getEndpointField(endpointsConfig, defaultEndpoint, 'type');
|
||||
if (!conversation.endpointType && endpointType) {
|
||||
conversation.endpointType = endpointType;
|
||||
}
|
||||
|
||||
const models = modelsConfig?.[defaultEndpoint] ?? [];
|
||||
conversation = buildDefaultConvo({
|
||||
conversation,
|
||||
lastConversationSetup: preset as TConversation,
|
||||
endpoint: defaultEndpoint,
|
||||
models,
|
||||
});
|
||||
}
|
||||
|
||||
setConversation(conversation);
|
||||
setMessages(messages);
|
||||
setSubmission({} as TSubmission);
|
||||
resetLatestMessage();
|
||||
|
||||
if (conversation.conversationId === 'new' && !modelsData) {
|
||||
navigate('new');
|
||||
}
|
||||
},
|
||||
[endpointsConfig, modelsQuery.data],
|
||||
);
|
||||
|
||||
const newConversation = useCallback(
|
||||
(template = {}, preset?: TPreset, modelsData?: TModelsConfig) => {
|
||||
switchToConversation(
|
||||
{
|
||||
conversationId: 'new',
|
||||
title: 'New Chat',
|
||||
...template,
|
||||
endpoint: null,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
},
|
||||
[],
|
||||
preset,
|
||||
modelsData,
|
||||
);
|
||||
},
|
||||
[switchToConversation],
|
||||
);
|
||||
|
||||
const searchPlaceholderConversation = useCallback(() => {
|
||||
switchToConversation(
|
||||
{
|
||||
conversationId: 'search',
|
||||
title: 'Search',
|
||||
endpoint: null,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
},
|
||||
[],
|
||||
);
|
||||
}, [switchToConversation]);
|
||||
|
||||
return {
|
||||
switchToConversation,
|
||||
newConversation,
|
||||
searchPlaceholderConversation,
|
||||
};
|
||||
};
|
||||
|
||||
export default useConversation;
|
||||
15
client/src/hooks/Conversations/useConversations.ts
Normal file
15
client/src/hooks/Conversations/useConversations.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { useSetRecoilState } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import store from '~/store';
|
||||
|
||||
const useConversations = () => {
|
||||
const setRefreshConversationsHint = useSetRecoilState(store.refreshConversationsHint);
|
||||
|
||||
const refreshConversations = useCallback(() => {
|
||||
setRefreshConversationsHint((prevState) => prevState + 1);
|
||||
}, [setRefreshConversationsHint]);
|
||||
|
||||
return { refreshConversations };
|
||||
};
|
||||
|
||||
export default useConversations;
|
||||
35
client/src/hooks/Conversations/useDefaultConvo.ts
Normal file
35
client/src/hooks/Conversations/useDefaultConvo.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { useGetEndpointsQuery, useGetModelsQuery } from 'librechat-data-provider/react-query';
|
||||
import type {
|
||||
TConversation,
|
||||
TPreset,
|
||||
TEndpointsConfig,
|
||||
TModelsConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import { getDefaultEndpoint, buildDefaultConvo } from '~/utils';
|
||||
|
||||
type TDefaultConvo = { conversation: Partial<TConversation>; preset?: Partial<TPreset> | null };
|
||||
|
||||
const useDefaultConvo = () => {
|
||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
const { data: modelsConfig = {} as TModelsConfig } = useGetModelsQuery();
|
||||
|
||||
const getDefaultConversation = ({ conversation, preset }: TDefaultConvo) => {
|
||||
const endpoint = getDefaultEndpoint({
|
||||
convoSetup: preset as TPreset,
|
||||
endpointsConfig,
|
||||
});
|
||||
|
||||
const models = modelsConfig[endpoint] || [];
|
||||
|
||||
return buildDefaultConvo({
|
||||
conversation: conversation as TConversation,
|
||||
endpoint,
|
||||
lastConversationSetup: preset as TConversation,
|
||||
models,
|
||||
});
|
||||
};
|
||||
|
||||
return getDefaultConversation;
|
||||
};
|
||||
|
||||
export default useDefaultConvo;
|
||||
59
client/src/hooks/Conversations/useNavigateToConvo.tsx
Normal file
59
client/src/hooks/Conversations/useNavigateToConvo.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useSetRecoilState, useResetRecoilState } from 'recoil';
|
||||
import { QueryKeys } from 'librechat-data-provider';
|
||||
import type { TConversation, TEndpointsConfig, TModelsConfig } from 'librechat-data-provider';
|
||||
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField } from '~/utils';
|
||||
import useOriginNavigate from '../useOriginNavigate';
|
||||
import store from '~/store';
|
||||
|
||||
const useNavigateToConvo = (index = 0) => {
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useOriginNavigate();
|
||||
const { setConversation } = store.useCreateConversationAtom(index);
|
||||
const setSubmission = useSetRecoilState(store.submissionByIndex(index));
|
||||
const resetLatestMessage = useResetRecoilState(store.latestMessageFamily(index));
|
||||
|
||||
const navigateToConvo = (conversation: TConversation, _resetLatestMessage = true) => {
|
||||
if (!conversation) {
|
||||
console.log('Conversation not provided');
|
||||
return;
|
||||
}
|
||||
setSubmission(null);
|
||||
if (_resetLatestMessage) {
|
||||
resetLatestMessage();
|
||||
}
|
||||
|
||||
let convo = { ...conversation };
|
||||
if (!convo?.endpoint) {
|
||||
/* undefined endpoint edge case */
|
||||
const modelsConfig = queryClient.getQueryData<TModelsConfig>([QueryKeys.models]);
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const defaultEndpoint = getDefaultEndpoint({
|
||||
convoSetup: conversation,
|
||||
endpointsConfig,
|
||||
});
|
||||
|
||||
const endpointType = getEndpointField(endpointsConfig, defaultEndpoint, 'type');
|
||||
if (!conversation.endpointType && endpointType) {
|
||||
conversation.endpointType = endpointType;
|
||||
}
|
||||
|
||||
const models = modelsConfig?.[defaultEndpoint ?? ''] ?? [];
|
||||
|
||||
convo = buildDefaultConvo({
|
||||
conversation,
|
||||
endpoint: defaultEndpoint,
|
||||
lastConversationSetup: conversation,
|
||||
models,
|
||||
});
|
||||
}
|
||||
setConversation(convo);
|
||||
navigate(convo?.conversationId);
|
||||
};
|
||||
|
||||
return {
|
||||
navigateToConvo,
|
||||
};
|
||||
};
|
||||
|
||||
export default useNavigateToConvo;
|
||||
166
client/src/hooks/Conversations/usePresetIndexOptions.ts
Normal file
166
client/src/hooks/Conversations/usePresetIndexOptions.ts
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import type { TPreset, TPlugin } from 'librechat-data-provider';
|
||||
import type { TSetOptionsPayload, TSetExample, TSetOption } from '~/common';
|
||||
import { useChatContext } from '~/Providers/ChatContext';
|
||||
import { cleanupPreset } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
type TUsePresetOptions = (preset?: TPreset | boolean | null) => TSetOptionsPayload | boolean;
|
||||
|
||||
const usePresetIndexOptions: TUsePresetOptions = (_preset) => {
|
||||
const setShowPluginStoreDialog = useSetRecoilState(store.showPluginStoreDialog);
|
||||
const availableTools = useRecoilValue(store.availableTools);
|
||||
const { preset, setPreset } = useChatContext();
|
||||
|
||||
if (!_preset) {
|
||||
return false;
|
||||
}
|
||||
const getConversation: () => TPreset | null = () => preset;
|
||||
|
||||
const setOption: TSetOption = (param) => (newValue) => {
|
||||
const update = {};
|
||||
update[param] = newValue;
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const setExample: TSetExample = (i, type, newValue = null) => {
|
||||
const update = {};
|
||||
const current = preset?.examples?.slice() || [];
|
||||
const currentExample = { ...current[i] } || {};
|
||||
currentExample[type] = { content: newValue };
|
||||
current[i] = currentExample;
|
||||
update['examples'] = current;
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const addExample: () => void = () => {
|
||||
const update = {};
|
||||
const current = preset?.examples?.slice() || [];
|
||||
current.push({ input: { content: '' }, output: { content: '' } });
|
||||
update['examples'] = current;
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const removeExample: () => void = () => {
|
||||
const update = {};
|
||||
const current = preset?.examples?.slice() || [];
|
||||
if (current.length <= 1) {
|
||||
update['examples'] = [{ input: { content: '' }, output: { content: '' } }];
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
current.pop();
|
||||
update['examples'] = current;
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const setAgentOption: TSetOption = (param) => (newValue) => {
|
||||
const editablePreset = JSON.parse(JSON.stringify(_preset));
|
||||
const { agentOptions } = editablePreset;
|
||||
agentOptions[param] = newValue;
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
agentOptions,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
function checkPluginSelection(value: string) {
|
||||
if (!preset?.tools) {
|
||||
return false;
|
||||
}
|
||||
return preset.tools.find((el) => {
|
||||
if (typeof el === 'string') {
|
||||
return el === value;
|
||||
}
|
||||
return el.pluginKey === value;
|
||||
})
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
const setTools: (newValue: string, remove?: boolean) => void = (newValue, remove) => {
|
||||
if (newValue === 'pluginStore') {
|
||||
setShowPluginStoreDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const update = {};
|
||||
const current =
|
||||
preset?.tools
|
||||
?.map((tool: string | TPlugin) => {
|
||||
if (typeof tool === 'string') {
|
||||
return availableTools[tool];
|
||||
}
|
||||
return tool;
|
||||
})
|
||||
?.filter((el) => !!el) || [];
|
||||
const isSelected = checkPluginSelection(newValue);
|
||||
const tool = availableTools[newValue];
|
||||
if (isSelected || remove) {
|
||||
update['tools'] = current.filter((el) => el.pluginKey !== newValue);
|
||||
} else {
|
||||
update['tools'] = [...current, tool];
|
||||
}
|
||||
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
setOption,
|
||||
setExample,
|
||||
addExample,
|
||||
removeExample,
|
||||
getConversation,
|
||||
checkPluginSelection,
|
||||
setAgentOption,
|
||||
setTools,
|
||||
};
|
||||
};
|
||||
|
||||
export default usePresetIndexOptions;
|
||||
|
|
@ -11,9 +11,9 @@ import {
|
|||
useDeletePresetMutation,
|
||||
useGetPresetsQuery,
|
||||
} from '~/data-provider';
|
||||
import { cleanupPreset, getEndpointField, removeUnavailableTools } from '~/utils';
|
||||
import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo';
|
||||
import { useChatContext, useToastContext } from '~/Providers';
|
||||
import { cleanupPreset, getEndpointField } from '~/utils';
|
||||
import useDefaultConvo from '~/hooks/useDefaultConvo';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
|
|
@ -28,6 +28,7 @@ export default function usePresets() {
|
|||
const { user, isAuthenticated } = useAuthContext();
|
||||
|
||||
const modularChat = useRecoilValue(store.modularChat);
|
||||
const availableTools = useRecoilValue(store.availableTools);
|
||||
const setPresetModalVisible = useSetRecoilState(store.presetModalVisible);
|
||||
const [_defaultPreset, setDefaultPreset] = useRecoilState(store.defaultPreset);
|
||||
const presetsQuery = useGetPresetsQuery({ enabled: !!user && isAuthenticated });
|
||||
|
|
@ -151,11 +152,13 @@ export default function usePresets() {
|
|||
importPreset(jsonPreset);
|
||||
};
|
||||
|
||||
const onSelectPreset = (newPreset: TPreset) => {
|
||||
if (!newPreset) {
|
||||
const onSelectPreset = (_newPreset: TPreset) => {
|
||||
if (!_newPreset) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newPreset = removeUnavailableTools(_newPreset, availableTools);
|
||||
|
||||
const toastTitle = newPreset.title
|
||||
? `"${newPreset.title}"`
|
||||
: localize('com_endpoint_preset_title');
|
||||
|
|
|
|||
170
client/src/hooks/Conversations/useSetIndexOptions.ts
Normal file
170
client/src/hooks/Conversations/useSetIndexOptions.ts
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { TPreset, TPlugin, TConversation, tConvoUpdateSchema } from 'librechat-data-provider';
|
||||
import type { TSetExample, TSetOption, TSetOptionsPayload } from '~/common';
|
||||
import usePresetIndexOptions from './usePresetIndexOptions';
|
||||
import { useChatContext } from '~/Providers/ChatContext';
|
||||
import store from '~/store';
|
||||
|
||||
type TUseSetOptions = (preset?: TPreset | boolean | null) => TSetOptionsPayload;
|
||||
|
||||
const useSetIndexOptions: TUseSetOptions = (preset = false) => {
|
||||
const setShowPluginStoreDialog = useSetRecoilState(store.showPluginStoreDialog);
|
||||
const availableTools = useRecoilValue(store.availableTools);
|
||||
const { conversation, setConversation } = useChatContext();
|
||||
|
||||
const result = usePresetIndexOptions(preset);
|
||||
|
||||
if (result && typeof result !== 'boolean') {
|
||||
return result;
|
||||
}
|
||||
|
||||
const setOption: TSetOption = (param) => (newValue) => {
|
||||
const update = {};
|
||||
update[param] = newValue;
|
||||
|
||||
if (param === 'presetOverride') {
|
||||
const currentOverride = conversation?.presetOverride || {};
|
||||
update['presetOverride'] = {
|
||||
...currentOverride,
|
||||
...(newValue as unknown as Partial<TPreset>),
|
||||
};
|
||||
}
|
||||
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
const setExample: TSetExample = (i, type, newValue = null) => {
|
||||
const update = {};
|
||||
const current = conversation?.examples?.slice() || [];
|
||||
const currentExample = { ...current[i] } || {};
|
||||
currentExample[type] = { content: newValue };
|
||||
current[i] = currentExample;
|
||||
update['examples'] = current;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
const addExample: () => void = () => {
|
||||
const update = {};
|
||||
const current = conversation?.examples?.slice() || [];
|
||||
current.push({ input: { content: '' }, output: { content: '' } });
|
||||
update['examples'] = current;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
const removeExample: () => void = () => {
|
||||
const update = {};
|
||||
const current = conversation?.examples?.slice() || [];
|
||||
if (current.length <= 1) {
|
||||
update['examples'] = [{ input: { content: '' }, output: { content: '' } }];
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}) as TConversation,
|
||||
);
|
||||
return;
|
||||
}
|
||||
current.pop();
|
||||
update['examples'] = current;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
function checkPluginSelection(value: string) {
|
||||
if (!conversation?.tools) {
|
||||
return false;
|
||||
}
|
||||
return conversation.tools.find((el) => {
|
||||
if (typeof el === 'string') {
|
||||
return el === value;
|
||||
}
|
||||
return el.pluginKey === value;
|
||||
})
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
const setAgentOption: TSetOption = (param) => (newValue) => {
|
||||
const editableConvo = JSON.stringify(conversation);
|
||||
const convo = JSON.parse(editableConvo);
|
||||
const { agentOptions } = convo;
|
||||
agentOptions[param] = newValue;
|
||||
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
agentOptions,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
const setTools: (newValue: string, remove?: boolean) => void = (newValue, remove) => {
|
||||
if (newValue === 'pluginStore') {
|
||||
setShowPluginStoreDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const update = {};
|
||||
const current =
|
||||
conversation?.tools
|
||||
?.map((tool: string | TPlugin) => {
|
||||
if (typeof tool === 'string') {
|
||||
return availableTools[tool];
|
||||
}
|
||||
return tool;
|
||||
})
|
||||
?.filter((el) => !!el) || [];
|
||||
const isSelected = checkPluginSelection(newValue);
|
||||
const tool = availableTools[newValue];
|
||||
if (isSelected || remove) {
|
||||
update['tools'] = current.filter((el) => el.pluginKey !== newValue);
|
||||
} else {
|
||||
update['tools'] = [...current, tool];
|
||||
}
|
||||
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
setOption,
|
||||
setExample,
|
||||
addExample,
|
||||
removeExample,
|
||||
setAgentOption,
|
||||
checkPluginSelection,
|
||||
setTools,
|
||||
};
|
||||
};
|
||||
|
||||
export default useSetIndexOptions;
|
||||
Loading…
Add table
Add a link
Reference in a new issue