feat: show per-stage timing (듣기/LLM/TTS) in the transcript channel
The transcript channel only showed STT and LLM seconds. Add wall-clock start/end times and durations for listening, LLM and TTS so it's obvious what takes long; STT surfaces as the gap between listening end and LLM start. - bridge: emit llm_start_ms/llm_end_ms on meta and tts_*_ms on the end event - bot: capture the listening window, assemble full timing after the stream, and render a per-stage breakdown in the transcript message Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
70
bot/src/userbot.test.ts
Normal file
70
bot/src/userbot.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
// userbot.ts imports the runtime `config`, which requires DISCORD_GUILD_ID.
|
||||
process.env.DISCORD_GUILD_ID ||= "test-guild";
|
||||
const { formatTurnMessage } = await import("./userbot.ts");
|
||||
|
||||
test("formatTurnMessage shows a per-stage timing breakdown with durations", () => {
|
||||
const msg = formatTurnMessage({
|
||||
transcript: "안녕하세요",
|
||||
reply: "네, 안녕하세요",
|
||||
listenStartMs: 10_000,
|
||||
listenEndMs: 12_000, // 듣기 2.0s
|
||||
llmStartMs: 12_500, // STT/전송 gap 0.5s
|
||||
llmEndMs: 14_100, // LLM 1.6s
|
||||
ttsStartMs: 14_100,
|
||||
ttsEndMs: 15_000, // TTS 0.9s
|
||||
});
|
||||
|
||||
expect(msg).toContain('🗣️ "안녕하세요"');
|
||||
expect(msg).toContain("🤖 답변: 네, 안녕하세요");
|
||||
expect(msg).toContain("👂 듣기 2.0초");
|
||||
expect(msg).toContain("🧠 LLM 1.6초");
|
||||
expect(msg).toContain("(STT/전송 0.5초)");
|
||||
expect(msg).toContain("🔊 TTS 0.9초");
|
||||
// Total spans listening start -> TTS end.
|
||||
expect(msg).toContain("합계 5.0초");
|
||||
});
|
||||
|
||||
test("formatTurnMessage omits the STT gap note when it is negligible", () => {
|
||||
const msg = formatTurnMessage({
|
||||
transcript: "테스트",
|
||||
reply: "응답",
|
||||
listenStartMs: 0,
|
||||
listenEndMs: 1000,
|
||||
llmStartMs: 1000, // no gap
|
||||
llmEndMs: 2000,
|
||||
});
|
||||
expect(msg).not.toContain("STT/전송");
|
||||
expect(msg).toContain("🧠 LLM 1.0초");
|
||||
});
|
||||
|
||||
test("formatTurnMessage reports a dropped turn with only the listening stage", () => {
|
||||
const msg = formatTurnMessage({
|
||||
transcript: "",
|
||||
reply: "",
|
||||
note: "너무 짧음(<300ms)",
|
||||
listenStartMs: 0,
|
||||
listenEndMs: 200,
|
||||
});
|
||||
expect(msg).toContain("❌ 너무 짧음(<300ms)");
|
||||
expect(msg).toContain("👂 듣기 0.2초");
|
||||
expect(msg).not.toContain("🧠 LLM");
|
||||
expect(msg).not.toContain("🔊 TTS");
|
||||
});
|
||||
|
||||
test("formatTurnMessage falls back to the plain line when no timing is present", () => {
|
||||
const msg = formatTurnMessage({ transcript: "안녕", reply: "하이" });
|
||||
expect(msg).toBe('🎤 들음 → 🗣️ "안녕"\n🤖 답변: 하이');
|
||||
expect(msg).not.toContain("⏱️");
|
||||
});
|
||||
|
||||
test("formatTurnMessage drops out-of-order (negative) spans instead of showing junk", () => {
|
||||
const msg = formatTurnMessage({
|
||||
transcript: "안녕",
|
||||
reply: "하이",
|
||||
listenStartMs: 5000,
|
||||
listenEndMs: 4000, // negative span -> omitted
|
||||
});
|
||||
expect(msg).not.toContain("👂 듣기");
|
||||
});
|
||||
Reference in New Issue
Block a user