LibreChat/packages/data-provider/src/models.ts
odrec 308347a7ad feat: add groupIcon property to modelSpecs for custom group icons
Added the ability to define icons for custom model spec groups in the UI selector.

Changes:
- Added  property to TModelSpec type and schema in data-provider
- Created GroupIcon component to render URL or built-in endpoint icons
- Updated CustomGroup component to display group icons
- Added documentation and examples in librechat.example.yaml

Usage:

The groupIcon can be:
- A built-in endpoint key (e.g., "openAI", "anthropic", "groq")
- A URL to a custom icon image

Only the first spec in a group needs groupIcon - all specs share the same icon.
2025-12-03 10:51:18 +01:00

67 lines
2.2 KiB
TypeScript

import { z } from 'zod';
import type { TPreset } from './schemas';
import {
EModelEndpoint,
tPresetSchema,
eModelEndpointSchema,
AuthType,
authTypeSchema,
} from './schemas';
export type TModelSpec = {
name: string;
label: string;
preset: TPreset;
order?: number;
default?: boolean;
description?: string;
/**
* Optional group name for organizing specs in the UI selector.
* - If it matches an endpoint name (e.g., "openAI", "groq"), the spec appears nested under that endpoint
* - If it's a custom name (doesn't match any endpoint), it creates a separate collapsible group
* - If omitted, the spec appears as a standalone item at the top level
*/
group?: string;
/**
* Optional icon URL for the group this spec belongs to.
* Only needs to be set on one spec per group - the first one found with a groupIcon will be used.
* Can be a URL or an endpoint name to use its icon.
*/
groupIcon?: string | EModelEndpoint;
showIconInMenu?: boolean;
showIconInHeader?: boolean;
iconURL?: string | EModelEndpoint; // Allow using project-included icons
authType?: AuthType;
webSearch?: boolean;
fileSearch?: boolean;
executeCode?: boolean;
mcpServers?: string[];
};
export const tModelSpecSchema = z.object({
name: z.string(),
label: z.string(),
preset: tPresetSchema,
order: z.number().optional(),
default: z.boolean().optional(),
description: z.string().optional(),
group: z.string().optional(),
groupIcon: z.union([z.string(), eModelEndpointSchema]).optional(),
showIconInMenu: z.boolean().optional(),
showIconInHeader: z.boolean().optional(),
iconURL: z.union([z.string(), eModelEndpointSchema]).optional(),
authType: authTypeSchema.optional(),
webSearch: z.boolean().optional(),
fileSearch: z.boolean().optional(),
executeCode: z.boolean().optional(),
mcpServers: z.array(z.string()).optional(),
});
export const specsConfigSchema = z.object({
enforce: z.boolean().default(false),
prioritize: z.boolean().default(true),
list: z.array(tModelSpecSchema).min(1),
addedEndpoints: z.array(z.union([z.string(), eModelEndpointSchema])).optional(),
});
export type TSpecsConfig = z.infer<typeof specsConfigSchema>;