mirror of
https://github.com/jeffusion/gitea-ai-assistant.git
synced 2026-03-27 10:05:50 +00:00
feat(review): remove legacy mode and harden agent/codex pipeline
Drop legacy runtime paths and role assignments across backend/frontend, and add upgrade-safe DB migration for existing installs. This aligns config, docs, tests, and UI to the agent-first architecture with codex as the only alternate engine.
This commit is contained in:
@@ -17,15 +17,12 @@ import { toast } from 'sonner';
|
||||
// Engine-specific field visibility
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type EngineMode = 'legacy' | 'agent' | 'codex';
|
||||
type EngineMode = 'agent' | 'codex';
|
||||
|
||||
/** The engine selector field — always visible at the top. */
|
||||
const ENGINE_FIELD = 'REVIEW_ENGINE';
|
||||
|
||||
/** Fields shared across legacy & agent (but NOT codex). */
|
||||
const LEGACY_AGENT_FIELDS = new Set([
|
||||
'CUSTOM_SUMMARY_PROMPT',
|
||||
'CUSTOM_LINE_COMMENT_PROMPT',
|
||||
const AGENT_SHARED_FIELDS = new Set([
|
||||
'GLOBAL_PROMPT',
|
||||
'REVIEW_WORKDIR',
|
||||
'REVIEW_MAX_PARALLEL_RUNS',
|
||||
@@ -65,10 +62,8 @@ function getVisibleFields(engine: EngineMode, fields: ConfigFieldDto[]): ConfigF
|
||||
return fields.filter((f) => {
|
||||
if (f.envKey === ENGINE_FIELD) return false; // rendered separately
|
||||
switch (engine) {
|
||||
case 'legacy':
|
||||
return LEGACY_AGENT_FIELDS.has(f.envKey);
|
||||
case 'agent':
|
||||
return LEGACY_AGENT_FIELDS.has(f.envKey) || AGENT_ONLY_FIELDS.has(f.envKey);
|
||||
return AGENT_SHARED_FIELDS.has(f.envKey) || AGENT_ONLY_FIELDS.has(f.envKey);
|
||||
case 'codex':
|
||||
return CODEX_FIELDS.has(f.envKey);
|
||||
default:
|
||||
@@ -82,7 +77,6 @@ function getVisibleFields(engine: EngineMode, fields: ConfigFieldDto[]): ConfigF
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const ENGINE_OPTIONS: { value: EngineMode; label: string; description: string }[] = [
|
||||
{ value: 'legacy', label: 'Legacy', description: '传统单次 LLM 审查' },
|
||||
{ value: 'agent', label: 'Agent', description: '多代理编排深度审查' },
|
||||
{ value: 'codex', label: 'Codex', description: 'Codex CLI 审查' },
|
||||
];
|
||||
@@ -105,7 +99,7 @@ export function ReviewConfigPage() {
|
||||
const engine: EngineMode = useMemo(() => {
|
||||
const val = localConfig[ENGINE_FIELD];
|
||||
if (val === 'agent' || val === 'codex') return val;
|
||||
return 'legacy';
|
||||
return 'agent';
|
||||
}, [localConfig]);
|
||||
|
||||
// Derived: review group and memory group from fetched data
|
||||
@@ -231,13 +225,11 @@ export function ReviewConfigPage() {
|
||||
const syntheticReviewGroup: ConfigGroupDto | null = reviewGroup
|
||||
? {
|
||||
...reviewGroup,
|
||||
label: engine === 'codex' ? 'Codex 审查设置' : engine === 'agent' ? 'Agent 审查设置' : 'Legacy 审查设置',
|
||||
label: engine === 'codex' ? 'Codex 审查设置' : 'Agent 审查设置',
|
||||
description:
|
||||
engine === 'codex'
|
||||
? 'Codex CLI 审查引擎配置'
|
||||
: engine === 'agent'
|
||||
? '多代理编排审查引擎配置'
|
||||
: '传统单次 LLM 审查引擎配置',
|
||||
: '多代理编排审查引擎配置',
|
||||
fields: visibleReviewFields,
|
||||
}
|
||||
: null;
|
||||
@@ -327,7 +319,7 @@ export function ReviewConfigPage() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-6 bg-zinc-950/20">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{ENGINE_OPTIONS.map((opt) => (
|
||||
<button
|
||||
key={opt.value}
|
||||
@@ -377,7 +369,6 @@ export function ReviewConfigPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* LLM Provider config — legacy & agent only */}
|
||||
{engine !== 'codex' && (
|
||||
<>
|
||||
<ProviderList />
|
||||
|
||||
@@ -10,14 +10,13 @@ import { fetchProviders, fetchRoles, setRole } from '@/services/llmProviderServi
|
||||
import { ModelCombobox } from './ModelCombobox';
|
||||
|
||||
const ROLE_LABELS: Record<string, { label: string; desc: string }> = {
|
||||
legacy: { label: 'Legacy 审查', desc: '基础的单次代码审查模式,速度快但分析较浅' },
|
||||
planner: { label: '规划器 Planner', desc: '多阶段审查的第一步,负责分析上下文并分配任务' },
|
||||
specialist: { label: '专家 Specialist', desc: '执行深度代码审查的主力模型,专注于发现具体问题' },
|
||||
judge: { label: '评审 Judge', desc: '对专家的建议进行审核、合并和过滤,确保评论质量' },
|
||||
embedding: { label: '嵌入 Embedding', desc: '用于向量化代码和注释,支持语义搜索 (Qdrant)' },
|
||||
};
|
||||
|
||||
const ROLES = ['legacy', 'planner', 'specialist', 'judge', 'embedding'];
|
||||
const ROLES = ['planner', 'specialist', 'judge', 'embedding'];
|
||||
|
||||
interface RoleState {
|
||||
providerId: string | null;
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('RoleAssignment', () => {
|
||||
|
||||
vi.mocked(fetchRoles).mockResolvedValueOnce([
|
||||
{
|
||||
role: 'legacy',
|
||||
role: 'planner',
|
||||
providerId: 'p1',
|
||||
providerName: 'OpenAI',
|
||||
providerType: 'openai_responses',
|
||||
@@ -77,7 +77,6 @@ describe('RoleAssignment', () => {
|
||||
renderWithQuery(<RoleAssignment />);
|
||||
|
||||
expect(await screen.findByText('角色分配')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Legacy 审查')).toBeInTheDocument();
|
||||
expect(await screen.findByText('规划器 Planner')).toBeInTheDocument();
|
||||
|
||||
// Radix Select renders placeholder in a span with pointer-events: none.
|
||||
@@ -89,11 +88,11 @@ describe('RoleAssignment', () => {
|
||||
|
||||
const modelInputs = screen.getAllByPlaceholderText('选择或输入模型...') as HTMLInputElement[];
|
||||
await waitFor(() => {
|
||||
expect(modelInputs[1].value).toBe('gpt-4o-mini');
|
||||
expect(modelInputs[0].value).toBe('gpt-4o');
|
||||
});
|
||||
|
||||
await user.clear(modelInputs[1]);
|
||||
await user.type(modelInputs[1], 'custom-planner-model');
|
||||
expect(modelInputs[1].value).toBe('custom-planner-model');
|
||||
await user.clear(modelInputs[0]);
|
||||
await user.type(modelInputs[0], 'custom-planner-model');
|
||||
expect(modelInputs[0].value).toBe('custom-planner-model');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user