Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 fix: Resolve 'Icon is Not a Function' Error in PresetItems #5260

Merged
merged 2 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion client/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,14 @@ export type IconMapProps = {
size?: number;
};

export type AgentIconMapProps = IconMapProps & { agentName: string };
export type IconComponent = React.ComponentType<IconMapProps>;
export type AgentIconComponent = React.ComponentType<AgentIconMapProps>;
export type IconComponentTypes = IconComponent | AgentIconComponent;
export type IconsRecord = {
[key in t.EModelEndpoint | 'unknown' | string]: IconComponentTypes | null | undefined;
};

export type AgentIconMapProps = IconMapProps & { agentName?: string };

export type NavLink = {
title: string;
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/Chat/Menus/Endpoints/Icons.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EModelEndpoint } from 'librechat-data-provider';
import type { IconMapProps, AgentIconMapProps } from '~/common';
import type { IconMapProps, AgentIconMapProps, IconsRecord } from '~/common';
import { Feather } from 'lucide-react';
import {
MinimalPlugin,
Expand Down Expand Up @@ -42,7 +42,7 @@ const AssistantAvatar = ({
};

const AgentAvatar = ({ className = '', avatar = '', agentName, size }: AgentIconMapProps) => {
if (agentName && avatar) {
if (agentName != null && agentName && avatar) {
return (
<img
src={avatar}
Expand All @@ -61,7 +61,7 @@ const Bedrock = ({ className = '' }: IconMapProps) => {
return <BedrockIcon className={cn(className, 'h-full w-full')} />;
};

export const icons = {
export const icons: IconsRecord = {
[EModelEndpoint.azureOpenAI]: AzureMinimalIcon,
[EModelEndpoint.openAI]: GPTIcon,
[EModelEndpoint.gptPlugins]: MinimalPlugin,
Expand Down
41 changes: 24 additions & 17 deletions client/src/components/Chat/Menus/Presets/PresetItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { cn } from '~/utils';
import store from '~/store';

const PresetItems: FC<{
presets: TPreset[];
presets?: Array<TPreset | undefined>;
onSetDefaultPreset: (preset: TPreset, remove?: boolean) => void;
onSelectPreset: (preset: TPreset) => void;
onChangePreset: (preset: TPreset) => void;
Expand Down Expand Up @@ -110,34 +110,41 @@ const PresetItems: FC<{
</div>
</div>
)}
<Flipper flipKey={presets.map(({ presetId }) => presetId).join('.')}>
<Flipper
flipKey={presets
?.map((preset) => preset?.presetId)
.filter((p) => p)
.join('.')}
>
{presets &&
presets.length > 0 &&
presets.map((preset, i) => {
if (!preset || !preset.presetId) {
const presetId = preset?.presetId ?? '';
if (!preset || !presetId) {
return null;
}

const iconKey = getIconKey({ endpoint: preset.endpoint, endpointsConfig });
const Icon = icons[iconKey];

return (
<Close asChild key={`preset-${preset.presetId}`}>
<div key={`preset-${preset.presetId}`}>
<Flipped flipId={preset.presetId}>
<Close asChild key={`preset-${presetId}`}>
<div key={`preset-${presetId}`}>
<Flipped flipId={presetId}>
<MenuItem
key={`preset-item-${preset.presetId}`}
key={`preset-item-${presetId}`}
textClassName="text-xs max-w-[150px] sm:max-w-[200px] truncate md:max-w-full "
title={getPresetTitle(preset)}
onClick={() => onSelectPreset(preset)}
icon={
Icon &&
Icon({
context: 'menu-item',
iconURL: getEndpointField(endpointsConfig, preset.endpoint, 'iconURL'),
className: 'icon-md mr-1 dark:text-white',
endpoint: preset.endpoint,
})
Icon != null && (
<Icon
context="menu-item"
iconURL={getEndpointField(endpointsConfig, preset.endpoint, 'iconURL')}
className="icon-md mr-1 dark:text-white"
endpoint={preset.endpoint}
/>
)
}
selected={false}
data-testid={`preset-item-${preset}`}
Expand All @@ -146,17 +153,17 @@ const PresetItems: FC<{
<button
className={cn(
'm-0 h-full rounded-md bg-transparent p-2 text-gray-400 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200',
defaultPreset?.presetId === preset.presetId
defaultPreset?.presetId === presetId
? ''
: 'sm:invisible sm:group-hover:visible',
)}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onSetDefaultPreset(preset, defaultPreset?.presetId === preset.presetId);
onSetDefaultPreset(preset, defaultPreset?.presetId === presetId);
}}
>
<PinIcon unpin={defaultPreset?.presetId === preset.presetId} />
<PinIcon unpin={defaultPreset?.presetId === presetId} />
</button>
<button
className="m-0 h-full rounded-md p-2 text-gray-400 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 sm:invisible sm:group-hover:visible"
Expand Down
4 changes: 1 addition & 3 deletions client/src/components/Chat/Menus/PresetsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ const PresetsMenu: FC = () => {
exportPreset,
} = usePresets();
const { preset } = useChatContext();

const presets = presetsQuery.data || [];
return (
<Root>
<Trigger asChild>
Expand Down Expand Up @@ -54,7 +52,7 @@ const PresetsMenu: FC = () => {
className="mt-2 max-h-[495px] overflow-x-hidden rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-700 dark:text-white md:min-w-[400px]"
>
<PresetItems
presets={presets}
presets={presetsQuery.data}
onSetDefaultPreset={onSetDefaultPreset}
onSelectPreset={onSelectPreset}
onChangePreset={onChangePreset}
Expand Down
4 changes: 2 additions & 2 deletions client/src/utils/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
isAssistantsEndpoint,
} from 'librechat-data-provider';
import type * as t from 'librechat-data-provider';
import type { LocalizeFunction } from '~/common';
import type { LocalizeFunction, IconsRecord } from '~/common';

export const getEntityName = ({
name = '',
Expand Down Expand Up @@ -222,7 +222,7 @@ export function getIconKey({
endpointsConfig?: t.TEndpointsConfig;
endpointType?: string | null;
endpointIconURL?: string;
}) {
}): keyof IconsRecord {
const endpointType = _eType ?? getEndpointField(endpointsConfig, endpoint, 'type') ?? '';
const endpointIconURL = iconURL ?? getEndpointField(endpointsConfig, endpoint, 'iconURL') ?? '';
if (endpointIconURL && EModelEndpoint[endpointIconURL] != null) {
Expand Down
42 changes: 19 additions & 23 deletions client/src/utils/presets.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
import type { TPreset, TPlugin } from 'librechat-data-provider';
import { EModelEndpoint } from 'librechat-data-provider';

export const getPresetIcon = (preset: TPreset, Icon) => {
return Icon({
size: 20,
endpoint: preset?.endpoint,
model: preset?.model,
error: false,
className: 'icon-md',
isCreatedByUser: false,
});
};

type TEndpoints = Array<string | EModelEndpoint>;

export const getPresetTitle = (preset: TPreset, mention?: boolean) => {
Expand All @@ -27,7 +16,7 @@ export const getPresetTitle = (preset: TPreset, mention?: boolean) => {
toneStyle,
} = preset;
let title = '';
let modelInfo = model || '';
let modelInfo = model ?? '';
let label = '';

const usesChatGPTLabel: TEndpoints = [
Expand All @@ -37,24 +26,31 @@ export const getPresetTitle = (preset: TPreset, mention?: boolean) => {
];
const usesModelLabel: TEndpoints = [EModelEndpoint.google, EModelEndpoint.anthropic];

if (endpoint && usesChatGPTLabel.includes(endpoint)) {
label = chatGptLabel || '';
} else if (endpoint && usesModelLabel.includes(endpoint)) {
label = modelLabel || '';
if (endpoint != null && endpoint && usesChatGPTLabel.includes(endpoint)) {
label = chatGptLabel ?? '';
} else if (endpoint != null && endpoint && usesModelLabel.includes(endpoint)) {
label = modelLabel ?? '';
} else if (endpoint === EModelEndpoint.bingAI) {
modelInfo = jailbreak ? 'Sydney' : modelInfo;
label = toneStyle ? `: ${toneStyle}` : '';
modelInfo = jailbreak === true ? 'Sydney' : modelInfo;
label = toneStyle != null && toneStyle ? `: ${toneStyle}` : '';
}

if (label && presetTitle && label.toLowerCase().includes(presetTitle.toLowerCase())) {
if (
label &&
presetTitle != null &&
presetTitle &&
label.toLowerCase().includes(presetTitle.toLowerCase())
) {
title = label + ': ';
label = '';
} else if (presetTitle && presetTitle.trim() !== 'New Chat') {
} else if (presetTitle != null && presetTitle && presetTitle.trim() !== 'New Chat') {
title = presetTitle + ': ';
}

if (mention) {
return `${modelInfo}${label ? ` | ${label}` : ''}${promptPrefix ? ` | ${promptPrefix}` : ''}${
if (mention === true) {
return `${modelInfo}${label ? ` | ${label}` : ''}${
promptPrefix != null && promptPrefix ? ` | ${promptPrefix}` : ''
}${
tools
? ` | ${tools
.map((tool: TPlugin | string) => {
Expand All @@ -74,7 +70,7 @@ export const getPresetTitle = (preset: TPreset, mention?: boolean) => {
/** Remove unavailable tools from the preset */
export const removeUnavailableTools = (
preset: TPreset,
availableTools: Record<string, TPlugin>,
availableTools: Record<string, TPlugin | undefined>,
) => {
const newPreset = { ...preset };

Expand Down
Loading