mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-29 04:43:33 +00:00
refactor(discord): share voice log preview formatting
This commit is contained in:
13
extensions/discord/src/voice/log-preview.test.ts
Normal file
13
extensions/discord/src/voice/log-preview.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { formatVoiceLogPreview } from "./log-preview.js";
|
||||
|
||||
describe("formatVoiceLogPreview", () => {
|
||||
it("collapses whitespace and trims the preview", () => {
|
||||
expect(formatVoiceLogPreview(" hello \n world\t")).toBe("hello world");
|
||||
});
|
||||
|
||||
it("truncates long previews after 500 characters", () => {
|
||||
const preview = formatVoiceLogPreview("x".repeat(501));
|
||||
expect(preview).toBe(`${"x".repeat(500)}...`);
|
||||
});
|
||||
});
|
||||
9
extensions/discord/src/voice/log-preview.ts
Normal file
9
extensions/discord/src/voice/log-preview.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
const DISCORD_VOICE_LOG_PREVIEW_CHARS = 500;
|
||||
|
||||
export function formatVoiceLogPreview(text: string): string {
|
||||
const oneLine = text.replace(/\s+/g, " ").trim();
|
||||
if (oneLine.length <= DISCORD_VOICE_LOG_PREVIEW_CHARS) {
|
||||
return oneLine;
|
||||
}
|
||||
return `${oneLine.slice(0, DISCORD_VOICE_LOG_PREVIEW_CHARS)}...`;
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
resolveDiscordVoiceIngressContext,
|
||||
runDiscordVoiceAgentTurn,
|
||||
} from "./ingress.js";
|
||||
import { formatVoiceLogPreview } from "./log-preview.js";
|
||||
import {
|
||||
DiscordRealtimeVoiceSession,
|
||||
type DiscordVoiceMode,
|
||||
@@ -67,7 +68,6 @@ import {
|
||||
import { DiscordVoiceSpeakerContextResolver } from "./speaker-context.js";
|
||||
|
||||
const logger = createSubsystemLogger("discord/voice");
|
||||
const VOICE_LOG_PREVIEW_CHARS = 500;
|
||||
const FOLLOW_USERS_RECONCILE_INTERVAL_MS = 10_000;
|
||||
const FOLLOW_USERS_RECONCILE_MAX_GUILDS_PER_RUN = 4;
|
||||
const FOLLOW_USERS_RECONCILE_MAX_REST_LOOKUPS_PER_RUN = 32;
|
||||
@@ -96,14 +96,6 @@ type VoiceChannelResidency = {
|
||||
channelId: string;
|
||||
};
|
||||
|
||||
function formatVoiceLogPreview(text: string): string {
|
||||
const oneLine = text.replace(/\s+/g, " ").trim();
|
||||
if (oneLine.length <= VOICE_LOG_PREVIEW_CHARS) {
|
||||
return oneLine;
|
||||
}
|
||||
return `${oneLine.slice(0, VOICE_LOG_PREVIEW_CHARS)}...`;
|
||||
}
|
||||
|
||||
function isVoiceConnectionDestroyed(
|
||||
connection: DiscordVoiceConnection,
|
||||
voiceSdk: DiscordVoiceSdk,
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
convertDiscordPcm48kStereoToRealtimePcm24kMono,
|
||||
convertRealtimePcm24kMonoToDiscordPcm48kStereo,
|
||||
} from "./audio.js";
|
||||
import { formatVoiceLogPreview } from "./log-preview.js";
|
||||
import { formatVoiceIngressPrompt } from "./prompt.js";
|
||||
import { loadDiscordVoiceSdk } from "./sdk-runtime.js";
|
||||
import {
|
||||
@@ -81,7 +82,6 @@ const DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_LIMIT = 16;
|
||||
const DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_TTL_MS = 15_000;
|
||||
const DISCORD_REALTIME_IGNORED_WAKE_NAME_CONTEXT_TTL_MS = 10_000;
|
||||
const DISCORD_REALTIME_WAKE_NAME_FOLLOWUP_TTL_MS = 10_000;
|
||||
const DISCORD_REALTIME_LOG_PREVIEW_CHARS = 500;
|
||||
const DISCORD_REALTIME_DEFAULT_MIN_BARGE_IN_AUDIO_END_MS = 250;
|
||||
const DISCORD_REALTIME_FORCED_CONSULT_FALLBACK_DELAY_MS = 200;
|
||||
const DISCORD_REALTIME_DUPLICATE_ERROR_SUPPRESS_MS = 60_000;
|
||||
@@ -139,14 +139,6 @@ type AgentProxyConsultState = {
|
||||
|
||||
type AgentProxyConsultHandle = RealtimeVoiceForcedConsultHandle<AgentProxyConsultState>;
|
||||
|
||||
function formatRealtimeLogPreview(text: string): string {
|
||||
const oneLine = text.replace(/\s+/g, " ").trim();
|
||||
if (oneLine.length <= DISCORD_REALTIME_LOG_PREVIEW_CHARS) {
|
||||
return oneLine;
|
||||
}
|
||||
return `${oneLine.slice(0, DISCORD_REALTIME_LOG_PREVIEW_CHARS)}...`;
|
||||
}
|
||||
|
||||
function formatRealtimeInterruptionLog(event: RealtimeVoiceBridgeEvent): string | undefined {
|
||||
const detail = event.detail ? ` ${event.detail}` : "";
|
||||
if (event.direction === "client") {
|
||||
@@ -522,7 +514,7 @@ export class DiscordRealtimeVoiceSession implements VoiceRealtimeSession {
|
||||
onTranscript: (role, text, isFinal) => {
|
||||
if (isFinal && text.trim()) {
|
||||
logger.info(
|
||||
`discord voice: realtime ${role} transcript (${text.length} chars): ${formatRealtimeLogPreview(text)}`,
|
||||
`discord voice: realtime ${role} transcript (${text.length} chars): ${formatVoiceLogPreview(text)}`,
|
||||
);
|
||||
}
|
||||
if (isFinal && role === "assistant") {
|
||||
@@ -1420,7 +1412,7 @@ export class DiscordRealtimeVoiceSession implements VoiceRealtimeSession {
|
||||
if (skipReason) {
|
||||
const context = this.consumePendingSpeakerContext();
|
||||
logger.info(
|
||||
`discord voice: realtime forced agent consult skipped reason=${skipReason} chars=${question.length} speaker=${context?.speakerLabel ?? "unknown"} transcript=${formatRealtimeLogPreview(question)}`,
|
||||
`discord voice: realtime forced agent consult skipped reason=${skipReason} chars=${question.length} speaker=${context?.speakerLabel ?? "unknown"} transcript=${formatVoiceLogPreview(question)}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { maybeControlDiscordVoiceAgentRun } from "./agent-control.js";
|
||||
import { createDiscordOpusPlaybackStream } from "./audio.js";
|
||||
import { resolveDiscordVoiceIngressContext, runDiscordVoiceAgentTurn } from "./ingress.js";
|
||||
import { formatVoiceLogPreview } from "./log-preview.js";
|
||||
import { formatVoiceIngressPrompt } from "./prompt.js";
|
||||
import { loadDiscordVoiceSdk } from "./sdk-runtime.js";
|
||||
import {
|
||||
@@ -19,17 +20,8 @@ import {
|
||||
import type { DiscordVoiceSpeakerContextResolver } from "./speaker-context.js";
|
||||
import { synthesizeVoiceReplyAudio, transcribeVoiceAudio } from "./tts.js";
|
||||
|
||||
const VOICE_TRANSCRIPT_LOG_PREVIEW_CHARS = 500;
|
||||
const logger = createSubsystemLogger("discord/voice");
|
||||
|
||||
function formatVoiceTranscriptLogPreview(text: string): string {
|
||||
const oneLine = text.replace(/\s+/g, " ").trim();
|
||||
if (oneLine.length <= VOICE_TRANSCRIPT_LOG_PREVIEW_CHARS) {
|
||||
return oneLine;
|
||||
}
|
||||
return `${oneLine.slice(0, VOICE_TRANSCRIPT_LOG_PREVIEW_CHARS)}...`;
|
||||
}
|
||||
|
||||
export async function processDiscordVoiceSegment(params: {
|
||||
entry: VoiceSessionEntry;
|
||||
wavPath: string;
|
||||
@@ -78,7 +70,7 @@ export async function processDiscordVoiceSegment(params: {
|
||||
`transcription ok (${transcript.length} chars): guild ${entry.guildId} channel ${entry.channelId}`,
|
||||
);
|
||||
logVoiceVerbose(
|
||||
`transcript from ${ingress.speakerLabel} (${userId}) in guild ${entry.guildId} channel ${entry.channelId}: ${formatVoiceTranscriptLogPreview(transcript)}`,
|
||||
`transcript from ${ingress.speakerLabel} (${userId}) in guild ${entry.guildId} channel ${entry.channelId}: ${formatVoiceLogPreview(transcript)}`,
|
||||
);
|
||||
if (params.transcripts) {
|
||||
await params.transcripts.onUtterance({
|
||||
|
||||
Reference in New Issue
Block a user