UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

181 lines (180 loc) 8.63 kB
import { b as parseFiniteNumber, c as asFiniteNumberInRange, f as asSafeIntegerInRange } from "./number-coercion-CLj0HTDM.js"; import { a as asOptionalRecord } from "./record-coerce-DItp3I4t.js"; import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { m as normalizeResolvedSecretInputString } from "./types.secrets-kC0nOetj.js"; import "./string-coerce-runtime-GQa0ehRA.js"; import "./secret-input-dpVVFmLG.js"; import { i as normalizeElevenLabsRealtimeBaseUrl } from "./shared-C4zbmF0I.js"; import { r as resolveElevenLabsApiKeyWithProfileFallback } from "./config-compat-D3gex3Bl.js"; import "./config-api-EUFzaTQX.js"; //#region extensions/elevenlabs/realtime-transcription-provider-factory.ts const ELEVENLABS_REALTIME_DEFAULT_MODEL = "scribe_v2_realtime"; const ELEVENLABS_REALTIME_DEFAULT_AUDIO_FORMAT = "ulaw_8000"; const ELEVENLABS_REALTIME_DEFAULT_SAMPLE_RATE = 8e3; const ELEVENLABS_REALTIME_DEFAULT_COMMIT_STRATEGY = "vad"; const ELEVENLABS_REALTIME_CONNECT_TIMEOUT_MS = 1e4; const ELEVENLABS_REALTIME_CLOSE_TIMEOUT_MS = 5e3; const ELEVENLABS_REALTIME_MAX_RECONNECT_ATTEMPTS = 5; const ELEVENLABS_REALTIME_RECONNECT_DELAY_MS = 1e3; const ELEVENLABS_REALTIME_MAX_QUEUED_BYTES = 2097152; function readNestedElevenLabsConfig(rawConfig) { const raw = asOptionalRecord(rawConfig); const providers = asOptionalRecord(raw?.providers); return asOptionalRecord(providers?.elevenlabs ?? raw?.elevenlabs ?? raw) ?? {}; } function normalizeCommitStrategy(value) { const normalized = normalizeOptionalString(value)?.toLowerCase(); if (!normalized) return; if (normalized === "manual" || normalized === "vad") return normalized; throw new Error(`Invalid ElevenLabs realtime transcription commit strategy: ${normalized}`); } function normalizePositiveSafeInteger(value) { const parsed = parseFiniteNumber(value); return asSafeIntegerInRange(parsed, { min: 1 }); } function normalizeFiniteRange(value, min, max) { const parsed = parseFiniteNumber(value); return asFiniteNumberInRange(parsed, { min, max }); } function normalizeIntegerRange(value, min, max) { const parsed = parseFiniteNumber(value); return asSafeIntegerInRange(parsed, { min, max }); } function normalizeProviderConfig(config) { const raw = readNestedElevenLabsConfig(config); return { apiKey: normalizeResolvedSecretInputString({ value: raw.apiKey, path: "plugins.entries.voice-call.config.streaming.providers.elevenlabs.apiKey" }), baseUrl: normalizeOptionalString(raw.baseUrl), modelId: normalizeOptionalString(raw.modelId ?? raw.model ?? raw.sttModel), audioFormat: normalizeOptionalString(raw.audioFormat ?? raw.audio_format ?? raw.encoding), sampleRate: normalizePositiveSafeInteger(raw.sampleRate ?? raw.sample_rate), languageCode: normalizeOptionalString(raw.languageCode ?? raw.language), commitStrategy: normalizeCommitStrategy(raw.commitStrategy ?? raw.commit_strategy), vadSilenceThresholdSecs: normalizeFiniteRange(raw.vadSilenceThresholdSecs ?? raw.vad_silence_threshold_secs, .3, 3), vadThreshold: normalizeFiniteRange(raw.vadThreshold ?? raw.vad_threshold, .1, .9), minSpeechDurationMs: normalizeIntegerRange(raw.minSpeechDurationMs ?? raw.min_speech_duration_ms, 50, 2e3), minSilenceDurationMs: normalizeIntegerRange(raw.minSilenceDurationMs ?? raw.min_silence_duration_ms, 50, 2e3) }; } function toElevenLabsRealtimeWsUrl(config) { const url = new URL(`${normalizeElevenLabsRealtimeBaseUrl(config.baseUrl)}/v1/speech-to-text/realtime`); url.searchParams.set("model_id", config.modelId); url.searchParams.set("audio_format", config.audioFormat); url.searchParams.set("commit_strategy", config.commitStrategy); url.searchParams.set("include_timestamps", "false"); url.searchParams.set("include_language_detection", "false"); if (config.languageCode) url.searchParams.set("language_code", config.languageCode); if (config.vadSilenceThresholdSecs != null) url.searchParams.set("vad_silence_threshold_secs", String(config.vadSilenceThresholdSecs)); if (config.vadThreshold != null) url.searchParams.set("vad_threshold", String(config.vadThreshold)); if (config.minSpeechDurationMs != null) url.searchParams.set("min_speech_duration_ms", String(config.minSpeechDurationMs)); if (config.minSilenceDurationMs != null) url.searchParams.set("min_silence_duration_ms", String(config.minSilenceDurationMs)); return url.toString(); } function readErrorDetail(event) { return normalizeOptionalString(event.error) ?? normalizeOptionalString(event.message) ?? normalizeOptionalString(event.code) ?? "ElevenLabs realtime transcription error"; } function createElevenLabsRealtimeTranscriptionSession(config, createRealtimeTranscriptionWebSocketSession) { let pendingTimestampEcho; const sendAudioChunk = (audio, transport) => { transport.sendJson({ message_type: "input_audio_chunk", audio_base_64: audio.toString("base64"), sample_rate: config.sampleRate, ...config.commitStrategy === "manual" ? { commit: true } : {} }); }; const handleEvent = (event, transport) => { if (event.message_type === "session_started") { pendingTimestampEcho = void 0; transport.markReady(); return; } const isError = typeof event.error === "string" || event.message_type?.includes("error"); if (!transport.isReady() && isError) { transport.failConnect(new Error(readErrorDetail(event))); return; } switch (event.message_type) { case "partial_transcript": if (event.text) config.onPartial?.(event.text); return; case "committed_transcript": case "committed_transcript_with_timestamps": if (event.text) { const hasTimestamps = event.message_type !== "committed_transcript"; const isEcho = hasTimestamps && pendingTimestampEcho === event.text; pendingTimestampEcho = hasTimestamps ? void 0 : event.text; if (!isEcho) config.onTranscript?.(event.text); } return; default: if (isError) config.onError?.(new Error(readErrorDetail(event))); } }; return createRealtimeTranscriptionWebSocketSession({ providerId: "elevenlabs", callbacks: config, url: () => toElevenLabsRealtimeWsUrl(config), headers: { "xi-api-key": config.apiKey }, connectTimeoutMs: ELEVENLABS_REALTIME_CONNECT_TIMEOUT_MS, closeTimeoutMs: ELEVENLABS_REALTIME_CLOSE_TIMEOUT_MS, maxReconnectAttempts: ELEVENLABS_REALTIME_MAX_RECONNECT_ATTEMPTS, reconnectDelayMs: ELEVENLABS_REALTIME_RECONNECT_DELAY_MS, maxQueuedBytes: ELEVENLABS_REALTIME_MAX_QUEUED_BYTES, connectTimeoutMessage: "ElevenLabs realtime transcription connection timeout", reconnectLimitMessage: "ElevenLabs realtime transcription reconnect limit reached", sendAudio: sendAudioChunk, onClose: (transport) => { transport.sendJson({ message_type: "input_audio_chunk", audio_base_64: "", sample_rate: config.sampleRate, commit: true }); }, onMessage: handleEvent }); } function resolveElevenLabsRealtimeApiKey(config) { return config.apiKey ?? resolveElevenLabsApiKeyWithProfileFallback() ?? normalizeOptionalString(process.env.XI_API_KEY); } function buildElevenLabsRealtimeTranscriptionProvider({ createRealtimeTranscriptionWebSocketSession }) { return { id: "elevenlabs", label: "ElevenLabs Realtime Transcription", aliases: ["elevenlabs-realtime", "scribe-v2-realtime"], defaultModel: ELEVENLABS_REALTIME_DEFAULT_MODEL, autoSelectOrder: 40, resolveConfig: ({ rawConfig }) => normalizeProviderConfig(rawConfig), isConfigured: ({ providerConfig }) => Boolean(resolveElevenLabsRealtimeApiKey(normalizeProviderConfig(providerConfig))), createSession: (req) => { const config = normalizeProviderConfig(req.providerConfig); const apiKey = resolveElevenLabsRealtimeApiKey(config); if (!apiKey) throw new Error("ElevenLabs API key missing"); return createElevenLabsRealtimeTranscriptionSession({ ...req, apiKey, baseUrl: normalizeElevenLabsRealtimeBaseUrl(config.baseUrl), modelId: config.modelId ?? ELEVENLABS_REALTIME_DEFAULT_MODEL, audioFormat: config.audioFormat ?? ELEVENLABS_REALTIME_DEFAULT_AUDIO_FORMAT, sampleRate: config.sampleRate ?? ELEVENLABS_REALTIME_DEFAULT_SAMPLE_RATE, commitStrategy: config.commitStrategy ?? ELEVENLABS_REALTIME_DEFAULT_COMMIT_STRATEGY, languageCode: config.languageCode, vadSilenceThresholdSecs: config.vadSilenceThresholdSecs, vadThreshold: config.vadThreshold, minSpeechDurationMs: config.minSpeechDurationMs, minSilenceDurationMs: config.minSilenceDurationMs }, createRealtimeTranscriptionWebSocketSession); } }; } //#endregion export { buildElevenLabsRealtimeTranscriptionProvider as t };