From feff3f376c0c084833025451466486f118af1fbb Mon Sep 17 00:00:00 2001 From: liucong2013 <10938633+liucong2013@users.noreply.github.com> Date: Fri, 17 Apr 2026 21:34:00 +0000 Subject: [PATCH] Fix provider list scope and key stability --- .../providers/OpenAISection/OpenAISection.tsx | 84 +++++++++---------- src/components/providers/ProviderList.tsx | 16 +++- src/pages/AiProvidersPage.module.scss | 32 +++++++ src/styles/components.scss | 26 ++---- 4 files changed, 95 insertions(+), 63 deletions(-) diff --git a/src/components/providers/OpenAISection/OpenAISection.tsx b/src/components/providers/OpenAISection/OpenAISection.tsx index 9d54e7e..ce0fe94 100644 --- a/src/components/providers/OpenAISection/OpenAISection.tsx +++ b/src/components/providers/OpenAISection/OpenAISection.tsx @@ -35,6 +35,11 @@ interface OpenAISectionProps { onDelete: (index: number) => void; } +interface IndexedOpenAIProvider { + config: OpenAIProviderConfig; + originalIndex: number; +} + export function OpenAISection({ configs, keyStats, @@ -149,10 +154,11 @@ export function OpenAISection({ return cache; }, [configs, usageDetailsBySource]); - const sortedConfigs = useMemo(() => { - const filtered = configs.filter((provider) => { + const sortedConfigs = useMemo(() => { + const indexed = configs.map((config, originalIndex) => ({ config, originalIndex })); + const filtered = indexed.filter(({ config }) => { if (selectedModels.size === 0) return true; - return provider.models?.some((model) => selectedModels.has(model.name)); + return config.models?.some((model) => selectedModels.has(model.name)); }); const sorted = [...filtered]; @@ -160,40 +166,40 @@ export function OpenAISection({ const providerStats = sortOption === 'recent-success' ? new Map( - sorted.map((provider) => [ - provider, - getOpenAIProviderStats(provider.apiKeyEntries, keyStats, provider.prefix), + sorted.map(({ config }) => [ + config, + getOpenAIProviderStats(config.apiKeyEntries, keyStats, config.prefix), ]) ) : null; switch (sortOption) { case 'name': - sorted.sort((a, b) => direction * a.name.localeCompare(b.name)); + sorted.sort((a, b) => direction * a.config.name.localeCompare(b.config.name)); break; case 'priority': sorted.sort((a, b) => { - const priorityA = a.priority ?? Number.MAX_SAFE_INTEGER; - const priorityB = b.priority ?? Number.MAX_SAFE_INTEGER; + const priorityA = a.config.priority ?? Number.MAX_SAFE_INTEGER; + const priorityB = b.config.priority ?? Number.MAX_SAFE_INTEGER; const priorityDiff = priorityA - priorityB; if (priorityDiff !== 0) { return direction * priorityDiff; } - return direction * a.name.localeCompare(b.name); + return direction * a.config.name.localeCompare(b.config.name); }); break; case 'recent-success': sorted.sort((a, b) => { const successDiff = - (providerStats?.get(a)?.success ?? 0) - (providerStats?.get(b)?.success ?? 0); + (providerStats?.get(a.config)?.success ?? 0) - (providerStats?.get(b.config)?.success ?? 0); if (successDiff !== 0) { return direction * successDiff; } - return direction * a.name.localeCompare(b.name); + return direction * a.config.name.localeCompare(b.config.name); }); break; default: @@ -203,11 +209,6 @@ export function OpenAISection({ return sorted; }, [configs, sortOption, sortDirection, keyStats, selectedModels]); - const getProviderKey = (item: OpenAIProviderConfig) => `${item.name}-${item.prefix ?? ''}-${item.baseUrl}`; - - const getProviderIndex = (item: OpenAIProviderConfig) => - configs.findIndex((config) => config === item); - const toggleModelSelection = (modelName: string) => { setSelectedModels((prev) => { const next = new Set(prev); @@ -351,49 +352,48 @@ export function OpenAISection({ } extra={renderToolbar('top')} > - + items={sortedConfigs} loading={loading} - keyField={(item) => getProviderKey(item)} + keyField={(item) => `openai-provider-${item.originalIndex}`} emptyTitle={t('ai_providers.openai_empty_title')} emptyDescription={t('ai_providers.openai_empty_desc')} + listClassName={styles.providerList} + rowClassName={styles.providerCard} + metaClassName={styles.providerMeta} + actionsClassName={styles.providerActions} onEdit={(item) => { - const index = getProviderIndex(item); - if (index >= 0) { - onEdit(index); - } + onEdit(item.originalIndex); }} onDelete={(item) => { - const index = getProviderIndex(item); - if (index >= 0) { - onDelete(index); - } + onDelete(item.originalIndex); }} actionsDisabled={actionsDisabled} renderContent={(item) => { - const stats = getOpenAIProviderStats(item.apiKeyEntries, keyStats, item.prefix); - const headerEntries = Object.entries(item.headers || {}); - const apiKeyEntries = item.apiKeyEntries || []; - const statusData = statusBarCache.get(item.name) || calculateStatusBarData([]); + const provider = item.config; + const stats = getOpenAIProviderStats(provider.apiKeyEntries, keyStats, provider.prefix); + const headerEntries = Object.entries(provider.headers || {}); + const apiKeyEntries = provider.apiKeyEntries || []; + const statusData = statusBarCache.get(provider.name) || calculateStatusBarData([]); return ( -
{item.name}
- {item.priority !== undefined && ( +
{provider.name}
+ {provider.priority !== undefined && (
{t('common.priority')}: - {item.priority} + {provider.priority}
)} - {item.prefix && ( + {provider.prefix && (
{t('common.prefix')}: - {item.prefix} + {provider.prefix}
)}
{t('common.base_url')}: - {item.baseUrl} + {provider.baseUrl}
{headerEntries.length > 0 && (
@@ -439,11 +439,11 @@ export function OpenAISection({ )}
{t('ai_providers.openai_models_count')}: - {item.models?.length || 0} + {provider.models?.length || 0}
- {item.models?.length ? ( + {provider.models?.length ? (
- {item.models.map((model) => ( + {provider.models.map((model) => ( {model.name} {model.alias && model.alias !== model.name && ( @@ -453,10 +453,10 @@ export function OpenAISection({ ))}
) : null} - {item.testModel && ( + {provider.testModel && (
Test Model: - {item.testModel} + {provider.testModel}
)}
diff --git a/src/components/providers/ProviderList.tsx b/src/components/providers/ProviderList.tsx index 4f2fada..c6fd8cf 100644 --- a/src/components/providers/ProviderList.tsx +++ b/src/components/providers/ProviderList.tsx @@ -16,6 +16,10 @@ interface ProviderListProps { actionsDisabled?: boolean; getRowDisabled?: (item: T, index: number) => boolean; renderExtraActions?: (item: T, index: number) => ReactNode; + listClassName?: string; + rowClassName?: string; + metaClassName?: string; + actionsClassName?: string; } export function ProviderList({ @@ -31,6 +35,10 @@ export function ProviderList({ actionsDisabled = false, getRowDisabled, renderExtraActions, + listClassName, + rowClassName, + metaClassName, + actionsClassName, }: ProviderListProps) { const { t } = useTranslation(); @@ -43,17 +51,17 @@ export function ProviderList({ } return ( -
+
{items.map((item, index) => { const rowDisabled = getRowDisabled ? getRowDisabled(item, index) : false; return (
-
{renderContent(item, index)}
-
+
{renderContent(item, index)}
+