fix: IoT多设备支持 + Review Pipeline审查消息 + 意图分析快速通道优化

- IoT Provider: 重写Execute()支持多设备命令批量执行,修复persona路径
- Intent Analyzer: 新增isStrongIoTCommand快速通道,跳过LLM分析节省2-3s
- Orchestrator: parseReviewMessages()内联审查 + 快速通道扩展(chat/greeting跳过子会话)
- Gateway: SSE review_messages解析→WebSocket结构化消息转发(action/chat)
- Persona: 对话风格注入action格式指令(括号包裹动作描述)
- Frontend: sessionStore历史消息msgType映射
- 新增E2E测试脚本 + 调试标准文档 + 第4轮修复报告

E2E验证: IoT设备操控 Review消息拆分 快速通道 响应时间~3.4s

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 22:51:27 +08:00
parent 773f19f009
commit a67b95cbc4
18 changed files with 1186 additions and 85 deletions
+147
View File
@@ -0,0 +1,147 @@
import { WebSocket } from 'ws';
import http from 'http';
const CDP_HOST = 'localhost';
const CDP_PORT = 9222;
function httpRequest(method, path) {
return new Promise((resolve, reject) => {
const req = http.request({ hostname: CDP_HOST, port: CDP_PORT, method, path }, (res) => {
let data = '';
res.on('data', (c) => data += c);
res.on('end', () => {
try { resolve(JSON.parse(data)); } catch(e) { resolve(data); }
});
});
req.on('error', reject);
req.end();
});
}
function cdpCommand(ws, method, params = {}) {
const id = Math.floor(Math.random() * 1000000);
return new Promise((resolve) => {
const handler = (raw) => {
try {
const msg = JSON.parse(raw.toString());
if (msg.id === id) {
ws.off('message', handler);
resolve(msg.result || msg);
}
} catch {}
};
ws.on('message', handler);
ws.send(JSON.stringify({ id, method, params }));
setTimeout(() => { ws.off('message', handler); resolve({ error: 'timeout' }); }, 10000);
});
}
async function testPage(url, label) {
console.log(`\n=== ${label} ===`);
const newTab = await httpRequest('PUT', '/json/new?about:blank');
const ws = new WebSocket(newTab.webSocketDebuggerUrl);
await new Promise((resolve) => ws.on('open', resolve));
await cdpCommand(ws, 'Runtime.enable');
await cdpCommand(ws, 'Page.enable');
await cdpCommand(ws, 'Log.enable');
const errors = [];
ws.on('message', (raw) => {
try {
const msg = JSON.parse(raw.toString());
if (msg.method === 'Runtime.exceptionThrown') {
const ed = msg.params.exceptionDetails;
errors.push({ text: ed.text || ed.exception?.description || '', stack: ed.stackTrace?.callFrames });
console.log(`${ed.text || ''}`);
}
if (msg.method === 'Log.entryAdded' && msg.params.entry.level === 'error') {
errors.push({ text: '[LOG] ' + msg.params.entry.text });
console.log(` LOG_ERR: ${msg.params.entry.text?.substring(0, 150)}`);
}
} catch {}
});
await cdpCommand(ws, 'Page.navigate', { url });
await new Promise(r => setTimeout(r, 6000));
const e1 = await cdpCommand(ws, 'Runtime.evaluate', {
expression: `(() => { const r = document.getElementById('root'); return r ? (r.innerHTML.trim() ? 'CONTENT(' + r.innerHTML.length + ')' : 'EMPTY') : 'NO_ROOT'; })()`,
returnByValue: true,
});
console.log(' #root:', e1?.result?.value);
console.log(' Errors:', errors.length);
ws.close();
return { rootStatus: e1?.result?.value, errors };
}
async function main() {
// Test dev server (port 5173) - no SW
console.log('Testing VITE DEV SERVER (port 5173)...');
const r1 = await testPage('http://localhost:5173/', 'DEV - No Token (Login page)');
// Test dev server with fake token
console.log('\nTesting VITE DEV with fake token...');
const newTab = await httpRequest('PUT', '/json/new?about:blank');
const ws = new WebSocket(newTab.webSocketDebuggerUrl);
await new Promise((resolve) => ws.on('open', resolve));
await cdpCommand(ws, 'Runtime.enable');
await cdpCommand(ws, 'Page.enable');
await cdpCommand(ws, 'Log.enable');
const errors = [];
ws.on('message', (raw) => {
try {
const msg = JSON.parse(raw.toString());
if (msg.method === 'Runtime.exceptionThrown') {
const ed = msg.params.exceptionDetails;
errors.push(ed.text || ed.exception?.description || '');
console.log(`${ed.text || ''}`);
if (ed.stackTrace?.callFrames) {
ed.stackTrace.callFrames.slice(0, 5).forEach(f => {
console.log(` at ${f.functionName || '(anon)'} (${f.url}:${f.lineNumber}:${f.columnNumber})`);
});
}
}
if (msg.method === 'Log.entryAdded' && (msg.params.entry.level === 'error' || msg.params.entry.level === 'warning')) {
console.log(` LOG [${msg.params.entry.level}]: ${msg.params.entry.text?.substring(0, 150)}`);
}
} catch {}
});
// Navigate first, then set token, then reload
await cdpCommand(ws, 'Page.navigate', { url: 'http://localhost:5173/' });
await new Promise(r => setTimeout(r, 2000));
await cdpCommand(ws, 'Runtime.evaluate', {
expression: `localStorage.setItem('token', 'fake_token_xyz'); localStorage.setItem('user_id', 'admin_test_user'); 'ok'`,
returnByValue: true,
});
console.log('Reloading with token...');
await cdpCommand(ws, 'Page.reload', { ignoreCache: true });
await new Promise(r => setTimeout(r, 8000));
const e2 = await cdpCommand(ws, 'Runtime.evaluate', {
expression: `(() => { const r = document.getElementById('root'); return r ? (r.innerHTML.trim() ? 'CONTENT(' + r.innerHTML.length + ')' : 'EMPTY') : 'NO_ROOT'; })()`,
returnByValue: true,
});
console.log(' DEV+AUTH #root:', e2?.result?.value);
// Check visible text
const e3 = await cdpCommand(ws, 'Runtime.evaluate', {
expression: `document.body ? document.body.innerText.substring(0, 300) : 'NO_BODY'`,
returnByValue: true,
});
console.log(' Text:', e3?.result?.value);
console.log(' Errors:', errors.length);
errors.forEach((e, i) => console.log(` ERR${i+1}: ${typeof e === 'string' ? e.substring(0, 200) : JSON.stringify(e).substring(0, 200)}`));
ws.close();
console.log('\n=== ALL TESTS DONE ===');
}
main().catch(e => { console.error('Fatal:', e.message); process.exit(1); });