UNPKG

agents

Version:

A home for your AI agents

122 lines (121 loc) 3.69 kB
import { t as isChannelMessageSurface } from "../surface-bZZJqBka.js"; import "../voice/types.js"; //#region src/channels/adapters/voice.ts const DEFAULT_SAMPLE_RATE = 16e3; function validSurface(surface) { if (!isChannelMessageSurface(surface) || surface.version !== 1 || surface.address === null || typeof surface.address !== "object" || Array.isArray(surface.address)) return false; return surface.address.connection === "current"; } function unavailable() { return { status: "failed", retryable: true, error: { code: "BROWSER_VOICE_UNAVAILABLE", message: "No browser voice surface is connected" } }; } function synthesisFailure(error) { return { status: "failed", retryable: true, error: { code: "BROWSER_VOICE_TTS_FAILED", message: error instanceof Error ? error.message : "The text-to-speech provider did not return audio" } }; } function deliveryFailure(error, contentDeliveryStarted) { if (!contentDeliveryStarted) { const connectionClosed = error instanceof TypeError && error.message.includes("WebSocket send() after close"); return { status: "failed", retryable: true, error: { code: connectionClosed ? "BROWSER_VOICE_CONNECTION_CLOSED" : "BROWSER_VOICE_DELIVERY_FAILED", message: connectionClosed ? "The browser voice surface disconnected before delivery" : error instanceof Error ? error.message : "Browser voice delivery failed before sending content" } }; } return { status: "uncertain", error: { code: "BROWSER_VOICE_DELIVERY_ERROR", message: error instanceof Error ? error.message : "Browser voice delivery failed with an unknown outcome" } }; } function sendJSON(connection, message) { connection.send(JSON.stringify(message)); } /** * Create an output-only channel that synthesizes one message and sends its * encoded audio to one browser using the Cloudflare Voice wire protocol. * * The browser should connect with `VoiceClient` or `useVoiceAgent()`. No * `withVoice()` mixin, microphone, or speech-to-text provider is required. */ function browserVoice(options) { const audioFormat = options.audioFormat ?? "mp3"; const sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE; const toSpeechText = options.toSpeechText ?? ((message) => message.markdown); return { isAvailable() { return options.getConnection() !== void 0; }, async deliver(surface, message) { if (!validSurface(surface)) return { status: "failed", retryable: false, error: { code: "BROWSER_VOICE_SURFACE_INVALID", message: `Browser voice cannot parse the address for Channel "${surface.channelKey}"` } }; const connection = options.getConnection(); if (!connection) return unavailable(); let audio; const speechText = toSpeechText(message); try { audio = await options.tts.synthesize(speechText); } catch (error) { return synthesisFailure(error); } if (!audio) return synthesisFailure(); let contentDeliveryStarted = false; try { sendJSON(connection, { type: "welcome", protocol_version: 1 }); sendJSON(connection, { type: "audio_config", format: audioFormat, sampleRate }); sendJSON(connection, { type: "status", status: "speaking" }); sendJSON(connection, { type: "transcript", role: "assistant", text: speechText }); contentDeliveryStarted = true; connection.send(audio); sendJSON(connection, { type: "status", status: "idle" }); return { status: "delivered" }; } catch (error) { return deliveryFailure(error, contentDeliveryStarted); } } }; } //#endregion export { browserVoice }; //# sourceMappingURL=voice.js.map