File size: 2,952 Bytes
253e783 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | // Transcript echo delivery sends best-effort preflight audio transcripts back
// through deliverable message channels.
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import type { MsgContext } from "../auto-reply/templating.js";
import type { OpenClawConfig } from "../config/types.js";
import { logVerbose, shouldLogVerbose } from "../globals.js";
import { createLazyRuntimeModule } from "../shared/lazy-runtime.js";
import { isDeliverableMessageChannel } from "../utils/message-channel.js";
// The message runtime is heavy and only needed when echo delivery actually
// proceeds to a deliverable channel.
const loadMessageRuntime = createLazyRuntimeModule(() => import("../channels/message/runtime.js"));
/** Default operator-visible transcript echo format for preflight audio transcription. */
export const DEFAULT_ECHO_TRANSCRIPT_FORMAT = '📝 "{transcript}"';
function formatEchoTranscript(transcript: string, format: string): string {
// Function replacer keeps `$` sequences in the transcript literal instead of
// being parsed as String.prototype.replace substitution patterns.
return format.replace("{transcript}", () => transcript);
}
/** Sends a best-effort transcript echo back to the originating deliverable chat. */
export async function sendTranscriptEcho(params: {
ctx: MsgContext;
cfg: OpenClawConfig;
transcript: string;
format?: string;
logSuccess?: boolean;
failureLogPrefix?: string;
}): Promise<void> {
const { ctx, cfg, transcript } = params;
const channel = ctx.Provider ?? ctx.Surface ?? "";
const to = ctx.OriginatingTo ?? ctx.From ?? "";
if (!channel || !to) {
if (shouldLogVerbose()) {
logVerbose("media: echo-transcript skipped (no channel/to resolved from ctx)");
}
return;
}
const normalizedChannel = normalizeLowercaseStringOrEmpty(channel);
if (!isDeliverableMessageChannel(normalizedChannel)) {
if (shouldLogVerbose()) {
logVerbose(
`media: echo-transcript skipped (channel "${normalizedChannel}" is not deliverable)`,
);
}
return;
}
const text = formatEchoTranscript(transcript, params.format ?? DEFAULT_ECHO_TRANSCRIPT_FORMAT);
try {
const { sendDurableMessageBatchCore } = await loadMessageRuntime();
const send = await sendDurableMessageBatchCore({
cfg,
channel: normalizedChannel,
to,
accountId: ctx.AccountId ?? undefined,
threadId: ctx.MessageThreadId ?? undefined,
payloads: [{ text }],
bestEffort: true,
durability: "best_effort",
});
if (send.status === "failed") {
throw send.error;
}
if ((params.logSuccess ?? true) && shouldLogVerbose()) {
logVerbose(`media: echo-transcript sent to ${normalizedChannel}/${to}`);
}
} catch (err) {
const prefix = params.failureLogPrefix ?? "media: echo-transcript delivery failed";
logVerbose(`${prefix}: ${String(err)}`);
}
}
|