mirror of
https://github.com/jeffusion/gitea-ai-assistant.git
synced 2026-03-27 10:05:50 +00:00
336 lines
8.9 KiB
TypeScript
336 lines
8.9 KiB
TypeScript
import type { Page, Route } from '@playwright/test';
|
|
|
|
const repositories = {
|
|
data: [
|
|
{ name: 'demo-repo-1', webhook_status: 'active', hook_id: 101 },
|
|
{ name: 'demo-repo-2', webhook_status: 'inactive', hook_id: null },
|
|
],
|
|
totalCount: 2,
|
|
page: 1,
|
|
limit: 30,
|
|
};
|
|
|
|
const configResponse = {
|
|
groups: [
|
|
{
|
|
key: 'gitea',
|
|
label: 'Gitea 连接',
|
|
description: '配置 Gitea 服务地址和访问凭据。',
|
|
icon: 'git-branch',
|
|
fields: [
|
|
{
|
|
envKey: 'GITEA_BASE_URL',
|
|
label: 'Gitea 地址',
|
|
description: 'Gitea API 根地址',
|
|
type: 'url',
|
|
sensitive: false,
|
|
value: 'https://gitea.example.com',
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
key: 'feishu',
|
|
label: '飞书通知',
|
|
description: '配置飞书 webhook 通知。',
|
|
icon: 'bell',
|
|
fields: [
|
|
{
|
|
envKey: 'FEISHU_WEBHOOK_URL',
|
|
label: '飞书 Webhook URL',
|
|
description: '用于发送审查通知',
|
|
type: 'url',
|
|
sensitive: false,
|
|
value: 'https://open.feishu.cn/mock/webhook',
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
key: 'security',
|
|
label: '安全设置',
|
|
description: '控制签名校验与访问策略。',
|
|
icon: 'shield',
|
|
fields: [
|
|
{
|
|
envKey: 'ENABLE_SIGNATURE_VERIFY',
|
|
label: '启用签名校验',
|
|
description: '是否开启 webhook 签名验证',
|
|
type: 'boolean',
|
|
sensitive: false,
|
|
value: true,
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
key: 'review',
|
|
label: '审查设置',
|
|
description: '控制审查引擎与执行策略。',
|
|
icon: 'search',
|
|
fields: [
|
|
{
|
|
envKey: 'REVIEW_ENGINE',
|
|
label: '审查引擎',
|
|
description: '当前使用的审查引擎',
|
|
type: 'enum',
|
|
enumValues: ['agent', 'codex'],
|
|
sensitive: false,
|
|
value: 'agent',
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'GLOBAL_PROMPT',
|
|
label: '全局提示词',
|
|
description: '审查上下文提示词模板',
|
|
type: 'text',
|
|
sensitive: false,
|
|
value: 'Review with focus on correctness and maintainability.',
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'REVIEW_WORKDIR',
|
|
label: '审查工作目录',
|
|
description: '任务执行工作目录',
|
|
type: 'string',
|
|
sensitive: false,
|
|
value: '/tmp/review-workdir',
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'REVIEW_MAX_PARALLEL_RUNS',
|
|
label: '最大并发任务',
|
|
description: '控制并发执行数量',
|
|
type: 'number',
|
|
sensitive: false,
|
|
value: 4,
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'REVIEW_MAX_FILES_PER_RUN',
|
|
label: '单次最大文件数',
|
|
description: '每轮审查最大文件数',
|
|
type: 'number',
|
|
sensitive: false,
|
|
value: 40,
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'REVIEW_MAX_FILE_CONTENT_CHARS',
|
|
label: '单文件最大字符',
|
|
description: '单文件读取上限',
|
|
type: 'number',
|
|
sensitive: false,
|
|
value: 20000,
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'LLM_MAX_CONCURRENT_CALLS',
|
|
label: 'LLM 并发调用数',
|
|
description: '限制并发调用',
|
|
type: 'number',
|
|
sensitive: false,
|
|
value: 4,
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'CODEX_MODEL',
|
|
label: 'Codex 模型',
|
|
description: 'Codex 模式默认模型',
|
|
type: 'string',
|
|
sensitive: false,
|
|
value: 'gpt-4o-mini',
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
{
|
|
envKey: 'CODEX_API_URL',
|
|
label: 'Codex API 地址',
|
|
description: 'Codex 服务地址',
|
|
type: 'url',
|
|
sensitive: false,
|
|
value: 'https://api.openai.com/v1',
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
key: 'memory',
|
|
label: '记忆设置',
|
|
description: '控制上下文记忆与保留策略。',
|
|
icon: 'database',
|
|
fields: [
|
|
{
|
|
envKey: 'MEMORY_ENABLED',
|
|
label: '启用记忆',
|
|
description: '是否启用长期记忆',
|
|
type: 'boolean',
|
|
sensitive: false,
|
|
value: true,
|
|
hasValue: true,
|
|
source: 'db',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
const providers = [
|
|
{
|
|
id: 'provider-openai',
|
|
name: 'OpenAI',
|
|
type: 'openai_responses',
|
|
baseUrl: null,
|
|
defaultModel: 'gpt-4o-mini',
|
|
isEnabled: true,
|
|
hasKey: true,
|
|
extraConfig: {},
|
|
createdAt: '2026-03-01T00:00:00.000Z',
|
|
updatedAt: '2026-03-02T00:00:00.000Z',
|
|
},
|
|
{
|
|
id: 'provider-deepseek',
|
|
name: 'DeepSeek',
|
|
type: 'openai_compatible',
|
|
baseUrl: 'https://api.deepseek.com/v1',
|
|
defaultModel: 'deepseek-chat',
|
|
isEnabled: true,
|
|
hasKey: true,
|
|
extraConfig: {},
|
|
createdAt: '2026-03-01T00:00:00.000Z',
|
|
updatedAt: '2026-03-02T00:00:00.000Z',
|
|
},
|
|
];
|
|
|
|
const roles = [
|
|
{
|
|
role: 'planner',
|
|
providerId: 'provider-openai',
|
|
providerName: 'OpenAI',
|
|
providerType: 'openai_responses',
|
|
model: 'gpt-4o-mini',
|
|
},
|
|
{
|
|
role: 'specialist',
|
|
providerId: 'provider-deepseek',
|
|
providerName: 'DeepSeek',
|
|
providerType: 'openai_compatible',
|
|
model: 'deepseek-chat',
|
|
},
|
|
];
|
|
|
|
const modelSuggestions = {
|
|
openai_compatible: ['deepseek-chat', 'gpt-4o-mini'],
|
|
openai_responses: ['gpt-4o', 'gpt-4o-mini', 'o3-mini'],
|
|
anthropic: ['claude-sonnet-4-20250514'],
|
|
gemini: ['gemini-2.5-pro'],
|
|
};
|
|
|
|
const json = async (route: Route, body: unknown, status = 200) => {
|
|
await route.fulfill({
|
|
status,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(body),
|
|
});
|
|
};
|
|
|
|
export async function installVisualApiMocks(page: Page) {
|
|
await page.route('**/admin/api/**', async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const path = url.pathname;
|
|
const method = route.request().method();
|
|
|
|
if (method === 'POST' && path.endsWith('/admin/api/login')) {
|
|
return json(route, { token: 'visual-token' });
|
|
}
|
|
|
|
if (method === 'GET' && path.endsWith('/admin/api/repositories')) {
|
|
return json(route, repositories);
|
|
}
|
|
|
|
if (method === 'POST' && /\/admin\/api\/repositories\/[^/]+\/webhook$/.test(path)) {
|
|
return json(route, { hook_id: 101, webhook_status: 'active' });
|
|
}
|
|
|
|
if (method === 'DELETE' && /\/admin\/api\/repositories\/[^/]+\/webhook\/\d+$/.test(path)) {
|
|
return route.fulfill({ status: 204, body: '' });
|
|
}
|
|
|
|
if (method === 'GET' && path.endsWith('/admin/api/config')) {
|
|
return json(route, configResponse);
|
|
}
|
|
|
|
if (method === 'PUT' && path.endsWith('/admin/api/config')) {
|
|
return route.fulfill({ status: 204, body: '' });
|
|
}
|
|
|
|
if (method === 'POST' && path.endsWith('/admin/api/config/reset')) {
|
|
return route.fulfill({ status: 204, body: '' });
|
|
}
|
|
|
|
if (method === 'GET' && path.endsWith('/admin/api/llm/model-suggestions')) {
|
|
return json(route, modelSuggestions);
|
|
}
|
|
|
|
if (method === 'GET' && path.endsWith('/admin/api/llm/providers')) {
|
|
return json(route, providers);
|
|
}
|
|
|
|
if (method === 'POST' && path.endsWith('/admin/api/llm/providers')) {
|
|
return json(route, providers[0]);
|
|
}
|
|
|
|
if (method === 'PUT' && /\/admin\/api\/llm\/providers\/[^/]+$/.test(path)) {
|
|
return json(route, providers[0]);
|
|
}
|
|
|
|
if (method === 'DELETE' && /\/admin\/api\/llm\/providers\/[^/]+$/.test(path)) {
|
|
return route.fulfill({ status: 204, body: '' });
|
|
}
|
|
|
|
if (method === 'PUT' && /\/admin\/api\/llm\/providers\/[^/]+\/key$/.test(path)) {
|
|
return route.fulfill({ status: 204, body: '' });
|
|
}
|
|
|
|
if (method === 'DELETE' && /\/admin\/api\/llm\/providers\/[^/]+\/key$/.test(path)) {
|
|
return route.fulfill({ status: 204, body: '' });
|
|
}
|
|
|
|
if (method === 'GET' && path.endsWith('/admin/api/llm/roles')) {
|
|
return json(route, roles);
|
|
}
|
|
|
|
if (method === 'PUT' && /\/admin\/api\/llm\/roles\/[^/]+$/.test(path)) {
|
|
return json(route, roles[0]);
|
|
}
|
|
|
|
if (method === 'POST' && /\/admin\/api\/llm\/providers\/[^/]+\/test$/.test(path)) {
|
|
return json(route, {
|
|
success: true,
|
|
latencyMs: 154,
|
|
model: 'gpt-4o-mini',
|
|
message: 'Test connection success',
|
|
});
|
|
}
|
|
|
|
return json(
|
|
route,
|
|
{
|
|
error: `Unhandled visual mock request: ${method} ${path}`,
|
|
},
|
|
501
|
|
);
|
|
});
|
|
}
|