UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

212 lines (211 loc) 7.93 kB
import { i as extractErrorCode, l as toErrorObject, s as readErrorName } from "./error-coercion-D_-xJ90S.js"; import { n as rawDataToString } from "./ws-C3ckvj65.js"; import { o as buildRealtimeVoiceAgentControlSpeechMessage } from "./agent-run-control-shared-zVaKVJuy.js"; import "./realtime-voice-provider-CoGArOTN.js"; import { r as buildOpenAIQuicksilverDelegationPrompt } from "./realtime-quicksilver-instructions-kTl3roXd.js"; import { t as parseOpenAIQuicksilverEvent } from "./realtime-quicksilver-events-CbzMdwSI.js"; import { n as boundOpenAIQuicksilverDelegationResult, s as chunkOpenAIQuicksilverAppendText, t as boundOpenAIQuicksilverContextItems } from "./realtime-quicksilver-wire-0W0-YPkD.js"; //#region extensions/openai/realtime-quicksilver-delegation-controller.ts const WEBSOCKET_OPEN = 1; const CONSULT_FAILURE_TEXT = "The agent task failed. Tell the user it did not complete and offer to try again."; function shortFailureReason(error, formatErrorMessage) { return formatErrorMessage(error).replaceAll(/\s+/g, " ").trim().slice(0, 180) || "unknown error"; } function readWireEventType(payload) { try { const decoded = JSON.parse(payload); return typeof decoded.type === "string" ? decoded.type : void 0; } catch { return; } } /** Owns the provider's single active delegation and its once-consumed transcript context. */ var OpenAIQuicksilverDelegationController = class { constructor(options, formatErrorMessage) { this.options = options; this.formatErrorMessage = formatErrorMessage; this.onSessionAbort = () => { const reason = this.options.signal.reason; this.stop(reason instanceof Error ? reason : /* @__PURE__ */ new Error("GPT-Live session stopped")); }; this.stopped = false; this.transcript = []; if (options.signal.aborted) this.onSessionAbort(); else options.signal.addEventListener("abort", this.onSessionAbort, { once: true }); } handleFrame(data, isBinary) { if (this.stopped) return; if (isBinary) { this.fail(/* @__PURE__ */ new Error("OpenAI GPT-Live sideband returned an unexpected binary frame")); return; } const payload = rawDataToString(data); if (this.options.onWireEventType) { const eventType = readWireEventType(payload); if (eventType) this.options.onWireEventType(eventType); } const event = parseOpenAIQuicksilverEvent(payload); if (event) this.handleEvent(event); } handleEvent(event) { if (this.stopped || event.kind === "ignored") return; if (event.kind === "unknown") { this.options.logger.debug?.(`OpenAI GPT-Live ignored sideband event: ${event.eventType}`); return; } if (event.kind === "session-started") { this.options.onSessionStarted?.(event.expiresAt); return; } if (event.kind === "transcript-delta" || event.kind === "transcript-done") { this.appendTranscript(event); this.options.onTranscript?.(event.role, event.text, event.kind === "transcript-done"); return; } if (event.kind === "error") { const error = /* @__PURE__ */ new Error(`OpenAI GPT-Live sideband error: ${event.message}`); this.options.logger.warn(error.message); if (event.fatalAuth) this.options.onFatalError(error); else this.options.onError?.(error); return; } if (event.kind === "audio") return; this.startDelegation(event.id, event.prompt); } sendSessionContext(text, channel) { const content = text.trim(); if (content) this.sendAppend({ type: "session.context.append" }, content, channel); } stop(reason) { if (this.stopped) return; this.markStopped(); this.consultController?.abort(reason); this.consultController = void 0; } /** Releases sideband ownership without canceling work already accepted by the host. */ detach() { if (this.stopped) return; this.markStopped(); } appendTranscript(event) { const last = this.transcript.at(-1); if (event.kind === "transcript-delta") { if (last?.role === event.role && this.partialTranscriptRole === event.role) last.text += event.text; else this.transcript.push({ role: event.role, text: event.text }); this.partialTranscriptRole = event.role; } else { if (last?.role === event.role && this.partialTranscriptRole === event.role) last.text = event.text; else this.transcript.push({ role: event.role, text: event.text }); this.partialTranscriptRole = void 0; } this.transcript = boundOpenAIQuicksilverContextItems(this.transcript); } startDelegation(id, input) { if (this.stopped || this.options.signal.aborted || !input.trim()) return; const handleInput = this.options.handleDelegationInput; if (handleInput) { const socket = this.options.getSocket(); let responded = false; const respond = (message) => { if (responded) return; responded = true; if (!socket || socket !== this.options.getSocket()) return; try { this.sendAppend({ type: "delegation.context.append", delegation_item_id: id }, message, "speakable", socket); } catch (error) { this.fail(toErrorObject(error, "OpenAI GPT-Live control response failed")); } }; try { if (handleInput(input, respond) === "control") return; } catch (error) { this.fail(toErrorObject(error, "OpenAI GPT-Live control admission failed")); return; } } const transcript = this.transcript; this.transcript = []; this.partialTranscriptRole = void 0; const delegation = { id, prompt: buildOpenAIQuicksilverDelegationPrompt({ input, transcript }) }; if (this.consultController) { this.pendingDelegation = delegation; this.consultController.abort(/* @__PURE__ */ new Error("GPT-Live delegation superseded")); return; } this.launchDelegation(delegation); } launchDelegation(delegation) { if (this.stopped || this.options.signal.aborted) return; const controller = new AbortController(); this.consultController = controller; this.runDelegation(delegation, controller.signal).catch((error) => this.fail(toErrorObject(error, "OpenAI GPT-Live delegation failed"))).finally(() => { if (this.consultController !== controller) return; this.consultController = void 0; const pending = this.pendingDelegation; this.pendingDelegation = void 0; if (pending) this.launchDelegation(pending); }); } markStopped() { this.stopped = true; this.options.signal.removeEventListener("abort", this.onSessionAbort); this.pendingDelegation = void 0; this.partialTranscriptRole = void 0; this.transcript = []; } async runDelegation(delegation, signal) { let text; try { if (this.options.handleDelegationInput) this.sendSessionContext(buildRealtimeVoiceAgentControlSpeechMessage("I’ll check that request."), "speakable"); const result = await this.options.runAgentConsult({ prompt: delegation.prompt, signal }); if (signal.aborted) return; text = boundOpenAIQuicksilverDelegationResult(result.text); } catch (error) { if (signal.aborted || readErrorName(error) === "AbortError" || extractErrorCode(error) === "ABORT_ERR") return; this.options.logger.warn(`OpenAI GPT-Live delegation consult failed: ${shortFailureReason(error, this.formatErrorMessage)}`); text = CONSULT_FAILURE_TEXT; } this.sendAppend({ type: "delegation.context.append", delegation_item_id: delegation.id }, text, "speakable"); } sendAppend(target, text, channel, socket = this.options.getSocket()) { for (const chunk of chunkOpenAIQuicksilverAppendText(text)) { if (this.stopped || this.options.signal.aborted || !socket || socket !== this.options.getSocket() || socket.readyState !== WEBSOCKET_OPEN) return; socket.send(JSON.stringify({ ...target, channel, content: [{ type: "input_text", text: chunk }] })); } } fail(error) { if (this.stopped) return; this.options.logger.warn(error.message); this.options.onFatalError(error); } }; //#endregion export { OpenAIQuicksilverDelegationController as t };