🏷️ refactor: EditPresetDialog UI and Remove chatGptLabel from Presets (#7543)

* fix: add necessary dep., remove unnecessary dep from useMentions memoization

* fix: Migrate deprecated chatGptLabel to modelLabel in cleanupPreset and simplify getPresetTitle logic

* fix: Enhance cleanupPreset to remove empty chatGptLabel and add comprehensive tests for label migration and preset handling

* chore: Update endpointType prop in PopoverButtons to allow null values for better flexibility

* refactor: Replace Dialog with OGDialog in EditPresetDialog for improved UI consistency and structure

* style: Update EditPresetDialog layout and styling for improved responsiveness and consistency
This commit is contained in:
Danny Avila 2025-05-24 19:24:42 -04:00 committed by GitHub
parent fc8d24fa5b
commit b45ff8e4ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 717 additions and 122 deletions

View file

@ -26,7 +26,7 @@ export default function PopoverButtons({
buttonClass?: string;
iconClass?: string;
endpoint?: EModelEndpoint | string;
endpointType?: EModelEndpoint | string;
endpointType?: EModelEndpoint | string | null;
model?: string | null;
}) {
const {

View file

@ -10,10 +10,16 @@ import {
mapEndpoints,
getConvoSwitchLogic,
} from '~/utils';
import { Input, Label, SelectDropDown, Dialog, DialogClose, DialogButton } from '~/components';
import {
Input,
Label,
OGDialog,
OGDialogTitle,
SelectDropDown,
OGDialogContent,
} from '~/components';
import { useSetIndexOptions, useLocalize, useDebouncedInput } from '~/hooks';
import PopoverButtons from '~/components/Chat/Input/PopoverButtons';
import DialogTemplate from '~/components/ui/DialogTemplate';
import { EndpointSettings } from '~/components/Endpoints';
import { useGetEndpointsQuery } from '~/data-provider';
import { useChatContext } from '~/Providers';
@ -117,111 +123,107 @@ const EditPresetDialog = ({
[queryClient, setOptions],
);
const handleOpenChange = (open: boolean) => {
setPresetModalVisible(open);
if (!open) {
setPreset(null);
}
};
const { endpoint: _endpoint, endpointType, model } = preset || {};
const endpoint = _endpoint ?? '';
if (!endpoint) {
return null;
} else if (isAgentsEndpoint(endpoint)) {
}
if (isAgentsEndpoint(endpoint)) {
return null;
}
return (
<Dialog
open={presetModalVisible}
onOpenChange={(open) => {
setPresetModalVisible(open);
if (!open) {
setPreset(null);
}
}}
>
<DialogTemplate
title={`${localize('com_ui_edit') + ' ' + localize('com_endpoint_preset')} - ${
preset?.title
}`}
className="h-full max-w-full overflow-y-auto pb-4 sm:w-[680px] sm:pb-0 md:h-[720px] md:w-[750px] md:overflow-y-hidden lg:w-[950px] xl:h-[720px]"
main={
<div className="flex w-full flex-col items-center gap-2 md:h-[550px] md:overflow-y-auto">
<div className="grid w-full">
<div className="col-span-4 flex flex-col items-start justify-start gap-6 pb-4 md:flex-row">
<div className="flex w-full flex-col">
<Label htmlFor="preset-name" className="mb-1 text-left text-sm font-medium">
{localize('com_endpoint_preset_name')}
</Label>
<Input
id="preset-name"
value={(title as string | undefined) ?? ''}
onChange={onTitleChange}
placeholder={localize('com_endpoint_set_custom_name')}
className={cn(
defaultTextProps,
'flex h-10 max-h-10 w-full resize-none px-3 py-2',
removeFocusOutlines,
)}
/>
</div>
<div className="flex w-full flex-col">
<Label htmlFor="endpoint" className="mb-1 text-left text-sm font-medium">
{localize('com_endpoint')}
</Label>
<SelectDropDown
value={endpoint || ''}
setValue={switchEndpoint}
showLabel={false}
emptyTitle={true}
searchPlaceholder={localize('com_endpoint_search')}
availableValues={availableEndpoints}
/>
</div>
</div>
<div className="col-span-2 flex items-start justify-between gap-4 sm:col-span-4">
<div className="flex w-full flex-col">
<Label
htmlFor="endpoint"
className="mb-1 hidden text-left text-sm font-medium sm:block"
>
{''}
</Label>
<PopoverButtons
buttonClass="ml-0 w-full border border-border-medium p-2 h-[40px] justify-center mt-0"
iconClass="hidden lg:block w-4 "
endpoint={endpoint}
endpointType={endpointType}
model={model}
/>
</div>
</div>
<OGDialog open={presetModalVisible} onOpenChange={handleOpenChange}>
<OGDialogContent className="h-[100dvh] max-h-[100dvh] w-full max-w-full overflow-y-auto bg-white dark:border-gray-700 dark:bg-gray-850 dark:text-gray-300 md:h-auto md:max-h-[90vh] md:max-w-[75vw] md:rounded-lg lg:max-w-[950px]">
<OGDialogTitle>
{`${localize('com_ui_edit')} ${localize('com_endpoint_preset')} - ${preset?.title}`}
</OGDialogTitle>
<div className="flex w-full flex-col gap-2 px-1 pb-4 md:gap-4">
{/* Header section with preset name and endpoint */}
<div className="grid w-full gap-2 md:grid-cols-2 md:gap-4">
<div className="flex w-full flex-col">
<Label htmlFor="preset-name" className="mb-1 text-left text-sm font-medium">
{localize('com_endpoint_preset_name')}
</Label>
<Input
id="preset-name"
value={(title as string | undefined) ?? ''}
onChange={onTitleChange}
placeholder={localize('com_endpoint_set_custom_name')}
className={cn(
defaultTextProps,
'flex h-10 max-h-10 w-full resize-none px-3 py-2',
removeFocusOutlines,
)}
/>
</div>
<div className="my-4 w-full border-t border-border-medium" />
<div className="w-full p-0">
<EndpointSettings
conversation={preset}
setOption={setOption}
isPreset={true}
className="h-full text-text-primary md:mb-4 md:h-[440px]"
<div className="flex w-full flex-col">
<Label htmlFor="endpoint" className="mb-1 text-left text-sm font-medium">
{localize('com_endpoint')}
</Label>
<SelectDropDown
value={endpoint || ''}
setValue={switchEndpoint}
showLabel={false}
emptyTitle={true}
searchPlaceholder={localize('com_endpoint_search')}
availableValues={availableEndpoints}
/>
</div>
</div>
}
buttons={
<div className="mb-6 md:mb-2">
<DialogButton
{/* PopoverButtons section */}
<div className="flex w-full">
<PopoverButtons
buttonClass="ml-0 w-full border border-border-medium p-2 h-[40px] justify-center mt-0"
iconClass="hidden lg:block w-4"
endpoint={endpoint}
endpointType={endpointType}
model={model}
/>
</div>
{/* Separator */}
<div className="w-full border-t border-border-medium" />
{/* Settings section */}
<div className="w-full flex-1">
<EndpointSettings
conversation={preset}
setOption={setOption}
isPreset={true}
className="text-text-primary"
/>
</div>
{/* Action buttons */}
<div className="flex justify-end gap-2 border-t border-border-medium pt-2 md:pt-4">
<button
onClick={exportPreset}
className="border-gray-100 hover:bg-gray-100 dark:border-gray-600 dark:hover:bg-gray-600"
className="rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 md:px-4"
>
{localize('com_endpoint_export')}
</DialogButton>
<DialogClose
</button>
<button
onClick={submitPreset}
className="ml-2 bg-green-500 text-white hover:bg-green-600 dark:hover:bg-green-600"
className="rounded-md bg-green-500 px-3 py-2 text-sm font-medium text-white hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 md:px-4"
>
{localize('com_ui_save')}
</DialogClose>
</button>
</div>
}
footerClassName="bg-white dark:bg-gray-700"
/>
</Dialog>
</div>
</OGDialogContent>
</OGDialog>
);
};

View file

@ -34,20 +34,20 @@ const assistantMapFn =
assistantMap: TAssistantsMap;
endpointsConfig: TEndpointsConfig;
}) =>
({ id, name, description }) => ({
type: endpoint,
label: name ?? '',
value: id,
description: description ?? '',
icon: EndpointIcon({
conversation: { assistant_id: id, endpoint },
containerClassName: 'shadow-stroke overflow-hidden rounded-full',
endpointsConfig: endpointsConfig,
context: 'menu-item',
assistantMap,
size: 20,
}),
});
({ id, name, description }) => ({
type: endpoint,
label: name ?? '',
value: id,
description: description ?? '',
icon: EndpointIcon({
conversation: { assistant_id: id, endpoint },
containerClassName: 'shadow-stroke overflow-hidden rounded-full',
endpointsConfig: endpointsConfig,
context: 'menu-item',
assistantMap,
size: 20,
}),
});
export default function useMentions({
assistantMap,
@ -226,7 +226,7 @@ export default function useMentions({
assistantListMap,
includeAssistants,
interfaceConfig.presets,
interfaceConfig.endpointsMenu,
interfaceConfig.modelSelect,
]);
return {

View file

@ -0,0 +1,224 @@
import { EModelEndpoint } from 'librechat-data-provider';
import cleanupPreset from '../cleanupPreset';
import type { TPreset } from 'librechat-data-provider';
// Mock parseConvo since we're focusing on testing the chatGptLabel migration logic
jest.mock('librechat-data-provider', () => ({
...jest.requireActual('librechat-data-provider'),
parseConvo: jest.fn((input) => {
// Return a simplified mock that passes through most properties
const { conversation } = input;
return {
...conversation,
model: conversation?.model || 'gpt-3.5-turbo',
};
}),
}));
describe('cleanupPreset', () => {
const basePreset = {
presetId: 'test-preset-id',
title: 'Test Preset',
endpoint: EModelEndpoint.openAI,
model: 'gpt-4',
temperature: 0.7,
};
beforeEach(() => {
jest.clearAllMocks();
});
describe('chatGptLabel migration', () => {
it('should migrate chatGptLabel to modelLabel when only chatGptLabel exists', () => {
const preset = {
...basePreset,
chatGptLabel: 'Custom ChatGPT Label',
};
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBe('Custom ChatGPT Label');
expect(result.chatGptLabel).toBeUndefined();
});
it('should prioritize modelLabel over chatGptLabel when both exist', () => {
const preset = {
...basePreset,
chatGptLabel: 'Old ChatGPT Label',
modelLabel: 'New Model Label',
};
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBe('New Model Label');
expect(result.chatGptLabel).toBeUndefined();
});
it('should keep modelLabel when only modelLabel exists', () => {
const preset = {
...basePreset,
modelLabel: 'Existing Model Label',
};
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBe('Existing Model Label');
expect(result.chatGptLabel).toBeUndefined();
});
it('should handle preset without either label', () => {
const preset = { ...basePreset };
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBeUndefined();
expect(result.chatGptLabel).toBeUndefined();
});
it('should handle empty chatGptLabel', () => {
const preset = {
...basePreset,
chatGptLabel: '',
modelLabel: 'Valid Model Label',
};
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBe('Valid Model Label');
expect(result.chatGptLabel).toBeUndefined();
});
it('should not migrate empty string chatGptLabel when modelLabel exists', () => {
const preset = {
...basePreset,
chatGptLabel: '',
};
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBeUndefined();
expect(result.chatGptLabel).toBeUndefined();
});
});
describe('presetOverride handling', () => {
it('should apply presetOverride and then handle label migration', () => {
const preset = {
...basePreset,
chatGptLabel: 'Original Label',
presetOverride: {
modelLabel: 'Override Model Label',
temperature: 0.9,
},
};
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBe('Override Model Label');
expect(result.chatGptLabel).toBeUndefined();
expect(result.temperature).toBe(0.9);
});
it('should handle label migration in presetOverride', () => {
const preset = {
...basePreset,
presetOverride: {
chatGptLabel: 'Override ChatGPT Label',
},
};
const result = cleanupPreset({ preset });
expect(result.modelLabel).toBe('Override ChatGPT Label');
expect(result.chatGptLabel).toBeUndefined();
});
});
describe('error handling', () => {
it('should handle undefined preset', () => {
const result = cleanupPreset({ preset: undefined });
expect(result).toEqual({
endpoint: null,
presetId: null,
title: 'New Preset',
});
});
it('should handle preset with null endpoint', () => {
const preset = {
...basePreset,
endpoint: null,
};
const result = cleanupPreset({ preset });
expect(result).toEqual({
endpoint: null,
presetId: 'test-preset-id',
title: 'Test Preset',
});
});
it('should handle preset with empty string endpoint', () => {
const preset = {
...basePreset,
endpoint: '',
};
const result = cleanupPreset({ preset });
expect(result).toEqual({
endpoint: null,
presetId: 'test-preset-id',
title: 'Test Preset',
});
});
});
describe('normal preset properties', () => {
it('should preserve all other preset properties', () => {
const preset = {
...basePreset,
promptPrefix: 'Custom prompt:',
temperature: 0.8,
top_p: 0.9,
modelLabel: 'Custom Model',
tools: ['plugin1', 'plugin2'],
};
const result = cleanupPreset({ preset });
expect(result.presetId).toBe('test-preset-id');
expect(result.title).toBe('Test Preset');
expect(result.endpoint).toBe(EModelEndpoint.openAI);
expect(result.modelLabel).toBe('Custom Model');
expect(result.promptPrefix).toBe('Custom prompt:');
expect(result.temperature).toBe(0.8);
expect(result.top_p).toBe(0.9);
expect(result.tools).toEqual(['plugin1', 'plugin2']);
});
it('should generate default title when title is missing', () => {
const preset = {
...basePreset,
title: undefined,
};
const result = cleanupPreset({ preset });
expect(result.title).toBe('New Preset');
});
it('should handle null presetId', () => {
const preset = {
...basePreset,
presetId: null,
};
const result = cleanupPreset({ preset });
expect(result.presetId).toBeNull();
});
});
});

View file

@ -0,0 +1,362 @@
import { EModelEndpoint } from 'librechat-data-provider';
import { getPresetTitle, removeUnavailableTools } from '../presets';
import type { TPreset, TPlugin } from 'librechat-data-provider';
describe('presets utils', () => {
describe('getPresetTitle', () => {
const basePreset: TPreset = {
presetId: 'test-id',
title: 'Test Preset',
endpoint: EModelEndpoint.openAI,
model: 'gpt-4',
};
describe('with modelLabel', () => {
it('should use modelLabel as the label', () => {
const preset = {
...basePreset,
modelLabel: 'Custom Model Name',
};
const result = getPresetTitle(preset);
expect(result).toBe('Test Preset: gpt-4 (Custom Model Name)');
});
it('should prioritize modelLabel over deprecated chatGptLabel', () => {
const preset = {
...basePreset,
modelLabel: 'New Model Label',
chatGptLabel: 'Old ChatGPT Label',
};
const result = getPresetTitle(preset);
expect(result).toBe('Test Preset: gpt-4 (New Model Label)');
});
it('should handle title that includes the label', () => {
const preset = {
...basePreset,
title: 'Custom Model Name Settings',
modelLabel: 'Custom Model Name',
};
const result = getPresetTitle(preset);
expect(result).toBe('Custom Model Name Settings: gpt-4 (Custom Model Name)');
});
it('should handle case-insensitive title matching', () => {
const preset = {
...basePreset,
title: 'custom model name preset',
modelLabel: 'Custom Model Name',
};
const result = getPresetTitle(preset);
expect(result).toBe('custom model name preset: gpt-4 (Custom Model Name)');
});
it('should use label as title when label includes the title', () => {
const preset = {
...basePreset,
title: 'GPT',
modelLabel: 'Custom GPT Assistant',
};
const result = getPresetTitle(preset);
expect(result).toBe('Custom GPT Assistant: gpt-4');
});
});
describe('without modelLabel', () => {
it('should work without modelLabel', () => {
const preset = { ...basePreset };
const result = getPresetTitle(preset);
expect(result).toBe('Test Preset: gpt-4');
});
it('should handle empty modelLabel', () => {
const preset = {
...basePreset,
modelLabel: '',
};
const result = getPresetTitle(preset);
expect(result).toBe('Test Preset: gpt-4');
});
it('should handle null modelLabel', () => {
const preset = {
...basePreset,
modelLabel: null,
};
const result = getPresetTitle(preset);
expect(result).toBe('Test Preset: gpt-4');
});
});
describe('title variations', () => {
it('should handle missing title', () => {
const preset = {
...basePreset,
title: null,
modelLabel: 'Custom Model',
};
const result = getPresetTitle(preset);
expect(result).toBe('gpt-4 (Custom Model)');
});
it('should handle empty title', () => {
const preset = {
...basePreset,
title: '',
modelLabel: 'Custom Model',
};
const result = getPresetTitle(preset);
expect(result).toBe('gpt-4 (Custom Model)');
});
it('should handle "New Chat" title', () => {
const preset = {
...basePreset,
title: 'New Chat',
modelLabel: 'Custom Model',
};
const result = getPresetTitle(preset);
expect(result).toBe('gpt-4 (Custom Model)');
});
it('should handle title with whitespace', () => {
const preset = {
...basePreset,
title: ' ',
modelLabel: 'Custom Model',
};
const result = getPresetTitle(preset);
expect(result).toBe(': gpt-4 (Custom Model)');
});
});
describe('mention mode', () => {
it('should return mention format with all components', () => {
const preset = {
...basePreset,
modelLabel: 'Custom Model',
promptPrefix: 'You are a helpful assistant',
tools: ['plugin1', 'plugin2'] as string[],
};
const result = getPresetTitle(preset, true);
expect(result).toBe(
'gpt-4 | Custom Model | You are a helpful assistant | plugin1, plugin2',
);
});
it('should handle mention format with object tools', () => {
const preset = {
...basePreset,
modelLabel: 'Custom Model',
tools: [
{ pluginKey: 'plugin1', name: 'Plugin 1' } as TPlugin,
{ pluginKey: 'plugin3', name: 'Plugin 3' } as TPlugin,
] as TPlugin[],
};
const result = getPresetTitle(preset, true);
expect(result).toBe('gpt-4 | Custom Model | plugin1, plugin3');
});
it('should handle mention format with minimal data', () => {
const preset = { ...basePreset };
const result = getPresetTitle(preset, true);
expect(result).toBe('gpt-4');
});
it('should handle mention format with only modelLabel', () => {
const preset = {
...basePreset,
modelLabel: 'Custom Model',
};
const result = getPresetTitle(preset, true);
expect(result).toBe('gpt-4 | Custom Model');
});
it('should handle mention format with only promptPrefix', () => {
const preset = {
...basePreset,
promptPrefix: 'Custom prompt',
};
const result = getPresetTitle(preset, true);
expect(result).toBe('gpt-4 | Custom prompt');
});
});
describe('edge cases', () => {
it('should handle missing model', () => {
const preset = {
...basePreset,
model: null,
modelLabel: 'Custom Model',
};
const result = getPresetTitle(preset);
expect(result).toBe('Test Preset: (Custom Model)');
});
it('should handle undefined model', () => {
const preset = {
...basePreset,
model: undefined,
modelLabel: 'Custom Model',
};
const result = getPresetTitle(preset);
expect(result).toBe('Test Preset: (Custom Model)');
});
it('should trim the final result', () => {
const preset = {
...basePreset,
title: '',
model: '',
modelLabel: '',
};
const result = getPresetTitle(preset);
expect(result).toBe('');
});
});
});
describe('removeUnavailableTools', () => {
const basePreset: TPreset = {
presetId: 'test-id',
title: 'Test Preset',
endpoint: EModelEndpoint.openAI,
model: 'gpt-4',
};
const availableTools: Record<string, TPlugin | undefined> = {
plugin1: { pluginKey: 'plugin1', name: 'Plugin 1' } as TPlugin,
plugin2: { pluginKey: 'plugin2', name: 'Plugin 2' } as TPlugin,
plugin3: { pluginKey: 'plugin3', name: 'Plugin 3' } as TPlugin,
};
it('should remove unavailable tools from string array', () => {
const preset = {
...basePreset,
tools: ['plugin1', 'unavailable-plugin', 'plugin2'] as string[],
};
const result = removeUnavailableTools(preset, availableTools);
expect(result.tools).toEqual(['plugin1', 'plugin2']);
});
it('should remove unavailable tools from object array', () => {
const preset = {
...basePreset,
tools: [
{ pluginKey: 'plugin1', name: 'Plugin 1' } as TPlugin,
{ pluginKey: 'unavailable-plugin', name: 'Unavailable' } as TPlugin,
{ pluginKey: 'plugin2', name: 'Plugin 2' } as TPlugin,
] as TPlugin[],
};
const result = removeUnavailableTools(preset, availableTools);
expect(result.tools).toEqual(['plugin1', 'plugin2']);
});
it('should handle preset without tools', () => {
const preset = { ...basePreset };
const result = removeUnavailableTools(preset, availableTools);
expect(result).toEqual(preset);
});
it('should handle preset with empty tools array', () => {
const preset = {
...basePreset,
tools: [] as string[],
};
const result = removeUnavailableTools(preset, availableTools);
expect(result.tools).toEqual([]);
});
it('should remove all tools when none are available', () => {
const preset = {
...basePreset,
tools: ['unavailable1', 'unavailable2'] as string[],
};
const result = removeUnavailableTools(preset, {});
expect(result.tools).toEqual([]);
});
it('should preserve all other preset properties', () => {
const preset = {
...basePreset,
tools: ['plugin1'] as string[],
modelLabel: 'Custom Model',
temperature: 0.8,
promptPrefix: 'Test prompt',
};
const result = removeUnavailableTools(preset, availableTools);
expect(result.presetId).toBe(preset.presetId);
expect(result.title).toBe(preset.title);
expect(result.endpoint).toBe(preset.endpoint);
expect(result.model).toBe(preset.model);
expect(result.modelLabel).toBe(preset.modelLabel);
expect(result.temperature).toBe(preset.temperature);
expect(result.promptPrefix).toBe(preset.promptPrefix);
expect(result.tools).toEqual(['plugin1']);
});
it('should not mutate the original preset', () => {
const preset = {
...basePreset,
tools: ['plugin1', 'unavailable-plugin'] as string[],
};
const originalTools = [...preset.tools];
removeUnavailableTools(preset, availableTools);
expect(preset.tools).toEqual(originalTools);
});
});
});

View file

@ -20,6 +20,21 @@ const cleanupPreset = ({ preset: _preset }: TCleanupPreset): TPreset => {
const { presetOverride = {}, ...rest } = _preset ?? {};
const preset = { ...rest, ...presetOverride };
// Handle deprecated chatGptLabel field
// If both chatGptLabel and modelLabel exist, prioritize modelLabel and remove chatGptLabel
// If only chatGptLabel exists, migrate it to modelLabel
if (preset.chatGptLabel && preset.modelLabel) {
// Both exist: prioritize modelLabel, remove chatGptLabel
delete preset.chatGptLabel;
} else if (preset.chatGptLabel && !preset.modelLabel) {
// Only chatGptLabel exists: migrate to modelLabel
preset.modelLabel = preset.chatGptLabel;
delete preset.chatGptLabel;
} else if ('chatGptLabel' in preset) {
// chatGptLabel exists but is empty/falsy: remove it
delete preset.chatGptLabel;
}
/* @ts-ignore: endpoint can be a custom defined name */
const parsedPreset = parseConvo({ endpoint, endpointType, conversation: preset });

View file

@ -17,18 +17,10 @@ export const getPresetTitle = (preset: TPreset, mention?: boolean) => {
let title = '';
let label = '';
const usesChatGPTLabel: TEndpoints = [
EModelEndpoint.azureOpenAI,
EModelEndpoint.openAI,
EModelEndpoint.custom,
];
const usesModelLabel: TEndpoints = [EModelEndpoint.google, EModelEndpoint.anthropic];
if (endpoint != null && endpoint && usesChatGPTLabel.includes(endpoint)) {
label = chatGptLabel ?? '';
} else if (endpoint != null && endpoint && usesModelLabel.includes(endpoint)) {
label = modelLabel ?? '';
if (modelLabel) {
label = modelLabel;
}
if (
label &&
presetTitle != null &&
@ -47,13 +39,13 @@ export const getPresetTitle = (preset: TPreset, mention?: boolean) => {
}${
tools
? ` | ${tools
.map((tool: TPlugin | string) => {
if (typeof tool === 'string') {
return tool;
}
return tool.pluginKey;
})
.join(', ')}`
.map((tool: TPlugin | string) => {
if (typeof tool === 'string') {
return tool;
}
return tool.pluginKey;
})
.join(', ')}`
: ''
}`;
}