Commit c52200fe by luoqi

perf(assistant): 听写提速三处 + 底部提示简化(固定文案)

服务器实测体感"启动慢"对症:
1. ASR 容器启动即预热(0.5s 静音跑一遍)— onnxruntime 首推建图/分配比稳态慢很多,
   之前重启后用户第一句要付这笔冷启动。
2. 网关首句快路径:攒够 ~0.6s 音频立刻出第一个 partial(不等节奏),开口更快上屏。
3. partial 节奏 700→450ms、tick 250→150ms(inFlight 防重叠不变,服务器解码 ~0.3s 扛得住)。

底部提示按用户简化为固定"结果仅供参考"(去掉听写状态行;修正引号原样渲染)。两端 tsc 0。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 1a8496a4
...@@ -25,6 +25,13 @@ recognizer = sherpa_onnx.OfflineRecognizer.from_sense_voice( ...@@ -25,6 +25,13 @@ recognizer = sherpa_onnx.OfflineRecognizer.from_sense_voice(
num_threads=NUM_THREADS, num_threads=NUM_THREADS,
) )
# 预热:onnxruntime 首次推理要建图/分配,比稳态慢很多 → 启动时拿 0.5s 静音跑一遍,
# 用户的第一句不再付这笔"冷启动"(2026-06-10 服务器实测体感:首句明显慢)。
_warm = recognizer.create_stream()
_warm.accept_waveform(16000, np.zeros(8000, dtype=np.float32))
recognizer.decode_stream(_warm)
del _warm
app = FastAPI() app = FastAPI()
......
...@@ -30,7 +30,7 @@ interface DictSession { ...@@ -30,7 +30,7 @@ interface DictSession {
timer: NodeJS.Timeout; timer: NodeJS.Timeout;
} }
const PARTIAL_EVERY_MS = 700; const PARTIAL_EVERY_MS = 450;
const FINALIZE_GAP_MS = 800; const FINALIZE_GAP_MS = 800;
const MAX_UTTERANCE_BYTES = 30 * 16_000 * 2; // 30s @16k PCM16,防失控 const MAX_UTTERANCE_BYTES = 30 * 16_000 * 2; // 30s @16k PCM16,防失控
...@@ -63,7 +63,7 @@ export class DictationGateway implements OnGatewayConnection, OnGatewayDisconnec ...@@ -63,7 +63,7 @@ export class DictationGateway implements OnGatewayConnection, OnGatewayDisconnec
lastDecodedBytes: 0, lastDecodedBytes: 0,
lastPartialAt: 0, lastPartialAt: 0,
inFlight: false, inFlight: false,
timer: setInterval(() => void this.tick(client), 250), timer: setInterval(() => void this.tick(client), 150),
}; };
this.sessions.set(client.id, session); this.sessions.set(client.id, session);
} }
...@@ -93,8 +93,12 @@ export class DictationGateway implements OnGatewayConnection, OnGatewayDisconnec ...@@ -93,8 +93,12 @@ export class DictationGateway implements OnGatewayConnection, OnGatewayDisconnec
const gap = now - s.lastFrameAt; const gap = now - s.lastFrameAt;
const shouldFinal = gap >= FINALIZE_GAP_MS || s.bytes >= MAX_UTTERANCE_BYTES; const shouldFinal = gap >= FINALIZE_GAP_MS || s.bytes >= MAX_UTTERANCE_BYTES;
// 首句快路径:攒够 ~0.6s 音频立即出第一个 partial(不等 PARTIAL_EVERY_MS 节奏)→ 开口更快上屏
const firstReady = s.lastDecodedBytes === 0 && s.bytes >= 0.6 * 16_000 * 2;
const shouldPartial = const shouldPartial =
!shouldFinal && now - s.lastPartialAt >= PARTIAL_EVERY_MS && s.bytes > s.lastDecodedBytes; !shouldFinal &&
s.bytes > s.lastDecodedBytes &&
(firstReady || now - s.lastPartialAt >= PARTIAL_EVERY_MS);
if (!shouldFinal && !shouldPartial) return; if (!shouldFinal && !shouldPartial) return;
const pcm = Buffer.concat(s.chunks, s.bytes); const pcm = Buffer.concat(s.chunks, s.bytes);
......
...@@ -663,13 +663,7 @@ export function AssistantChat() { ...@@ -663,13 +663,7 @@ export function AssistantChat() {
)} )}
</div> </div>
<p className="mt-1.5 text-center text-[10.5px] text-slate-400"> <p className="mt-1.5 text-center text-[10.5px] text-slate-400">
{voiceErr ? ( 结果仅供参考 请核对后使用
<span className="text-rose-500">{voiceErr}</span>
) : voice === 'live' ? (
<span className="text-rose-500">● 听写中,边说边出字… 再点麦克风退出</span>
) : (
'结果仅供参考 请核对后使用'
)}
</p> </p>
</div> </div>
</div> </div>
......
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