refactor: 简化模型管理,移除启用/禁用,优化测试和布局
- 移除供应商/模型启用禁用能力,清理DB schema/migration/API/前端 - 供应商测试改为Base URL连通性+/models探测 - 新增POST /api/models/test模型连接测试 - 新增GET /api/providers/options专用供应商选项接口 - 统一工具栏为ModelsToolbar,参考项目管理布局 - 模型弹窗优化:默认能力、响应式3列标签、并排数值 - 前后端正整数校验、供应商下拉loading/error/empty状态 - 表格列宽统一,操作列/名称列固定宽度
This commit is contained in:
@@ -38,6 +38,12 @@ export async function handleCreateModel(req: Request, db: Database, mode: Runtim
|
||||
return jsonResponse(createApiError(`Invalid capabilities: ${invalidCaps.join(", ")}`, 400), { mode, status: 400 });
|
||||
}
|
||||
|
||||
const numberError = validateOptionalPositiveInteger("contextLength", body.contextLength);
|
||||
if (numberError) return jsonResponse(createApiError(numberError, 400), { mode, status: 400 });
|
||||
|
||||
const tokenError = validateOptionalPositiveInteger("maxOutputTokens", body.maxOutputTokens);
|
||||
if (tokenError) return jsonResponse(createApiError(tokenError, 400), { mode, status: 400 });
|
||||
|
||||
const result = createModel(db, body);
|
||||
if ("error" in result) {
|
||||
return jsonResponse(createApiError(result.error, result.status), { mode, status: result.status });
|
||||
@@ -45,3 +51,9 @@ export async function handleCreateModel(req: Request, db: Database, mode: Runtim
|
||||
|
||||
return jsonResponse(result, { mode, status: 201 });
|
||||
}
|
||||
|
||||
function validateOptionalPositiveInteger(field: string, value: null | number | undefined): null | string {
|
||||
if (value === undefined || value === null) return null;
|
||||
if (!Number.isInteger(value) || value <= 0) return `${field} must be a positive integer`;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import type Database from "bun:sqlite";
|
||||
|
||||
import type { RuntimeMode } from "../../../shared/api";
|
||||
|
||||
import { disableModel } from "../../db/models";
|
||||
import { createApiError, jsonResponse } from "../../helpers";
|
||||
import { validateIdParam } from "../../middleware";
|
||||
|
||||
export function handleDisableModel(req: Request, db: Database, mode: RuntimeMode): Response {
|
||||
const url = new URL(req.url);
|
||||
const idStr = url.pathname.split("/")[3];
|
||||
|
||||
const validated = validateIdParam(idStr ?? "", mode);
|
||||
if (validated instanceof Response) return validated;
|
||||
|
||||
const result = disableModel(db, validated.id);
|
||||
if ("error" in result) {
|
||||
return jsonResponse(createApiError(result.error, result.status), { mode, status: result.status });
|
||||
}
|
||||
|
||||
return jsonResponse(result, { mode });
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import type Database from "bun:sqlite";
|
||||
|
||||
import type { RuntimeMode } from "../../../shared/api";
|
||||
|
||||
import { enableModel } from "../../db/models";
|
||||
import { createApiError, jsonResponse } from "../../helpers";
|
||||
import { validateIdParam } from "../../middleware";
|
||||
|
||||
export function handleEnableModel(req: Request, db: Database, mode: RuntimeMode): Response {
|
||||
const url = new URL(req.url);
|
||||
const idStr = url.pathname.split("/")[3];
|
||||
|
||||
const validated = validateIdParam(idStr ?? "", mode);
|
||||
if (validated instanceof Response) return validated;
|
||||
|
||||
const result = enableModel(db, validated.id);
|
||||
if ("error" in result) {
|
||||
return jsonResponse(createApiError(result.error, result.status), { mode, status: result.status });
|
||||
}
|
||||
|
||||
return jsonResponse(result, { mode });
|
||||
}
|
||||
42
src/server/routes/models/test.ts
Normal file
42
src/server/routes/models/test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type Database from "bun:sqlite";
|
||||
|
||||
import type { RuntimeMode, TestModelRequest } from "../../../shared/api";
|
||||
|
||||
import { testModelConnection } from "../../ai/registry";
|
||||
import { getProvider } from "../../db/providers";
|
||||
import { createApiError, jsonResponse } from "../../helpers";
|
||||
|
||||
export async function handleTestModelConfig(req: Request, db: Database, mode: RuntimeMode): Promise<Response> {
|
||||
let body: TestModelRequest;
|
||||
try {
|
||||
body = (await req.json()) as TestModelRequest;
|
||||
} catch {
|
||||
return jsonResponse(createApiError("Invalid JSON body", 400), { mode, status: 400 });
|
||||
}
|
||||
|
||||
if (!body.providerId || typeof body.providerId !== "string") {
|
||||
return jsonResponse(createApiError("providerId is required", 400), { mode, status: 400 });
|
||||
}
|
||||
|
||||
if (!body.modelId || typeof body.modelId !== "string") {
|
||||
return jsonResponse(createApiError("modelId is required", 400), { mode, status: 400 });
|
||||
}
|
||||
|
||||
const providerResult = getProvider(db, body.providerId);
|
||||
if ("error" in providerResult) {
|
||||
return jsonResponse(createApiError(providerResult.error, providerResult.status), {
|
||||
mode,
|
||||
status: providerResult.status,
|
||||
});
|
||||
}
|
||||
|
||||
const testResult = await testModelConnection({
|
||||
apiKey: providerResult.provider.apiKey,
|
||||
baseUrl: providerResult.provider.baseUrl,
|
||||
modelId: body.modelId,
|
||||
name: providerResult.provider.name,
|
||||
type: providerResult.provider.type,
|
||||
});
|
||||
|
||||
return jsonResponse({ modelTestResponse: testResult }, { mode });
|
||||
}
|
||||
@@ -34,6 +34,12 @@ export async function handleUpdateModel(req: Request, db: Database, mode: Runtim
|
||||
}
|
||||
}
|
||||
|
||||
const numberError = validateOptionalPositiveInteger("contextLength", body.contextLength);
|
||||
if (numberError) return jsonResponse(createApiError(numberError, 400), { mode, status: 400 });
|
||||
|
||||
const tokenError = validateOptionalPositiveInteger("maxOutputTokens", body.maxOutputTokens);
|
||||
if (tokenError) return jsonResponse(createApiError(tokenError, 400), { mode, status: 400 });
|
||||
|
||||
const result = updateModel(db, validated.id, body);
|
||||
if ("error" in result) {
|
||||
return jsonResponse(createApiError(result.error, result.status), { mode, status: result.status });
|
||||
@@ -41,3 +47,9 @@ export async function handleUpdateModel(req: Request, db: Database, mode: Runtim
|
||||
|
||||
return jsonResponse(result, { mode });
|
||||
}
|
||||
|
||||
function validateOptionalPositiveInteger(field: string, value: null | number | undefined): null | string {
|
||||
if (value === undefined || value === null) return null;
|
||||
if (!Number.isInteger(value) || value <= 0) return `${field} must be a positive integer`;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import type Database from "bun:sqlite";
|
||||
|
||||
import type { RuntimeMode } from "../../../shared/api";
|
||||
|
||||
import { disableProvider } from "../../db/providers";
|
||||
import { createApiError, jsonResponse } from "../../helpers";
|
||||
import { validateIdParam } from "../../middleware";
|
||||
|
||||
export function handleDisableProvider(req: Request, db: Database, mode: RuntimeMode): Response {
|
||||
const url = new URL(req.url);
|
||||
const idStr = url.pathname.split("/")[3];
|
||||
|
||||
const validated = validateIdParam(idStr ?? "", mode);
|
||||
if (validated instanceof Response) return validated;
|
||||
|
||||
const result = disableProvider(db, validated.id);
|
||||
if ("error" in result) {
|
||||
return jsonResponse(createApiError(result.error, result.status), { mode, status: result.status });
|
||||
}
|
||||
|
||||
return jsonResponse(result, { mode });
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import type Database from "bun:sqlite";
|
||||
|
||||
import type { RuntimeMode } from "../../../shared/api";
|
||||
|
||||
import { enableProvider } from "../../db/providers";
|
||||
import { createApiError, jsonResponse } from "../../helpers";
|
||||
import { validateIdParam } from "../../middleware";
|
||||
|
||||
export function handleEnableProvider(req: Request, db: Database, mode: RuntimeMode): Response {
|
||||
const url = new URL(req.url);
|
||||
const idStr = url.pathname.split("/")[3];
|
||||
|
||||
const validated = validateIdParam(idStr ?? "", mode);
|
||||
if (validated instanceof Response) return validated;
|
||||
|
||||
const result = enableProvider(db, validated.id);
|
||||
if ("error" in result) {
|
||||
return jsonResponse(createApiError(result.error, result.status), { mode, status: result.status });
|
||||
}
|
||||
|
||||
return jsonResponse(result, { mode });
|
||||
}
|
||||
10
src/server/routes/providers/options.ts
Normal file
10
src/server/routes/providers/options.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type Database from "bun:sqlite";
|
||||
|
||||
import type { RuntimeMode } from "../../../shared/api";
|
||||
|
||||
import { listProviderOptions } from "../../db/providers";
|
||||
import { jsonResponse } from "../../helpers";
|
||||
|
||||
export function handleListProviderOptions(db: Database, mode: RuntimeMode): Response {
|
||||
return jsonResponse({ items: listProviderOptions(db) }, { mode });
|
||||
}
|
||||
@@ -3,35 +3,7 @@ import type Database from "bun:sqlite";
|
||||
import type { CreateProviderRequest, RuntimeMode } from "../../../shared/api";
|
||||
|
||||
import { testProviderConnection } from "../../ai/registry";
|
||||
import { getProvider } from "../../db/providers";
|
||||
import { createApiError, jsonResponse } from "../../helpers";
|
||||
import { validateIdParam } from "../../middleware";
|
||||
|
||||
export async function handleTestProvider(req: Request, db: Database, mode: RuntimeMode): Promise<Response> {
|
||||
const url = new URL(req.url);
|
||||
const idStr = url.pathname.split("/")[3];
|
||||
|
||||
const validated = validateIdParam(idStr ?? "", mode);
|
||||
if (validated instanceof Response) return validated;
|
||||
|
||||
const providerResult = getProvider(db, validated.id);
|
||||
if ("error" in providerResult) {
|
||||
return jsonResponse(createApiError(providerResult.error, providerResult.status), {
|
||||
mode,
|
||||
status: providerResult.status,
|
||||
});
|
||||
}
|
||||
|
||||
const provider = providerResult.provider;
|
||||
const testResult = await testProviderConnection({
|
||||
apiKey: provider.apiKey,
|
||||
baseUrl: provider.baseUrl,
|
||||
name: provider.name,
|
||||
type: provider.type,
|
||||
});
|
||||
|
||||
return jsonResponse({ providerTestResponse: testResult }, { mode });
|
||||
}
|
||||
|
||||
export async function handleTestProviderConfig(req: Request, _db: Database, mode: RuntimeMode): Promise<Response> {
|
||||
const validated = await readProviderConfig(req, mode);
|
||||
|
||||
Reference in New Issue
Block a user