feat: use managed model capabilities for reasoning and image input

This commit is contained in:
2026-09-12 21:37:18 +08:00
parent e7701d1f2c
commit 26cbb29aa9
29 changed files with 652 additions and 271 deletions

View File

@@ -81,9 +81,10 @@ async function installCodingFirstChatHost(
electronApp: ElectronApplication,
hostConnection: HostConnection,
featureComplete = false,
managedCapabilities = false,
): Promise<void> {
await electronApp.evaluate(async (_, payload) => {
const { connection, featureComplete } = payload;
const { connection, featureComplete, managedCapabilities } = payload;
const { ipcMain } = process.mainModule!.require('electron') as typeof import('electron');
type MainState = {
captured: CapturedRequest[];
@@ -116,7 +117,7 @@ async function installCodingFirstChatHost(
lastOpenedAt: now,
};
const configuredModel = {
accountId: 'account-e2e',
accountId: managedCapabilities ? 'niancode-user-models' : 'account-e2e',
modelId: 'model-a',
thinkingLevel: 'off',
};
@@ -516,12 +517,20 @@ async function installCodingFirstChatHost(
}
if (path === '/api/provider-accounts') {
return respond([{
id: 'account-e2e',
id: managedCapabilities ? 'niancode-user-models' : 'account-e2e',
vendorId: 'custom',
label: 'E2E account',
authMode: 'api_key',
model: 'model-a',
fallbackModels: ['model-b'],
...(managedCapabilities ? { metadata: { worksSquareModelCapabilitiesV2: {
schemaVersion: 2, fetchedAt: now, refreshStatus: 'fresh', models: {
'model-a': { inputModalities: ['text'], outputModalities: ['text'],
reasoning: { supported: true, canDisable: true, defaultEnabled: true,
effortValues: ['xhigh', 'medium', 'low'], defaultEffort: 'xhigh', controlFormat: 'qwen', budget: null },
limits: null, resolutionStatus: 'ready', issues: [], updatedAt: now },
},
} } } : {}),
enabled: true,
isDefault: true,
createdAt: now,
@@ -703,7 +712,7 @@ async function installCodingFirstChatHost(
if (path === '/api/coding/runtime/diagnostics') return respond({ runtime: { revision: { provider: 1, resources: 1 }, workers: [{ conversationId: conversation.id, generation: 1, state: 'running', stage: 'running' }] } });
return respond({ success: false, error: `Unhandled E2E route: ${method} ${path}` }, 404);
});
}, { connection: hostConnection, featureComplete });
}, { connection: hostConnection, featureComplete, managedCapabilities });
}
async function readState(electronApp: ElectronApplication): Promise<{
@@ -744,6 +753,36 @@ async function settleSnapshot(electronApp: ElectronApplication): Promise<void> {
});
}
test('managed capabilities expose native xhigh and block unsupported image input', async ({ launchElectronApp }) => {
const electronApp = await launchElectronApp({ skipSetup: true });
let page = await getStableWindow(electronApp);
const connection = await page.evaluate(async () => ({
token: await window.electron.ipcRenderer.invoke('hostapi:token') as string,
baseUrl: await window.electron.ipcRenderer.invoke('hostapi:base-url') as string,
}));
await installCodingFirstChatHost(electronApp, connection, true, true);
await settleSnapshot(electronApp);
await disableCodingEventSource(page);
try {
await page.reload();
page = await getStableWindow(electronApp);
await page.getByTestId('ai-module-option-programming').click();
await expect(page.getByTestId('main-layout')).toBeVisible();
await page.evaluate(() => { window.location.hash = '/chat'; });
await expect(page.getByRole('button', { name: '添加图片', exact: true })).toBeDisabled();
const controls = page.getByTestId('coding-composer-runtime-controls');
await expect(controls).toContainText('模型默认');
await controls.getByRole('button').click();
await page.getByRole('menuitem', { name: /推理强度/ }).click();
await expect(page.getByRole('menuitemradio', { name: '模型默认', exact: true })).toBeVisible();
await expect(page.getByRole('menuitemradio', { name: '关闭思考', exact: true })).toBeVisible();
await page.getByRole('menuitemradio', { name: 'xhigh', exact: true }).click();
await expect.poll(async () => (await readState(electronApp)).captured.find(
request => request.path.endsWith('/thinking') && request.method === 'POST')?.body?.reasoningChoice,
).toEqual({ mode: 'enabled', effort: 'xhigh' });
} finally { await releaseSnapshot(electronApp); }
});
test('first PI Conversation is editable under 500 ms and submits before runtime Snapshot', async ({
launchElectronApp,
}) => {