AGC 把执行器与线协议拆成正交维度
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 3m1s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 1m37s
Project CI / AI game creator shell Rust crates (push) Successful in 1m25s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m6s
Project CI / Frontend tests (push) Successful in 2m52s
Project CI / Backend tests (push) Failing after 4m14s
Project CI / Repository checks (push) Successful in 3m42s
Project CI / AI game creator shell web tests (push) Successful in 2m5s
Project CI / Native shell tests (push) Successful in 8m2s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 3m1s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 1m37s
Project CI / AI game creator shell Rust crates (push) Successful in 1m25s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m6s
Project CI / Frontend tests (push) Successful in 2m52s
Project CI / Backend tests (push) Failing after 4m14s
Project CI / Repository checks (push) Successful in 3m42s
Project CI / AI game creator shell web tests (push) Successful in 2m5s
Project CI / Native shell tests (push) Successful in 8m2s
AgcAgentMode 回退为仅 codex/cc,只表示执行器;新增 AgcModelProtocol 表示线协议 协议取值 openai_responses/openai_chat/anthropic,与执行器组合封闭并在校验时失败关闭 存量目录缺 protocol 时按执行器推导;历史误写的 agentMode=anthropic 读取时归一为 cc 后台 AGC 模型页拆成 Agent 模式与协议两列,新增协议下拉与对应测试 公开目录 /api/llm/models 与后台 DTO 同时下发解析后的 protocol 客户端持久化 selectedModelProtocol,选模型时按执行器推导兜底协议并校验组合 Codex wire_api 按协议在 responses/chat 间切换,不再硬编码 responses 同步 appSurface 与模型选择测试断言,补 protocol 字段 同步模型别名文档与后端架构文档的正交维度说明
This commit is contained in:
@@ -1075,7 +1075,11 @@ export interface AdminRechargeRefundActionResponse {
|
||||
export interface AdminWalletRestrictionResponse {
|
||||
wallet: AdminProfileWalletPayload;
|
||||
}
|
||||
export type AdminAgcAgentMode = 'codex' | 'cc' | 'anthropic';
|
||||
export type AdminAgcAgentMode = 'codex' | 'cc';
|
||||
export type AdminAgcModelProtocol =
|
||||
| 'openai_responses'
|
||||
| 'openai_chat'
|
||||
| 'anthropic';
|
||||
|
||||
export interface AdminAgcModel {
|
||||
id: string;
|
||||
@@ -1083,6 +1087,8 @@ export interface AdminAgcModel {
|
||||
modelId: string;
|
||||
enabled: boolean;
|
||||
agentMode: AdminAgcAgentMode;
|
||||
/** 上游线协议,与执行器正交。 */
|
||||
protocol?: AdminAgcModelProtocol;
|
||||
}
|
||||
|
||||
export interface AdminAgcModelCatalog {
|
||||
|
||||
@@ -104,3 +104,49 @@ test('new models default to Codex and save the selected Agent mode', async () =>
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('协议与 Agent 模式是独立字段,可按模型单独选择', async () => {
|
||||
const catalog = {
|
||||
revision: 3,
|
||||
defaultModelId: 'quality',
|
||||
models: [
|
||||
{
|
||||
id: 'quality',
|
||||
alias: '高质量',
|
||||
modelId: 'claude-opus-5-5',
|
||||
enabled: true,
|
||||
agentMode: 'cc' as const,
|
||||
protocol: 'anthropic' as const,
|
||||
},
|
||||
],
|
||||
};
|
||||
vi.mocked(getAgcModelCatalog).mockResolvedValue(catalog);
|
||||
vi.mocked(saveAgcModelCatalog).mockImplementation(async (_, input) => ({
|
||||
...input,
|
||||
revision: 4,
|
||||
}));
|
||||
render(<AdminAgcModelsPage token="test" onUnauthorized={vi.fn()} />);
|
||||
await screen.findByDisplayValue('claude-opus-5-5');
|
||||
|
||||
const agentMode = screen.getByLabelText('模型 1 Agent 模式') as HTMLSelectElement;
|
||||
const protocol = screen.getByLabelText('模型 1 协议') as HTMLSelectElement;
|
||||
expect(agentMode.value).toBe('cc');
|
||||
expect(protocol.value).toBe('anthropic');
|
||||
// 执行器下拉里不再混入协议取值。
|
||||
expect(
|
||||
Array.from(agentMode.options).map((option) => option.value),
|
||||
).toEqual(['codex', 'cc']);
|
||||
|
||||
fireEvent.change(agentMode, { target: { value: 'codex' } });
|
||||
fireEvent.change(protocol, { target: { value: 'openai_chat' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存' }));
|
||||
|
||||
await waitFor(() => {
|
||||
const calls = vi.mocked(saveAgcModelCatalog).mock.calls;
|
||||
const payload = calls[calls.length - 1]?.[1];
|
||||
expect(payload?.models[0]).toMatchObject({
|
||||
agentMode: 'codex',
|
||||
protocol: 'openai_chat',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -117,6 +117,7 @@ export function AdminAgcModelsPage({
|
||||
{ key: 'alias', label: '别名' },
|
||||
{ key: 'modelId', label: '实际模型名' },
|
||||
{ key: 'agentMode', label: 'Agent 模式' },
|
||||
{ key: 'protocol', label: '协议' },
|
||||
{ key: 'enabled', label: '启用' },
|
||||
{ key: 'default', label: '默认' },
|
||||
{ key: 'actions', label: '操作' },
|
||||
@@ -157,6 +158,7 @@ export function AdminAgcModelsPage({
|
||||
modelId: '',
|
||||
enabled: true,
|
||||
agentMode: 'codex',
|
||||
protocol: 'openai_responses',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -226,7 +228,22 @@ export function AdminAgcModelsPage({
|
||||
>
|
||||
<option value="codex">Codex</option>
|
||||
<option value="cc">Claude Code</option>
|
||||
<option value="anthropic">Anthropic 协议</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
aria-label={`模型 ${index + 1} 协议`}
|
||||
value={model.protocol ?? 'openai_responses'}
|
||||
disabled={busy}
|
||||
onChange={(e) =>
|
||||
update(model.id, {
|
||||
protocol: e.target.value as AdminAgcModel['protocol'],
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="openai_responses">OpenAI Responses</option>
|
||||
<option value="openai_chat">OpenAI Chat Completions</option>
|
||||
<option value="anthropic">Anthropic Messages</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Reference in New Issue
Block a user