UNPKG

@openai/agents-realtime

Version:

The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows. This package contains the logic for building realtime voice agents on the server or in the browser.

252 lines 8.38 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WEBSOCKET_META = exports.HEADERS = void 0; exports.base64ToArrayBuffer = base64ToArrayBuffer; exports.arrayBufferToBase64 = arrayBufferToBase64; exports.getLastTextFromAudioOutputMessage = getLastTextFromAudioOutputMessage; exports.diffRealtimeHistory = diffRealtimeHistory; exports.hasWebRTCSupport = hasWebRTCSupport; exports.removeAudioFromContent = removeAudioFromContent; exports.updateRealtimeHistory = updateRealtimeHistory; exports.realtimeApprovalItemToApprovalItem = realtimeApprovalItemToApprovalItem; exports.approvalItemToRealtimeApprovalItem = approvalItemToRealtimeApprovalItem; const metadata_1 = __importDefault(require("./metadata.js")); const agents_core_1 = require("@openai/agents-core"); /** * Converts a base64 string to an ArrayBuffer * @param {string} base64 * @returns {ArrayBuffer} */ function base64ToArrayBuffer(base64) { const binaryString = atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; } /** * Converts an ArrayBuffer to a base64 string * @param {ArrayBuffer} arrayBuffer * @returns {string} */ function arrayBufferToBase64(arrayBuffer) { const binaryString = String.fromCharCode(...new Uint8Array(arrayBuffer)); return btoa(binaryString); } /** * Get the last text from an audio output message * @param item * @returns */ function getLastTextFromAudioOutputMessage(item) { if (typeof item === 'undefined' || item === null || typeof item !== 'object' || !('type' in item) || typeof item.type !== 'string' || !item.type) { return undefined; } if (item.type !== 'message') { return undefined; } if (!('content' in item) || !Array.isArray(item.content) || item.content.length < 1) { return undefined; } const lastContentItem = item.content[item.content.length - 1]; if (!('type' in lastContentItem) || typeof lastContentItem.type !== 'string') { return undefined; } if (lastContentItem.type === 'output_text') { return typeof lastContentItem.text === 'string' ? lastContentItem.text : undefined; } if (lastContentItem.type === 'output_audio') { return typeof lastContentItem.transcript === 'string' ? lastContentItem.transcript : undefined; } return undefined; } /** * Compare two conversation histories to determine the removals, additions, and updates. * @param oldHistory - The old history. * @param newHistory - The new history. * @returns A diff of the two histories. */ function diffRealtimeHistory(oldHistory, newHistory) { const removals = oldHistory.filter((item) => !newHistory.some((newItem) => newItem.itemId === item.itemId)); const additions = newHistory.filter((item) => !oldHistory.some((oldItem) => oldItem.itemId === item.itemId)); const updates = newHistory.filter((item) => oldHistory.some((oldItem) => oldItem.itemId === item.itemId && JSON.stringify(oldItem) !== JSON.stringify(item))); return { removals, additions, updates, }; } /** * Check if the browser supports WebRTC. * @returns True if WebRTC is supported, false otherwise. */ function hasWebRTCSupport() { if (typeof window === 'undefined') { return false; } return typeof window['RTCPeerConnection'] !== 'undefined'; } /** * Removes the audio data from all content in a message by setting it to null. * @param item * @returns */ function removeAudioFromContent(item) { if (item.role === 'system') { return item; } if (item.role === 'assistant') { return { ...item, content: item.content.map((entry) => { if (entry.type === 'output_audio') { return { ...entry, audio: null, }; } return entry; }), }; } if (item.role === 'user') { return { ...item, content: item.content.map((entry) => { if (entry.type === 'input_audio') { return { ...entry, audio: null, }; } return entry; }), }; } return item; } /** * Updates the realtime history array based on the incoming event and options. * @param history - The current history array. * @param event - The event to process (RealtimeItem). * @param shouldIncludeAudioData - Whether to include audio data in message items. * @returns The updated history array. */ function updateRealtimeHistory(history, event, shouldIncludeAudioData) { // Merge transcript into placeholder input_audio message if (event.type === 'conversation.item.input_audio_transcription.completed') { return history.map((item) => { if (item.itemId === event.item_id && item.type === 'message' && 'role' in item && item.role === 'user') { const updatedContent = item.content.map((entry) => { if (entry.type === 'input_audio') { return { ...entry, transcript: event.transcript, }; } return entry; }); return { ...item, content: updatedContent, status: 'completed', }; } return item; }); } const newEvent = !shouldIncludeAudioData && event.type === 'message' ? removeAudioFromContent(event) : event; const existingIndex = history.findIndex((item) => item.itemId === event.itemId); if (existingIndex !== -1) { // Update existing item return history.map((item, idx) => { if (idx === existingIndex) { return newEvent; } if (!shouldIncludeAudioData && item.type === 'message') { return removeAudioFromContent(item); } return item; }); } else if (event.previousItemId) { // Insert after previousItemId if found, else at end const prevIndex = history.findIndex((item) => item.itemId === event.previousItemId); if (prevIndex !== -1) { return [ ...history.slice(0, prevIndex + 1), newEvent, ...history.slice(prevIndex + 1), ]; } else { return [...history, newEvent]; } } else { return [...history, newEvent]; } } /** * The headers to use for the Realtime API. */ exports.HEADERS = { 'User-Agent': `Agents/JavaScript ${metadata_1.default.version}`, 'X-OpenAI-Agents-SDK': `openai-agents-sdk.${metadata_1.default.version}`, }; /** * Browser websocket header */ exports.WEBSOCKET_META = `openai-agents-sdk.${metadata_1.default.version}`; function realtimeApprovalItemToApprovalItem(agent, item) { const { name, arguments: args, ...rest } = item; return new agents_core_1.RunToolApprovalItem({ type: 'hosted_tool_call', name, arguments: JSON.stringify(args), status: 'in_progress', providerData: { ...rest, }, }, agent); } function approvalItemToRealtimeApprovalItem(item) { const { name, arguments: args, providerData } = item.rawItem; const { itemId, serverLabel, ...rest } = providerData ?? {}; if (!itemId || !serverLabel) { throw new Error('Invalid approval item for Realtime MCP approval request'); } return { type: 'mcp_approval_request', itemId, serverLabel, ...rest, name, arguments: args ? JSON.parse(args) : {}, approved: null, }; } //# sourceMappingURL=utils.js.map