diff --git a/app/client/api.ts b/app/client/api.ts index f10e47618..b13e0f8a4 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -64,12 +64,14 @@ export interface LLMModel { displayName?: string; available: boolean; provider: LLMModelProvider; + sorted: number; } export interface LLMModelProvider { id: string; providerName: string; providerType: string; + sorted: number; } export abstract class LLMApi { diff --git a/app/constant.ts b/app/constant.ts index 5251b5b4f..8ca17c4b3 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -320,86 +320,105 @@ const tencentModels = [ const moonshotModes = ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"]; +let seq = 1000; // 内置的模型序号生成器从1000开始 export const DEFAULT_MODELS = [ ...openaiModels.map((name) => ({ name, available: true, + sorted: seq++, // Global sequence sort(index) provider: { id: "openai", providerName: "OpenAI", providerType: "openai", + sorted: 1, // 这里是固定的,确保顺序与之前内置的版本一致 }, })), ...openaiModels.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "azure", providerName: "Azure", providerType: "azure", + sorted: 2, }, })), ...googleModels.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "google", providerName: "Google", providerType: "google", + sorted: 3, }, })), ...anthropicModels.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "anthropic", providerName: "Anthropic", providerType: "anthropic", + sorted: 4, }, })), ...baiduModels.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "baidu", providerName: "Baidu", providerType: "baidu", + sorted: 5, }, })), ...bytedanceModels.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "bytedance", providerName: "ByteDance", providerType: "bytedance", + sorted: 6, }, })), ...alibabaModes.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "alibaba", providerName: "Alibaba", providerType: "alibaba", + sorted: 7, }, })), ...tencentModels.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "tencent", providerName: "Tencent", providerType: "tencent", + sorted: 8, }, })), ...moonshotModes.map((name) => ({ name, available: true, + sorted: seq++, provider: { id: "moonshot", providerName: "Moonshot", providerType: "moonshot", + sorted: 9, }, })), ] as const; diff --git a/app/utils/model.ts b/app/utils/model.ts index b117b5eb6..0b62b53be 100644 --- a/app/utils/model.ts +++ b/app/utils/model.ts @@ -1,32 +1,39 @@ import { DEFAULT_MODELS } from "../constant"; import { LLMModel } from "../client/api"; +const CustomSeq = { + val: -1000, //To ensure the custom model located at front, start from -1000, refer to constant.ts + cache: new Map(), + next: (id: string) => { + if (CustomSeq.cache.has(id)) { + return CustomSeq.cache.get(id) as number; + } else { + let seq = CustomSeq.val++; + CustomSeq.cache.set(id, seq); + return seq; + } + }, +}; + const customProvider = (providerName: string) => ({ id: providerName.toLowerCase(), providerName: providerName, providerType: "custom", + sorted: CustomSeq.next(providerName), }); -const sortModelTable = ( - models: ReturnType, - rule: "custom-first" | "default-first", -) => +/** + * Sorts an array of models based on specified rules. + * + * First, sorted by provider; if the same, sorted by model + */ +const sortModelTable = (models: ReturnType) => models.sort((a, b) => { - if (a.provider === undefined && b.provider === undefined) { - return 0; - } - - let aIsCustom = a.provider?.providerType === "custom"; - let bIsCustom = b.provider?.providerType === "custom"; - - if (aIsCustom === bIsCustom) { - return 0; - } - - if (aIsCustom) { - return rule === "custom-first" ? -1 : 1; + if (a.provider && b.provider) { + let cmp = a.provider.sorted - b.provider.sorted; + return cmp === 0 ? a.sorted - b.sorted : cmp; } else { - return rule === "custom-first" ? 1 : -1; + return a.sorted - b.sorted; } }); @@ -40,6 +47,7 @@ export function collectModelTable( available: boolean; name: string; displayName: string; + sorted: number; provider?: LLMModel["provider"]; // Marked as optional isDefault?: boolean; } @@ -107,6 +115,7 @@ export function collectModelTable( displayName: displayName || customModelName, available, provider, // Use optional chaining + sorted: CustomSeq.next(`${customModelName}@${provider?.id}`), }; } } @@ -151,7 +160,7 @@ export function collectModels( const modelTable = collectModelTable(models, customModels); let allModels = Object.values(modelTable); - allModels = sortModelTable(allModels, "custom-first"); + allModels = sortModelTable(allModels); return allModels; } @@ -168,7 +177,7 @@ export function collectModelsWithDefaultModel( ); let allModels = Object.values(modelTable); - allModels = sortModelTable(allModels, "custom-first"); + allModels = sortModelTable(allModels); return allModels; }