mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-20 15:37:27 +08:00
fix(wizard): prepare default Local Agent and refine Runner setup
This commit is contained in:
@@ -301,7 +301,7 @@ function makePipeline(
|
||||
};
|
||||
}
|
||||
|
||||
function pipelineMetadata(withRunnerToolSelector = false) {
|
||||
export function pipelineMetadata(withRunnerToolSelector = false) {
|
||||
const runnerId = 'plugin:langbot-team/LocalAgent/default';
|
||||
return {
|
||||
configs: [
|
||||
|
||||
@@ -0,0 +1,542 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import {
|
||||
installLangBotApiMocks,
|
||||
pipelineMetadata,
|
||||
} from './fixtures/langbot-api';
|
||||
|
||||
const runnerId = 'plugin:langbot-team/LocalAgent/default';
|
||||
const ok = (data: unknown) => JSON.stringify({ code: 0, message: 'ok', data });
|
||||
|
||||
for (const failsFirst of [false, true]) {
|
||||
test(`prepares Local Agent before enabling the bot${failsFirst ? ' after an installation retry' : ''}`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await installLangBotApiMocks(page, {
|
||||
authenticated: true,
|
||||
withAdapterEvents: true,
|
||||
});
|
||||
let installed = false;
|
||||
let installCount = 0;
|
||||
let modelCount = 0;
|
||||
let messageReceived = false;
|
||||
const order: string[] = [];
|
||||
const configs: Record<string, any>[] = [];
|
||||
await page.route('**/api/v1/pipelines/_/metadata', async (route) => {
|
||||
const metadata = pipelineMetadata();
|
||||
if (!installed) metadata.configs[0].stages = [];
|
||||
else {
|
||||
const model = metadata.configs[0].stages.find(
|
||||
(stage) => stage.name === runnerId,
|
||||
)!.config[0];
|
||||
model.default = { primary: '', fallbacks: [] } as never;
|
||||
}
|
||||
await route.fulfill({
|
||||
contentType: 'application/json',
|
||||
body: ok(metadata),
|
||||
});
|
||||
});
|
||||
await page.route(
|
||||
'**/api/v1/marketplace/plugins/langbot-team/LocalAgent',
|
||||
async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: 'application/json',
|
||||
body: ok({
|
||||
plugin: {
|
||||
author: 'langbot-team',
|
||||
name: 'LocalAgent',
|
||||
label: { en_US: 'Local Agent' },
|
||||
latest_version: '0.1.7',
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
);
|
||||
await page.route('**/api/v1/plugins/install/marketplace', async (route) => {
|
||||
installCount += 1;
|
||||
expect(route.request().postDataJSON()).toEqual({
|
||||
plugin_author: 'langbot-team',
|
||||
plugin_name: 'LocalAgent',
|
||||
plugin_version: '0.1.7',
|
||||
});
|
||||
order.push('install');
|
||||
await route.fulfill({
|
||||
contentType: 'application/json',
|
||||
body: ok({ task_id: installCount }),
|
||||
});
|
||||
});
|
||||
await page.route('**/api/v1/system/tasks/*', async (route) => {
|
||||
const failed = failsFirst && installCount === 1;
|
||||
installed = !failed;
|
||||
await route.fulfill({
|
||||
contentType: 'application/json',
|
||||
body: ok({
|
||||
runtime: {
|
||||
done: true,
|
||||
exception: failed ? 'Download failed: connection reset' : null,
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.route(
|
||||
'**/api/v1/system/wizard/recommended-model',
|
||||
async (route) => {
|
||||
modelCount += 1;
|
||||
order.push('model');
|
||||
await route.fulfill({
|
||||
contentType: 'application/json',
|
||||
body: ok({ uuid: 'recommended-model', name: 'Recommended' }),
|
||||
});
|
||||
},
|
||||
);
|
||||
await page.route('**/api/v1/platform/bots/*/logs', async (route) => {
|
||||
await route.fulfill({
|
||||
contentType: 'application/json',
|
||||
body: ok({
|
||||
logs: messageReceived
|
||||
? [
|
||||
{
|
||||
seq_id: 1,
|
||||
timestamp: Date.now() / 1000,
|
||||
level: 'info',
|
||||
text: 'Received message',
|
||||
images: [],
|
||||
message_session_id: 'person_123',
|
||||
},
|
||||
]
|
||||
: [],
|
||||
total_count: messageReceived ? 1 : 0,
|
||||
}),
|
||||
});
|
||||
});
|
||||
page.on('request', (request) => {
|
||||
const path = new URL(request.url()).pathname;
|
||||
if (
|
||||
request.method() === 'PUT' &&
|
||||
path === '/api/v1/pipelines/pipeline-1'
|
||||
) {
|
||||
configs.push(request.postDataJSON().config);
|
||||
order.push('configure');
|
||||
}
|
||||
if (
|
||||
request.method() === 'PUT' &&
|
||||
path === '/api/v1/platform/bots/bot-1' &&
|
||||
request.postDataJSON().enable
|
||||
)
|
||||
order.push('enable');
|
||||
});
|
||||
await page.goto('/wizard');
|
||||
await page.getByText('Playwright Adapter', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Confirm, Create Bot' }).click();
|
||||
await page.getByRole('button', { name: 'Save & Enable Bot' }).click();
|
||||
if (failsFirst) {
|
||||
await expect(
|
||||
page.getByText(/Download failed: connection reset/),
|
||||
).toBeVisible();
|
||||
expect(order).toEqual(['install']);
|
||||
await expect(page.getByRole('button', { name: 'Next' })).toBeDisabled();
|
||||
await page.getByRole('button', { name: 'Save & Enable Bot' }).click();
|
||||
}
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Re-save Configuration' }),
|
||||
).toBeVisible();
|
||||
expect(order.slice(-4)).toEqual([
|
||||
'install',
|
||||
'model',
|
||||
'configure',
|
||||
'enable',
|
||||
]);
|
||||
expect(configs[0].ai.runner.id).toBe(runnerId);
|
||||
expect(configs[0].ai.runner_config[runnerId].model.primary).toBe(
|
||||
'recommended-model',
|
||||
);
|
||||
await page.getByRole('button', { name: 'Re-save Configuration' }).click();
|
||||
await expect.poll(() => configs.length).toBe(2);
|
||||
expect(installCount).toBe(failsFirst ? 2 : 1);
|
||||
expect(modelCount).toBe(1);
|
||||
messageReceived = true;
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await expect(
|
||||
page.getByText('Use the default setup', { exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText('Local Agent', { exact: true })).toHaveCount(0);
|
||||
await page.getByRole('button', { name: 'Done', exact: true }).click();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Back to Workbench' }),
|
||||
).toBeVisible();
|
||||
expect(configs.length).toBe(2);
|
||||
});
|
||||
}
|
||||
|
||||
test('uses a custom model in the existing Local Agent pipeline', async ({
|
||||
page,
|
||||
}) => {
|
||||
await installLangBotApiMocks(page, {
|
||||
authenticated: true,
|
||||
withAdapterEvents: true,
|
||||
});
|
||||
await page.route('**/api/v1/platform/bots/*/logs', async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
logs: [
|
||||
{
|
||||
seq_id: 1,
|
||||
timestamp: Date.now() / 1000,
|
||||
level: 'info',
|
||||
text: 'Received message',
|
||||
images: [],
|
||||
message_session_id: 'person_123',
|
||||
},
|
||||
],
|
||||
total_count: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.route('**/api/v1/provider/**', async (route) => {
|
||||
const path = new URL(route.request().url()).pathname;
|
||||
if (path.endsWith('/requesters'))
|
||||
return route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
requesters: [
|
||||
{
|
||||
name: 'openai',
|
||||
label: { en_US: 'OpenAI API' },
|
||||
description: { en_US: '' },
|
||||
spec: {
|
||||
provider_category: 'manufacturer',
|
||||
support_type: ['llm'],
|
||||
config: [
|
||||
{ name: 'base_url', default: 'https://api.openai.com/v1' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
if (path.endsWith('/providers') && route.request().method() === 'POST')
|
||||
return route.fulfill({
|
||||
json: { code: 0, data: { uuid: 'own-provider' } },
|
||||
});
|
||||
if (path.endsWith('/scan-models'))
|
||||
return route.fulfill({ json: { code: 0, data: { models: [] } } });
|
||||
if (path.endsWith('/models/llm') && route.request().method() === 'POST')
|
||||
return route.fulfill({ json: { code: 0, data: { uuid: 'own-model' } } });
|
||||
await route.fallback();
|
||||
});
|
||||
await page.goto('/wizard');
|
||||
await page.getByText('Playwright Adapter', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Confirm, Create Bot' }).click();
|
||||
await page.getByRole('button', { name: 'Save & Enable Bot' }).click();
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByText('Use My Own Model', { exact: true }).click();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Use Selected Model & Finish' }),
|
||||
).toBeDisabled();
|
||||
await page.locator('input[name="name"]').fill('Wizard custom provider');
|
||||
await page
|
||||
.getByRole('button', { name: 'Select Provider Type', exact: true })
|
||||
.click();
|
||||
await page.getByRole('button', { name: /OpenAI API/ }).click();
|
||||
await page.locator('input[name="api_key"]').fill('fixture-key');
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
await page.getByLabel('Model ID', { exact: false }).fill('test-model');
|
||||
const update = page.waitForRequest(
|
||||
(request) =>
|
||||
request.method() === 'PUT' &&
|
||||
new URL(request.url()).pathname === '/api/v1/pipelines/pipeline-1',
|
||||
);
|
||||
await page
|
||||
.getByRole('button', { name: 'Use Selected Model & Finish' })
|
||||
.click();
|
||||
const config = (await update).postDataJSON().config;
|
||||
expect(config.ai.runner.id).toBe(runnerId);
|
||||
expect(config.ai.runner_config[runnerId].model.primary).toBe('own-model');
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Back to Workbench' }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('only labels debugging Runners even when the marketplace is offline', async ({
|
||||
page,
|
||||
}) => {
|
||||
await installLangBotApiMocks(page, { authenticated: true });
|
||||
await page.route('**/api/v1/system/info', async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
version: 'fixture',
|
||||
edition: 'community',
|
||||
cloud_service_url: 'https://space.langbot.app',
|
||||
enable_marketplace: true,
|
||||
wizard_status: 'none',
|
||||
wizard_progress: {
|
||||
step: 2,
|
||||
created_bot_uuid: 'bot-1',
|
||||
created_pipeline_uuid: 'pipeline-1',
|
||||
selected_adapter: 'test',
|
||||
bot_saved: true,
|
||||
message_received: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
const plugins = [
|
||||
{ name: 'local', label: 'Local Runner', debug: false },
|
||||
{ name: 'debug', label: 'Debug Runner', debug: true },
|
||||
];
|
||||
await page.route('**/api/v1/plugins', async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
plugins: plugins.map((plugin) => ({
|
||||
manifest: {
|
||||
manifest: {
|
||||
metadata: {
|
||||
author: 'qa',
|
||||
name: plugin.name,
|
||||
description: { en_US: `${plugin.label} description` },
|
||||
},
|
||||
},
|
||||
},
|
||||
debug: plugin.debug,
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.route('**/api/v1/pipelines/_/metadata', async (route) => {
|
||||
const metadata = pipelineMetadata();
|
||||
const selector = metadata.configs[0].stages[0].config[0];
|
||||
if (!('options' in selector))
|
||||
throw new Error('Missing Runner options in fixture');
|
||||
const options = selector.options;
|
||||
for (const plugin of plugins) {
|
||||
options.push({
|
||||
name: `plugin:qa/${plugin.name}/default`,
|
||||
label: { en_US: plugin.label, zh_Hans: plugin.label },
|
||||
});
|
||||
}
|
||||
await route.fulfill({ json: { code: 0, data: metadata } });
|
||||
});
|
||||
await page.route('**/api/v1/marketplace/**', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 503,
|
||||
json: { code: 503, msg: 'Marketplace offline' },
|
||||
});
|
||||
});
|
||||
await page.goto('/wizard');
|
||||
await page
|
||||
.getByRole('radio', { name: 'Connect an External Agent', exact: true })
|
||||
.click();
|
||||
const local = page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ hasText: 'Local Runner' });
|
||||
const debug = page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ hasText: 'Debug Runner' });
|
||||
await expect(local.getByText('Installed', { exact: true })).toHaveCount(0);
|
||||
await expect(debug.getByText('Debugging', { exact: true })).toBeVisible();
|
||||
await expect(debug.getByText('Installed', { exact: true })).toHaveCount(0);
|
||||
await debug.getByRole('button', { name: 'Use This Runner' }).click();
|
||||
await expect(
|
||||
page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ hasText: 'Debug Runner' })
|
||||
.getByText('Debugging', { exact: true }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows installation progress inside the matching card button and recovers after failure', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await installLangBotApiMocks(page, { authenticated: true });
|
||||
await page.route('**/api/v1/system/info', async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
version: 'fixture',
|
||||
edition: 'community',
|
||||
cloud_service_url: 'https://space.langbot.app',
|
||||
enable_marketplace: true,
|
||||
wizard_status: 'none',
|
||||
wizard_progress: {
|
||||
step: 2,
|
||||
created_bot_uuid: 'bot-1',
|
||||
created_pipeline_uuid: 'pipeline-1',
|
||||
selected_adapter: 'test',
|
||||
bot_saved: true,
|
||||
message_received: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
const plugin = (name: string) => ({
|
||||
name,
|
||||
author: 'qa',
|
||||
label: { en_US: name },
|
||||
description: { en_US: 'Test Runner' },
|
||||
components: { Runner: 1 },
|
||||
runner_usages: ['agent'],
|
||||
latest_version: '1.0.0',
|
||||
install_count: 0,
|
||||
type: 'plugin',
|
||||
});
|
||||
await page.route(
|
||||
'**/api/v1/marketplace/extensions/search**',
|
||||
async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
extensions: [plugin('ProgressRunner'), plugin('OtherRunner')],
|
||||
total: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
let attempt = 0;
|
||||
let phase: 'download' | 'deps' | 'failed' | 'done' = 'download';
|
||||
let registered = false;
|
||||
await page.route('**/api/v1/plugins/install/marketplace', async (route) => {
|
||||
attempt += 1;
|
||||
phase = 'download';
|
||||
await route.fulfill({ json: { code: 0, data: { task_id: attempt } } });
|
||||
});
|
||||
await page.route('**/api/v1/system/tasks/*', async (route) => {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
data: {
|
||||
id: attempt,
|
||||
name: 'plugin-install-marketplace',
|
||||
label: 'ProgressRunner',
|
||||
runtime: {
|
||||
done: phase === 'done' || phase === 'failed',
|
||||
exception:
|
||||
phase === 'failed' ? 'Dependency installation failed' : null,
|
||||
},
|
||||
task_context: {
|
||||
current_action:
|
||||
phase === 'download'
|
||||
? 'Downloading package'
|
||||
: 'Installing dependencies',
|
||||
metadata: { progress_percent: phase === 'download' ? 24 : 64 },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.route('**/api/v1/pipelines/_/metadata', async (route) => {
|
||||
const metadata = pipelineMetadata();
|
||||
const selector = metadata.configs[0].stages[0].config[0];
|
||||
if ('options' in selector)
|
||||
selector.options.push({
|
||||
name: 'plugin:qa/ExistingRunner/default',
|
||||
label: { en_US: 'ExistingRunner', zh_Hans: 'ExistingRunner' },
|
||||
});
|
||||
metadata.configs[0].stages.push({
|
||||
name: 'plugin:qa/ExistingRunner/default',
|
||||
label: { en_US: 'ExistingRunner', zh_Hans: 'ExistingRunner' },
|
||||
config: [
|
||||
{
|
||||
name: 'api-key',
|
||||
label: { en_US: 'API Key', zh_Hans: 'API Key' },
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
} as (typeof metadata.configs)[0]['stages'][number]);
|
||||
if (registered && 'options' in selector)
|
||||
selector.options.push({
|
||||
name: 'plugin:qa/ProgressRunner/default',
|
||||
label: { en_US: 'ProgressRunner', zh_Hans: 'ProgressRunner' },
|
||||
});
|
||||
await route.fulfill({ json: { code: 0, data: metadata } });
|
||||
});
|
||||
await page.goto('/wizard');
|
||||
await page
|
||||
.getByRole('radio', { name: 'Connect an External Agent', exact: true })
|
||||
.click();
|
||||
const card = page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ has: page.getByText('ProgressRunner', { exact: true }) });
|
||||
const other = page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ has: page.getByText('OtherRunner', { exact: true }) });
|
||||
const back = page.getByRole('button', {
|
||||
name: /Back to (options|list)/,
|
||||
exact: true,
|
||||
});
|
||||
await expect(back).toBeVisible();
|
||||
const pickerBounds = await back.boundingBox();
|
||||
await page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ has: page.getByText('ExistingRunner', { exact: true }) })
|
||||
.getByRole('button', { name: 'Use This Runner' })
|
||||
.click();
|
||||
await expect(page.getByText('ExistingRunner Configuration')).toBeVisible();
|
||||
await expect
|
||||
.poll(async () => (await back.boundingBox())?.x)
|
||||
.toBeCloseTo(pickerBounds!.x, 0);
|
||||
await expect
|
||||
.poll(async () => (await back.boundingBox())?.y)
|
||||
.toBeCloseTo(pickerBounds!.y, 0);
|
||||
await page.getByRole('textbox').fill('unsaved-test-key');
|
||||
await page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ has: page.getByText('ExistingRunner', { exact: true }) })
|
||||
.getByRole('button', { name: 'Use This Runner' })
|
||||
.click();
|
||||
await expect(page.getByRole('textbox')).toHaveValue('unsaved-test-key');
|
||||
await card
|
||||
.getByRole('button', { name: 'Install & Continue', exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
card.getByRole('button', { name: /Downloading.*24%/ }),
|
||||
).toBeDisabled();
|
||||
await expect(page.getByRole('progressbar')).toHaveCount(0);
|
||||
await expect(
|
||||
other.getByRole('button', { name: 'Install & Continue', exact: true }),
|
||||
).toBeDisabled();
|
||||
phase = 'deps';
|
||||
await expect(
|
||||
card.getByRole('button', { name: /Installing Dependencies.*64%/ }),
|
||||
).toBeDisabled();
|
||||
phase = 'failed';
|
||||
await card
|
||||
.getByRole('button', { name: 'Failed · Retry', exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
card.getByRole('button', { name: /Downloading.*24%/ }),
|
||||
).toBeDisabled();
|
||||
phase = 'done';
|
||||
await expect(
|
||||
card.getByRole('button', {
|
||||
name: /Starting and refreshing components.*95%/,
|
||||
}),
|
||||
).toBeDisabled();
|
||||
registered = true;
|
||||
await expect(
|
||||
card.getByRole('button', { name: 'Use This Runner' }),
|
||||
).toBeEnabled();
|
||||
await expect(
|
||||
card.getByRole('button', { name: 'Use This Runner' }),
|
||||
).toHaveAttribute('aria-pressed', 'true');
|
||||
await expect(
|
||||
other.getByRole('button', { name: 'Install & Continue' }),
|
||||
).toBeEnabled();
|
||||
expect(attempt).toBe(2);
|
||||
});
|
||||
@@ -509,6 +509,17 @@ test.describe('wizard and QR platform regressions', () => {
|
||||
name: 'ai',
|
||||
label: { en_US: 'AI Feature', zh_Hans: 'AI 能力' },
|
||||
stages: [
|
||||
{
|
||||
name: 'plugin:langbot-team/LocalAgent/default',
|
||||
config: [
|
||||
{
|
||||
name: 'model',
|
||||
type: 'model-fallback-selector',
|
||||
required: true,
|
||||
default: { primary: 'llm-valid', fallbacks: [] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'runner',
|
||||
label: { en_US: 'Runtime', zh_Hans: '运行方式' },
|
||||
@@ -562,7 +573,15 @@ test.describe('wizard and QR platform regressions', () => {
|
||||
await expect(page.getByRole('button', { name: 'Next' })).toBeDisabled();
|
||||
messageReceived = true;
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
await page.getByText('External Runner', { exact: true }).click();
|
||||
await expect(
|
||||
page.getByText('Use the default setup', { exact: true }),
|
||||
).toBeVisible();
|
||||
await page.getByText('Connect an External Agent', { exact: true }).click();
|
||||
await page
|
||||
.locator('[data-slot="card"]')
|
||||
.filter({ has: page.getByText('External Runner', { exact: true }) })
|
||||
.getByRole('button', { name: 'Use This Runner' })
|
||||
.click();
|
||||
|
||||
const deployButton = page.getByRole('button', { name: 'Create & Deploy' });
|
||||
await expect(deployButton).toBeDisabled();
|
||||
@@ -571,21 +590,14 @@ test.describe('wizard and QR platform regressions', () => {
|
||||
const bindingRequest = page.waitForRequest(
|
||||
(request) =>
|
||||
request.method() === 'PUT' &&
|
||||
new URL(request.url()).pathname === '/api/v1/platform/bots/bot-1',
|
||||
new URL(request.url()).pathname === '/api/v1/pipelines/pipeline-1',
|
||||
);
|
||||
await deployButton.click();
|
||||
const body = (await bindingRequest).postDataJSON();
|
||||
expect(body.event_bindings).toEqual([
|
||||
{
|
||||
event_pattern: 'message.received',
|
||||
target_type: 'pipeline',
|
||||
target_uuid: 'pipeline-1',
|
||||
filters: [],
|
||||
priority: 0,
|
||||
enabled: true,
|
||||
description: '',
|
||||
},
|
||||
]);
|
||||
expect(body.config.ai.runner.id).toBe('external-runner');
|
||||
expect(body.config.ai.runner_config['external-runner']['api-key']).toBe(
|
||||
'app-real-api-key',
|
||||
);
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Back to Workbench' }),
|
||||
).toBeVisible();
|
||||
|
||||
@@ -28,6 +28,7 @@ function loadWizardUtils() {
|
||||
}
|
||||
|
||||
const {
|
||||
LOCAL_AGENT_RUNNER_ID,
|
||||
configureLocalAgentPrimaryModel,
|
||||
ensureHttpBotSigningSecret,
|
||||
findDefaultPipeline,
|
||||
@@ -81,23 +82,31 @@ test('configures the selected model as the Local Agent primary model', () => {
|
||||
const config = {
|
||||
trigger: { prefix: '!' },
|
||||
ai: {
|
||||
runner: { runner: 'plugin:external', timeout: 30 },
|
||||
'local-agent': {
|
||||
model: { primary: 'old-model', fallbacks: ['fallback-model'] },
|
||||
tools: { enabled: true },
|
||||
runner: { id: 'plugin:external', timeout: 30 },
|
||||
runner_config: {
|
||||
[LOCAL_AGENT_RUNNER_ID]: {
|
||||
model: { primary: 'old-model', fallbacks: ['fallback-model'] },
|
||||
tools: { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const updated = configureLocalAgentPrimaryModel(config, 'selected-model');
|
||||
|
||||
assert.equal(updated.ai.runner.runner, 'local-agent');
|
||||
assert.equal(updated.ai.runner.id, LOCAL_AGENT_RUNNER_ID);
|
||||
assert.equal(updated.ai.runner.timeout, 30);
|
||||
assert.equal(updated.ai['local-agent'].model.primary, 'selected-model');
|
||||
assert.deepEqual(updated.ai['local-agent'].model.fallbacks, [
|
||||
'fallback-model',
|
||||
]);
|
||||
assert.deepEqual(updated.ai['local-agent'].tools, { enabled: true });
|
||||
assert.equal(
|
||||
updated.ai.runner_config[LOCAL_AGENT_RUNNER_ID].model.primary,
|
||||
'selected-model',
|
||||
);
|
||||
assert.deepEqual(
|
||||
updated.ai.runner_config[LOCAL_AGENT_RUNNER_ID].model.fallbacks,
|
||||
['fallback-model'],
|
||||
);
|
||||
assert.deepEqual(updated.ai.runner_config[LOCAL_AGENT_RUNNER_ID].tools, {
|
||||
enabled: true,
|
||||
});
|
||||
assert.deepEqual(updated.trigger, { prefix: '!' });
|
||||
});
|
||||
|
||||
|
||||
@@ -89,13 +89,10 @@ test('requires the selected Runner mandatory configuration before finishing', ()
|
||||
wizardSource,
|
||||
/isRequiredRunnerConfigComplete\(selectedRunnerConfigItems, runnerConfig\)/,
|
||||
);
|
||||
assert.match(wizardSource, /aiChoice === 'external'/);
|
||||
assert.match(
|
||||
wizardSource,
|
||||
/return selectedRunner !== null && isRunnerConfigComplete/,
|
||||
);
|
||||
assert.match(
|
||||
wizardSource,
|
||||
/!selectedRunner \|\|[\s\S]*?!isRunnerConfigComplete \|\|[\s\S]*?!createdBotUuid/,
|
||||
/!createdBotUuid \|\| !createdPipelineUuid \|\| !canProceed\(\)/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user