feat: 后端代理层(Jev 透传 + ASR 预留端点),前端支持代理模式

This commit is contained in:
Jayway Liu
2026-10-03 12:55:00 +08:00
parent bed4d734ca
commit 6117ef894f
15 changed files with 740 additions and 11 deletions
+76
View File
@@ -0,0 +1,76 @@
/**
* 代理冒烟(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<string, { type: string; score?: number; confidence?: number }>;
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);
}