跳到主要内容

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

使用示例
使用示例的直接链接

src/mastra/index.ts
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
= 'wss://api.inworld.ai/api/v1/realtime/session'
Realtime WebSocket 端点。会自动附加客户端生成的 session key 和协议参数。

model?:

string
= 'inworld/models/gemma-4-26b-a4b-it'
LLM Router 模型 ID。通过初始 session.update 发送,而不是放在 URL 中。可使用 Inworld router 支持的任何模型。

speaker?:

string
= 'Sarah'
用于语音合成的默认音色 ID。可使用 Inworld 音色目录中的任何音色。

sessionId?:

string
= 'voice-{Date.now()}'
客户端生成的 session key,以 URL 的 key 参数呈现。省略时会自动生成基于时间戳的 key。

instructions?:

string
随初始 session.update 发送的系统提示词。

session?:

Partial<InworldSessionConfig>
强类型的一等 session 选项(audio、tool_choice、output_modalities、temperature 等)。这些选项会深度合并到每次 session.update 中,因此 audio.output.voice 和 audio.output.speed 等嵌套字段会组合,而不是相互覆盖。请参阅下方的 session 字段。

debug?:

boolean
= false
记录原始服务端事件。

providerData?:

InworldProviderData
强类型 Inworld 扩展配置(stt、tts、memory、backchannel、responsiveness,以及 user_id 和 metadata)。每次 session.update 时都会在 session.providerData 下发送。它会与通过 session 字段设置的任何 session.providerData 合并;key 冲突时以构造函数选项为准。

connectTimeoutMs?:

number
= 15000
connect() 等待 WebSocket 握手和初始 session.updated 往返的最长时间。WebSocket 打开前发生错误或关闭,或超过此超时时间时,会以 rejected Promise 呈现,而不是成为未捕获的 socket 错误。

session(强类型控制项)
session-typed-knobs的直接链接

使用强类型 session 字段配置已记录的 Inworld realtime 选项。这些字段会与连接时的默认值组合(例如 audio.output.voicespeaker 设置):

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<string, unknown> }
分布式 Trace 配置。使用 auto 采用服务端默认值,或显式指定 Workflow/组名称。

include?:

Array<"item.input_audio_transcription.logprobs">
选择让服务端在触发的事件中包含的额外字段。

prompt?:

string | null
对服务端提示词模板的引用。传入 null 可将其清除。

providerData(Inworld 扩展)
providerdata-inworld-extensions的直接链接

providerData 是用于 Inworld 特定 realtime 扩展的强类型对象。它会在 session.providerData 下随每次 session.update 发送,并与任何 session.providerData 合并;你可以通过 session 字段设置后者,key 冲突时以构造函数的 providerData 为准。

它包含五个分支和两个 session 级字段:

  • stt:STT 调优,例如 promptvoice_profilelanguage_hints,以及 VAD 或轮次结束阈值。
  • tts:TTS 分段和交付,例如 segmenter_strategysteering_handlingdelivery_modeconversationaluser_turn_mode
  • memory:自动滚动 Memory,例如 enabledturn_intervalmax_facts。Inworld 会通过 memory 事件回传其状态。
  • backchannel: 用户说话时的简短回应(如“嗯哼”)。音频通过 backchannel 事件到达。
  • responsiveness: 生成主响应期间的早期填充音频。填充音频复用常规 speakerspeaking 事件,因此没有单独的事件。
  • user_idmetadata: 传递给 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 打开前发生 errorclose(或握手超过 connectTimeoutMs,默认 15 秒)时,会以 rejected Promise 呈现,而不是成为未捕获的 socket 错误。Promise reject 后,半开 socket 会关闭。

await voice.connect()

返回: Promise<void>

speak()
speak的直接链接

向模型发送文本消息并触发音频响应。返回的 Promise 仅在完整响应生命周期结束后(本次调用触发的响应收到 response.done)才 resolve;如果响应被用户语音中断或发生传输错误,则会 reject。

支持的模式是串行调用 speak()。并发调用会共享同一个监听器池,响应绑定顺序未定义。

input:

string | NodeJS.ReadableStream
要转换为语音的文本或文本流。

options?:

Options
每次调用的配置。
Options

speaker?:

string
用于本次特定请求的音色 ID。

返回: Promise<void>

listen()
listen的直接链接

将单个音频缓冲区作为用户轮次发送,并要求模型仅以文本响应。

audioData:

NodeJS.ReadableStream
要转写的音频流。

返回: Promise<void>

send()
send的直接链接

将音频数据实时流式传输到服务端。适用于连续的麦克风输入。

audioData:

NodeJS.ReadableStream | Int16Array
要流式传输的音频数据。Int16Array 会作为单个 Base64 分块发送;可读流则逐块转发。

eventId?:

string
随每个音频分块转发到服务端的可选事件 ID。

返回: Promise<void>

updateConfig()
updateconfig的直接链接

向服务端发送 session.update。强类型 session 字段会深度合并到 payload 中,构造函数的所有 providerData 都会嵌套在 session.providerData 下。

sessionConfig:

InworldSessionConfig | Record<string, unknown>
要应用的部分 session 配置。

返回: void

addInstructions()
addinstructions的直接链接

设置下一次调用 connect()updateConfig() 时使用的系统指令。

instructions?:

string
模型的系统提示词。

返回: void

addTools()
addtools的直接链接

注册模型可在 session 中调用的 Tool。将 InworldRealtimeVoice 附加到 Agent 后,为 Agent 配置的 Tool 会自动可用。

tools?:

ToolsInput
要配备的 Tool 配置。

返回: void

answer()
answer的直接链接

发送 response.create 事件以触发模型响应,也可提供每个响应的选项。

options?:

Record<string, unknown>
转发到服务端的响应选项。

返回: 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:

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 音色目录中的任何音色 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/pcmuaudio/pcma 以及 audio/float32,可通过 session.audio.output.format 配置。
  • 在调用任何 send、speak 或 listen 方法前使用 connect()。WebSocket 打开前发送的事件会进入队列,并在服务端确认 session.updated 后发出。
  • 必须使用 close()disconnect() 关闭语音实例以释放 WebSocket。
  • audio.input.turn_detectionsession 未提供该字段时默认使用语义 VAD。可以用自定义对象覆盖,或传入 null 完全禁用轮次检测。
  • audio.input.transcription 默认为 { model: 'inworld/inworld-stt-1' },因此用户侧 writing 事件开箱即用。可以用自定义对象覆盖,或传入 null 禁用用户侧转写。
  • on()off() 根据 InworldVoiceEventMap 确定类型。已知事件名称会得到强类型回调 payload,未知名称则回退为 unknown