feat: release v1.0.2 LeKiwi 语言控制与网站嵌入
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
集成服务器托管模型、自然语言移动与有界抓放、内置 LeKiwi URL 导入和双摄像头;同步部署契约与指定域名 iframe 白名单,保留原有物理安全、会话及调用预算防护。 更新 npm 包及锁文件版本、CHANGELOG 与发布文档。提交前 typecheck、120 项定向前端测试和 44 项后端测试通过(3 项可选跳过);真实 v2 云模型抓放仍待单独验收,不包含运行密钥或构建产物。
This commit is contained in:
@@ -1,77 +1,36 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { resolve } from 'node:path';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { startLiveDecisionService } from './fixtures/decisionService';
|
||||
import type {} from '../physics/agent';
|
||||
|
||||
test.use({ trace: 'off', video: 'off' });
|
||||
test('显式 opt-in:主工作台 DeepSeek flash + OpenRouter Jev 真实物理回合', async ({
|
||||
test('显式 opt-in:旧 v1 Bearer 客户端真实 API 物理兼容(非 v2 产品验收)', async ({
|
||||
page,
|
||||
}, info) => {
|
||||
test.skip(
|
||||
process.env.LEKIWI_LIVE_API !== '1',
|
||||
'需明确授权 .env 两个角色与一次付费回合;不自动运行',
|
||||
);
|
||||
test.skip(process.env.LEKIWI_LIVE_API !== '1', '需明确授权 .env 两个角色与一次付费回合');
|
||||
const service = await startLiveDecisionService();
|
||||
try {
|
||||
await page.goto('/');
|
||||
await page
|
||||
.locator('input[type="file"]')
|
||||
.first()
|
||||
.setInputFiles(resolve('build/lekiwi/lekiwi-v1.zip'));
|
||||
const dialog = page.getByRole('dialog', { name: '配置 URDF 仿真组件' });
|
||||
await expect(dialog).toBeVisible({ timeout: 30000 });
|
||||
await dialog.getByLabel('URDF 机器人 profile').selectOption('lekiwi-v1');
|
||||
await dialog.getByRole('button', { name: '转换并加载' }).click();
|
||||
await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled({ timeout: 60000 });
|
||||
await page.getByRole('tab', { name: '控制台', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'LeKiwi 智能抓放', exact: true }).click();
|
||||
const panel = page.getByLabel('LeKiwi 智能抓放任务', { exact: true });
|
||||
await panel.getByText('模型连接与官方订阅登录', { exact: true }).click();
|
||||
await panel.getByLabel('本机模型服务', { exact: true }).fill(service.endpoint);
|
||||
await panel.getByLabel('本机服务令牌', { exact: true }).fill(service.token);
|
||||
await panel.getByRole('button', { name: '连接本机模型服务', exact: true }).click();
|
||||
await expect(panel).toContainText('本机桥已连接');
|
||||
await expect(panel.getByLabel('LLM 型号', { exact: true })).toHaveValue('deepseek-flash');
|
||||
await expect(panel.getByLabel('JEV 型号', { exact: true })).toHaveValue('typesafe/jev-1.13');
|
||||
await expect(panel.getByLabel('LLM Base URL', { exact: true })).toHaveValue(
|
||||
'https://api.deepseek.com',
|
||||
await page.goto('/physics/agent.html');
|
||||
await page.waitForFunction(() => Boolean(window.lekiwiAgent));
|
||||
await page.evaluate(
|
||||
(url) => window.lekiwiAgent.boot(url),
|
||||
`/@fs${resolve('build/lekiwi/lekiwi-v1.zip')}`,
|
||||
);
|
||||
await expect(panel.getByLabel('JEV Base URL', { exact: true })).toHaveValue(
|
||||
'https://openrouter.ai/api/alpha/decisions',
|
||||
);
|
||||
await panel.getByText('模型连接与官方订阅登录', { exact: true }).click();
|
||||
await panel.getByLabel('智能任务决策模式').selectOption('api');
|
||||
await panel.getByRole('button', { name: '开始任务(真实 API)', exact: true }).click();
|
||||
const status = panel.getByTestId('agent-task-state');
|
||||
await expect(status).toContainText(/物理验收成功|失败 \/ 安全停止|已取消/, { timeout: 240000 });
|
||||
const pendingDownload = page.waitForEvent('download');
|
||||
await panel.getByRole('button', { name: '导出任务证据', exact: true }).click();
|
||||
const downloaded = await pendingDownload;
|
||||
const data = JSON.parse(await readFile((await downloaded.path())!, 'utf8'));
|
||||
const encoded = JSON.stringify(data, null, 2);
|
||||
if (encoded.includes(service.token)) throw new Error('任务导出包含本机令牌');
|
||||
await writeFile(info.outputPath('live-physical-evidence.json'), encoded);
|
||||
console.log('真实 API 回合(非订阅)', {
|
||||
state: data.state,
|
||||
error: data.error,
|
||||
llm: data.llmCalls,
|
||||
jev: data.jevCalls,
|
||||
simTime: data.sample.time,
|
||||
transported: data.transported,
|
||||
models: [...new Set(data.receipts.map((r: { model: string }) => r.model))],
|
||||
const data = await page.evaluate((connection) => window.lekiwiAgent.task(0, connection), {
|
||||
address: service.endpoint,
|
||||
token: service.token,
|
||||
});
|
||||
expect(data.mode).toBe('api');
|
||||
const encoded = JSON.stringify(data, null, 2);
|
||||
if (encoded.includes(service.token)) throw new Error('证据包含服务令牌');
|
||||
await writeFile(info.outputPath('v1-live-physical-evidence.json'), encoded);
|
||||
expect(data.state, data.error).toBe('succeeded');
|
||||
expect(data.succeeded).toBe(true);
|
||||
expect(data.transported).toBeGreaterThanOrEqual(0.5);
|
||||
expect(data.llmCalls).toBe(1);
|
||||
expect(data.jevCalls).toBe(11);
|
||||
expect(data.receipts).toHaveLength(12);
|
||||
await page.screenshot({ path: info.outputPath('live-workbench.png') });
|
||||
} catch (error) {
|
||||
// Playwright action errors may quote input values. Close before automatic DOM diagnostics.
|
||||
await page.close().catch(() => undefined);
|
||||
// eslint-disable-next-line preserve-caught-error -- The original cause may contain the private bridge token.
|
||||
// eslint-disable-next-line preserve-caught-error -- Private bridge credentials must not enter errors.
|
||||
throw new Error(String(error).replaceAll(service.token, '[redacted]'));
|
||||
} finally {
|
||||
await service.stop();
|
||||
|
||||
Reference in New Issue
Block a user