Inworld Realtime voice
InworldRealtimeVoice 类通过 WebSocket 使用 Inworld AI 的 Realtime API 提供实时全双工语音交互。它支持语音到语音、Tool 调用,以及语义语音活动检测、MCP Tool 路由和播放速度等 Inworld 特定 session 控制。
Inworld 的线路协议采用 OpenAI Realtime GA 规范,因此客户端和服务端事件名称与 @mastra/voice-openai-realtime 一致。Provider 层面的差异包括:端点(URL 中使用客户端生成的 session key)、Authorization: Basic <key> 标头、用于 Inworld 特定控制项的强类型 session 构造函数字段,以及用于 Inworld 扩展(STT、TTS、Memory、back-channel、响应性)的强类型 providerData 对象;该对象在 session.providerData 下发送。
有关批量文本转语音和语音转文本,请参阅 @mastra/voice-inworld。
使用示例使用示例的直接链接
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?:
url?:
model?:
speaker?:
sessionId?:
key 参数呈现。省略时会自动生成基于时间戳的 key。instructions?:
session?:
debug?:
providerData?:
session 字段设置的任何 session.providerData 合并;key 冲突时以构造函数选项为准。connectTimeoutMs?:
connect() 等待 WebSocket 握手和初始 session.updated 往返的最长时间。WebSocket 打开前发生错误或关闭,或超过此超时时间时,会以 rejected Promise 呈现,而不是成为未捕获的 socket 错误。session(强类型控制项)session-typed-knobs的直接链接
使用强类型 session 字段配置已记录的 Inworld realtime 选项。这些字段会与连接时的默认值组合(例如 audio.output.voice 由 speaker 设置):
output_modalities?:
audio.output.voice?:
speaker。audio.output.speed?:
audio.output.model?:
audio.output.format?:
{ type, rate? } 对象。rate(Hz)适用于 audio/pcm 和 audio/float32(默认 24000);audio/pcmu 和 audio/pcma 固定为 8 kHz。audio.input.format?:
audio.output.format 相同,即 codec 字符串或 { type, rate? } 对象。audio.input.noise_reduction?:
audio.input.transcription?:
{ model: "inworld/inworld-stt-1" }。prompt 可通过词汇、拼写或风格提示对转写施加偏置。提供自定义对象可覆盖默认值;设为 null 可禁用用户侧转写。audio.input.turn_detection?:
{ type: "semantic_vad", eagerness: "medium", create_response: true, interrupt_response: true }。提供自定义对象可覆盖默认值;设为 null 可完全禁用轮次检测。eagerness 字段控制语义 VAD 结束用户轮次的速度:low 会等待更明确的停顿(更不易被打断),high 会更早结束轮次(响应更快,但更容易截断用户)。默认值 medium 会在两者之间平衡。idle_timeout_ms(仅 server_vad)设置服务端提交轮次前的空闲时间窗口。tool_choice?:
temperature?:
max_output_tokens?:
truncation?:
tracing?:
include?:
prompt?:
providerData(Inworld 扩展)providerdata-inworld-extensions的直接链接
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 级标识符。
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()connect的直接链接
打开 WebSocket 连接,发送初始 session.update,并在服务端通过 session.updated 确认后 resolve。必须在 speak()、listen() 或 send() 前调用。
WebSocket 打开前发生 error 或 close(或握手超过 connectTimeoutMs,默认 15 秒)时,会以 rejected Promise 呈现,而不是成为未捕获的 socket 错误。Promise reject 后,半开 socket 会关闭。
await voice.connect()
返回: Promise<void>
speak()speak的直接链接
向模型发送文本消息并触发音频响应。返回的 Promise 仅在完整响应生命周期结束后(本次调用触发的响应收到 response.done)才 resolve;如果响应被用户语音中断或发生传输错误,则会 reject。
支持的模式是串行调用 speak()。并发调用会共享同一个监听器池,响应绑定顺序未定义。
input:
options?:
speaker?:
返回: Promise<void>
listen()listen的直接链接
将单个音频缓冲区作为用户轮次发送,并要求模型仅以文本响应。
audioData:
返回: Promise<void>
send()send的直接链接
将音频数据实时流式传输到服务端。适用于连续的麦克风输入。
audioData:
eventId?:
返回: Promise<void>
updateConfig()updateconfig的直接链接
向服务端发送 session.update。强类型 session 字段会深度合并到 payload 中,构造函数的所有 providerData 都会嵌套在 session.providerData 下。
sessionConfig:
返回: void
addInstructions()addinstructions的直接链接
设置下一次调用 connect() 或 updateConfig() 时使用的系统指令。
instructions?:
返回: void
addTools()addtools的直接链接
注册模型可在 session 中调用的 Tool。将 InworldRealtimeVoice 附加到 Agent 后,为 Agent 配置的 Tool 会自动可用。
tools?:
返回: void
answer()answer的直接链接
发送 response.create 事件以触发模型响应,也可提供每个响应的选项。
options?:
返回: Promise<void>
轮次控制轮次控制的直接链接
commitInput()commitinput的直接链接
手动将缓冲的输入音频提交为用户轮次。当 turn_detection 设为 null 时,可用于按键通话或手动轮次控制。
voice.commitInput()
返回: void
clearInput()clearinput的直接链接
丢弃缓冲的输入音频,不将其提交为用户轮次。
voice.clearInput()
返回: void
clearOutput()clearoutput的直接链接
清空服务端的整个输出音频缓冲区并停止播放。这也会停止所有正在传输的 back-channel 音频。默认的打断路径(response.cancel,在 interrupted 时执行)对 back-channel 安全,应优先使用。仅当需要清空所有内容时才使用 clearOutput()。
voice.clearOutput()
返回: void
close() 和 disconnect()close-and-disconnect的直接链接
这两个方法都会关闭 WebSocket,并将实例标记为已断开连接。
返回: void
getSpeakers()getspeakers的直接链接
返回软件包附带的精选音色列表。Inworld 的目录比此列表更大;运行时可向 speaker 传入任何音色 ID。
返回: Promise<Array<{ voiceId: string }>>
on() 和 off()on-and-off的直接链接
注册和移除事件监听器。请参阅下方的事件。
事件事件的直接链接
InworldRealtimeVoice 类会触发以下事件:
speaker:
speaking:
speaking.done:
writing:
speech-started:
input_audio_buffer.speech_started VAD 边沿事件。speech-stopped:
input_audio_buffer.speech_stopped VAD 边沿事件。interrupted:
response_id 触发一次。可使用此事件在插话打断时停止主响应播放。回调接收 { response_id: string }。它仅携带主响应 ID,不会携带 back-channel ID,因此停止匹配的 speaker 流后,backchannel 流仍会播放(back-channel 本来就应与用户语音重叠,且绝不会因插话打断而取消)。turn-suggestion:
turn-suggestion-revoked:
input-committed:
input-cleared:
input-timeout:
output-audio-started:
output-audio-stopped:
output-audio-cleared:
memory:
backchannel:
.id 都是 backchannel_id,且绝不会出现在 interrupted 中,因此请在不会被插话打断停止的单独音轨上播放。需要启用 providerData.backchannel.enabled。backchannel.done:
backchannel.skipped:
response.created:
response.done:
conversation.item.added:
conversation.item.done:
function_call.arguments:
tool-call-start:
tool-call-result:
error:
音色音色的直接链接
该软件包包含一组由 getSpeakers() 返回的精选音色 ID:
DennisHadesWendyEdwardOliviaSarahTimothyPriyaRonaldDeborah
运行时可将 Inworld 音色目录中的任何音色 ID 传给 speaker。
注意事项注意事项的直接链接
- 可以通过构造函数选项或
INWORLD_API_KEY环境变量提供 API key。key 已预先采用 Basic 编码,请勿重新编码。 - WebSocket URL 会附加
?key=<sessionId>&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。