mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-17 16:01:34 +00:00
fix(discord): keep realtime partial transcript tail UTF-16 safe (#104896)
* fix(discord): keep realtime partial transcript tail UTF-16 safe * refactor(discord): isolate realtime transcript merging --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
30
extensions/discord/src/voice/realtime-transcript.test.ts
Normal file
30
extensions/discord/src/voice/realtime-transcript.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { mergeRealtimePartialTranscript } from "./realtime-transcript.js";
|
||||
|
||||
describe("mergeRealtimePartialTranscript", () => {
|
||||
it("returns previous transcript when the next chunk is blank", () => {
|
||||
expect(mergeRealtimePartialTranscript("hello", " ")).toBe("hello");
|
||||
});
|
||||
|
||||
it("replaces with the growing chunk when it extends the previous prefix", () => {
|
||||
expect(mergeRealtimePartialTranscript("hel", "hello world")).toBe("hello world");
|
||||
});
|
||||
|
||||
it("appends when the next chunk is not a continuation of previous", () => {
|
||||
expect(mergeRealtimePartialTranscript("hello", " there")).toBe("hello there");
|
||||
});
|
||||
|
||||
it("does not split a surrogate pair at the tail cap boundary", () => {
|
||||
const tail = "x".repeat(239);
|
||||
const next = `${"y".repeat(50)}🦞${tail}`;
|
||||
|
||||
expect(mergeRealtimePartialTranscript("", next)).toBe(tail);
|
||||
});
|
||||
|
||||
it("keeps an intact surrogate pair that sits just inside the cap", () => {
|
||||
const tail = `🦞${"w".repeat(238)}`;
|
||||
const next = `${"z".repeat(10)}${tail}`;
|
||||
|
||||
expect(mergeRealtimePartialTranscript("", next)).toBe(tail);
|
||||
});
|
||||
});
|
||||
13
extensions/discord/src/voice/realtime-transcript.ts
Normal file
13
extensions/discord/src/voice/realtime-transcript.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// Discord plugin module owns realtime transcript accumulation.
|
||||
import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||
|
||||
const PARTIAL_TRANSCRIPT_MAX_CHARS = 240;
|
||||
|
||||
export function mergeRealtimePartialTranscript(previous: string, next: string): string {
|
||||
const trimmed = next.trim();
|
||||
if (!trimmed) {
|
||||
return previous;
|
||||
}
|
||||
const merged = trimmed.startsWith(previous) ? trimmed : `${previous}${next}`;
|
||||
return sliceUtf16Safe(merged, -PARTIAL_TRANSCRIPT_MAX_CHARS);
|
||||
}
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
} from "./audio.js";
|
||||
import { formatVoiceLogPreview } from "./log-preview.js";
|
||||
import { formatVoiceIngressPrompt } from "./prompt.js";
|
||||
import { mergeRealtimePartialTranscript } from "./realtime-transcript.js";
|
||||
import { loadDiscordVoiceSdk } from "./sdk-runtime.js";
|
||||
import {
|
||||
logVoiceVerbose,
|
||||
@@ -88,7 +89,6 @@ const DISCORD_REALTIME_DUPLICATE_ERROR_SUPPRESS_MS = 60_000;
|
||||
const DISCORD_REALTIME_CONTROL_SPEECH_DEDUPE_MS = 5_000;
|
||||
const DISCORD_REALTIME_OUTPUT_PLAYBACK_WATCHDOG_MARGIN_MS = 1_500;
|
||||
const DISCORD_REALTIME_WAKE_ACKS = ["Yeah.", "Mm-hmm.", "Got it.", "One sec."];
|
||||
const DISCORD_REALTIME_PARTIAL_TRANSCRIPT_MAX_CHARS = 240;
|
||||
const REALTIME_PCM16_BYTES_PER_SAMPLE = 2;
|
||||
const DISCORD_RAW_PCM_FRAME_BYTES = 3_840;
|
||||
const DISCORD_REALTIME_OUTPUT_PREROLL_FRAMES = 25;
|
||||
@@ -314,15 +314,6 @@ function normalizeControlSpeechText(text: string): string {
|
||||
return text.toLowerCase().replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function mergeRealtimePartialTranscript(previous: string, next: string): string {
|
||||
const trimmed = next.trim();
|
||||
if (!trimmed) {
|
||||
return previous;
|
||||
}
|
||||
const merged = trimmed.startsWith(previous) ? trimmed : `${previous}${next}`;
|
||||
return merged.slice(-DISCORD_REALTIME_PARTIAL_TRANSCRIPT_MAX_CHARS);
|
||||
}
|
||||
|
||||
function resolveDiscordRealtimeWakeNames(params: {
|
||||
config: DiscordRealtimeVoiceConfig;
|
||||
cfg: OpenClawConfig;
|
||||
|
||||
Reference in New Issue
Block a user