/** * 代理冒烟(pnpm --dir server smoke): * 通过本地代理调一次真实 Jev(1 个 score 问题,控制 token), * 再用一段程序生成的 WAV 测 /api/asr(Mock provider)。 * key 只存在于服务端,本脚本不接触、不打印 key。 * 先启动服务:pnpm --dir server dev */ const BASE = process.env.PROXY_URL ?? 'http://localhost:8787'; // --- 1. /api/jev:1 个 score 问题 --- const jevRes = await fetch(`${BASE}/api/jev`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'jev-latest', state: '用户在互动剧情中选择「直接进城,赌一把」,并向同伴坦言「我们是去记录的,不是去拿的」。', questions: { risk: { type: 'score', instructions: '根据用户行为评估其「风险偏好」维度水平', criteria: ['非常保守', '较为保守', '中等', '较为冒险', '非常冒险'], }, }, }), }); console.log(`[1] POST /api/jev → ${jevRes.status}`); const jevBody = (await jevRes.json()) as { answers?: Record; usage?: { input_tokens: number; output_tokens: number }; }; if (!jevRes.ok) { console.error(' 上游错误:', JSON.stringify(jevBody)); process.exit(1); } const risk = jevBody.answers?.risk; console.log(` risk: score=${risk?.score} confidence=${risk?.confidence}`); console.log(` usage: input=${jevBody.usage?.input_tokens} output=${jevBody.usage?.output_tokens} tokens`); // --- 2. /api/asr:程序生成的 0.5s 静音 WAV,走 Mock provider --- const wav = makeSilentWav(0.5); const form = new FormData(); form.append('audio', new Blob([wav], { type: 'audio/wav' }), 'smoke.wav'); const asrRes = await fetch(`${BASE}/api/asr`, { method: 'POST', body: form }); console.log(`[2] POST /api/asr → ${asrRes.status}`); console.log(' ', JSON.stringify(await asrRes.json())); // --- 3. CORS 预检:模拟 Tauri 开发端口来源 --- const preflight = await fetch(`${BASE}/api/jev`, { method: 'OPTIONS', headers: { Origin: 'http://localhost:1420', 'Access-Control-Request-Method': 'POST' }, }); console.log(`[3] OPTIONS /api/jev (Origin: localhost:1420) → ${preflight.status}, allow-origin: ${preflight.headers.get('access-control-allow-origin')}`); console.log('\n冒烟完成'); /** 生成一段 16-bit PCM 单声道静音 WAV(44 字节头 + 数据) */ function makeSilentWav(seconds: number): Uint8Array { const rate = 16000; const n = Math.floor(rate * seconds); const buf = new ArrayBuffer(44 + n * 2); const v = new DataView(buf); const writeStr = (off: number, s: string) => [...s].forEach((ch, i) => v.setUint8(off + i, ch.charCodeAt(0))); writeStr(0, 'RIFF'); v.setUint32(4, 36 + n * 2, true); writeStr(8, 'WAVEfmt '); v.setUint32(16, 16, true); v.setUint16(20, 1, true); // PCM v.setUint16(22, 1, true); // mono v.setUint32(24, rate, true); v.setUint32(28, rate * 2, true); v.setUint16(32, 2, true); v.setUint16(34, 16, true); writeStr(36, 'data'); v.setUint32(40, n * 2, true); return new Uint8Array(buf); }