Commit 353c90c1 authored by AI-甘富林's avatar AI-甘富林

chore: 更新项目配置并添加测试脚本

- 添加workspace/和backup-cache/目录到.gitignore
- 新增desktop-session-switch-stream-smoke.ps1测试脚本
- 实现会话切换过程中的流式响应测试逻辑
Co-Authored-By: 's avatarClaude Opus 4.7 <noreply@anthropic.com>
parent b78a75ae
......@@ -29,3 +29,8 @@ skills/**/__pycache__/
skills/**/browser_data/
skills/**/*.pyc
skills/**/api_key.txt
# 用户工作空间目录
workspace/
# 备份缓存目录
backup-cache/
param(
[int]$GatewayPort = 18889,
[string]$GatewayToken = 'qjc-bundled-runtime-token',
[int]$SmokePort = 4321,
[string]$SmokeToken = 'smoke-token',
[string]$BaseOutputDir,
[int]$TimeoutSeconds = 180,
[switch]$SkipMaterializeRuntime
)
$ErrorActionPreference = 'Stop'
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
if (-not $BaseOutputDir) {
$BaseOutputDir = Join-Path $repoRoot '.tmp\desktop-session-switch-stream-smoke'
}
$BaseOutputDir = [System.IO.Path]::GetFullPath($BaseOutputDir)
$userDataPath = Join-Path $BaseOutputDir 'user-data'
$logsPath = Join-Path $BaseOutputDir 'logs'
$smokeOutput = Join-Path $BaseOutputDir 'result.json'
$electronSmokeScript = Join-Path $repoRoot 'build\scripts\electron-smoke.ps1'
if (Test-Path $BaseOutputDir) {
Remove-Item $BaseOutputDir -Recurse -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Force -Path $BaseOutputDir, $userDataPath, $logsPath | Out-Null
if (-not $SkipMaterializeRuntime) {
Write-Host "Materializing bundled runtime payload on port $GatewayPort"
powershell -ExecutionPolicy Bypass -File (Join-Path $repoRoot 'build\scripts\materialize-runtime-payload.ps1') -GatewayPort $GatewayPort -GatewayToken $GatewayToken
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
}
$smokeSettingsConfig = [ordered]@{
copywriting = [ordered]@{
baseUrl = "http://127.0.0.1:$SmokePort/openai/v1"
apiKey = 'runtime-provider-token'
modelId = 'gpt-5.4-mini'
}
} | ConvertTo-Json -Depth 6 -Compress
$env:QJCLAW_SMOKE_SKIP_SETTINGS_SAVE = '1'
$env:QJCLAW_SMOKE_SETTINGS_CONFIG_JSON = $smokeSettingsConfig
$env:QJCLAW_SMOKE_SCENARIO = 'session-switch-stream'
powershell -ExecutionPolicy Bypass -File $electronSmokeScript @(
'-SmokeOutput', $smokeOutput,
'-SmokePort', $SmokePort,
'-SmokeToken', $SmokeToken,
'-UserDataPath', $userDataPath,
'-LogsPath', $logsPath,
'-RuntimeMode', 'bundled-runtime',
'-ExpectBundledRuntime',
'-SmokeViewMode', 'chat',
'-SmokePrompt', 'Smoke session switching while assistant is still responding.',
'-TimeoutSeconds', $TimeoutSeconds
)
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
$summaryScript = @"
const fs = require('fs');
const [smokeOutput] = process.argv.slice(1);
const result = JSON.parse(fs.readFileSync(smokeOutput, 'utf8'));
if (!result.ok) {
throw new Error(result.error || 'Desktop session-switch-stream smoke failed.');
}
const sendResult = result.sendResult || {};
const streamSmoke = sendResult.streamSmoke || {};
const scenario = sendResult.scenarioResult || {};
const visibleMessages = Array.isArray(scenario.visibleMessagesAfterReturn) ? scenario.visibleMessagesAfterReturn : [];
const cachedMessages = Array.isArray(scenario.cachedMessagesAfterReturn) ? scenario.cachedMessagesAfterReturn : [];
const userMessage = visibleMessages.find((message) => String(message.role || '') === 'user') || null;
const assistantMessage = [...visibleMessages].reverse().find((message) => String(message.role || '') === 'assistant') || null;
if (String(sendResult.smokeScenario || '') !== 'session-switch-stream') {
throw new Error('Smoke did not report session-switch-stream scenario.');
}
if (String(streamSmoke.phase || '') !== 'completed') {
throw new Error('Stream did not complete: ' + String(streamSmoke.phase || ''));
}
if (Number(streamSmoke.startedEventCount || 0) < 1) {
throw new Error('Stream did not emit a started event.');
}
if (Number(streamSmoke.deltaEventCount || 0) < 1) {
throw new Error('Stream did not emit a delta event.');
}
if (Number(streamSmoke.completedEventCount || 0) < 1) {
throw new Error('Stream did not emit a completed event.');
}
if (Number(streamSmoke.errorEventCount || 0) !== 0) {
throw new Error('Stream emitted unexpected error events: ' + Number(streamSmoke.errorEventCount || 0));
}
if (!String(scenario.streamSessionId || '').startsWith('project:home-chat:')) {
throw new Error('Primary session was not a home-chat session: ' + String(scenario.streamSessionId || ''));
}
if (!String(scenario.alternateSessionId || '').startsWith('project:home-chat:')) {
throw new Error('Alternate session was not a home-chat session: ' + String(scenario.alternateSessionId || ''));
}
if (String(scenario.switchedAwayActiveSessionId || '') !== String(scenario.alternateSessionId || '')) {
throw new Error('Smoke did not remain on the alternate session after switching away.');
}
if (String(scenario.returnedActiveSessionId || '') !== String(scenario.streamSessionId || '')) {
throw new Error('Smoke did not return to the streaming session.');
}
if (['completed', 'fallback', 'error'].includes(String(scenario.phaseBeforeSwitch || ''))) {
throw new Error('Stream was already terminal before switching sessions: ' + String(scenario.phaseBeforeSwitch || ''));
}
if (['completed', 'fallback', 'error'].includes(String(scenario.phaseAtReturn || ''))) {
throw new Error('Stream was already terminal when the smoke returned to the original session: ' + String(scenario.phaseAtReturn || ''));
}
if (Number(scenario.completedEventCountAtReturn || 0) !== 0) {
throw new Error('Stream had already completed by the time the smoke returned to the original session.');
}
if (visibleMessages.length < 2) {
throw new Error('Visible messages after returning were incomplete: ' + visibleMessages.length);
}
if (cachedMessages.length < 2) {
throw new Error('Cached messages after returning were incomplete: ' + cachedMessages.length);
}
if (!userMessage || !String(userMessage.content || '').includes('Smoke session switching while assistant is still responding.')) {
throw new Error('Returned user message was missing or incorrect.');
}
if (!assistantMessage) {
throw new Error('Returned assistant placeholder was missing.');
}
if (!String(sendResult.lastAssistantMessage && sendResult.lastAssistantMessage.content || '').includes('Smoke stream ok:')) {
throw new Error('Final persisted assistant message did not contain the smoke reply.');
}
console.log(JSON.stringify({
ok: true,
smokeOutput,
streamSessionId: scenario.streamSessionId || null,
alternateSessionId: scenario.alternateSessionId || null,
returnedVisibleMessageCount: visibleMessages.length,
returnedCachedMessageCount: cachedMessages.length,
phaseBeforeSwitch: scenario.phaseBeforeSwitch || null,
phaseAtReturn: scenario.phaseAtReturn || null,
streamPhase: streamSmoke.phase || null,
startedEventCount: Number(streamSmoke.startedEventCount || 0),
deltaEventCount: Number(streamSmoke.deltaEventCount || 0),
completedEventCount: Number(streamSmoke.completedEventCount || 0)
}, null, 2));
"@
$summary = & node -e $summaryScript $smokeOutput
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Output $summary
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment