From 6cc6ee3207acbf9be4017d0c9a20d79e5195effc Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Fri, 13 Feb 2026 22:46:14 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=B3=20refactor:=20Optimize=20Model=20S?= =?UTF-8?q?elector=20(#11787)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- .../Endpoints/components/EndpointItem.tsx | 128 ++++++++++-------- 1 file changed, 69 insertions(+), 59 deletions(-) diff --git a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx index 27c1236cb2..6f73f76d79 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx @@ -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 ( +
+
+ ); + } + + const filteredModels = searchValue + ? filterModels( + endpoint, + (endpoint.models || []).map((model) => model.name), + searchValue, + agentsMap, + assistantsMap, + ) + : null; + + return ( + <> + {endpointSpecs.map((spec: TModelSpec) => ( + + ))} + {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) { setEndpointSearchValue(endpoint.value, value)} combobox={} @@ -170,39 +212,7 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) { } > - {isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined ? ( -
-
- ) : ( - <> - {/* Render modelSpecs for this endpoint */} - {endpointSpecs.map((spec: TModelSpec) => ( - - ))} - {/* Render endpoint models */} - {filteredModels - ? renderEndpointModels( - endpoint, - endpoint.models || [], - selectedModel, - filteredModels, - endpointIndex, - ) - : endpoint.models && - renderEndpointModels( - endpoint, - endpoint.models, - selectedModel, - undefined, - endpointIndex, - )} - - )} +
); } else {