refactor(providers): expose model names and priority on the resource view model

The workbench page reached back into resource.raw with brand-specific parsing to derive model names and priority, bypassing the adapter layer. Normalize both in the adapters and consume the typed fields.
This commit is contained in:
LTbinglingfeng
2026-06-13 02:15:43 +08:00
Unverified
parent f62b09a57b
commit 5e7225da21
3 changed files with 36 additions and 41 deletions
@@ -11,7 +11,6 @@ import {
type ProviderRecentUsageMap,
} from '@/components/providers/utils';
import type { OpenAIProviderConfig } from '@/types';
import { isRecord } from '@/utils/helpers';
import { ProviderHeaderCard } from './components/ProviderHeaderCard';
import { ProviderCategoryList } from './components/ProviderCategoryList';
import { ProviderResourcePanel } from './components/ProviderResourcePanel';
@@ -67,38 +66,6 @@ const matchesFilter = (r: ProviderResource, normalized: string): boolean => {
return haystack.some((v) => v.includes(normalized));
};
const getResourceModels = (resource: ProviderResource): string[] => {
if (!isRecord(resource.raw)) return [];
if (resource.brand === 'ampcode') {
const mappings = resource.raw.modelMappings;
if (!Array.isArray(mappings)) return [];
const seen = new Set<string>();
mappings.forEach((mapping) => {
if (!isRecord(mapping)) return;
const from = typeof mapping.from === 'string' ? mapping.from.trim() : '';
const to = typeof mapping.to === 'string' ? mapping.to.trim() : '';
if (from) seen.add(from);
if (to) seen.add(to);
});
return Array.from(seen);
}
const models = resource.raw.models;
if (!Array.isArray(models)) return [];
const seen = new Set<string>();
models.forEach((model) => {
if (!isRecord(model)) return;
const name = typeof model.name === 'string' ? model.name.trim() : '';
if (name) seen.add(name);
});
return Array.from(seen);
};
const getResourcePriority = (resource: ProviderResource): number => {
if (!isRecord(resource.raw)) return 0;
const priority = resource.raw.priority;
return typeof priority === 'number' && Number.isFinite(priority) ? priority : 0;
};
const getResourceSortName = (resource: ProviderResource): string =>
(resource.name ?? resource.identifier ?? resource.apiKeyPreview ?? '').toLowerCase();
@@ -216,7 +183,7 @@ export function ProvidersWorkbenchPage() {
if (!activeGroup) return [];
const seen = new Set<string>();
activeGroup.resources.forEach((r) => {
getResourceModels(r).forEach((name) => seen.add(name));
r.models.forEach((name) => seen.add(name));
});
return Array.from(seen).sort();
}, [activeGroup]);
@@ -232,10 +199,7 @@ export function ProvidersWorkbenchPage() {
const visibleResources = useMemo(() => {
let arr = filteredResources;
if (selectedModels.size > 0) {
arr = arr.filter((r) => {
const models = getResourceModels(r);
return models.some((name) => selectedModels.has(name));
});
arr = arr.filter((r) => r.models.some((name) => selectedModels.has(name)));
}
const sorted = [...arr].sort((a, b) => {
@@ -243,9 +207,7 @@ export function ProvidersWorkbenchPage() {
if (providerSortBy === 'name') {
diff = getResourceSortName(a).localeCompare(getResourceSortName(b));
} else if (providerSortBy === 'priority') {
const ap = getResourcePriority(a);
const bp = getResourcePriority(b);
diff = ap - bp;
diff = a.priority - b.priority;
} else {
diff =
getResourceRecentSuccess(a, usageByProvider) -
+29
View File
@@ -18,6 +18,29 @@ import type {
const countHeaders = (headers?: Record<string, string>): number =>
headers ? Object.keys(headers).length : 0;
const collectModelNames = (models?: Array<{ name?: string }>): string[] => {
const seen = new Set<string>();
(models ?? []).forEach((model) => {
const name = (model?.name ?? '').trim();
if (name) seen.add(name);
});
return Array.from(seen);
};
const collectAmpcodeModelNames = (mappings: AmpcodeConfig['modelMappings']): string[] => {
const seen = new Set<string>();
(mappings ?? []).forEach((mapping) => {
const from = (mapping?.from ?? '').trim();
const to = (mapping?.to ?? '').trim();
if (from) seen.add(from);
if (to) seen.add(to);
});
return Array.from(seen);
};
const normalizePriority = (priority?: number): number =>
typeof priority === 'number' && Number.isFinite(priority) ? priority : 0;
const buildId = (brand: ProviderBrand, index: number, fragment: string) =>
`${brand}:${index}:${fragment || 'item'}`;
@@ -64,6 +87,8 @@ function providerKeyToResource(
proxyUrl: config.proxyUrl ?? null,
prefix: config.prefix ?? null,
modelCount: config.models?.length ?? 0,
models: collectModelNames(config.models),
priority: normalizePriority(config.priority),
headerCount: countHeaders(config.headers),
excludedModelCount: stripDisableAllModelsRule(config.excludedModels).length,
apiKeyEntryCount: 0,
@@ -110,6 +135,8 @@ export function openaiToResource(
proxyUrl: null,
prefix: config.prefix ?? null,
modelCount: config.models?.length ?? 0,
models: collectModelNames(config.models),
priority: normalizePriority(config.priority),
headerCount: countHeaders(config.headers),
excludedModelCount: 0,
apiKeyEntryCount: config.apiKeyEntries?.length ?? 0,
@@ -139,6 +166,8 @@ export function ampcodeToResource(config?: AmpcodeConfig | null): ProviderResour
proxyUrl: null,
prefix: null,
modelCount: safe.modelMappings?.length ?? 0,
models: collectAmpcodeModelNames(safe.modelMappings),
priority: 0,
headerCount: 0,
excludedModelCount: 0,
apiKeyEntryCount: upstreamKeyMappingsCount,
+4
View File
@@ -50,6 +50,10 @@ export interface ProviderResource {
proxyUrl: string | null;
prefix: string | null;
modelCount: number;
/** 去重后的模型名(ampcode 为映射两端), 供筛选/搜索用 */
models: string[];
/** 排序用优先级,未配置时为 0 */
priority: number;
headerCount: number;
excludedModelCount: number;
/** 仅 OpenAI 有意义,其它 brand 该字段不展示但保留 */