feat: support configurable frontend model display order
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-20 17:37:33 +08:00
parent 56a16d2e7a
commit daca9aeb7a
3 changed files with 43 additions and 5 deletions

View File

@ -7,6 +7,19 @@ import {
import { ChatSDKError } from "@/lib/errors";
import { isAiModelAgentAuto } from "@/lib/db/ai-model-flags";
function sortModelsForDisplay<T extends { sortOrder?: number | null; displayName?: string | null }>(
models: T[]
) {
return [...models].sort((a, b) => {
const leftOrder = Number.isFinite(a.sortOrder as number) ? Number(a.sortOrder) : 0;
const rightOrder = Number.isFinite(b.sortOrder as number) ? Number(b.sortOrder) : 0;
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
const leftName = String(a.displayName || "");
const rightName = String(b.displayName || "");
return leftName.localeCompare(rightName, "zh-Hans-CN");
});
}
// 获取前端可用的 AI 模型列表(需要登录但不需要管理员权限)
export async function GET() {
try {
@ -20,12 +33,22 @@ export async function GET() {
const modelsWithProvider = await getEnabledAiModelsWithProvider(false);
const agentModelWithProvider = await getAgentAiModelWithProvider();
const defaultModel = await getDefaultAiModel();
const visibleModelsWithProvider = modelsWithProvider.filter(
({ model }) => !isAiModelAgentAuto(model)
const visibleModelsWithProvider = sortModelsForDisplay(
modelsWithProvider
.filter(({ model }) => !isAiModelAgentAuto(model))
.map((item) => item.model)
);
const visibleModelIdSet = new Set(visibleModelsWithProvider.map((m) => m.id));
const visibleModelsWithProviderSorted = modelsWithProvider.filter(({ model }) =>
visibleModelIdSet.has(model.id)
).sort((a, b) => {
const left = visibleModelsWithProvider.findIndex((m) => m.id === a.model.id);
const right = visibleModelsWithProvider.findIndex((m) => m.id === b.model.id);
return left - right;
});
// 转换为前端需要的格式
const models = visibleModelsWithProvider.map(({ model, provider }) => ({
const models = visibleModelsWithProviderSorted.map(({ model, provider }) => ({
id: model.id,
modelId: model.modelId,
displayName: model.displayName,
@ -40,6 +63,7 @@ export async function GET() {
parentModelId: model.parentModelId,
capabilities: model.capabilities, // 🆕 返回子模型的能力配置
imageGenConfig: model.imageGenConfig,
sortOrder: model.sortOrder ?? 0,
}));
const resolvedDefaultModelId =

View File

@ -70,6 +70,19 @@ function normalizeModel(model: any) {
};
}
function sortModelsForDisplay<T extends { sortOrder?: number | null; displayName?: string | null }>(
models: T[]
) {
return [...models].sort((a, b) => {
const leftOrder = Number.isFinite(a.sortOrder as number) ? Number(a.sortOrder) : 0;
const rightOrder = Number.isFinite(b.sortOrder as number) ? Number(b.sortOrder) : 0;
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
const leftName = String(a.displayName || "");
const rightName = String(b.displayName || "");
return leftName.localeCompare(rightName, "zh-Hans-CN");
});
}
export async function GET(request: Request) {
try {
await requireAdmin();
@ -88,11 +101,11 @@ export async function GET(request: Request) {
if (providerId) {
const models = await getAiModelsByProviderId({ providerId });
return Response.json({ models: models.map(normalizeModel) });
return Response.json({ models: sortModelsForDisplay(models.map(normalizeModel)) });
}
const models = await getAllAiModels();
return Response.json({ models: models.map(normalizeModel) });
return Response.json({ models: sortModelsForDisplay(models.map(normalizeModel)) });
} catch (error) {
if (error instanceof ChatSDKError) {
return error.toResponse();

View File

@ -1345,6 +1345,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
avatarUrl: modelsData?.agentModel?.avatarUrl || null,
providerType: "auto",
supportsImageGen: true,
imageGenConfig: undefined,
};
// 所有可选模型(Agent模式 + 可视化模型)