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:
@@ -0,0 +1,88 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { loadWebsiteRobot, runWebsiteCommand } from './website.helpers';
|
||||
|
||||
test('显式授权:公网内置模型,前进/转向/抓放,无手填密钥或自动重试', async ({ page }, info) => {
|
||||
test.skip(process.env.CADWORLD_LIVE_API !== '1', '仅经用户授权后运行,会消耗服务器公共额度');
|
||||
test.setTimeout(360000);
|
||||
const calls = { command: 0, plan: 0, decide: 0, blocked: 0 };
|
||||
let csrf = '',
|
||||
cleanup = false;
|
||||
await page.route('**/api/decision/v1/**', async (route) => {
|
||||
const endpoint = new URL(route.request().url()).pathname.split('/').at(-1);
|
||||
if (endpoint === 'command' || endpoint === 'plan' || endpoint === 'decide') {
|
||||
const limit = { command: 3, plan: 1, decide: 13 };
|
||||
if (calls[endpoint] >= limit[endpoint]) {
|
||||
calls.blocked++;
|
||||
await route.abort();
|
||||
return;
|
||||
}
|
||||
calls[endpoint]++;
|
||||
} else if (endpoint === 'test') {
|
||||
calls.blocked++;
|
||||
await route.abort();
|
||||
return;
|
||||
}
|
||||
await route.continue();
|
||||
});
|
||||
let capture: Promise<void> | undefined;
|
||||
const captureSession = () =>
|
||||
page
|
||||
.waitForResponse((r) => r.url().endsWith('/session') && r.request().method() === 'POST')
|
||||
.then(async (r) => {
|
||||
csrf = (await r.json()).csrfToken;
|
||||
})
|
||||
.catch(() => undefined);
|
||||
// Observe capture on all outcomes; no pre-existing keys/Cookies or storage state used.
|
||||
try {
|
||||
await loadWebsiteRobot(page);
|
||||
capture = captureSession();
|
||||
const move = await runWebsiteCommand(page, '前进0.5米', info, 'live-forward');
|
||||
expect(move.distance).toBeGreaterThan(0.49);
|
||||
expect(move.distance).toBeLessThan(0.51);
|
||||
const turn = await runWebsiteCommand(page, '左转90度', info, 'live-turn');
|
||||
expect(turn.turnDegrees).toBeGreaterThan(89);
|
||||
expect(turn.turnDegrees).toBeLessThan(91);
|
||||
expect(turn.start.x).toBeCloseTo(move.sample.x, 2);
|
||||
const panel = page.getByLabel('机器人语言控制', { exact: true });
|
||||
await panel.getByRole('button', { name: '载入预设场景' }).click();
|
||||
await expect(panel.getByRole('status')).toContainText('预设已载入', { timeout: 90000 });
|
||||
const grasp = await runWebsiteCommand(page, '把方块搬到 B 区', info, 'live-pick-place');
|
||||
expect(grasp.transported).toBeGreaterThanOrEqual(0.5);
|
||||
expect(grasp.sample.onGoalSupport).toBe(true);
|
||||
expect(calls).toEqual({ command: 3, plan: 1, decide: 13, blocked: 0 });
|
||||
await page.screenshot({ path: info.outputPath('hosted-control.png') });
|
||||
} finally {
|
||||
await capture;
|
||||
if (csrf)
|
||||
cleanup = await page
|
||||
.evaluate(
|
||||
async (token) =>
|
||||
(
|
||||
await fetch('/api/decision/v1/session', {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': token },
|
||||
body: '{}',
|
||||
})
|
||||
).status === 200,
|
||||
csrf,
|
||||
)
|
||||
.catch(() => false);
|
||||
await page.close();
|
||||
await writeFile(
|
||||
info.outputPath('hosted-live-summary.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
calls,
|
||||
sessionDestroyed: cleanup,
|
||||
maximumUpstreamCalls: { llm: 4, jev: 16 },
|
||||
retries: 0,
|
||||
credentialInput: false,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
}
|
||||
expect(cleanup).toBe(true);
|
||||
});
|
||||
Reference in New Issue
Block a user