> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt # Inworld Realtime voice `InworldRealtimeVoice` 类通过 WebSocket 使用 [Inworld AI 的 Realtime API](https://docs.inworld.ai/realtime/quickstart-websocket) 提供实时全双工语音交互。它支持语音到语音、Tool 调用,以及语义语音活动检测、MCP Tool 路由和播放速度等 Inworld 特定 session 控制。 Inworld 的线路协议采用 OpenAI Realtime GA 规范,因此客户端和服务端事件名称与 `@mastra/voice-openai-realtime` 一致。Provider 层面的差异包括:端点(URL 中使用客户端生成的 session key)、`Authorization: Basic ` 标头、用于 Inworld 特定控制项的强类型 `session` 构造函数字段,以及用于 Inworld 扩展(STT、TTS、Memory、back-channel、响应性)的强类型 `providerData` 对象;该对象在 `session.providerData` 下发送。 有关批量文本转语音和语音转文本,请参阅 [`@mastra/voice-inworld`](https://mastra.zisheng.pro/reference/voice/inworld)。 ## 使用示例 ```typescript import { InworldRealtimeVoice } from '@mastra/voice-inworld' import { playAudio, getMicrophoneStream } from '@mastra/node-audio' // Initialize with INWORLD_API_KEY from the environment const voice = new InworldRealtimeVoice() // Or initialize with explicit configuration const voiceWithConfig = new InworldRealtimeVoice({ apiKey: 'your-inworld-api-key', model: 'inworld/models/gemma-4-26b-a4b-it', speaker: 'Sarah', instructions: 'You are a helpful voice assistant.', session: { audio: { output: { speed: 1.1 }, input: { turn_detection: { type: 'semantic_vad', eagerness: 'high' } }, }, }, }) // Establish connection await voice.connect() // Listen for audio output (PCM16 @ 24 kHz by default) voice.on('speaker', stream => { playAudio(stream) }) voice.on('writing', ({ text, role }) => { console.log(`${role}: ${text}`) }) // Convert text to speech await voice.speak('Hello, how can I help you today?', { speaker: 'Hades', }) // Stream microphone audio to the model const microphoneStream = getMicrophoneStream() await voice.send(microphoneStream) // Clean up voice.close() ``` > Inworld API key 已预先进行 Basic 编码。请将其原样粘贴到 `INWORLD_API_KEY` 中。该软件包不会重新编码。 ## 构造函数参数 **apiKey** (`string`): Inworld API key。未提供时使用 INWORLD\_API\_KEY 环境变量。key 已采用 Basic 编码,并会原样放入 Authorization 标头。 **url** (`string`): Realtime WebSocket 端点。会自动附加客户端生成的 session key 和协议参数。 (Default: `'wss://api.inworld.ai/api/v1/realtime/session'`) **model** (`string`): LLM Router 模型 ID。通过初始 session.update 发送,而不是放在 URL 中。可使用 Inworld router 支持的任何模型。 (Default: `'inworld/models/gemma-4-26b-a4b-it'`) **speaker** (`string`): 用于语音合成的默认音色 ID。可使用 Inworld 音色目录中的任何音色。 (Default: `'Sarah'`) **sessionId** (`string`): 客户端生成的 session key,以 URL 的 key 参数呈现。省略时会自动生成基于时间戳的 key。 (Default: `'voice-{Date.now()}'`) **instructions** (`string`): 随初始 session.update 发送的系统提示词。 **session** (`Partial`): 强类型的一等 session 选项(audio、tool\_choice、output\_modalities、temperature 等)。这些选项会深度合并到每次 session.update 中,因此 audio.output.voice 和 audio.output.speed 等嵌套字段会组合,而不是相互覆盖。请参阅下方的 session 字段。 **debug** (`boolean`): 记录原始服务端事件。 (Default: `false`) **providerData** (`InworldProviderData`): 强类型 Inworld 扩展配置(stt、tts、memory、backchannel、responsiveness,以及 user\_id 和 metadata)。每次 session.update 时都会在 session.providerData 下发送。它会与通过 session 字段设置的任何 session.providerData 合并;key 冲突时以构造函数选项为准。 **connectTimeoutMs** (`number`): connect() 等待 WebSocket 握手和初始 session.updated 往返的最长时间。WebSocket 打开前发生错误或关闭,或超过此超时时间时,会以 rejected Promise 呈现,而不是成为未捕获的 socket 错误。 (Default: `15000`) ### `session`(强类型控制项) 使用强类型 `session` 字段配置已记录的 Inworld realtime 选项。这些字段会与连接时的默认值组合(例如 `audio.output.voice` 由 `speaker` 设置): **output\_modalities** (`Array<"text" | "audio">`): 模型应生成的模态。 **audio.output.voice** (`string`): 音色目录 ID。省略时使用构造函数的 speaker。 **audio.output.speed** (`number`): 合成音频的播放速度倍数(0.25 到 1.5)。 **audio.output.model** (`string`): Inworld TTS 模型(例如 inworld-tts-2)。 **audio.output.format** (`InworldAudioFormat`): 输出音频编码。可以是 codec 字符串(例如 audio/pcm、audio/pcmu、audio/pcma、audio/float32)或 { type, rate? } 对象。rate(Hz)适用于 audio/pcm 和 audio/float32(默认 24000);audio/pcmu 和 audio/pcma 固定为 8 kHz。 **audio.input.format** (`InworldAudioFormat`): 发送到服务端的输入音频编码。结构与 audio.output.format 相同,即 codec 字符串或 { type, rate? } 对象。 **audio.input.noise\_reduction** (`{ type: "near_field" | "far_field" }`): 在转写和 VAD 前应用的输入降噪模式。 **audio.input.transcription** (`{ model?: string; language?: string; prompt?: string }`): 对传入用户音频进行服务端转写。默认为 { model: "inworld/inworld-stt-1" }。prompt 可通过词汇、拼写或风格提示对转写施加偏置。提供自定义对象可覆盖默认值;设为 null 可禁用用户侧转写。 **audio.input.turn\_detection** (`InworldTurnDetection | null`): 语音活动/轮次检测。默认为 { type: "semantic\_vad", eagerness: "medium", create\_response: true, interrupt\_response: true }。提供自定义对象可覆盖默认值;设为 null 可完全禁用轮次检测。eagerness 字段控制语义 VAD 结束用户轮次的速度:low 会等待更明确的停顿(更不易被打断),high 会更早结束轮次(响应更快,但更容易截断用户)。默认值 medium 会在两者之间平衡。idle\_timeout\_ms(仅 server\_vad)设置服务端提交轮次前的空闲时间窗口。 **tool\_choice** (`string | { type: "function"; name: string } | { type: "mcp"; server_label: string }`): Tool 选择策略。使用 mcp 变体可通过已配置的 Inworld MCP 服务端路由 Tool 调用。 **temperature** (`number`): 模型的采样温度。 **max\_output\_tokens** (`number | "inf"`): 每个响应最多生成的 token 数。 **truncation** (`"auto" | "disabled" | { type: "retention_ratio"; retention_ratio: number }`): 对话截断策略。 **tracing** (`"auto" | { workflow_name?: string; group_id?: string; metadata?: Record }`): 分布式 Trace 配置。使用 auto 采用服务端默认值,或显式指定 Workflow/组名称。 **include** (`Array<"item.input_audio_transcription.logprobs">`): 选择让服务端在触发的事件中包含的额外字段。 **prompt** (`string | null`): 对服务端提示词模板的引用。传入 null 可将其清除。 ### `providerData`(Inworld 扩展) `providerData` 是用于 Inworld 特定 realtime 扩展的强类型对象。它会在 `session.providerData` 下随每次 `session.update` 发送,并与任何 `session.providerData` 合并;你可以通过 `session` 字段设置后者,key 冲突时以构造函数的 `providerData` 为准。 它包含五个分支和两个 session 级字段: - `stt`:STT 调优,例如 `prompt`、`voice_profile`、`language_hints`,以及 VAD 或轮次结束阈值。 - `tts`:TTS 分段和交付,例如 `segmenter_strategy`、`steering_handling`、`delivery_mode`、`conversational` 和 `user_turn_mode`。 - `memory`:自动滚动 Memory,例如 `enabled`、`turn_interval` 和 `max_facts`。Inworld 会通过 `memory` 事件回传其状态。 - `backchannel`: 用户说话时的简短回应(如“嗯哼”)。音频通过 `backchannel` 事件到达。 - `responsiveness`: 生成主响应期间的早期填充音频。填充音频复用常规 `speaker` 和 `speaking` 事件,因此没有单独的事件。 - `user_id` 和 `metadata`: 传递给 Inworld 的 session 级标识符。 ```typescript const voice = new InworldRealtimeVoice({ providerData: { stt: { voice_profile: true, language_hints: ['en-US'] }, tts: { delivery_mode: 'CREATIVE', segmenter_strategy: 'balanced' }, memory: { enabled: true, turn_interval: 4 }, backchannel: { enabled: true, max_per_turn: 1 }, user_id: 'user-123', }, }) ``` ## 方法 ### `connect()` 打开 WebSocket 连接,发送初始 `session.update`,并在服务端通过 `session.updated` 确认后 resolve。必须在 `speak()`、`listen()` 或 `send()` 前调用。 WebSocket 打开前发生 `error` 或 `close`(或握手超过 `connectTimeoutMs`,默认 15 秒)时,会以 rejected Promise 呈现,而不是成为未捕获的 socket 错误。Promise reject 后,半开 socket 会关闭。 ```typescript await voice.connect() ``` 返回: `Promise` ### `speak()` 向模型发送文本消息并触发音频响应。返回的 Promise 仅在完整响应生命周期结束后(本次调用触发的响应收到 `response.done`)才 resolve;如果响应被用户语音中断或发生传输错误,则会 reject。 支持的模式是串行调用 `speak()`。并发调用会共享同一个监听器池,响应绑定顺序未定义。 **input** (`string | NodeJS.ReadableStream`): 要转换为语音的文本或文本流。 **options** (`Options`): 每次调用的配置。 **options.speaker** (`string`): 用于本次特定请求的音色 ID。 返回: `Promise` ### `listen()` 将单个音频缓冲区作为用户轮次发送,并要求模型仅以文本响应。 **audioData** (`NodeJS.ReadableStream`): 要转写的音频流。 返回: `Promise` ### `send()` 将音频数据实时流式传输到服务端。适用于连续的麦克风输入。 **audioData** (`NodeJS.ReadableStream | Int16Array`): 要流式传输的音频数据。Int16Array 会作为单个 Base64 分块发送;可读流则逐块转发。 **eventId** (`string`): 随每个音频分块转发到服务端的可选事件 ID。 返回: `Promise` ### `updateConfig()` 向服务端发送 `session.update`。强类型 `session` 字段会深度合并到 payload 中,构造函数的所有 `providerData` 都会嵌套在 `session.providerData` 下。 **sessionConfig** (`InworldSessionConfig | Record`): 要应用的部分 session 配置。 返回: `void` ### `addInstructions()` 设置下一次调用 `connect()` 或 `updateConfig()` 时使用的系统指令。 **instructions** (`string`): 模型的系统提示词。 返回: `void` ### `addTools()` 注册模型可在 session 中调用的 Tool。将 `InworldRealtimeVoice` 附加到 Agent 后,为 Agent 配置的 Tool 会自动可用。 **tools** (`ToolsInput`): 要配备的 Tool 配置。 返回: `void` ### `answer()` 发送 `response.create` 事件以触发模型响应,也可提供每个响应的选项。 **options** (`Record`): 转发到服务端的响应选项。 返回: `Promise` ### 轮次控制 #### `commitInput()` 手动将缓冲的输入音频提交为用户轮次。当 `turn_detection` 设为 `null` 时,可用于按键通话或手动轮次控制。 ```typescript voice.commitInput() ``` 返回: `void` #### `clearInput()` 丢弃缓冲的输入音频,不将其提交为用户轮次。 ```typescript voice.clearInput() ``` 返回: `void` #### `clearOutput()` 清空服务端的整个输出音频缓冲区并停止播放。这也会停止所有正在传输的 back-channel 音频。默认的打断路径(`response.cancel`,在 `interrupted` 时执行)对 back-channel 安全,应优先使用。仅当需要清空所有内容时才使用 `clearOutput()`。 ```typescript voice.clearOutput() ``` 返回: `void` ### `close()` 和 `disconnect()` 这两个方法都会关闭 WebSocket,并将实例标记为已断开连接。 返回: `void` ### `getSpeakers()` 返回软件包附带的精选音色列表。Inworld 的目录比此列表更大;运行时可向 `speaker` 传入任何音色 ID。 返回: `Promise>` ### `on()` 和 `off()` 注册和移除事件监听器。请参阅下方的[事件](#events)。 ## 事件 `InworldRealtimeVoice` 类会触发以下事件: **speaker** (`event`): 每个响应触发一次,提供 PCM 音频的 PassThrough 流。将音频通过管道传给播放器时使用此事件。 **speaking** (`event`): 每个音频增量触发。回调接收 { audio: Buffer, response\_id: string }。 **speaking.done** (`event`): 响应的音频输出完成时触发。回调接收 { response\_id: string }。 **writing** (`event`): 转写文本可用时触发。回调接收 { text: string, response\_id: string, role: "assistant" | "user", voiceProfile? }。同一响应中的音频转写和文本增量会去重,因此单个响应只发出一个流。在用户事件中,启用 providerData.stt.voice\_profile 时会包含 voiceProfile。 **speech-started** (`event`): 服务端原始 input\_audio\_buffer.speech\_started VAD 边沿事件。 **speech-stopped** (`event`): 服务端原始 input\_audio\_buffer.speech\_stopped VAD 边沿事件。 **interrupted** (`event`): 客户端合成信号:用户开始说话时,为每个正在传输的 response\_id 触发一次。可使用此事件在插话打断时停止主响应播放。回调接收 { response\_id: string }。它仅携带主响应 ID,不会携带 back-channel ID,因此停止匹配的 speaker 流后,backchannel 流仍会播放(back-channel 本来就应与用户语音重叠,且绝不会因插话打断而取消)。 **turn-suggestion** (`event`): 针对缓冲用户话语的智能轮次端点提示。回调接收 { item\_id, utterance\_index, probability, trailing\_silence\_ms?, audio\_duration\_ms?, inference\_ms? }。 **turn-suggestion-revoked** (`event`): 之前触发的轮次建议已撤回。回调接收 { item\_id, utterance\_index }。 **input-committed** (`event`): 缓冲的输入音频已作为用户轮次提交(通过 commitInput() 或自动 VAD)。回调接收 { item\_id, previous\_item\_id? },其中 previous\_item\_id 可能为 null。 **input-cleared** (`event`): 缓冲的输入音频已丢弃(通过 clearInput())。回调接收 {}。 **input-timeout** (`event`): 服务端 VAD 空闲超时提交了一个用户轮次。回调接收 { audio\_start\_ms, audio\_end\_ms, item\_id }。 **output-audio-started** (`event`): 服务端开始发出输出音频。回调接收 {}。 **output-audio-stopped** (`event`): 服务端停止为当前响应发出输出音频。回调接收 {}。 **output-audio-cleared** (`event`): 服务端输出音频缓冲区已清空并停止播放(通过 clearOutput())。回调接收 {}。 **memory** (`event`): 触发时携带 Inworld 的滚动摘要和事实状态,并按版本去重。需要启用 providerData.memory.enabled。回调接收 InworldMemoryState。 **backchannel** (`event`): 触发时提供 back-channel PCM 音频(用户说话期间的简短回应)的 PassThrough 流。每个流的 .id 都是 backchannel\_id,且绝不会出现在 interrupted 中,因此请在不会被插话打断停止的单独音轨上播放。需要启用 providerData.backchannel.enabled。 **backchannel.done** (`event`): back-channel 完成时触发。回调接收 { backchannel\_id: string, phrase? }。 **backchannel.skipped** (`event`): 决策器在生成任何音频前跳过 back-channel 时触发。回调接收 { reason: string }。 **response.created** (`event`): 新响应开始时触发。回调接收完整服务端事件。 **response.done** (`event`): 响应完成时触发。回调接收完整服务端事件。 **conversation.item.added** (`event`): 追加新对话项目时触发。 **conversation.item.done** (`event`): 对话项目完成时触发。 **function\_call.arguments** (`event`): 触发时携带完整的 Tool 调用参数。回调接收 { call\_id, name, arguments }。 **tool-call-start** (`event`): 已注册 Tool 执行前触发。 **tool-call-result** (`event`): 已注册 Tool 返回后触发。 **error** (`event`): 发生传输或服务端错误时触发。 ## 音色 该软件包包含一组由 `getSpeakers()` 返回的精选音色 ID: - `Dennis` - `Hades` - `Wendy` - `Edward` - `Olivia` - `Sarah` - `Timothy` - `Priya` - `Ronald` - `Deborah` 运行时可将 [Inworld 音色目录](https://docs.inworld.ai/quickstart-tts)中的任何音色 ID 传给 `speaker`。 ## 注意事项 - 可以通过构造函数选项或 `INWORLD_API_KEY` 环境变量提供 API key。key 已预先采用 Basic 编码,请勿重新编码。 - WebSocket URL 会附加 `?key=&protocol=realtime`。模型通过初始 `session.update` 配置,而不是通过 URL 配置。 - 每次调用的 `speak(input, { speaker })` 仅针对单个响应覆盖音色(通过扁平的 `response.voice` 字段),不会更改 session。 - 音频输出默认为 24 kHz 的 PCM16。还支持 8 kHz 的电话音频 `audio/pcmu`、`audio/pcma` 以及 `audio/float32`,可通过 `session.audio.output.format` 配置。 - 在调用任何 send、speak 或 listen 方法前使用 `connect()`。WebSocket 打开前发送的事件会进入队列,并在服务端确认 `session.updated` 后发出。 - 必须使用 `close()` 或 `disconnect()` 关闭语音实例以释放 WebSocket。 - `audio.input.turn_detection` 在 `session` 未提供该字段时默认使用语义 VAD。可以用自定义对象覆盖,或传入 `null` 完全禁用轮次检测。 - `audio.input.transcription` 默认为 `{ model: 'inworld/inworld-stt-1' }`,因此用户侧 `writing` 事件开箱即用。可以用自定义对象覆盖,或传入 `null` 禁用用户侧转写。 - `on()` 和 `off()` 根据 `InworldVoiceEventMap` 确定类型。已知事件名称会得到强类型回调 payload,未知名称则回退为 `unknown`。