diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx
index 1a1463a414..01d96432a7 100644
--- a/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx
+++ b/client/src/components/Chat/Menus/Endpoints/ModelSelector.tsx
@@ -1,6 +1,7 @@
import React, { useMemo } from 'react';
import type { ModelSelectorProps } from '~/common';
import { ModelSelectorProvider, useModelSelectorContext } from './ModelSelectorContext';
+import { ModelSelectorChatProvider } from './ModelSelectorChatContext';
import { renderModelSpecs, renderEndpoints, renderSearchResults } from './components';
import { getSelectedIcon, getDisplayValue } from './utils';
import { CustomMenu as Menu } from './CustomMenu';
@@ -12,6 +13,7 @@ function ModelSelectorContent() {
const {
// LibreChat
+ agentsMap,
modelSpecs,
mappedEndpoints,
endpointsConfig,
@@ -43,11 +45,12 @@ function ModelSelectorContent() {
() =>
getDisplayValue({
localize,
+ agentsMap,
modelSpecs,
selectedValues,
mappedEndpoints,
}),
- [localize, modelSpecs, selectedValues, mappedEndpoints],
+ [localize, agentsMap, modelSpecs, selectedValues, mappedEndpoints],
);
const trigger = (
@@ -100,8 +103,10 @@ function ModelSelectorContent() {
export default function ModelSelector({ startupConfig }: ModelSelectorProps) {
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx
new file mode 100644
index 0000000000..bd639523d8
--- /dev/null
+++ b/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx
@@ -0,0 +1,54 @@
+import React, { createContext, useContext, useMemo } from 'react';
+import type { EModelEndpoint } from 'librechat-data-provider';
+import { useChatContext } from '~/Providers/ChatContext';
+
+interface ModelSelectorChatContextValue {
+ endpoint?: EModelEndpoint | null;
+ model?: string | null;
+ spec?: string | null;
+ agent_id?: string | null;
+ assistant_id?: string | null;
+ newConversation: ReturnType['newConversation'];
+}
+
+const ModelSelectorChatContext = createContext(
+ undefined,
+);
+
+export function ModelSelectorChatProvider({ children }: { children: React.ReactNode }) {
+ const { conversation, newConversation } = useChatContext();
+
+ /** Context value only created when relevant conversation properties change */
+ const contextValue = useMemo(
+ () => ({
+ endpoint: conversation?.endpoint,
+ model: conversation?.model,
+ spec: conversation?.spec,
+ agent_id: conversation?.agent_id,
+ assistant_id: conversation?.assistant_id,
+ newConversation,
+ }),
+ [
+ conversation?.endpoint,
+ conversation?.model,
+ conversation?.spec,
+ conversation?.agent_id,
+ conversation?.assistant_id,
+ newConversation,
+ ],
+ );
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useModelSelectorChatContext() {
+ const context = useContext(ModelSelectorChatContext);
+ if (!context) {
+ throw new Error('useModelSelectorChatContext must be used within ModelSelectorChatProvider');
+ }
+ return context;
+}
diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx
index 01fc0e5a7a..fa27a3244a 100644
--- a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx
+++ b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx
@@ -3,10 +3,16 @@ import React, { createContext, useContext, useState, useMemo } from 'react';
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
import type * as t from 'librechat-data-provider';
import type { Endpoint, SelectedValues } from '~/common';
-import { useAgentsMapContext, useAssistantsMapContext, useChatContext } from '~/Providers';
-import { useEndpoints, useSelectorEffects, useKeyDialog } from '~/hooks';
+import {
+ useAgentDefaultPermissionLevel,
+ useSelectorEffects,
+ useKeyDialog,
+ useEndpoints,
+} from '~/hooks';
+import { useAgentsMapContext, useAssistantsMapContext } from '~/Providers';
+import { useGetEndpointsQuery, useListAgentsQuery } from '~/data-provider';
+import { useModelSelectorChatContext } from './ModelSelectorChatContext';
import useSelectMention from '~/hooks/Input/useSelectMention';
-import { useGetEndpointsQuery } from '~/data-provider';
import { filterItems } from './utils';
type ModelSelectorContextType = {
@@ -51,14 +57,24 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector
const agentsMap = useAgentsMapContext();
const assistantsMap = useAssistantsMapContext();
const { data: endpointsConfig } = useGetEndpointsQuery();
- const { conversation, newConversation } = useChatContext();
+ const { endpoint, model, spec, agent_id, assistant_id, newConversation } =
+ useModelSelectorChatContext();
const modelSpecs = useMemo(() => startupConfig?.modelSpecs?.list ?? [], [startupConfig]);
+ const permissionLevel = useAgentDefaultPermissionLevel();
+ const { data: agents = null } = useListAgentsQuery(
+ { requiredPermission: permissionLevel },
+ {
+ select: (data) => data?.data,
+ },
+ );
+
const { mappedEndpoints, endpointRequiresUserKey } = useEndpoints({
- agentsMap,
+ agents,
assistantsMap,
startupConfig,
endpointsConfig,
});
+
const { onSelectEndpoint, onSelectSpec } = useSelectMention({
// presets,
modelSpecs,
@@ -70,13 +86,21 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector
// State
const [selectedValues, setSelectedValues] = useState({
- endpoint: conversation?.endpoint || '',
- model: conversation?.model || '',
- modelSpec: conversation?.spec || '',
+ endpoint: endpoint || '',
+ model: model || '',
+ modelSpec: spec || '',
});
useSelectorEffects({
agentsMap,
- conversation,
+ conversation: endpoint
+ ? ({
+ endpoint: endpoint ?? null,
+ model: model ?? null,
+ spec: spec ?? null,
+ agent_id: agent_id ?? null,
+ assistant_id: assistant_id ?? null,
+ } as any)
+ : null,
assistantsMap,
setSelectedValues,
});
@@ -86,7 +110,7 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector
const keyProps = useKeyDialog();
- // Memoized search results
+ /** Memoized search results */
const searchResults = useMemo(() => {
if (!searchValue) {
return null;
@@ -95,7 +119,6 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector
return filterItems(allItems, searchValue, agentsMap, assistantsMap || {});
}, [searchValue, modelSpecs, mappedEndpoints, agentsMap, assistantsMap]);
- // Functions
const setDebouncedSearchValue = useMemo(
() =>
debounce((value: string) => {
diff --git a/client/src/components/Chat/Menus/Endpoints/utils.ts b/client/src/components/Chat/Menus/Endpoints/utils.ts
index 5ed155c6a0..1681ed7f1d 100644
--- a/client/src/components/Chat/Menus/Endpoints/utils.ts
+++ b/client/src/components/Chat/Menus/Endpoints/utils.ts
@@ -167,11 +167,13 @@ export const getDisplayValue = ({
mappedEndpoints,
selectedValues,
modelSpecs,
+ agentsMap,
}: {
localize: ReturnType;
selectedValues: SelectedValues;
mappedEndpoints: Endpoint[];
modelSpecs: TModelSpec[];
+ agentsMap?: TAgentsMap;
}) => {
if (selectedValues.modelSpec) {
const spec = modelSpecs.find((s) => s.name === selectedValues.modelSpec);
@@ -190,6 +192,9 @@ export const getDisplayValue = ({
endpoint.agentNames[selectedValues.model]
) {
return endpoint.agentNames[selectedValues.model];
+ } else if (isAgentsEndpoint(endpoint.value) && agentsMap) {
+ const agent = agentsMap[selectedValues.model];
+ return agent?.name || selectedValues.model;
}
if (
diff --git a/client/src/components/SidePanel/Agents/AgentSelect.tsx b/client/src/components/SidePanel/Agents/AgentSelect.tsx
index 411f1c45fc..6379a64ee1 100644
--- a/client/src/components/SidePanel/Agents/AgentSelect.tsx
+++ b/client/src/components/SidePanel/Agents/AgentSelect.tsx
@@ -7,8 +7,8 @@ import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-que
import type { Agent, AgentCreateParams } from 'librechat-data-provider';
import type { TAgentCapabilities, AgentForm } from '~/common';
import { cn, createProviderOption, processAgentOption, getDefaultAgentFormValues } from '~/utils';
-import { useGetStartupConfig, useListAgentsQuery } from '~/data-provider';
import { useLocalize, useAgentDefaultPermissionLevel } from '~/hooks';
+import { useListAgentsQuery } from '~/data-provider';
const keys = new Set(Object.keys(defaultAgentFormValues));
@@ -26,8 +26,6 @@ export default function AgentSelect({
const localize = useLocalize();
const lastSelectedAgent = useRef(null);
const { control, reset } = useFormContext();
-
- const { data: startupConfig } = useGetStartupConfig();
const permissionLevel = useAgentDefaultPermissionLevel();
const { data: agents = null } = useListAgentsQuery(
@@ -40,7 +38,6 @@ export default function AgentSelect({
...agent,
name: agent.name || agent.id,
},
- instanceProjectId: startupConfig?.instanceProjectId,
}),
),
},
@@ -122,7 +119,7 @@ export default function AgentSelect({
reset(formValues);
},
- [reset, startupConfig],
+ [reset],
);
const onSelect = useCallback(
diff --git a/client/src/hooks/Agents/useAgentsMap.ts b/client/src/hooks/Agents/useAgentsMap.ts
index 19a0b1d996..020990a5c0 100644
--- a/client/src/hooks/Agents/useAgentsMap.ts
+++ b/client/src/hooks/Agents/useAgentsMap.ts
@@ -9,7 +9,7 @@ export default function useAgentsMap({
}: {
isAuthenticated: boolean;
}): TAgentsMap | undefined {
- const { data: agentsList = null } = useListAgentsQuery(
+ const { data: mappedAgents = null } = useListAgentsQuery(
{ requiredPermission: PermissionBits.VIEW },
{
select: (res) => mapAgents(res.data),
@@ -17,9 +17,9 @@ export default function useAgentsMap({
},
);
- const agents = useMemo(() => {
- return agentsList !== null ? agentsList : undefined;
- }, [agentsList]);
+ const agentsMap = useMemo(() => {
+ return mappedAgents !== null ? mappedAgents : undefined;
+ }, [mappedAgents]);
- return agents;
+ return agentsMap;
}
diff --git a/client/src/hooks/Endpoint/useEndpoints.ts b/client/src/hooks/Endpoint/useEndpoints.ts
index 5a074a0fc6..f43f8b5350 100644
--- a/client/src/hooks/Endpoint/useEndpoints.ts
+++ b/client/src/hooks/Endpoint/useEndpoints.ts
@@ -1,71 +1,56 @@
import React, { useMemo, useCallback } from 'react';
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
import {
- EModelEndpoint,
- PermissionTypes,
Permissions,
alternateName,
+ EModelEndpoint,
+ PermissionTypes,
} from 'librechat-data-provider';
import type {
- Agent,
- Assistant,
TEndpointsConfig,
- TAgentsMap,
TAssistantsMap,
TStartupConfig,
+ Assistant,
+ Agent,
} from 'librechat-data-provider';
import type { Endpoint } from '~/common';
import { mapEndpoints, getIconKey, getEndpointField } from '~/utils';
import { useGetEndpointsQuery } from '~/data-provider';
-import { useChatContext } from '~/Providers';
import { useHasAccess } from '~/hooks';
import { icons } from './Icons';
export const useEndpoints = ({
- agentsMap,
+ agents,
assistantsMap,
endpointsConfig,
startupConfig,
}: {
- agentsMap?: TAgentsMap;
+ agents?: Agent[] | null;
assistantsMap?: TAssistantsMap;
endpointsConfig: TEndpointsConfig;
startupConfig: TStartupConfig | undefined;
}) => {
const modelsQuery = useGetModelsQuery();
- const { conversation } = useChatContext();
const { data: endpoints = [] } = useGetEndpointsQuery({ select: mapEndpoints });
- const { instanceProjectId } = startupConfig ?? {};
const interfaceConfig = startupConfig?.interface ?? {};
const includedEndpoints = useMemo(
() => new Set(startupConfig?.modelSpecs?.addedEndpoints ?? []),
[startupConfig?.modelSpecs?.addedEndpoints],
);
- const { endpoint } = conversation ?? {};
-
const hasAgentAccess = useHasAccess({
permissionType: PermissionTypes.AGENTS,
permission: Permissions.USE,
});
- const agents = useMemo(
- () =>
- Object.values(agentsMap ?? {}).filter(
- (agent): agent is Agent & { name: string } =>
- agent !== undefined && 'id' in agent && 'name' in agent && agent.name !== null,
- ),
- [agentsMap],
- );
-
const assistants: Assistant[] = useMemo(
() => Object.values(assistantsMap?.[EModelEndpoint.assistants] ?? {}),
- [endpoint, assistantsMap],
+ [assistantsMap],
);
const azureAssistants: Assistant[] = useMemo(
() => Object.values(assistantsMap?.[EModelEndpoint.azureAssistants] ?? {}),
- [endpoint, assistantsMap],
+ [assistantsMap],
);
const filteredEndpoints = useMemo(() => {
@@ -84,7 +69,7 @@ export const useEndpoints = ({
}
return result;
- }, [endpoints, hasAgentAccess, includedEndpoints]);
+ }, [endpoints, hasAgentAccess, includedEndpoints, interfaceConfig.modelSelect]);
const endpointRequiresUserKey = useCallback(
(ep: string) => {
@@ -100,7 +85,7 @@ export const useEndpoints = ({
const Icon = icons[iconKey];
const endpointIconURL = getEndpointField(endpointsConfig, ep, 'iconURL');
const hasModels =
- (ep === EModelEndpoint.agents && agents?.length > 0) ||
+ (ep === EModelEndpoint.agents && (agents?.length ?? 0) > 0) ||
(ep === EModelEndpoint.assistants && assistants?.length > 0) ||
(ep !== EModelEndpoint.assistants &&
ep !== EModelEndpoint.agents &&
@@ -122,16 +107,16 @@ export const useEndpoints = ({
};
// Handle agents case
- if (ep === EModelEndpoint.agents && agents.length > 0) {
- result.models = agents.map((agent) => ({
+ if (ep === EModelEndpoint.agents && (agents?.length ?? 0) > 0) {
+ result.models = agents?.map((agent) => ({
name: agent.id,
isGlobal: agent.isPublic ?? false,
}));
- result.agentNames = agents.reduce((acc, agent) => {
+ result.agentNames = agents?.reduce((acc, agent) => {
acc[agent.id] = agent.name || '';
return acc;
}, {});
- result.modelIcons = agents.reduce((acc, agent) => {
+ result.modelIcons = agents?.reduce((acc, agent) => {
acc[agent.id] = agent?.avatar?.filepath;
return acc;
}, {});
@@ -192,7 +177,7 @@ export const useEndpoints = ({
return result;
});
- }, [filteredEndpoints, endpointsConfig, modelsQuery.data, agents, assistants]);
+ }, [filteredEndpoints, endpointsConfig, modelsQuery.data, agents, assistants, azureAssistants]);
return {
mappedEndpoints,
diff --git a/client/src/utils/forms.tsx b/client/src/utils/forms.tsx
index d8fa4cc8a8..ae6ed69a6e 100644
--- a/client/src/utils/forms.tsx
+++ b/client/src/utils/forms.tsx
@@ -57,11 +57,9 @@ export const getDefaultAgentFormValues = () => ({
export const processAgentOption = ({
agent: _agent,
fileMap,
- instanceProjectId,
}: {
agent?: Agent;
fileMap?: Record;
- instanceProjectId?: string;
}): TAgentOption => {
const isGlobal = _agent?.isPublic ?? false;
const agent: TAgentOption = {