Merge pull request #121 from Chiron-Brahm/fix/custom-models-selector-118

fix(ui): 修复自定义模型无法在模型选择器中显示的问题
This commit is contained in:
Chiron
2026-01-13 23:06:42 +08:00
committed by GitHub
3 changed files with 175 additions and 71 deletions
+80 -48
View File
@@ -108,38 +108,6 @@ const ALIAS_CONFIG_MAPPING: Record<string, string> = {
gemini_api_key: "gemini",
};
// 根据 API 类型获取对应的模型 provider_id 列表
const getModelProviderIds = (apiType: ApiType, provider?: string): string[] => {
// Antigravity 使用自己的模型列表(别名配置)
if (provider?.toLowerCase() === "antigravity") {
return ["antigravity"];
}
// Codex 使用自己的模型列表(别名配置)
if (provider?.toLowerCase() === "codex") {
return ["codex"];
}
// Gemini 使用别名配置
if (
provider?.toLowerCase() === "gemini" ||
provider?.toLowerCase() === "gemini_api_key"
) {
return ["gemini"];
}
switch (apiType) {
case "gemini":
return ["gemini"]; // 使用别名配置
case "anthropic":
return ["anthropic"];
case "openai":
return ["openai", "azure", "deepseek", "alibaba"];
default:
return [];
}
};
// 可用的 Provider 信息(合并 OAuth 凭证池和 API Key Provider)
interface AvailableProvider {
id: string;
@@ -308,31 +276,95 @@ export function ApiServerPage() {
} else {
// 非别名 Provider,使用模型注册表
// 首先检查是否是自定义 API Key Provider
// 如果是,使用其 type 字段来确定 API 类型
let effectiveProvider = provider;
// 如果是,优先使用其 custom_models 字段
let customModels: string[] | undefined;
let registryId = provider; // 默认使用 provider ID 作为 registryId
let fallbackRegistryId: string | undefined;
if (providers) {
const customProvider = providers.find((p) => p.id === provider);
if (customProvider) {
// 使用自定义 Provider 的 type 字段
effectiveProvider = customProvider.type;
// 获取自定义模型列表
customModels = customProvider.custom_models;
// 使用 provider.id 作为 registryId(适用于系统预设的 Provider,如 deepseek, moonshot)
// 如果模型注册表中没有该 id 的模型,则回退到使用 type 映射
registryId = customProvider.id;
// 根据 type 获取回退的 registryId
const typeToRegistryMap: Record<string, string> = {
openai: "openai",
anthropic: "anthropic",
"anthropic-compatible": "anthropic",
gemini: "gemini",
"azure-openai": "openai",
vertexai: "google",
ollama: "ollama",
};
fallbackRegistryId =
typeToRegistryMap[customProvider.type.toLowerCase()] ||
customProvider.type.toLowerCase();
}
}
// 根据 Provider 的 API 类型过滤模型
const apiType = getProviderApiType(effectiveProvider);
const providerIds = getModelProviderIds(apiType, provider);
// 收集所有模型
let allModels: EnhancedModelMetadata[] = [];
const existingIds = new Set<string>();
if (providerIds.length > 0) {
// 获取所有匹配 provider_id 的模型
const modelPromises = providerIds.map((id) =>
getModelsForProvider(id),
// 1. 首先添加自定义模型(排在最前面)
if (customModels && customModels.length > 0) {
const customModelList = customModels.map(
(modelName): EnhancedModelMetadata => ({
id: modelName,
display_name: modelName,
provider_id: provider,
provider_name: provider,
family: null,
tier: "pro" as const,
capabilities: {
vision: false,
tools: true,
streaming: true,
json_mode: true,
function_calling: true,
reasoning: modelName.includes("thinking"),
},
pricing: null,
limits: {
context_length: null,
max_output_tokens: null,
requests_per_minute: null,
tokens_per_minute: null,
},
status: "active" as const,
release_date: null,
is_latest: false,
description: `自定义模型: ${modelName}`,
source: "custom" as const,
created_at: Date.now() / 1000,
updated_at: Date.now() / 1000,
}),
);
const modelArrays = await Promise.all(modelPromises);
models = modelArrays.flat();
} else {
// 未知类型,显示所有模型
models = await getModelRegistry();
allModels = [...customModelList];
customModels.forEach((id) => existingIds.add(id));
}
// 2. 添加模型注册表中的模型
// 优先使用 registryId,如果没有模型则回退到 fallbackRegistryId
let registryModels = await getModelsForProvider(registryId);
if (
registryModels.length === 0 &&
fallbackRegistryId &&
fallbackRegistryId !== registryId
) {
registryModels = await getModelsForProvider(fallbackRegistryId);
}
// 过滤掉已存在的模型
const newModels = registryModels.filter(
(m) => !existingIds.has(m.id),
);
allModels = [...allModels, ...newModels];
models = allModels;
}
} else {
models = await getModelRegistry();
+3
View File
@@ -34,6 +34,8 @@ export interface ConfiguredProvider {
credentialType?: string;
/** Provider ID(用于 API Key Provider) */
providerId?: string;
/** 自定义模型列表(用于 API Key Provider) */
customModels?: string[];
}
export interface UseConfiguredProvidersResult {
@@ -127,6 +129,7 @@ export function useConfiguredProviders(): UseConfiguredProvidersResult {
type: provider.type,
credentialType: `${provider.type}_key`,
providerId: provider.id,
customModels: provider.custom_models,
});
}
});
+92 -23
View File
@@ -62,6 +62,48 @@ function sortModels(models: EnhancedModelMetadata[]): EnhancedModelMetadata[] {
});
}
/**
* 将自定义模型列表转换为 EnhancedModelMetadata 格式
*/
function convertCustomModelsToMetadata(
models: string[],
providerId: string,
providerName: string,
): EnhancedModelMetadata[] {
return models.map((modelName): EnhancedModelMetadata => {
return {
id: modelName,
display_name: modelName,
provider_id: providerId,
provider_name: providerName,
family: null,
tier: "pro" as const,
capabilities: {
vision: false,
tools: true,
streaming: true,
json_mode: true,
function_calling: true,
reasoning: modelName.includes("thinking"),
},
pricing: null,
limits: {
context_length: null,
max_output_tokens: null,
requests_per_minute: null,
tokens_per_minute: null,
},
status: "active" as const,
release_date: null,
is_latest: false,
description: `自定义模型: ${modelName}`,
source: "custom" as const,
created_at: Date.now() / 1000,
updated_at: Date.now() / 1000,
};
});
}
/**
* 将别名配置中的模型转换为 EnhancedModelMetadata 格式
*/
@@ -155,42 +197,69 @@ export function useProviderModels(
return { modelIds: [], models: [] };
}
// 对于别名 Provider,使用别名配置中的模型列表
if (isAliasProvider(selectedProvider.key) && aliasConfig) {
const modelIds = aliasConfig.models;
// 收集所有模型
let allModels: EnhancedModelMetadata[] = [];
let allModelIds: string[] = [];
if (returnFullMetadata) {
const models = convertAliasModelsToMetadata(
modelIds,
aliasConfig,
selectedProvider.key,
selectedProvider.label,
);
return { modelIds, models };
}
return { modelIds, models: [] };
// 1. 首先添加自定义模型(排在最前面)
if (
selectedProvider.customModels &&
selectedProvider.customModels.length > 0
) {
const customModels = convertCustomModelsToMetadata(
selectedProvider.customModels,
selectedProvider.key,
selectedProvider.label,
);
allModels = [...customModels];
allModelIds = [...selectedProvider.customModels];
}
// 从模型注册表获取模型
let models = registryModels.filter(
// 2. 对于别名 Provider,添加别名配置中的模型
if (isAliasProvider(selectedProvider.key) && aliasConfig) {
const aliasModels = convertAliasModelsToMetadata(
aliasConfig.models,
aliasConfig,
selectedProvider.key,
selectedProvider.label,
);
// 过滤掉已存在的模型(避免重复)
const newAliasModels = aliasModels.filter(
(m) => !allModelIds.includes(m.id),
);
allModels = [...allModels, ...newAliasModels];
allModelIds = [...allModelIds, ...newAliasModels.map((m) => m.id)];
}
// 3. 从模型注册表获取模型
let registryFilteredModels = registryModels.filter(
(m) => m.provider_id === selectedProvider.registryId,
);
// 如果没有找到模型,尝试使用 fallbackRegistryId
if (models.length === 0 && selectedProvider.fallbackRegistryId) {
models = registryModels.filter(
if (
registryFilteredModels.length === 0 &&
selectedProvider.fallbackRegistryId
) {
registryFilteredModels = registryModels.filter(
(m) => m.provider_id === selectedProvider.fallbackRegistryId,
);
}
// 排序
const sortedModels = sortModels(models);
const modelIds = sortedModels.map((m) => m.id);
// 过滤掉已存在的模型(避免重复)
const newRegistryModels = registryFilteredModels.filter(
(m) => !allModelIds.includes(m.id),
);
// 排序注册表模型
const sortedRegistryModels = sortModels(newRegistryModels);
allModels = [...allModels, ...sortedRegistryModels];
allModelIds = [...allModelIds, ...sortedRegistryModels.map((m) => m.id)];
return {
modelIds,
models: returnFullMetadata ? sortedModels : [],
modelIds: allModelIds,
models: returnFullMetadata ? allModels : [],
};
}, [selectedProvider, registryModels, aliasConfig, returnFullMetadata]);