diff --git a/client/src/common/selector.ts b/client/src/common/selector.ts index a6380552c7..1c7061d16e 100644 --- a/client/src/common/selector.ts +++ b/client/src/common/selector.ts @@ -1,11 +1,11 @@ import React from 'react'; -import { TModelSpec, TInterfaceConfig } from 'librechat-data-provider'; +import { TModelSpec, TStartupConfig } from 'librechat-data-provider'; export interface Endpoint { value: string; label: string; hasModels: boolean; - models?: string[]; + models?: Array<{ name: string; isGlobal?: boolean }>; icon: React.ReactNode; agentNames?: Record; assistantNames?: Record; @@ -19,6 +19,6 @@ export interface SelectedValues { } export interface ModelSelectorProps { - interfaceConfig: TInterfaceConfig; + startupConfig: TStartupConfig | undefined; modelSpecs: TModelSpec[]; } diff --git a/client/src/common/types.ts b/client/src/common/types.ts index 71e2e7a0c9..ce47a4667b 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -496,17 +496,6 @@ export interface ExtendedFile { metadata?: t.TFile['metadata']; } -export interface ExtendedEndpoint { - value: EModelEndpoint; - label: string; - hasModels: boolean; - icon: JSX.Element | null; - models?: string[]; - agentNames?: Record; - assistantNames?: Record; - modelIcons?: Record; -} - export interface ModelItemProps { modelName: string; endpoint: EModelEndpoint; diff --git a/client/src/components/Chat/Header.tsx b/client/src/components/Chat/Header.tsx index d8a499128f..3901a03729 100644 --- a/client/src/components/Chat/Header.tsx +++ b/client/src/components/Chat/Header.tsx @@ -9,7 +9,6 @@ import ExportAndShareMenu from './ExportAndShareMenu'; import { useMediaQuery, useHasAccess } from '~/hooks'; import BookmarkMenu from './Menus/BookmarkMenu'; import AddMultiConvo from './AddMultiConvo'; - const defaultInterface = getConfigDefaults().interface; export default function Header() { @@ -38,7 +37,7 @@ export default function Header() {
{!navVisible && } - {} + {} {interfaceConfig.presets === true && interfaceConfig.modelSelect && } {hasAccessToBookmarks === true && } {hasAccessToMultiConvo === true && } diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx index 28594ebfd4..bcab56fb5c 100644 --- a/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx +++ b/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx @@ -98,9 +98,9 @@ function ModelSelectorContent() { ); } -export default function ModelSelector({ interfaceConfig, modelSpecs }: ModelSelectorProps) { +export default function ModelSelector({ startupConfig, modelSpecs }: ModelSelectorProps) { return ( - + ); diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx index 68e95d44cb..79caa67d3f 100644 --- a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx +++ b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx @@ -45,13 +45,13 @@ export function useModelSelectorContext() { interface ModelSelectorProviderProps { children: React.ReactNode; modelSpecs: t.TModelSpec[]; - interfaceConfig: t.TInterfaceConfig; + startupConfig: t.TStartupConfig | undefined; } export function ModelSelectorProvider({ children, modelSpecs, - interfaceConfig, + startupConfig, }: ModelSelectorProviderProps) { const agentsMap = useAgentsMapContext(); const assistantsMap = useAssistantsMapContext(); @@ -61,7 +61,7 @@ export function ModelSelectorProvider({ agentsMap, assistantsMap, endpointsConfig, - interfaceConfig, + startupConfig, }); const { onSelectEndpoint, onSelectSpec } = useSelectMention({ // presets, diff --git a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx index 3c5987b09e..cece241a91 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx @@ -89,7 +89,13 @@ export function EndpointItem({ endpoint }: EndpointItemProps) { if (endpoint.hasModels) { const filteredModels = searchValue - ? filterModels(endpoint, endpoint.models || [], searchValue, agentsMap, assistantsMap) + ? filterModels( + endpoint, + (endpoint.models || []).map((model) => model.name), + searchValue, + agentsMap, + assistantsMap, + ) : null; const placeholder = isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value) diff --git a/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx index 34fe72c414..11f82af938 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { EarthIcon } from 'lucide-react'; import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider'; import type { Endpoint } from '~/common'; import { useModelSelectorContext } from '../ModelSelectorContext'; @@ -12,12 +13,16 @@ interface EndpointModelItemProps { export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointModelItemProps) { const { handleSelectModel } = useModelSelectorContext(); + let isGlobal = false; let modelName = modelId; const avatarUrl = endpoint?.modelIcons?.[modelId ?? ''] || null; // Use custom names if available if (endpoint && modelId && isAgentsEndpoint(endpoint.value) && endpoint.agentNames?.[modelId]) { modelName = endpoint.agentNames[modelId]; + + const modelInfo = endpoint?.models?.find((m) => m.name === modelId); + isGlobal = modelInfo?.isGlobal ?? false; } else if ( endpoint && modelId && @@ -46,6 +51,7 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod ) : null} {modelName}
+ {isGlobal && } {isSelected && ( , selectedModel: string | null, filteredModels?: string[], ) { - const modelsToRender = filteredModels || models; + const modelsToRender = filteredModels || models.map((model) => model.name); return modelsToRender.map( (modelId) => diff --git a/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx b/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx index 87455d4d86..f3ee7ed6e7 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx @@ -1,4 +1,5 @@ import React, { Fragment } from 'react'; +import { EarthIcon } from 'lucide-react'; import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider'; import type { TModelSpec } from 'librechat-data-provider'; import type { Endpoint } from '~/common'; @@ -20,8 +21,6 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP handleSelectModel, handleSelectEndpoint, endpointsConfig, - agentsMap, - assistantsMap, } = useModelSelectorContext(); const { @@ -102,20 +101,20 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP const lowerQuery = searchValue.toLowerCase(); const filteredModels = endpoint.label.toLowerCase().includes(lowerQuery) ? endpoint.models - : endpoint.models.filter((modelId) => { - let modelName = modelId; + : endpoint.models.filter((model) => { + let modelName = model.name; if ( isAgentsEndpoint(endpoint.value) && endpoint.agentNames && - endpoint.agentNames[modelId] + endpoint.agentNames[model.name] ) { - modelName = endpoint.agentNames[modelId]; + modelName = endpoint.agentNames[model.name]; } else if ( isAssistantsEndpoint(endpoint.value) && endpoint.assistantNames && - endpoint.assistantNames[modelId] + endpoint.assistantNames[model.name] ) { - modelName = endpoint.assistantNames[modelId]; + modelName = endpoint.assistantNames[model.name]; } return modelName.toLowerCase().includes(lowerQuery); }); @@ -134,7 +133,10 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP )} {endpoint.label}
- {filteredModels.map((modelId) => { + {filteredModels.map((model) => { + const modelId = model.name; + + let isGlobal = false; let modelName = modelId; if ( isAgentsEndpoint(endpoint.value) && @@ -142,6 +144,8 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP endpoint.agentNames[modelId] ) { modelName = endpoint.agentNames[modelId]; + const modelInfo = endpoint?.models?.find((m) => m.name === modelId); + isGlobal = modelInfo?.isGlobal ?? false; } else if ( isAssistantsEndpoint(endpoint.value) && endpoint.assistantNames && @@ -168,6 +172,7 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP )} {modelName} + {isGlobal && } {selectedEndpoint === endpoint.value && selectedModel === modelId && ( ; + }, >( items: T[], searchValue: string, @@ -36,18 +41,18 @@ export function filterItems< if (item.models && item.models.length > 0) { return item.models.some((modelId) => { - if (modelId.toLowerCase().includes(searchTermLower)) { + if (modelId.name.toLowerCase().includes(searchTermLower)) { return true; } - if (isAgentsEndpoint(item.value) && agentsMap && modelId in agentsMap) { - const agentName = agentsMap[modelId]?.name; + if (isAgentsEndpoint(item.value) && agentsMap && modelId.name in agentsMap) { + const agentName = agentsMap[modelId.name]?.name; return typeof agentName === 'string' && agentName.toLowerCase().includes(searchTermLower); } if (isAssistantsEndpoint(item.value) && assistantsMap) { const endpoint = item.value ?? ''; - const assistant = assistantsMap[endpoint][modelId]; + const assistant = assistantsMap[endpoint][modelId.name]; if (assistant && typeof assistant.name === 'string') { return assistant.name.toLowerCase().includes(searchTermLower); } diff --git a/client/src/components/ui/Badge.tsx b/client/src/components/ui/Badge.tsx index e100f170f4..d99017c73f 100644 --- a/client/src/components/ui/Badge.tsx +++ b/client/src/components/ui/Badge.tsx @@ -44,9 +44,7 @@ export default function Badge({ } if (!isEditing && onToggle) { - if (typeof window !== 'undefined' && window.innerWidth >= 768) { - e.preventDefault(); - } + e.preventDefault(); e.stopPropagation(); onToggle(); } diff --git a/client/src/hooks/Endpoint/useEndpoints.ts b/client/src/hooks/Endpoint/useEndpoints.ts index ae56d1d0b5..34b8ab4461 100644 --- a/client/src/hooks/Endpoint/useEndpoints.ts +++ b/client/src/hooks/Endpoint/useEndpoints.ts @@ -11,10 +11,10 @@ import type { Assistant, TEndpointsConfig, TAgentsMap, - TInterfaceConfig, TAssistantsMap, + TStartupConfig, } from 'librechat-data-provider'; -import type { ExtendedEndpoint } from '~/common'; +import type { Endpoint } from '~/common'; import { mapEndpoints, getIconKey, getEndpointField } from '~/utils'; import { useGetEndpointsQuery } from '~/data-provider'; import { useChatContext } from '~/Providers'; @@ -25,16 +25,18 @@ export const useEndpoints = ({ agentsMap, assistantsMap, endpointsConfig, - interfaceConfig, + startupConfig, }: { agentsMap?: TAgentsMap; assistantsMap?: TAssistantsMap; endpointsConfig: TEndpointsConfig; - interfaceConfig: TInterfaceConfig; + startupConfig: TStartupConfig | undefined; }) => { const modelsQuery = useGetModelsQuery(); const { conversation } = useChatContext(); const { data: endpoints = [] } = useGetEndpointsQuery({ select: mapEndpoints }); + const { instanceProjectId } = startupConfig ?? {}; + const interfaceConfig = startupConfig?.interface ?? {}; const { endpoint } = conversation ?? {}; @@ -84,7 +86,7 @@ export const useEndpoints = ({ [endpointsConfig], ); - const mappedEndpoints: ExtendedEndpoint[] = useMemo(() => { + const mappedEndpoints: Endpoint[] = useMemo(() => { return filteredEndpoints.map((ep) => { const endpointType = getEndpointField(endpointsConfig, ep, 'type'); const iconKey = getIconKey({ endpoint: ep, endpointsConfig, endpointType }); @@ -98,7 +100,7 @@ export const useEndpoints = ({ (modelsQuery.data?.[ep]?.length ?? 0) > 0); // Base result object with formatted default icon - const result: ExtendedEndpoint = { + const result: Endpoint = { value: ep, label: alternateName[ep] || ep, hasModels, @@ -114,7 +116,11 @@ export const useEndpoints = ({ // Handle agents case if (ep === EModelEndpoint.agents && agents.length > 0) { - result.models = agents.map((agent) => agent.id); + result.models = agents.map((agent) => ({ + name: agent.id, + isGlobal: + (instanceProjectId != null && agent.projectIds?.includes(instanceProjectId)) ?? false, + })); result.agentNames = agents.reduce((acc, agent) => { acc[agent.id] = agent.name || ''; return acc; @@ -127,7 +133,10 @@ export const useEndpoints = ({ // Handle assistants case else if (ep === EModelEndpoint.assistants && assistants.length > 0) { - result.models = assistants.map((assistant: { id: string }) => assistant.id); + result.models = assistants.map((assistant: { id: string }) => ({ + name: assistant.id, + isGlobal: false, + })); result.assistantNames = assistants.reduce( (acc: Record, assistant: Assistant) => { acc[assistant.id] = assistant.name || ''; @@ -143,7 +152,10 @@ export const useEndpoints = ({ {}, ); } else if (ep === EModelEndpoint.azureAssistants && azureAssistants.length > 0) { - result.models = azureAssistants.map((assistant: { id: string }) => assistant.id); + result.models = azureAssistants.map((assistant: { id: string }) => ({ + name: assistant.id, + isGlobal: false, + })); result.assistantNames = azureAssistants.reduce( (acc: Record, assistant: Assistant) => { acc[assistant.id] = assistant.name || ''; @@ -166,7 +178,10 @@ export const useEndpoints = ({ ep !== EModelEndpoint.assistants && (modelsQuery.data?.[ep]?.length ?? 0) > 0 ) { - result.models = modelsQuery.data?.[ep]; + result.models = modelsQuery.data?.[ep]?.map((model) => ({ + name: model, + isGlobal: false, + })); } return result; diff --git a/client/src/utils/endpointFilter.ts b/client/src/utils/endpointFilter.ts deleted file mode 100644 index 608b6d096f..0000000000 --- a/client/src/utils/endpointFilter.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider'; -import { ExtendedEndpoint } from '~/common'; - -export const filterMenuItems = ( - searchTerm: string, - mappedEndpoints: ExtendedEndpoint[], - agents: any[], - assistants: any[], - modelsData: any, -): ExtendedEndpoint[] => { - if (!searchTerm.trim()) { - return mappedEndpoints; - } - - const lowercaseSearchTerm = searchTerm.toLowerCase(); - - return mappedEndpoints - .map((ep) => { - if (ep.hasModels) { - if (isAgentsEndpoint(ep.value)) { - const filteredAgents = agents.filter((agent) => - agent.name?.toLowerCase().includes(lowercaseSearchTerm), - ); - if (ep.label.toLowerCase().includes(lowercaseSearchTerm) || filteredAgents.length > 0) { - return { - ...ep, - models: filteredAgents.map((agent) => agent.id), - agentNames: filteredAgents.reduce((acc: Record, agent) => { - acc[agent.id] = agent.name || ''; - return acc; - }, {}), - }; - } - return null; - } else if (isAssistantsEndpoint(ep.value)) { - const filteredAssistants = assistants.filter((assistant) => - assistant.name?.toLowerCase().includes(lowercaseSearchTerm), - ); - if ( - ep.label.toLowerCase().includes(lowercaseSearchTerm) || - filteredAssistants.length > 0 - ) { - return { - ...ep, - models: filteredAssistants.map((assistant) => assistant.id), - assistantNames: filteredAssistants.reduce( - (acc: Record, assistant) => { - acc[assistant.id] = assistant.name || ''; - return acc; - }, - {}, - ), - }; - } - return null; - } else { - const allModels = modelsData?.[ep.value] ?? []; - const filteredModels = allModels.filter((model: string) => - model.toLowerCase().includes(lowercaseSearchTerm), - ); - if (ep.label.toLowerCase().includes(lowercaseSearchTerm) || filteredModels.length > 0) { - return { ...ep, models: filteredModels }; - } - return null; - } - } else { - return ep.label.toLowerCase().includes(lowercaseSearchTerm) ? { ...ep, models: [] } : null; - } - }) - .filter(Boolean) as ExtendedEndpoint[]; -}; - -export default filterMenuItems; diff --git a/client/src/utils/index.ts b/client/src/utils/index.ts index 7c51ab6361..1a295837f4 100644 --- a/client/src/utils/index.ts +++ b/client/src/utils/index.ts @@ -20,7 +20,6 @@ export { default as logger } from './logger'; export { default as buildTree } from './buildTree'; export { default as getLoginError } from './getLoginError'; export { default as cleanupPreset } from './cleanupPreset'; -export { default as filterMenuItems } from './endpointFilter'; export { default as buildDefaultConvo } from './buildDefaultConvo'; export { default as getDefaultEndpoint } from './getDefaultEndpoint';