diff --git a/src/config/__tests__/config-manager.test.ts b/src/config/__tests__/config-manager.test.ts index 10c422b..0312228 100644 --- a/src/config/__tests__/config-manager.test.ts +++ b/src/config/__tests__/config-manager.test.ts @@ -23,9 +23,6 @@ const SCHEMA_KEYS = [ 'GITEA_API_URL', 'GITEA_ACCESS_TOKEN', 'GITEA_ADMIN_TOKEN', - 'OPENAI_BASE_URL', - 'OPENAI_API_KEY', - 'OPENAI_MODEL', 'CUSTOM_SUMMARY_PROMPT', 'CUSTOM_LINE_COMMENT_PROMPT', 'GLOBAL_PROMPT', @@ -37,9 +34,6 @@ const SCHEMA_KEYS = [ 'JWT_SECRET', 'REVIEW_ENGINE', 'REVIEW_WORKDIR', - 'REVIEW_MODEL_PLANNER', - 'REVIEW_MODEL_SPECIALIST', - 'REVIEW_MODEL_JUDGE', 'REVIEW_MAX_PARALLEL_RUNS', 'REVIEW_MAX_FILES_PER_RUN', 'REVIEW_MAX_FILE_CONTENT_CHARS', @@ -113,20 +107,20 @@ describe('ConfigManager', () => { describe('layering: defaults < env < override', () => { test('Zod default used when env and override are absent', async () => { const cm = await importFresh(); - expect(cm.getCurrent().openai.model).toBe('gpt-4o-mini'); + expect(cm.getCurrent().review.engine).toBe('legacy'); }); test('env value overrides Zod default', async () => { - process.env.OPENAI_MODEL = 'env-model'; + process.env.REVIEW_ENGINE = 'agent'; const cm = await importFresh(); - expect(cm.getCurrent().openai.model).toBe('env-model'); + expect(cm.getCurrent().review.engine).toBe('agent'); }); test('override wins over env', async () => { - process.env.OPENAI_MODEL = 'env-model'; + process.env.REVIEW_ENGINE = 'agent'; const cm = await importFresh(); - await cm.setOverrides({ OPENAI_MODEL: 'override-model' }); - expect(cm.getCurrent().openai.model).toBe('override-model'); + await cm.setOverrides({ REVIEW_ENGINE: 'legacy' }); + expect(cm.getCurrent().review.engine).toBe('legacy'); }); }); @@ -135,14 +129,14 @@ describe('ConfigManager', () => { describe('empty string resets override', () => { test('setting override to "" removes it, value falls back to Zod default', async () => { const cm = await importFresh(); - await cm.setOverrides({ OPENAI_MODEL: 'temp-override' }); - expect(cm.getCurrent().openai.model).toBe('temp-override'); + await cm.setOverrides({ REVIEW_ENGINE: 'agent' }); + expect(cm.getCurrent().review.engine).toBe('agent'); - await cm.setOverrides({ OPENAI_MODEL: '' }); + await cm.setOverrides({ REVIEW_ENGINE: '' }); - // OPENAI_MODEL is '' in env (neutralised) → falls to Zod default - expect(cm.getCurrent().openai.model).toBe('gpt-4o-mini'); - expect(cm.getOverrides()).not.toHaveProperty('OPENAI_MODEL'); + // REVIEW_ENGINE is '' in env (neutralised) → falls to Zod default + expect(cm.getCurrent().review.engine).toBe('legacy'); + expect(cm.getOverrides()).not.toHaveProperty('REVIEW_ENGINE'); }); }); @@ -151,18 +145,18 @@ describe('ConfigManager', () => { describe('persistence', () => { test('setOverrides writes JSON file; new instance loads it', async () => { const cm1 = await importFresh(); - await cm1.setOverrides({ OPENAI_MODEL: 'persisted-model' }); + await cm1.setOverrides({ REVIEW_ENGINE: 'agent' }); // File structure check const raw = await readFile(tmpPath, 'utf-8'); const data = JSON.parse(raw); expect(data.version).toBe(1); expect(typeof data.updatedAt).toBe('string'); - expect(data.overrides.OPENAI_MODEL).toBe('persisted-model'); + expect(data.overrides.REVIEW_ENGINE).toBe('agent'); // Fresh instance picks it up const cm2 = await importFresh(); - expect(cm2.getCurrent().openai.model).toBe('persisted-model'); + expect(cm2.getCurrent().review.engine).toBe('agent'); }); }); @@ -170,22 +164,22 @@ describe('ConfigManager', () => { describe('getSource()', () => { test('returns "default" when neither env nor override is set', async () => { - // OPENAI_MODEL = '' (neutralised) → getSource sees '' → 'default' + // REVIEW_ENGINE = '' (neutralised) → getSource sees '' → 'default' const cm = await importFresh(); - expect(cm.getSource('OPENAI_MODEL')).toBe('default'); + expect(cm.getSource('REVIEW_ENGINE')).toBe('default'); }); test('returns "env" when process.env has a non-empty value', async () => { - process.env.OPENAI_MODEL = 'from-env'; + process.env.REVIEW_ENGINE = 'agent'; const cm = await importFresh(); - expect(cm.getSource('OPENAI_MODEL')).toBe('env'); + expect(cm.getSource('REVIEW_ENGINE')).toBe('env'); }); test('returns "override" when override is set', async () => { - process.env.OPENAI_MODEL = 'from-env'; + process.env.REVIEW_ENGINE = 'agent'; const cm = await importFresh(); - await cm.setOverrides({ OPENAI_MODEL: 'from-override' }); - expect(cm.getSource('OPENAI_MODEL')).toBe('override'); + await cm.setOverrides({ REVIEW_ENGINE: 'legacy' }); + expect(cm.getSource('REVIEW_ENGINE')).toBe('override'); }); }); diff --git a/src/config/config-manager.ts b/src/config/config-manager.ts index af96f92..58f7712 100644 --- a/src/config/config-manager.ts +++ b/src/config/config-manager.ts @@ -40,10 +40,6 @@ const envSchema = z.object({ GITEA_ACCESS_TOKEN: z.string().default('test_token'), GITEA_ADMIN_TOKEN: z.string().optional(), - // OpenAI - OPENAI_BASE_URL: z.string().url().default('https://api.openai.com/v1'), - OPENAI_API_KEY: z.string().default('test_openai_key'), - OPENAI_MODEL: z.string().default('gpt-4o-mini'), CUSTOM_SUMMARY_PROMPT: z.string().optional(), CUSTOM_LINE_COMMENT_PROMPT: z.string().optional(), GLOBAL_PROMPT: z.string().optional(), @@ -66,9 +62,6 @@ const envSchema = z.object({ // Review engine REVIEW_ENGINE: z.enum(['legacy', 'agent']).default('legacy'), REVIEW_WORKDIR: z.string().default('/tmp/gitea-assistant'), - REVIEW_MODEL_PLANNER: z.string().default('gpt-4o-mini'), - REVIEW_MODEL_SPECIALIST: z.string().default('gpt-4o-mini'), - REVIEW_MODEL_JUDGE: z.string().default('gpt-4o-mini'), REVIEW_MAX_PARALLEL_RUNS: z.coerce.number().int().min(1).max(8).default(2), REVIEW_MAX_FILES_PER_RUN: z.coerce.number().int().min(1).max(1000).default(200), REVIEW_MAX_FILE_CONTENT_CHARS: z.coerce.number().int().min(1000).max(1_000_000).default(40_000), @@ -113,14 +106,6 @@ export interface AppConfig { apiUrl: string; accessToken: string; }; - openai: { - baseUrl: string; - apiKey: string; - model: string; - customSummaryPrompt: string | undefined; - customLineCommentPrompt: string | undefined; - globalPrompt: string | undefined; - }; feishu: { webhookUrl: string | undefined; webhookSecret: string | undefined; @@ -137,9 +122,9 @@ export interface AppConfig { review: { engine: string; workdir: string; - modelPlanner: string; - modelSpecialist: string; - modelJudge: string; + customSummaryPrompt: string | undefined; + customLineCommentPrompt: string | undefined; + globalPrompt: string | undefined; maxParallelRuns: number; maxFilesPerRun: number; maxFileContentChars: number; @@ -267,14 +252,6 @@ class ConfigManager { apiUrl: env.GITEA_API_URL, accessToken: env.GITEA_ACCESS_TOKEN, }, - openai: { - baseUrl: env.OPENAI_BASE_URL, - apiKey: env.OPENAI_API_KEY, - model: env.OPENAI_MODEL, - customSummaryPrompt: env.CUSTOM_SUMMARY_PROMPT, - customLineCommentPrompt: env.CUSTOM_LINE_COMMENT_PROMPT, - globalPrompt: env.GLOBAL_PROMPT, - }, feishu: { webhookUrl: env.FEISHU_WEBHOOK_URL, webhookSecret: env.FEISHU_WEBHOOK_SECRET, @@ -291,9 +268,9 @@ class ConfigManager { review: { engine: env.REVIEW_ENGINE, workdir: env.REVIEW_WORKDIR, - modelPlanner: env.REVIEW_MODEL_PLANNER, - modelSpecialist: env.REVIEW_MODEL_SPECIALIST, - modelJudge: env.REVIEW_MODEL_JUDGE, + customSummaryPrompt: env.CUSTOM_SUMMARY_PROMPT, + customLineCommentPrompt: env.CUSTOM_LINE_COMMENT_PROMPT, + globalPrompt: env.GLOBAL_PROMPT, maxParallelRuns: env.REVIEW_MAX_PARALLEL_RUNS, maxFilesPerRun: env.REVIEW_MAX_FILES_PER_RUN, maxFileContentChars: env.REVIEW_MAX_FILE_CONTENT_CHARS, diff --git a/src/config/config-schema.ts b/src/config/config-schema.ts index 3239e0b..87935e1 100644 --- a/src/config/config-schema.ts +++ b/src/config/config-schema.ts @@ -7,7 +7,7 @@ // Types // --------------------------------------------------------------------------- -export type ConfigGroup = 'gitea' | 'openai' | 'feishu' | 'app' | 'admin' | 'review' | 'memory'; +export type ConfigGroup = 'gitea' | 'feishu' | 'app' | 'admin' | 'review' | 'memory'; export type ConfigFieldType = 'string' | 'number' | 'boolean' | 'url' | 'text' | 'enum'; @@ -44,12 +44,6 @@ export const CONFIG_GROUPS: ConfigGroupMeta[] = [ description: 'Gitea 实例地址与访问令牌', icon: 'link', }, - { - key: 'openai', - label: 'OpenAI / LLM', - description: 'AI 模型接口与自定义提示词', - icon: 'bot', - }, { key: 'feishu', label: '飞书通知', @@ -115,37 +109,9 @@ export const CONFIG_FIELDS: ConfigFieldMeta[] = [ sensitive: true, }, - // ── OpenAI ────────────────────────────────────────────────────────────── - { - envKey: 'OPENAI_BASE_URL', - group: 'openai', - label: 'API 地址', - description: 'OpenAI 兼容 API 的基础 URL', - type: 'url', - sensitive: false, - defaultValue: 'https://api.openai.com/v1', - }, - { - envKey: 'OPENAI_API_KEY', - group: 'openai', - label: 'API 密钥', - description: 'OpenAI API 密钥', - type: 'string', - sensitive: true, - defaultValue: 'test_openai_key', - }, - { - envKey: 'OPENAI_MODEL', - group: 'openai', - label: '模型', - description: '默认使用的 OpenAI 模型名称', - type: 'string', - sensitive: false, - defaultValue: 'gpt-4o-mini', - }, { envKey: 'CUSTOM_SUMMARY_PROMPT', - group: 'openai', + group: 'review', label: '自定义总结提示词', description: '覆盖默认的代码审查总结提示词(留空使用内置提示词)', type: 'text', @@ -153,7 +119,7 @@ export const CONFIG_FIELDS: ConfigFieldMeta[] = [ }, { envKey: 'CUSTOM_LINE_COMMENT_PROMPT', - group: 'openai', + group: 'review', label: '自定义行评论提示词', description: '覆盖默认的行级评论提示词(留空使用内置提示词)', type: 'text', @@ -161,7 +127,7 @@ export const CONFIG_FIELDS: ConfigFieldMeta[] = [ }, { envKey: 'GLOBAL_PROMPT', - group: 'openai', + group: 'review', label: '全局提示词', description: '附加到所有 LLM 调用的系统提示词中(例如:"请始终使用中文回复")', type: 'text', @@ -251,33 +217,6 @@ export const CONFIG_FIELDS: ConfigFieldMeta[] = [ sensitive: false, defaultValue: '/tmp/gitea-assistant', }, - { - envKey: 'REVIEW_MODEL_PLANNER', - group: 'review', - label: '规划模型', - description: 'Agent 模式下规划阶段使用的模型', - type: 'string', - sensitive: false, - defaultValue: 'gpt-4o-mini', - }, - { - envKey: 'REVIEW_MODEL_SPECIALIST', - group: 'review', - label: '专家模型', - description: 'Agent 模式下专家子代理使用的模型', - type: 'string', - sensitive: false, - defaultValue: 'gpt-4o-mini', - }, - { - envKey: 'REVIEW_MODEL_JUDGE', - group: 'review', - label: '评审模型', - description: 'Agent 模式下 Judge 聚合阶段使用的模型', - type: 'string', - sensitive: false, - defaultValue: 'gpt-4o-mini', - }, { envKey: 'REVIEW_MAX_PARALLEL_RUNS', group: 'review', diff --git a/src/controllers/config.ts b/src/controllers/config.ts index 1c76e62..74ff9f1 100644 --- a/src/controllers/config.ts +++ b/src/controllers/config.ts @@ -39,19 +39,13 @@ function getEffectiveValue( return current.gitea.accessToken; case 'GITEA_ADMIN_TOKEN': return current.admin.giteaAdminToken; - // OpenAI - case 'OPENAI_BASE_URL': - return current.openai.baseUrl; - case 'OPENAI_API_KEY': - return current.openai.apiKey; - case 'OPENAI_MODEL': - return current.openai.model; + // Review prompts (moved from OpenAI group) case 'CUSTOM_SUMMARY_PROMPT': - return current.openai.customSummaryPrompt; + return current.review.customSummaryPrompt; case 'CUSTOM_LINE_COMMENT_PROMPT': - return current.openai.customLineCommentPrompt; + return current.review.customLineCommentPrompt; case 'GLOBAL_PROMPT': - return current.openai.globalPrompt; + return current.review.globalPrompt; // Feishu case 'FEISHU_WEBHOOK_URL': return current.feishu.webhookUrl; @@ -72,12 +66,6 @@ function getEffectiveValue( return current.review.engine; case 'REVIEW_WORKDIR': return current.review.workdir; - case 'REVIEW_MODEL_PLANNER': - return current.review.modelPlanner; - case 'REVIEW_MODEL_SPECIALIST': - return current.review.modelSpecialist; - case 'REVIEW_MODEL_JUDGE': - return current.review.modelJudge; case 'REVIEW_MAX_PARALLEL_RUNS': return current.review.maxParallelRuns; case 'REVIEW_MAX_FILES_PER_RUN':