fix: eliminate ~5s delay when opening Switch Models panel with local provider (#8626)
Signed-off-by: jh-block <jhugo@block.xyz>
This commit is contained in:
@@ -2283,6 +2283,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/local-inference/sync-featured": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"super::routes::local_inference"
|
||||
],
|
||||
"operationId": "sync_featured_models",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Featured models synced to registry"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/mcp-ui-proxy": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3412,6 +3412,20 @@ export type SearchHfModelsResponses = {
|
||||
|
||||
export type SearchHfModelsResponse = SearchHfModelsResponses[keyof SearchHfModelsResponses];
|
||||
|
||||
export type SyncFeaturedModelsData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/local-inference/sync-featured';
|
||||
};
|
||||
|
||||
export type SyncFeaturedModelsResponses = {
|
||||
/**
|
||||
* Featured models synced to registry
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type McpUiProxyData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import {
|
||||
listLocalModels,
|
||||
syncFeaturedModels,
|
||||
downloadHfModel,
|
||||
getLocalModelDownloadProgress,
|
||||
cancelLocalModelDownload,
|
||||
@@ -128,6 +129,7 @@ export default function LocalModelPicker({ onConfigured, onBack }: LocalModelPic
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
try {
|
||||
await syncFeaturedModels();
|
||||
const response = await listLocalModels({ throwOnError: true });
|
||||
if (response.data) {
|
||||
setModels(response.data);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useModelAndProvider } from '../../ModelAndProviderContext';
|
||||
import { defineMessages, useIntl } from '../../../i18n';
|
||||
import {
|
||||
listLocalModels,
|
||||
syncFeaturedModels,
|
||||
downloadHfModel,
|
||||
getLocalModelDownloadProgress,
|
||||
cancelLocalModelDownload,
|
||||
@@ -159,6 +160,7 @@ export const LocalInferenceSettings = () => {
|
||||
|
||||
const loadModels = useCallback(async (): Promise<LocalModelResponse[] | undefined> => {
|
||||
try {
|
||||
await syncFeaturedModels();
|
||||
const response = await listLocalModels();
|
||||
if (response.data) {
|
||||
setModels(response.data);
|
||||
|
||||
@@ -300,6 +300,10 @@ export const SwitchModelModal = ({
|
||||
const [userClearedModel, setUserClearedModel] = useState(false);
|
||||
const [providerErrors, setProviderErrors] = useState<Record<string, string>>({});
|
||||
const [providerWarnings, setProviderWarnings] = useState<Record<string, string>>({});
|
||||
const [activeProvidersList, setActiveProvidersList] = useState<
|
||||
import('../../../../api').ProviderDetails[]
|
||||
>([]);
|
||||
const fetchedProviders = useRef<Set<string>>(new Set());
|
||||
const [thinkingLevel, setThinkingLevel] = useState<string>('low');
|
||||
const [claudeThinkingType, setClaudeThinkingType] = useState<string>('disabled');
|
||||
const [claudeThinkingEffort, setClaudeThinkingEffort] = useState<string>('high');
|
||||
@@ -465,18 +469,16 @@ export const SwitchModelModal = ({
|
||||
}, [currentModel, currentProvider, usePredefinedModels, provider, model, initialProvider]);
|
||||
|
||||
useEffect(() => {
|
||||
// Load predefined models if enabled
|
||||
if (usePredefinedModels) {
|
||||
const models = getPredefinedModelsFromEnv();
|
||||
setPredefinedModels(models);
|
||||
}
|
||||
|
||||
// Load providers for manual model selection
|
||||
(async () => {
|
||||
try {
|
||||
const providersResponse = await getProviders(false);
|
||||
const activeProviders = providersResponse.filter((provider) => provider.is_configured);
|
||||
// Create provider options and add "Use other provider" option
|
||||
setActiveProvidersList(activeProviders);
|
||||
setProviderOptions([
|
||||
...activeProviders.map(({ metadata, name }) => ({
|
||||
value: name,
|
||||
@@ -487,24 +489,43 @@ export const SwitchModelModal = ({
|
||||
label: intl.formatMessage(i18n.useOtherProvider),
|
||||
},
|
||||
]);
|
||||
} catch (error: unknown) {
|
||||
console.error('Failed to query providers:', error);
|
||||
}
|
||||
})();
|
||||
}, [getProviders, usePredefinedModels, read, intl]);
|
||||
|
||||
setLoadingModels(true);
|
||||
useEffect(() => {
|
||||
if (!provider || usePredefinedModels) return;
|
||||
if (fetchedProviders.current.has(provider)) {
|
||||
setLoadingModels(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const results = await fetchModelsForProviders(activeProviders);
|
||||
const activeProvider = activeProvidersList.find((p) => p.name === provider);
|
||||
if (!activeProvider) return;
|
||||
|
||||
// Process results and build grouped options
|
||||
const groupedOptions: {
|
||||
let cancelled = false;
|
||||
|
||||
(async () => {
|
||||
setLoadingModels(true);
|
||||
try {
|
||||
const results = await fetchModelsForProviders([activeProvider]);
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
const newGroupedOptions: {
|
||||
options: { value: string; label: string; provider: string; providerType: ProviderType }[];
|
||||
}[] = [];
|
||||
const errorMap: Record<string, string> = {};
|
||||
const warningMap: Record<string, string> = {};
|
||||
const newErrors: Record<string, string> = {};
|
||||
const newWarnings: Record<string, string> = {};
|
||||
|
||||
results.forEach(({ provider: p, models, error, warning }) => {
|
||||
if (warning) {
|
||||
warningMap[p.name] = warning;
|
||||
newWarnings[p.name] = warning;
|
||||
}
|
||||
if (error) {
|
||||
errorMap[p.name] = error;
|
||||
newErrors[p.name] = error;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -532,23 +553,38 @@ export const SwitchModelModal = ({
|
||||
}
|
||||
|
||||
if (options.length > 0) {
|
||||
groupedOptions.push({ options });
|
||||
newGroupedOptions.push({ options });
|
||||
}
|
||||
});
|
||||
|
||||
// Save provider errors and warnings to state
|
||||
setProviderErrors(errorMap);
|
||||
setProviderWarnings(warningMap);
|
||||
setProviderErrors((prev) => {
|
||||
const next = { ...prev, ...newErrors };
|
||||
if (!newErrors[activeProvider.name]) delete next[activeProvider.name];
|
||||
return next;
|
||||
});
|
||||
setProviderWarnings((prev) => {
|
||||
const next = { ...prev, ...newWarnings };
|
||||
if (!newWarnings[activeProvider.name]) delete next[activeProvider.name];
|
||||
return next;
|
||||
});
|
||||
|
||||
setModelOptions(groupedOptions);
|
||||
setOriginalModelOptions(groupedOptions);
|
||||
setModelOptions((prev) => [...prev, ...newGroupedOptions]);
|
||||
setOriginalModelOptions((prev) => [...prev, ...newGroupedOptions]);
|
||||
fetchedProviders.current.add(provider);
|
||||
} catch (error: unknown) {
|
||||
console.error('Failed to query providers:', error);
|
||||
console.error(`Failed to fetch models for ${provider}:`, error);
|
||||
} finally {
|
||||
setLoadingModels(false);
|
||||
if (!cancelled) {
|
||||
setLoadingModels(false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, [getProviders, usePredefinedModels, read, intl]);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
setLoadingModels(false);
|
||||
};
|
||||
}, [provider, activeProvidersList, usePredefinedModels, intl]);
|
||||
|
||||
const filteredModelOptions = provider
|
||||
? modelOptions.filter((group) => group.options[0]?.provider === provider)
|
||||
|
||||
Reference in New Issue
Block a user