| | import debounce from 'lodash/debounce'; |
| | import React, { createContext, useContext, useState, useMemo } from 'react'; |
| | import { EModelEndpoint, isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider'; |
| | import type * as t from 'librechat-data-provider'; |
| | import type { Endpoint, SelectedValues } from '~/common'; |
| | 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 { filterItems } from './utils'; |
| |
|
| | type ModelSelectorContextType = { |
| | |
| | searchValue: string; |
| | selectedValues: SelectedValues; |
| | endpointSearchValues: Record<string, string>; |
| | searchResults: (t.TModelSpec | Endpoint)[] | null; |
| | |
| | modelSpecs: t.TModelSpec[]; |
| | mappedEndpoints: Endpoint[]; |
| | agentsMap: t.TAgentsMap | undefined; |
| | assistantsMap: t.TAssistantsMap | undefined; |
| | endpointsConfig: t.TEndpointsConfig; |
| |
|
| | |
| | endpointRequiresUserKey: (endpoint: string) => boolean; |
| | setSelectedValues: React.Dispatch<React.SetStateAction<SelectedValues>>; |
| | setSearchValue: (value: string) => void; |
| | setEndpointSearchValue: (endpoint: string, value: string) => void; |
| | handleSelectSpec: (spec: t.TModelSpec) => void; |
| | handleSelectEndpoint: (endpoint: Endpoint) => void; |
| | handleSelectModel: (endpoint: Endpoint, model: string) => void; |
| | } & ReturnType<typeof useKeyDialog>; |
| |
|
| | const ModelSelectorContext = createContext<ModelSelectorContextType | undefined>(undefined); |
| |
|
| | export function useModelSelectorContext() { |
| | const context = useContext(ModelSelectorContext); |
| | if (context === undefined) { |
| | throw new Error('useModelSelectorContext must be used within a ModelSelectorProvider'); |
| | } |
| | return context; |
| | } |
| |
|
| | interface ModelSelectorProviderProps { |
| | children: React.ReactNode; |
| | startupConfig: t.TStartupConfig | undefined; |
| | } |
| |
|
| | export function ModelSelectorProvider({ children, startupConfig }: ModelSelectorProviderProps) { |
| | const agentsMap = useAgentsMapContext(); |
| | const assistantsMap = useAssistantsMapContext(); |
| | const { data: endpointsConfig } = useGetEndpointsQuery(); |
| | const { endpoint, model, spec, agent_id, assistant_id, conversation, newConversation } = |
| | useModelSelectorChatContext(); |
| | const modelSpecs = useMemo(() => { |
| | const specs = startupConfig?.modelSpecs?.list ?? []; |
| | if (!agentsMap) { |
| | return specs; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | return specs.filter((spec) => { |
| | if (spec.preset?.endpoint === EModelEndpoint.agents && spec.preset?.agent_id) { |
| | return spec.preset.agent_id in agentsMap; |
| | } |
| | |
| | return true; |
| | }); |
| | }, [startupConfig, agentsMap]); |
| |
|
| | const permissionLevel = useAgentDefaultPermissionLevel(); |
| | const { data: agents = null } = useListAgentsQuery( |
| | { requiredPermission: permissionLevel }, |
| | { |
| | select: (data) => data?.data, |
| | }, |
| | ); |
| |
|
| | const { mappedEndpoints, endpointRequiresUserKey } = useEndpoints({ |
| | agents, |
| | assistantsMap, |
| | startupConfig, |
| | endpointsConfig, |
| | }); |
| |
|
| | const { onSelectEndpoint, onSelectSpec } = useSelectMention({ |
| | |
| | modelSpecs, |
| | conversation, |
| | assistantsMap, |
| | endpointsConfig, |
| | newConversation, |
| | returnHandlers: true, |
| | }); |
| |
|
| | |
| | const [selectedValues, setSelectedValues] = useState<SelectedValues>({ |
| | endpoint: endpoint || '', |
| | model: model || '', |
| | modelSpec: spec || '', |
| | }); |
| | useSelectorEffects({ |
| | agentsMap, |
| | 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, |
| | }); |
| |
|
| | const [searchValue, setSearchValueState] = useState(''); |
| | const [endpointSearchValues, setEndpointSearchValues] = useState<Record<string, string>>({}); |
| |
|
| | const keyProps = useKeyDialog(); |
| |
|
| | |
| | const searchResults = useMemo(() => { |
| | if (!searchValue) { |
| | return null; |
| | } |
| | const allItems = [...modelSpecs, ...mappedEndpoints]; |
| | return filterItems(allItems, searchValue, agentsMap, assistantsMap || {}); |
| | }, [searchValue, modelSpecs, mappedEndpoints, agentsMap, assistantsMap]); |
| |
|
| | const setDebouncedSearchValue = useMemo( |
| | () => |
| | debounce((value: string) => { |
| | setSearchValueState(value); |
| | }, 200), |
| | [], |
| | ); |
| | const setEndpointSearchValue = (endpoint: string, value: string) => { |
| | setEndpointSearchValues((prev) => ({ |
| | ...prev, |
| | [endpoint]: value, |
| | })); |
| | }; |
| |
|
| | const handleSelectSpec = (spec: t.TModelSpec) => { |
| | let model = spec.preset.model ?? null; |
| | onSelectSpec?.(spec); |
| | if (isAgentsEndpoint(spec.preset.endpoint)) { |
| | model = spec.preset.agent_id ?? ''; |
| | } else if (isAssistantsEndpoint(spec.preset.endpoint)) { |
| | model = spec.preset.assistant_id ?? ''; |
| | } |
| | setSelectedValues({ |
| | endpoint: spec.preset.endpoint, |
| | model, |
| | modelSpec: spec.name, |
| | }); |
| | }; |
| |
|
| | const handleSelectEndpoint = (endpoint: Endpoint) => { |
| | if (!endpoint.hasModels) { |
| | if (endpoint.value) { |
| | onSelectEndpoint?.(endpoint.value); |
| | } |
| | setSelectedValues({ |
| | endpoint: endpoint.value, |
| | model: '', |
| | modelSpec: '', |
| | }); |
| | } |
| | }; |
| |
|
| | const handleSelectModel = (endpoint: Endpoint, model: string) => { |
| | if (isAgentsEndpoint(endpoint.value)) { |
| | onSelectEndpoint?.(endpoint.value, { |
| | agent_id: model, |
| | model: agentsMap?.[model]?.model ?? '', |
| | }); |
| | } else if (isAssistantsEndpoint(endpoint.value)) { |
| | onSelectEndpoint?.(endpoint.value, { |
| | assistant_id: model, |
| | model: assistantsMap?.[endpoint.value]?.[model]?.model ?? '', |
| | }); |
| | } else if (endpoint.value) { |
| | onSelectEndpoint?.(endpoint.value, { model }); |
| | } |
| | setSelectedValues({ |
| | endpoint: endpoint.value, |
| | model, |
| | modelSpec: '', |
| | }); |
| | }; |
| |
|
| | const value = { |
| | |
| | searchValue, |
| | searchResults, |
| | selectedValues, |
| | endpointSearchValues, |
| | |
| | agentsMap, |
| | modelSpecs, |
| | assistantsMap, |
| | mappedEndpoints, |
| | endpointsConfig, |
| |
|
| | |
| | handleSelectSpec, |
| | handleSelectModel, |
| | setSelectedValues, |
| | handleSelectEndpoint, |
| | setEndpointSearchValue, |
| | endpointRequiresUserKey, |
| | setSearchValue: setDebouncedSearchValue, |
| | |
| | ...keyProps, |
| | }; |
| |
|
| | return <ModelSelectorContext.Provider value={value}>{children}</ModelSelectorContext.Provider>; |
| | } |
| |
|