feat: 新增模型管理功能(供应商 + 模型 CRUD)
- 新增 providers/models 数据库表、迁移和数据访问层 - 新增 15 个后端 API 路由(供应商/模型 CRUD + 连通性测试) - 新增 AI 服务层(registry.ts: buildProviderRegistry + testProviderConnection) - 新增前端模型管理页面(Tabs: 供应商/模型,含表格、表单、工具栏) - 新增前端 hooks(use-providers, use-models) - 新增共享类型和 MODEL_CAPABILITIES 常量 - 新增 10 个测试文件(66 个测试用例,4 个因 bun test ESM 兼容问题待修复) - 更新开发文档(architecture, backend, frontend) - 附带 apply-review 修复:统一错误响应、提取共享常量、清理重复测试 注意:registry.test.ts 中 4 个测试因 bun test 无法解析 createProviderRegistry ESM 导出而失败,详情见 context.md
This commit is contained in:
78
tests/server/ai/registry.test.ts
Normal file
78
tests/server/ai/registry.test.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { describe, expect, mock, test } from "bun:test";
|
||||
|
||||
import { createMigratedTestDatabase } from "../../helpers";
|
||||
|
||||
describe("AI registry", () => {
|
||||
test("testProviderConnection rejects invalid config", async () => {
|
||||
void mock.module("ai", () => ({
|
||||
generateText: mock(() => {
|
||||
throw new Error("Connection failed");
|
||||
}),
|
||||
}));
|
||||
|
||||
const { testProviderConnection } = await import("../../../src/server/ai/registry");
|
||||
|
||||
const result = await testProviderConnection({
|
||||
apiKey: "bad-key",
|
||||
baseUrl: "https://0.0.0.0:1",
|
||||
name: "Bad",
|
||||
type: "openai-compatible",
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.message).toContain("连接失败");
|
||||
expect(typeof result.message).toBe("string");
|
||||
});
|
||||
|
||||
test("testProviderConnection return shape is correct", async () => {
|
||||
void mock.module("ai", () => ({
|
||||
generateText: mock((_opts: unknown) => ({})),
|
||||
}));
|
||||
|
||||
const { testProviderConnection } = await import("../../../src/server/ai/registry");
|
||||
|
||||
const result = await testProviderConnection({
|
||||
apiKey: "sk-test",
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
name: "Test",
|
||||
type: "openai",
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.message).toBe("连接成功");
|
||||
});
|
||||
|
||||
test("buildProviderRegistry 从 DB 构建包含启用供应商的注册表", async () => {
|
||||
const handle = createMigratedTestDatabase("registry-build-test");
|
||||
const now = new Date().toISOString();
|
||||
|
||||
handle.db
|
||||
.prepare(
|
||||
"INSERT INTO providers (id, name, type, base_url, api_key, enabled, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
)
|
||||
.run("pv1", "OpenAI", "openai", "https://api.openai.com/v1", "sk-test", 1, now, now);
|
||||
handle.db
|
||||
.prepare(
|
||||
"INSERT INTO providers (id, name, type, base_url, api_key, enabled, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
)
|
||||
.run("pv2", "Disabled", "anthropic", "https://api.anthropic.com", "sk-off", 0, now, now);
|
||||
|
||||
const { buildProviderRegistry } = await import("../../../src/server/ai/registry");
|
||||
const registry = buildProviderRegistry(handle.db);
|
||||
|
||||
expect(() => registry.languageModel("pv1:gpt-4o")).not.toThrow();
|
||||
|
||||
handle.cleanup();
|
||||
});
|
||||
|
||||
test("buildProviderRegistry 无启用供应商时返回空注册表", async () => {
|
||||
const handle = createMigratedTestDatabase("registry-empty-test");
|
||||
|
||||
const { buildProviderRegistry } = await import("../../../src/server/ai/registry");
|
||||
const registry = buildProviderRegistry(handle.db);
|
||||
|
||||
expect(typeof registry.languageModel).toBe("function");
|
||||
|
||||
handle.cleanup();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user