UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

188 lines (187 loc) • 6.77 kB
import { r as createLazyRuntimeModule } from "./lazy-runtime-CJrzFzqU.mjs"; import { n as createRealtimeVoiceAudioQueue } from "./realtime-session-lifecycle-B-hGG-bm.mjs"; import { t as createLazyRealtimeVoiceBridgeLifecycle } from "./realtime-voice-provider-D_Fp61Jy.mjs"; import { y as serializeXaiRealtimeToolResult } from "./realtime-voice-config-B8a2ekKs.mjs"; import { s as assertXaiRealtimeVoiceRequestSupported } from "./capability-provider-metadata-factory-DKDXqC4Q.mjs"; //#region extensions/xai/realtime-voice-lazy.ts const MAX_LAZY_REALTIME_VOICE_USER_MESSAGES = 128; const MAX_LAZY_REALTIME_VOICE_USER_MESSAGE_BYTES = 262144; const MAX_LAZY_REALTIME_VOICE_TOOL_RESULTS = 128; const MAX_LAZY_REALTIME_VOICE_TOOL_RESULT_BYTES = 262144; const loadXaiRealtimeVoiceProvider = createLazyRuntimeModule(async () => (await import("./extensions/xai/realtime-voice-provider.js")).buildXaiRealtimeVoiceProvider()); function createLazyXaiRealtimeVoiceBridge(req) { assertXaiRealtimeVoiceRequestSupported(req); let acceptsInput = false; let pendingMediaTimestamp; let pendingGreeting; let pendingUserMessageCount = 0; let pendingUserMessageBytes = 0; let pendingToolResultCount = 0; let pendingToolResultBytes = 0; const pendingAudio = createRealtimeVoiceAudioQueue("reject-newest"); const pendingOperations = []; const lifecycle = createLazyRealtimeVoiceBridgeLifecycle({ label: "xAI", request: req, load: async (request) => (await loadXaiRealtimeVoiceProvider()).createBridge(request), clearPending: () => { acceptsInput = false; pendingAudio.clear(); pendingOperations.length = 0; pendingMediaTimestamp = void 0; pendingGreeting = void 0; pendingUserMessageCount = 0; pendingUserMessageBytes = 0; pendingToolResultCount = 0; pendingToolResultBytes = 0; }, onConnected: (bridge, isCurrent) => flushPendingInput(bridge, isCurrent) }); const replacePendingOperation = (previous, next) => { if (previous) { const previousIndex = pendingOperations.indexOf(previous); if (previousIndex >= 0) pendingOperations.splice(previousIndex, 1); } pendingOperations.push(next); return next; }; const flushPendingInput = async (loadedBridge, isCurrent) => { if (!isCurrent()) return; while (true) { if (!isCurrent()) return; const operation = pendingOperations.shift(); if (!operation) { acceptsInput = true; return; } switch (operation.type) { case "audio": { const chunk = pendingAudio.dequeue(); if (!chunk) throw new Error("xAI realtime voice pending audio queue invariant violated"); loadedBridge.sendAudio(chunk); break; } case "media-timestamp": if (pendingMediaTimestamp === operation) pendingMediaTimestamp = void 0; loadedBridge.setMediaTimestamp(operation.timestamp); break; case "user-message": loadedBridge.sendUserMessage?.(operation.text); break; case "tool-result": await loadedBridge.submitToolResult(operation.callId, operation.result, operation.options); break; case "greeting": if (pendingGreeting === operation) pendingGreeting = void 0; loadedBridge.triggerGreeting?.(operation.instructions); } if (!isCurrent()) return; if (operation.type === "user-message") { pendingUserMessageCount -= 1; pendingUserMessageBytes -= operation.bytes; } else if (operation.type === "tool-result") { pendingToolResultCount -= 1; pendingToolResultBytes -= operation.bytes; } } }; return { get supportsToolResultContinuation() { return lifecycle.bridge?.supportsToolResultContinuation ?? false; }, connect: lifecycle.connect, sendAudio: (audio) => { if (!lifecycle.isActive()) return; const bridge = lifecycle.bridge; if (acceptsInput && bridge) { bridge.sendAudio(audio); return; } if (pendingAudio.enqueue(audio)) pendingOperations.push({ type: "audio" }); }, setMediaTimestamp: (timestamp) => { if (!lifecycle.isActive()) return; const bridge = lifecycle.bridge; if (acceptsInput && bridge) { bridge.setMediaTimestamp(timestamp); return; } pendingMediaTimestamp = replacePendingOperation(pendingMediaTimestamp, { timestamp, type: "media-timestamp" }); }, sendUserMessage: (text) => { if (!lifecycle.isActive()) return; const bridge = lifecycle.bridge; if (acceptsInput && bridge) { bridge.sendUserMessage?.(text); return; } const messageBytes = Buffer.byteLength(text, "utf8"); if (pendingUserMessageCount >= MAX_LAZY_REALTIME_VOICE_USER_MESSAGES || pendingUserMessageBytes + messageBytes > MAX_LAZY_REALTIME_VOICE_USER_MESSAGE_BYTES) { req.onError?.(/* @__PURE__ */ new Error("xAI realtime voice pending user message overflow during lazy startup")); return; } pendingOperations.push({ bytes: messageBytes, text, type: "user-message" }); pendingUserMessageCount += 1; pendingUserMessageBytes += messageBytes; }, triggerGreeting: (instructions) => { if (!lifecycle.isActive()) return; const bridge = lifecycle.bridge; if (acceptsInput && bridge) { bridge.triggerGreeting?.(instructions); return; } pendingGreeting = replacePendingOperation(pendingGreeting, { instructions, type: "greeting" }); }, handleBargeIn: (options) => { if (lifecycle.isActive()) lifecycle.bridge?.handleBargeIn?.(options); }, submitToolResult: (callId, result, options) => { if (!lifecycle.isActive() || options?.willContinue === true) return; const bridge = lifecycle.bridge; if (acceptsInput && bridge) return bridge.submitToolResult(callId, result, options); let serialized; try { serialized = serializeXaiRealtimeToolResult(result); } catch (error) { req.onError?.(error); throw error; } const pending = { callId, result: JSON.parse(serialized), ...options ? { options } : {} }; const resultBytes = Buffer.byteLength(JSON.stringify(pending), "utf8"); if (pendingToolResultCount >= MAX_LAZY_REALTIME_VOICE_TOOL_RESULTS || pendingToolResultBytes + resultBytes > MAX_LAZY_REALTIME_VOICE_TOOL_RESULT_BYTES) { const error = /* @__PURE__ */ new Error("xAI realtime voice pending tool result overflow during lazy startup"); req.onError?.(error); throw error; } pendingOperations.push({ ...pending, bytes: resultBytes, type: "tool-result" }); pendingToolResultCount += 1; pendingToolResultBytes += resultBytes; }, acknowledgeMark: (markName) => { if (lifecycle.isActive()) lifecycle.bridge?.acknowledgeMark(markName); }, close: lifecycle.close, isConnected: () => lifecycle.isActive() && (lifecycle.bridge?.isConnected() ?? false) }; } //#endregion export { createLazyXaiRealtimeVoiceBridge as t };