mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-15 06:58:10 +01:00
📳 refactor: Optimize Model Selector (#11787)
- Introduced a new `EndpointMenuContent` component to lazily render endpoint submenu content, improving performance by deferring expensive model-list rendering until the submenu is mounted. - Refactored `EndpointItem` to utilize the new component, simplifying the code and enhancing readability. - Removed redundant filtering logic and model specifications handling from `EndpointItem`, centralizing it within `EndpointMenuContent` for better maintainability.
This commit is contained in:
parent
dc489e7b25
commit
6cc6ee3207
1 changed files with 69 additions and 59 deletions
|
|
@ -80,12 +80,76 @@ const SettingsButton = ({
|
|||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lazily-rendered content for an endpoint submenu. By extracting this into a
|
||||
* separate component, the expensive model-list rendering (and per-item hooks
|
||||
* such as MutationObservers in EndpointModelItem) only runs when the submenu
|
||||
* is actually mounted — which Ariakit defers via `unmountOnHide`.
|
||||
*/
|
||||
function EndpointMenuContent({
|
||||
endpoint,
|
||||
endpointIndex,
|
||||
}: {
|
||||
endpoint: Endpoint;
|
||||
endpointIndex: number;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { agentsMap, assistantsMap, modelSpecs, selectedValues, endpointSearchValues } =
|
||||
useModelSelectorContext();
|
||||
const { model: selectedModel, modelSpec: selectedSpec } = selectedValues;
|
||||
const searchValue = endpointSearchValues[endpoint.value] || '';
|
||||
|
||||
const endpointSpecs = useMemo(() => {
|
||||
if (!modelSpecs || !modelSpecs.length) {
|
||||
return [];
|
||||
}
|
||||
return modelSpecs.filter((spec: TModelSpec) => spec.group === endpoint.value);
|
||||
}, [modelSpecs, endpoint.value]);
|
||||
|
||||
if (isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined) {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-center p-2"
|
||||
role="status"
|
||||
aria-label={localize('com_ui_loading')}
|
||||
>
|
||||
<Spinner aria-hidden="true" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const filteredModels = searchValue
|
||||
? filterModels(
|
||||
endpoint,
|
||||
(endpoint.models || []).map((model) => model.name),
|
||||
searchValue,
|
||||
agentsMap,
|
||||
assistantsMap,
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{endpointSpecs.map((spec: TModelSpec) => (
|
||||
<ModelSpecItem key={spec.name} spec={spec} isSelected={selectedSpec === spec.name} />
|
||||
))}
|
||||
{filteredModels
|
||||
? renderEndpointModels(
|
||||
endpoint,
|
||||
endpoint.models || [],
|
||||
selectedModel,
|
||||
filteredModels,
|
||||
endpointIndex,
|
||||
)
|
||||
: endpoint.models &&
|
||||
renderEndpointModels(endpoint, endpoint.models, selectedModel, undefined, endpointIndex)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
|
||||
const localize = useLocalize();
|
||||
const {
|
||||
agentsMap,
|
||||
assistantsMap,
|
||||
modelSpecs,
|
||||
selectedValues,
|
||||
handleOpenKeyDialog,
|
||||
handleSelectEndpoint,
|
||||
|
|
@ -93,19 +157,7 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
|
|||
setEndpointSearchValue,
|
||||
endpointRequiresUserKey,
|
||||
} = useModelSelectorContext();
|
||||
const {
|
||||
model: selectedModel,
|
||||
endpoint: selectedEndpoint,
|
||||
modelSpec: selectedSpec,
|
||||
} = selectedValues;
|
||||
|
||||
// Filter modelSpecs for this endpoint (by group matching endpoint value)
|
||||
const endpointSpecs = useMemo(() => {
|
||||
if (!modelSpecs || !modelSpecs.length) {
|
||||
return [];
|
||||
}
|
||||
return modelSpecs.filter((spec: TModelSpec) => spec.group === endpoint.value);
|
||||
}, [modelSpecs, endpoint.value]);
|
||||
const { endpoint: selectedEndpoint } = selectedValues;
|
||||
|
||||
const searchValue = endpointSearchValues[endpoint.value] || '';
|
||||
const isUserProvided = useMemo(
|
||||
|
|
@ -130,15 +182,6 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
|
|||
const isEndpointSelected = selectedEndpoint === endpoint.value;
|
||||
|
||||
if (endpoint.hasModels) {
|
||||
const filteredModels = searchValue
|
||||
? filterModels(
|
||||
endpoint,
|
||||
(endpoint.models || []).map((model) => model.name),
|
||||
searchValue,
|
||||
agentsMap,
|
||||
assistantsMap,
|
||||
)
|
||||
: null;
|
||||
const placeholder =
|
||||
isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value)
|
||||
? localize('com_endpoint_search_var', { 0: endpoint.label })
|
||||
|
|
@ -147,7 +190,6 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
|
|||
<Menu
|
||||
id={`endpoint-${endpoint.value}-menu`}
|
||||
key={`endpoint-${endpoint.value}-item`}
|
||||
defaultOpen={endpoint.value === selectedEndpoint}
|
||||
searchValue={searchValue}
|
||||
onSearch={(value) => setEndpointSearchValue(endpoint.value, value)}
|
||||
combobox={<input placeholder=" " />}
|
||||
|
|
@ -170,39 +212,7 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
|
|||
</div>
|
||||
}
|
||||
>
|
||||
{isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined ? (
|
||||
<div
|
||||
className="flex items-center justify-center p-2"
|
||||
role="status"
|
||||
aria-label={localize('com_ui_loading')}
|
||||
>
|
||||
<Spinner aria-hidden="true" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Render modelSpecs for this endpoint */}
|
||||
{endpointSpecs.map((spec: TModelSpec) => (
|
||||
<ModelSpecItem key={spec.name} spec={spec} isSelected={selectedSpec === spec.name} />
|
||||
))}
|
||||
{/* Render endpoint models */}
|
||||
{filteredModels
|
||||
? renderEndpointModels(
|
||||
endpoint,
|
||||
endpoint.models || [],
|
||||
selectedModel,
|
||||
filteredModels,
|
||||
endpointIndex,
|
||||
)
|
||||
: endpoint.models &&
|
||||
renderEndpointModels(
|
||||
endpoint,
|
||||
endpoint.models,
|
||||
selectedModel,
|
||||
undefined,
|
||||
endpointIndex,
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<EndpointMenuContent endpoint={endpoint} endpointIndex={endpointIndex} />
|
||||
</Menu>
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue