mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 17:30:16 +01:00
🔧 refactor: Consolidate Logging, Model Selection & Actions Optimizations, Minor Fixes (#6553)
* 🔧 feat: Enhance logging configuration for production and debug environments * 🔒 feat: Implement encryption and decryption functions for sensitive values in ActionService with URL encoding/decoding * refactor: optimize action service for agent tools * refactor: optimize action processing for Assistants API * fix: handle case where agent is not found in loadAgent function * refactor: improve error handling in API calls by throwing new Error with logAxiosError output * chore: bump @librechat/agents to 2.3.95, fixes "Invalid tool call structure: No preceding AIMessage with tool_call_ids" * refactor: enhance error logging in logAxiosError function to include response status * refactor: remove unused useModelSelection hook from Endpoint * refactor: add support for assistants in useSelectorEffects hook * refactor: replace string easing with imported easings in Landing component * chore: remove duplicate translation * refactor: update model selection logic and improve localization for UI elements * refactor: replace endpoint value checks with helper functions for agents and assistants * refactor: optimize display value logic and utilize useMemo for performance improvements * refactor: clean up imports and optimize display/icon value logic in endpoint components, fix spec selection * refactor: enhance error logging in axios utility to include stack traces for better debugging * refactor: update logging configuration to use DEBUG_LOGGING and streamline log level handling * refactor: adjust className for export menu button to improve layout consistency and remove unused title prop from ShareButton * refactor: update import path for logAxiosError utility to improve module organization and clarity * refactor: implement debounced search value setter in ModelSelectorContext for improved performance
This commit is contained in:
parent
801b602e27
commit
299cabd6ed
26 changed files with 970 additions and 1135 deletions
|
|
@ -79,7 +79,7 @@ export default function ExportAndShareMenu({
|
|||
<Ariakit.MenuButton
|
||||
id="export-menu-button"
|
||||
aria-label="Export options"
|
||||
className="inline-flex size-10 items-center justify-center rounded-lg border border-border-light bg-transparent text-text-primary transition-all ease-in-out hover:bg-surface-tertiary disabled:pointer-events-none disabled:opacity-50 radix-state-open:bg-surface-tertiary"
|
||||
className="inline-flex size-10 flex-shrink-0 items-center justify-center rounded-lg border border-border-light bg-transparent text-text-primary transition-all ease-in-out hover:bg-surface-tertiary disabled:pointer-events-none disabled:opacity-50 radix-state-open:bg-surface-tertiary"
|
||||
>
|
||||
<Upload
|
||||
className="icon-md text-text-secondary"
|
||||
|
|
@ -103,7 +103,6 @@ export default function ExportAndShareMenu({
|
|||
<ShareButton
|
||||
triggerRef={shareButtonRef}
|
||||
conversationId={conversation.conversationId ?? ''}
|
||||
title={conversation.title ?? ''}
|
||||
open={showShareDialog}
|
||||
onOpenChange={setShowShareDialog}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useMemo, useCallback, useState, useEffect } from 'react';
|
||||
import { easings } from '@react-spring/web';
|
||||
import { useMemo, useCallback } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers';
|
||||
import { useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider';
|
||||
import { BirthdayIcon, TooltipAnchor, SplitText } from '~/components';
|
||||
|
|
@ -117,7 +117,7 @@ export default function Landing({ centerFormOnLanding }: { centerFormOnLanding:
|
|||
textAlign="center"
|
||||
animationFrom={{ opacity: 0, transform: 'translate3d(0,50px,0)' }}
|
||||
animationTo={{ opacity: 1, transform: 'translate3d(0,0,0)' }}
|
||||
easing="easeOutCubic"
|
||||
easing={easings.easeOutCubic}
|
||||
threshold={0}
|
||||
rootMargin="0px"
|
||||
/>
|
||||
|
|
@ -131,7 +131,7 @@ export default function Landing({ centerFormOnLanding }: { centerFormOnLanding:
|
|||
textAlign="center"
|
||||
animationFrom={{ opacity: 0, transform: 'translate3d(0,50px,0)' }}
|
||||
animationTo={{ opacity: 1, transform: 'translate3d(0,0,0)' }}
|
||||
easing="easeOutCubic"
|
||||
easing={easings.easeOutCubic}
|
||||
threshold={0}
|
||||
rootMargin="0px"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import type { ModelSelectorProps } from '~/common';
|
||||
import { ModelSelectorProvider, useModelSelectorContext } from './ModelSelectorContext';
|
||||
import { renderModelSpecs, renderEndpoints, renderSearchResults } from './components';
|
||||
import { getSelectedIcon, getDisplayValue } from './utils';
|
||||
import { CustomMenu as Menu } from './CustomMenu';
|
||||
import DialogManager from './DialogManager';
|
||||
import { getSelectedIcon } from './utils';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
function ModelSelectorContent() {
|
||||
|
|
@ -22,7 +22,6 @@ function ModelSelectorContent() {
|
|||
|
||||
// Functions
|
||||
setSearchValue,
|
||||
getDisplayValue,
|
||||
setSelectedValues,
|
||||
// Dialog
|
||||
keyDialogOpen,
|
||||
|
|
@ -30,18 +29,31 @@ function ModelSelectorContent() {
|
|||
keyDialogEndpoint,
|
||||
} = useModelSelectorContext();
|
||||
|
||||
const selectedIcon = getSelectedIcon({
|
||||
mappedEndpoints: mappedEndpoints ?? [],
|
||||
selectedValues,
|
||||
modelSpecs,
|
||||
endpointsConfig,
|
||||
});
|
||||
const selectedDisplayValue = getDisplayValue();
|
||||
const selectedIcon = useMemo(
|
||||
() =>
|
||||
getSelectedIcon({
|
||||
mappedEndpoints: mappedEndpoints ?? [],
|
||||
selectedValues,
|
||||
modelSpecs,
|
||||
endpointsConfig,
|
||||
}),
|
||||
[mappedEndpoints, selectedValues, modelSpecs, endpointsConfig],
|
||||
);
|
||||
const selectedDisplayValue = useMemo(
|
||||
() =>
|
||||
getDisplayValue({
|
||||
localize,
|
||||
modelSpecs,
|
||||
selectedValues,
|
||||
mappedEndpoints,
|
||||
}),
|
||||
[localize, modelSpecs, selectedValues, mappedEndpoints],
|
||||
);
|
||||
|
||||
const trigger = (
|
||||
<button
|
||||
className="my-1 flex h-10 w-full max-w-[70vw] items-center justify-center gap-2 rounded-xl border border-border-light bg-surface-secondary px-3 py-2 text-sm text-text-primary hover:bg-surface-tertiary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
|
||||
aria-label={localize('com_endpoint_select_model')}
|
||||
aria-label={localize('com_ui_select_model')}
|
||||
>
|
||||
{selectedIcon && React.isValidElement(selectedIcon) && (
|
||||
<div className="flex flex-shrink-0 items-center justify-center overflow-hidden">
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React, { startTransition, createContext, useContext, useState, useMemo } from 'react';
|
||||
import { EModelEndpoint, isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import debounce from 'lodash/debounce';
|
||||
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, useLocalize } from '~/hooks';
|
||||
import { useEndpoints, useSelectorEffects, useKeyDialog } from '~/hooks';
|
||||
import useSelectMention from '~/hooks/Input/useSelectMention';
|
||||
import { useGetEndpointsQuery } from '~/data-provider';
|
||||
import { filterItems } from './utils';
|
||||
|
|
@ -22,7 +23,6 @@ type ModelSelectorContextType = {
|
|||
endpointsConfig: t.TEndpointsConfig;
|
||||
|
||||
// Functions
|
||||
getDisplayValue: () => string;
|
||||
endpointRequiresUserKey: (endpoint: string) => boolean;
|
||||
setSelectedValues: React.Dispatch<React.SetStateAction<SelectedValues>>;
|
||||
setSearchValue: (value: string) => void;
|
||||
|
|
@ -53,7 +53,6 @@ export function ModelSelectorProvider({
|
|||
modelSpecs,
|
||||
interfaceConfig,
|
||||
}: ModelSelectorProviderProps) {
|
||||
const localize = useLocalize();
|
||||
const agentsMap = useAgentsMapContext();
|
||||
const assistantsMap = useAssistantsMapContext();
|
||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||
|
|
@ -101,10 +100,13 @@ export function ModelSelectorProvider({
|
|||
}, [searchValue, modelSpecs, mappedEndpoints, agentsMap, assistantsMap]);
|
||||
|
||||
// Functions
|
||||
const setSearchValue = (value: string) => {
|
||||
startTransition(() => setSearchValueState(value));
|
||||
};
|
||||
|
||||
const setDebouncedSearchValue = useMemo(
|
||||
() =>
|
||||
debounce((value: string) => {
|
||||
setSearchValueState(value);
|
||||
}, 200),
|
||||
[],
|
||||
);
|
||||
const setEndpointSearchValue = (endpoint: string, value: string) => {
|
||||
setEndpointSearchValues((prev) => ({
|
||||
...prev,
|
||||
|
|
@ -113,10 +115,16 @@ export function ModelSelectorProvider({
|
|||
};
|
||||
|
||||
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: spec.preset.model ?? null,
|
||||
model,
|
||||
modelSpec: spec.name,
|
||||
});
|
||||
};
|
||||
|
|
@ -154,46 +162,6 @@ export function ModelSelectorProvider({
|
|||
});
|
||||
};
|
||||
|
||||
const getDisplayValue = () => {
|
||||
if (selectedValues.modelSpec) {
|
||||
const spec = modelSpecs.find((s) => s.name === selectedValues.modelSpec);
|
||||
return spec?.label || localize('com_endpoint_select_model');
|
||||
}
|
||||
|
||||
if (selectedValues.model && selectedValues.endpoint) {
|
||||
const endpoint = mappedEndpoints.find((e) => e.value === selectedValues.endpoint);
|
||||
if (!endpoint) {
|
||||
return localize('com_endpoint_select_model');
|
||||
}
|
||||
|
||||
if (
|
||||
endpoint.value === EModelEndpoint.agents &&
|
||||
endpoint.agentNames &&
|
||||
endpoint.agentNames[selectedValues.model]
|
||||
) {
|
||||
return endpoint.agentNames[selectedValues.model];
|
||||
}
|
||||
|
||||
if (
|
||||
(endpoint.value === EModelEndpoint.assistants ||
|
||||
endpoint.value === EModelEndpoint.azureAssistants) &&
|
||||
endpoint.assistantNames &&
|
||||
endpoint.assistantNames[selectedValues.model]
|
||||
) {
|
||||
return endpoint.assistantNames[selectedValues.model];
|
||||
}
|
||||
|
||||
return selectedValues.model;
|
||||
}
|
||||
|
||||
if (selectedValues.endpoint) {
|
||||
const endpoint = mappedEndpoints.find((e) => e.value === selectedValues.endpoint);
|
||||
return endpoint?.label || localize('com_endpoint_select_model');
|
||||
}
|
||||
|
||||
return localize('com_endpoint_select_model');
|
||||
};
|
||||
|
||||
const value = {
|
||||
// State
|
||||
searchValue,
|
||||
|
|
@ -208,14 +176,13 @@ export function ModelSelectorProvider({
|
|||
endpointsConfig,
|
||||
|
||||
// Functions
|
||||
setSearchValue,
|
||||
getDisplayValue,
|
||||
handleSelectSpec,
|
||||
handleSelectModel,
|
||||
setSelectedValues,
|
||||
handleSelectEndpoint,
|
||||
setEndpointSearchValue,
|
||||
endpointRequiresUserKey,
|
||||
setSearchValue: setDebouncedSearchValue,
|
||||
// Dialog
|
||||
...keyProps,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -116,17 +116,15 @@ export function EndpointItem({ endpoint }: EndpointItemProps) {
|
|||
</div>
|
||||
}
|
||||
>
|
||||
{(endpoint.value === EModelEndpoint.assistants ||
|
||||
endpoint.value === EModelEndpoint.azureAssistants) &&
|
||||
endpoint.models === undefined ? (
|
||||
<div className="flex items-center justify-center p-2">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : filteredModels ? (
|
||||
renderEndpointModels(endpoint, endpoint.models || [], selectedModel, filteredModels)
|
||||
) : (
|
||||
endpoint.models && renderEndpointModels(endpoint, endpoint.models, selectedModel)
|
||||
)}
|
||||
{isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined ? (
|
||||
<div className="flex items-center justify-center p-2">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : filteredModels ? (
|
||||
renderEndpointModels(endpoint, endpoint.models || [], selectedModel, filteredModels)
|
||||
) : (
|
||||
endpoint.models && renderEndpointModels(endpoint, endpoint.models, selectedModel)
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { Endpoint } from '~/common';
|
||||
import { useModelSelectorContext } from '../ModelSelectorContext';
|
||||
import { CustomMenuItem as MenuItem } from '../CustomMenu';
|
||||
|
|
@ -16,18 +16,12 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
|||
const avatarUrl = endpoint?.modelIcons?.[modelId ?? ''] || null;
|
||||
|
||||
// Use custom names if available
|
||||
if (
|
||||
endpoint &&
|
||||
modelId &&
|
||||
endpoint.value === EModelEndpoint.agents &&
|
||||
endpoint.agentNames?.[modelId]
|
||||
) {
|
||||
if (endpoint && modelId && isAgentsEndpoint(endpoint.value) && endpoint.agentNames?.[modelId]) {
|
||||
modelName = endpoint.agentNames[modelId];
|
||||
} else if (
|
||||
endpoint &&
|
||||
modelId &&
|
||||
(endpoint.value === EModelEndpoint.assistants ||
|
||||
endpoint.value === EModelEndpoint.azureAssistants) &&
|
||||
isAssistantsEndpoint(endpoint.value) &&
|
||||
endpoint.assistantNames?.[modelId]
|
||||
) {
|
||||
modelName = endpoint.assistantNames[modelId];
|
||||
|
|
@ -44,9 +38,7 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
|||
<div className="flex h-5 w-5 items-center justify-center overflow-hidden rounded-full">
|
||||
<img src={avatarUrl} alt={modelName ?? ''} className="h-full w-full object-cover" />
|
||||
</div>
|
||||
) : (endpoint.value === EModelEndpoint.agents ||
|
||||
endpoint.value === EModelEndpoint.assistants ||
|
||||
endpoint.value === EModelEndpoint.azureAssistants) &&
|
||||
) : (isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value)) &&
|
||||
endpoint.icon ? (
|
||||
<div className="flex h-5 w-5 items-center justify-center overflow-hidden rounded-full">
|
||||
{endpoint.icon}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { TModelSpec } from 'librechat-data-provider';
|
||||
import type { Endpoint } from '~/common';
|
||||
import { useModelSelectorContext } from '../ModelSelectorContext';
|
||||
|
|
@ -105,14 +105,13 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP
|
|||
: endpoint.models.filter((modelId) => {
|
||||
let modelName = modelId;
|
||||
if (
|
||||
endpoint.value === EModelEndpoint.agents &&
|
||||
isAgentsEndpoint(endpoint.value) &&
|
||||
endpoint.agentNames &&
|
||||
endpoint.agentNames[modelId]
|
||||
) {
|
||||
modelName = endpoint.agentNames[modelId];
|
||||
} else if (
|
||||
(endpoint.value === EModelEndpoint.assistants ||
|
||||
endpoint.value === EModelEndpoint.azureAssistants) &&
|
||||
isAssistantsEndpoint(endpoint.value) &&
|
||||
endpoint.assistantNames &&
|
||||
endpoint.assistantNames[modelId]
|
||||
) {
|
||||
|
|
@ -138,14 +137,13 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP
|
|||
{filteredModels.map((modelId) => {
|
||||
let modelName = modelId;
|
||||
if (
|
||||
endpoint.value === EModelEndpoint.agents &&
|
||||
isAgentsEndpoint(endpoint.value) &&
|
||||
endpoint.agentNames &&
|
||||
endpoint.agentNames[modelId]
|
||||
) {
|
||||
modelName = endpoint.agentNames[modelId];
|
||||
} else if (
|
||||
(endpoint.value === EModelEndpoint.assistants ||
|
||||
endpoint.value === EModelEndpoint.azureAssistants) &&
|
||||
isAssistantsEndpoint(endpoint.value) &&
|
||||
endpoint.assistantNames &&
|
||||
endpoint.assistantNames[modelId]
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import React from 'react';
|
||||
import { Bot } from 'lucide-react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type {
|
||||
TModelSpec,
|
||||
TAgentsMap,
|
||||
TAssistantsMap,
|
||||
TEndpointsConfig,
|
||||
TModelSpec,
|
||||
} from 'librechat-data-provider';
|
||||
import type { useLocalize } from '~/hooks';
|
||||
import SpecIcon from '~/components/Chat/Menus/Endpoints/components/SpecIcon';
|
||||
import { Endpoint, SelectedValues } from '~/common';
|
||||
|
||||
|
|
@ -39,17 +40,13 @@ export function filterItems<
|
|||
return true;
|
||||
}
|
||||
|
||||
if (item.value === EModelEndpoint.agents && agentsMap && modelId in agentsMap) {
|
||||
if (isAgentsEndpoint(item.value) && agentsMap && modelId in agentsMap) {
|
||||
const agentName = agentsMap[modelId]?.name;
|
||||
return typeof agentName === 'string' && agentName.toLowerCase().includes(searchTermLower);
|
||||
}
|
||||
|
||||
if (
|
||||
(item.value === EModelEndpoint.assistants ||
|
||||
item.value === EModelEndpoint.azureAssistants) &&
|
||||
assistantsMap
|
||||
) {
|
||||
const endpoint = item.value;
|
||||
if (isAssistantsEndpoint(item.value) && assistantsMap) {
|
||||
const endpoint = item.value ?? '';
|
||||
const assistant = assistantsMap[endpoint][modelId];
|
||||
if (assistant && typeof assistant.name === 'string') {
|
||||
return assistant.name.toLowerCase().includes(searchTermLower);
|
||||
|
|
@ -80,11 +77,10 @@ export function filterModels(
|
|||
return models.filter((modelId) => {
|
||||
let modelName = modelId;
|
||||
|
||||
if (endpoint.value === EModelEndpoint.agents && agentsMap && agentsMap[modelId]) {
|
||||
if (isAgentsEndpoint(endpoint.value) && agentsMap && agentsMap[modelId]) {
|
||||
modelName = agentsMap[modelId].name || modelId;
|
||||
} else if (
|
||||
(endpoint.value === EModelEndpoint.assistants ||
|
||||
endpoint.value === EModelEndpoint.azureAssistants) &&
|
||||
isAssistantsEndpoint(endpoint.value) &&
|
||||
assistantsMap &&
|
||||
assistantsMap[endpoint.value]
|
||||
) {
|
||||
|
|
@ -160,3 +156,52 @@ export function getSelectedIcon({
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
export const getDisplayValue = ({
|
||||
localize,
|
||||
mappedEndpoints,
|
||||
selectedValues,
|
||||
modelSpecs,
|
||||
}: {
|
||||
localize: ReturnType<typeof useLocalize>;
|
||||
selectedValues: SelectedValues;
|
||||
mappedEndpoints: Endpoint[];
|
||||
modelSpecs: TModelSpec[];
|
||||
}) => {
|
||||
if (selectedValues.modelSpec) {
|
||||
const spec = modelSpecs.find((s) => s.name === selectedValues.modelSpec);
|
||||
return spec?.label || spec?.name || localize('com_ui_select_model');
|
||||
}
|
||||
|
||||
if (selectedValues.model && selectedValues.endpoint) {
|
||||
const endpoint = mappedEndpoints.find((e) => e.value === selectedValues.endpoint);
|
||||
if (!endpoint) {
|
||||
return localize('com_ui_select_model');
|
||||
}
|
||||
|
||||
if (
|
||||
isAgentsEndpoint(endpoint.value) &&
|
||||
endpoint.agentNames &&
|
||||
endpoint.agentNames[selectedValues.model]
|
||||
) {
|
||||
return endpoint.agentNames[selectedValues.model];
|
||||
}
|
||||
|
||||
if (
|
||||
isAssistantsEndpoint(endpoint.value) &&
|
||||
endpoint.assistantNames &&
|
||||
endpoint.assistantNames[selectedValues.model]
|
||||
) {
|
||||
return endpoint.assistantNames[selectedValues.model];
|
||||
}
|
||||
|
||||
return selectedValues.model;
|
||||
}
|
||||
|
||||
if (selectedValues.endpoint) {
|
||||
const endpoint = mappedEndpoints.find((e) => e.value === selectedValues.endpoint);
|
||||
return endpoint?.label || localize('com_ui_select_model');
|
||||
}
|
||||
|
||||
return localize('com_ui_select_model');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
export { default as useKeyDialog } from './useKeyDialog';
|
||||
export { default as useModelSelection } from './useModels';
|
||||
export { default as useEndpoints } from './useEndpoints';
|
||||
export { default as useSelectorEffects } from './useSelectorEffects';
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
import { useCallback, useRef, useContext, useMemo } from 'react';
|
||||
import { EModelEndpoint, LocalStorageKeys } from 'librechat-data-provider';
|
||||
import { getConvoSwitchLogic } from '~/utils';
|
||||
import { mainTextareaId } from '~/common';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useSetIndexOptions, useDefaultConvo } from '~/hooks';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
import { useGetEndpointsQuery } from '~/data-provider';
|
||||
import store from '~/store';
|
||||
|
||||
export const useModelSelection = () => {
|
||||
const { setOption } = useSetIndexOptions();
|
||||
const getDefaultConversation = useDefaultConvo();
|
||||
const { conversation, newConversation, index } = useChatContext();
|
||||
const { data: endpointsConfig = {} } = useGetEndpointsQuery();
|
||||
const modularChat = useRecoilValue(store.modularChat);
|
||||
const assistantsMapResult = useAssistantsMapContext();
|
||||
const assistantsMap = useMemo(() => assistantsMapResult ?? {}, [assistantsMapResult]);
|
||||
|
||||
const timeoutIdRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
|
||||
const setAgentId = useCallback(
|
||||
(agentId: string) => {
|
||||
setOption('agent_id')(agentId);
|
||||
localStorage.setItem(`${LocalStorageKeys.AGENT_ID_PREFIX}${index}`, agentId);
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
timeoutIdRef.current = setTimeout(() => {
|
||||
const textarea = document.getElementById(mainTextareaId);
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
}
|
||||
}, 150);
|
||||
},
|
||||
[setOption, index, timeoutIdRef],
|
||||
);
|
||||
|
||||
const setAssistantId = useCallback(
|
||||
(endpoint: string, assistantId: string) => {
|
||||
const assistant = assistantsMap[endpoint]?.[assistantId];
|
||||
if (assistant) {
|
||||
setOption('model')(assistant.model);
|
||||
setOption('assistant_id')(assistantId);
|
||||
localStorage.setItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}${endpoint}`, assistantId);
|
||||
}
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
timeoutIdRef.current = setTimeout(() => {
|
||||
const textarea = document.getElementById(mainTextareaId);
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
}
|
||||
}, 150);
|
||||
},
|
||||
[setOption, index, assistantsMap, timeoutIdRef],
|
||||
);
|
||||
|
||||
const setModel = useCallback(
|
||||
(model: string) => {
|
||||
setOption('model')(model);
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
timeoutIdRef.current = setTimeout(() => {
|
||||
const textarea = document.getElementById(mainTextareaId);
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
}
|
||||
}, 150);
|
||||
},
|
||||
[setOption, timeoutIdRef],
|
||||
);
|
||||
|
||||
const handleModelSelect = useCallback(
|
||||
(ep: EModelEndpoint, selectedModel: string) => {
|
||||
if (ep === EModelEndpoint.assistants) {
|
||||
if (conversation?.endpoint === ep) {
|
||||
setAssistantId(ep, selectedModel);
|
||||
return;
|
||||
}
|
||||
|
||||
const { template } = getConvoSwitchLogic({
|
||||
newEndpoint: ep,
|
||||
modularChat: false,
|
||||
conversation,
|
||||
endpointsConfig,
|
||||
});
|
||||
|
||||
const assistant = assistantsMap[ep]?.[selectedModel];
|
||||
|
||||
const currentConvo = getDefaultConversation({
|
||||
conversation: {
|
||||
...conversation,
|
||||
endpoint: ep,
|
||||
assistant_id: selectedModel,
|
||||
model: assistant?.model || '',
|
||||
},
|
||||
preset: {
|
||||
...template,
|
||||
endpoint: ep,
|
||||
assistant_id: selectedModel,
|
||||
model: assistant?.model || '',
|
||||
},
|
||||
});
|
||||
|
||||
newConversation({
|
||||
template: currentConvo,
|
||||
preset: currentConvo,
|
||||
keepLatestMessage: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (ep === EModelEndpoint.agents) {
|
||||
if (conversation?.endpoint === ep) {
|
||||
setAgentId(selectedModel);
|
||||
return;
|
||||
}
|
||||
|
||||
const { template } = getConvoSwitchLogic({
|
||||
newEndpoint: ep,
|
||||
modularChat: false,
|
||||
conversation,
|
||||
endpointsConfig,
|
||||
});
|
||||
|
||||
const currentConvo = getDefaultConversation({
|
||||
conversation: { ...conversation, endpoint: ep, agent_id: selectedModel },
|
||||
preset: { ...template, endpoint: ep, agent_id: selectedModel },
|
||||
});
|
||||
|
||||
newConversation({
|
||||
template: currentConvo,
|
||||
preset: currentConvo,
|
||||
keepLatestMessage: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
template,
|
||||
shouldSwitch,
|
||||
isNewModular,
|
||||
newEndpointType,
|
||||
isCurrentModular,
|
||||
isExistingConversation,
|
||||
} = getConvoSwitchLogic({
|
||||
newEndpoint: ep,
|
||||
modularChat,
|
||||
conversation,
|
||||
endpointsConfig,
|
||||
});
|
||||
|
||||
const isModular = isCurrentModular && isNewModular && shouldSwitch;
|
||||
|
||||
if (isExistingConversation && isModular) {
|
||||
template.endpointType = newEndpointType;
|
||||
|
||||
const currentConvo = getDefaultConversation({
|
||||
conversation: { ...(conversation ?? {}), endpointType: template.endpointType },
|
||||
preset: template,
|
||||
});
|
||||
|
||||
newConversation({
|
||||
template: currentConvo,
|
||||
preset: currentConvo,
|
||||
keepLatestMessage: true,
|
||||
keepAddedConvos: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
newConversation({
|
||||
template: { ...(template as any) },
|
||||
keepAddedConvos: isModular,
|
||||
});
|
||||
|
||||
setModel(selectedModel);
|
||||
},
|
||||
[
|
||||
conversation,
|
||||
endpointsConfig,
|
||||
modularChat,
|
||||
newConversation,
|
||||
getDefaultConversation,
|
||||
setModel,
|
||||
setAgentId,
|
||||
setAssistantId,
|
||||
assistantsMap,
|
||||
],
|
||||
);
|
||||
|
||||
const handleEndpointSelect = useCallback(
|
||||
(ep: string, hasModels: boolean, agents: any[], assistants: any[], modelsData: any) => {
|
||||
if (hasModels) {
|
||||
if (conversation?.endpoint !== ep) {
|
||||
const newEndpoint = ep as EModelEndpoint;
|
||||
const { template } = getConvoSwitchLogic({
|
||||
newEndpoint,
|
||||
modularChat: false,
|
||||
conversation,
|
||||
endpointsConfig,
|
||||
});
|
||||
|
||||
let initialModel = '';
|
||||
let initialAgentId = '';
|
||||
let initialAssistantId = '';
|
||||
|
||||
if (newEndpoint === EModelEndpoint.agents && agents.length > 0) {
|
||||
initialAgentId = agents[0].id;
|
||||
} else if (newEndpoint === EModelEndpoint.assistants && assistants.length > 0) {
|
||||
initialAssistantId = assistants[0].id;
|
||||
initialModel = assistantsMap[newEndpoint]?.[initialAssistantId]?.model || '';
|
||||
} else if (modelsData && modelsData[newEndpoint] && modelsData[newEndpoint].length > 0) {
|
||||
initialModel = modelsData[newEndpoint][0];
|
||||
}
|
||||
|
||||
const currentConvo = getDefaultConversation({
|
||||
conversation: {
|
||||
...conversation,
|
||||
endpoint: newEndpoint,
|
||||
model: initialModel,
|
||||
agent_id: initialAgentId,
|
||||
assistant_id: initialAssistantId,
|
||||
},
|
||||
preset: {
|
||||
...template,
|
||||
endpoint: newEndpoint,
|
||||
model: initialModel,
|
||||
agent_id: initialAgentId,
|
||||
assistant_id: initialAssistantId,
|
||||
},
|
||||
});
|
||||
|
||||
newConversation({
|
||||
template: currentConvo,
|
||||
preset: currentConvo,
|
||||
keepLatestMessage: true,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasModels) {
|
||||
const newEndpoint = ep as EModelEndpoint;
|
||||
const { template } = getConvoSwitchLogic({
|
||||
newEndpoint,
|
||||
modularChat: false,
|
||||
conversation,
|
||||
endpointsConfig,
|
||||
});
|
||||
const currentConvo = getDefaultConversation({
|
||||
conversation: { ...conversation, endpoint: newEndpoint },
|
||||
preset: { ...template, endpoint: newEndpoint },
|
||||
});
|
||||
newConversation({
|
||||
template: currentConvo,
|
||||
preset: currentConvo,
|
||||
keepLatestMessage: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
conversation,
|
||||
endpointsConfig,
|
||||
newConversation,
|
||||
getDefaultConversation,
|
||||
assistantsMap,
|
||||
modularChat,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
handleModelSelect,
|
||||
handleEndpointSelect,
|
||||
setAgentId,
|
||||
setAssistantId,
|
||||
setModel,
|
||||
};
|
||||
};
|
||||
|
||||
export default useModelSelection;
|
||||
|
|
@ -21,22 +21,51 @@ export default function useSelectorEffects({
|
|||
const agents: t.Agent[] = useMemo(() => {
|
||||
return Object.values(agentsMap ?? {}) as t.Agent[];
|
||||
}, [agentsMap]);
|
||||
const { agent_id: selectedAgentId = null, endpoint } = conversation ?? {};
|
||||
const {
|
||||
agent_id: selectedAgentId = null,
|
||||
assistant_id: selectedAssistantId = null,
|
||||
endpoint,
|
||||
} = conversation ?? {};
|
||||
const assistants: t.Assistant[] = useMemo(() => {
|
||||
if (!isAssistantsEndpoint(endpoint)) {
|
||||
return [];
|
||||
}
|
||||
return Object.values(assistantsMap?.[endpoint ?? ''] ?? {}) as t.Assistant[];
|
||||
}, [assistantsMap, endpoint]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAgentsEndpoint(endpoint as string)) {
|
||||
return;
|
||||
}
|
||||
if (selectedAgentId == null && agents.length > 0) {
|
||||
let agent_id = localStorage.getItem(`${LocalStorageKeys.AGENT_ID_PREFIX}${index}`);
|
||||
if (agent_id == null) {
|
||||
agent_id = agents[0].id;
|
||||
agent_id = agents[0]?.id;
|
||||
}
|
||||
const agent = agentsMap?.[agent_id];
|
||||
|
||||
if (agent !== undefined && isAgentsEndpoint(endpoint as string) === true) {
|
||||
if (agent !== undefined) {
|
||||
setOption('model')('');
|
||||
setOption('agent_id')(agent_id);
|
||||
}
|
||||
}
|
||||
}, [index, agents, selectedAgentId, agentsMap, endpoint, setOption]);
|
||||
useEffect(() => {
|
||||
if (!isAssistantsEndpoint(endpoint as string)) {
|
||||
return;
|
||||
}
|
||||
if (selectedAssistantId == null && assistants.length > 0) {
|
||||
let assistant_id = localStorage.getItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}`);
|
||||
if (assistant_id == null) {
|
||||
assistant_id = assistants[0]?.id;
|
||||
}
|
||||
const assistant = assistantsMap?.[endpoint ?? '']?.[assistant_id];
|
||||
if (assistant !== undefined) {
|
||||
setOption('model')('');
|
||||
setOption('assistant_id')(assistant_id);
|
||||
}
|
||||
}
|
||||
}, [index, assistants, selectedAssistantId, assistantsMap, endpoint, setOption]);
|
||||
|
||||
const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
|
|
@ -64,7 +93,7 @@ export default function useSelectorEffects({
|
|||
debouncedSetSelectedValues({
|
||||
endpoint: conversation.endpoint || '',
|
||||
model: conversation.agent_id ?? '',
|
||||
modelSpec: '',
|
||||
modelSpec: conversation.spec || '',
|
||||
});
|
||||
return;
|
||||
} else if (isAssistantsEndpoint(conversation?.endpoint)) {
|
||||
|
|
|
|||
|
|
@ -249,7 +249,6 @@
|
|||
"com_endpoint_search_var": "Search {{0}}...",
|
||||
"com_endpoint_search_models": "Search models...",
|
||||
"com_endpoint_search_endpoint_models": "Search {{0}} models...",
|
||||
"com_endpoint_select_model": "Select a model",
|
||||
"com_endpoint_set_custom_name": "Set a custom name, in case you can find this preset",
|
||||
"com_endpoint_skip_hover": "Enable skipping the completion step, which reviews the final answer and generated steps",
|
||||
"com_endpoint_stop": "Stop Sequences",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { ExtendedEndpoint } from '~/common';
|
||||
|
||||
export const filterMenuItems = (
|
||||
|
|
@ -17,7 +17,7 @@ export const filterMenuItems = (
|
|||
return mappedEndpoints
|
||||
.map((ep) => {
|
||||
if (ep.hasModels) {
|
||||
if (ep.value === EModelEndpoint.agents) {
|
||||
if (isAgentsEndpoint(ep.value)) {
|
||||
const filteredAgents = agents.filter((agent) =>
|
||||
agent.name?.toLowerCase().includes(lowercaseSearchTerm),
|
||||
);
|
||||
|
|
@ -32,7 +32,7 @@ export const filterMenuItems = (
|
|||
};
|
||||
}
|
||||
return null;
|
||||
} else if (ep.value === EModelEndpoint.assistants) {
|
||||
} else if (isAssistantsEndpoint(ep.value)) {
|
||||
const filteredAssistants = assistants.filter((assistant) =>
|
||||
assistant.name?.toLowerCase().includes(lowercaseSearchTerm),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue