RoadmapDemo · 对外 API

HTTP + WebSocket,默认 http://<host>:8000 ← 返回对话页 Swagger(HTTP 自动文档)

1. 会话 WebSocket WS /ws

一条连接就是一场对话:上行 16 kHz 单声道 PCM(int16,每帧 80 ms = 1280 样本),下行 JSON 事件 + TTS PCM + 数字人视频帧。连上后服务端先发 hello。

上行:二进制帧

内容说明
int16 PCM,16 kHz,mono,1280 样本/帧持续发送麦克风音频。VAD / EOT / ASR / 打断全部在服务端判断,客户端不必做端点检测。

上行:控制 JSON

type字段作用
texttext用文字代替一次语音提问,走同一条 LLM → TTS → 数字人链路。
set_voicespk_id切换音色(见 /voices),下一句回复生效。
set_personatext, persist(默认 true)改人设 system prompt。控制标记规则固定拼在后面;会清空对话历史。回 persona 事件。
reset—清空历史、状态机复位。
avatar_stop—手动打断数字人(等价于用户插话)。
playback_done—客户端播完本回合全部 PCM 后回报,服务端据此回到倾听态。
avatar_testtext调试:直接 TTS → 数字人,不经过 LLM。

下行:JSON 事件

type关键字段含义
hellosid, in_sample_rate, frame_samples, tts_sample_rate, components, speaker_lock连接就绪与采样率约定。开启第一说话人锁定时 speaker_lock=true。
speaker_lockstate (locked / ignore / reset), sim已锁定第一说话人 / 忽略其他人声 / 会话重置后重新登记。
statestate (listening / armed / speaking), reasonOpenTurn 状态机变化。
vadp, speech_ms, silence_ms每帧 VAD 概率与连续语音/静音时长。
asr_partial / asr_finaltext, preview流式识别中间结果 / 本回合最终文本。
actionname状态机动作流:commit / speculative / interrupt / user_speech_start …
llm_delta / llm_donetextLLM 增量与完整回复(已剥掉控制标记)。
logmsg过程日志,含 ctrl emo=… act=…(LLM 输出的情绪/动作预测)。
pcmsr, data(base64 int16), splice, seqTTS 音频,24 kHz。收到即按顺序排队播放。
bot_speaking / bot_doneaudio_sec回合开始发声 / 本回合音频已全部下发。
interrupt / cut_audiosplice, idle_from_ms, keep_ms, turn_seq, hard用户插话:立刻停声。pts < idle_from_ms 的视频帧是在途口型,之后是倾听帧。
metricscommit_to_llm_first_ms, commit_to_first_audio_ms, first_chunk_ms, ttff_ms …本回合延迟指标。
voice / personaspk_id / text设置已应用。
avatar_stream_readyrun_id, transport, persistent数字人视频流可用;transport=push 时帧通过本连接二进制下发。
avatar_statusstate (warm / streaming_audio / idle / waiting …), pts_offset_ms, turn_seq, video_sec数字人阶段变化。
avatar_chunkindex, frames, video_secLiveAct 又解码出一块(16 帧 = 0.8 s)。
avatar_audio_startpts_offset_ms, lead_ms, sr, turn_seq口型已生成到开口位置:从此刻起播放本回合 PCM,视频帧按 pts - pts_offset_ms 与音频时钟对齐。
avatar_stopped / avatar_done / avatar_errorreason / state / message数字人被打断 / 结束 / 出错。

下行:二进制视频帧(push 传输)

magic "AVF1" | uint32 index | uint32 pts_ms | uint16 width | uint16 height | uint32 jpeg_len | JPEG…   (大端)

20 fps,256×416。pts_ms 是该帧在数字人时间线上的位置;倾听态按 20 fps 顺播即可,说话态用 avatar_audio_start.pts_offset_ms 对齐到音频时钟。每条 run 的前 8 帧(index < 8)是预热图,建议丢弃。

2. 音色 (音色设置)

接口说明
GET/voices音色列表:{speakers:[{id,name,builtin}], default}
POST/voicesmultipart:file(3–6 s 参考音频)、name、prompt_text(可空,自动转写)。one-shot 克隆,返回 {id,name,prompt_text,speech_sec}。
POST/voices/previewJSON {spk_id, text?} → WAV 试听。
DELETE/voices/{spk_id}删除克隆音色。

情绪由 LLM 每句预测(happy / sad / angry / surprise / gentle / excited / calm),服务端转成 CosyVoice instruct 送 TTS,不需要客户端干预。

3. 人设 (对话设置)

接口说明
GET/api/persona{persona, default, control_rules, max_chars}
POST/api/personaJSON {persona}。写入 persona.json,新会话生效;已连接会话请发 WS set_persona。

4. 形象 (形象设置)

接口说明
GET/api/avatar-image{avatar_id, mask_revision, preview_url, overlay_url}
POST/api/avatar-imagemultipart image(半身正面照, ≤20 MB)。抠像 → 切换 → 重开数字人流,同步返回;期间画面会空几秒。写入 avatar_settings.json,重启保留。
GET/api/avatar-image/{avatar_id}/original.png|overlay.jpg|mask.png形象预览资源。

5. 其它

接口说明
GET/healthz存活检查。
GET/docsFastAPI 自动生成的 HTTP 文档。

6. 最小客户端示意(浏览器)

const ws = new WebSocket(`ws://${location.host}/ws`); ws.binaryType = 'arraybuffer';
ws.onmessage = (e) => {
  if (e.data instanceof ArrayBuffer) { /* AVF1 视频帧 → 解 JPEG 画到 canvas */ return; }
  const m = JSON.parse(e.data);
  if (m.type === 'pcm') queuePcm(m.data, m.sr);            // base64 int16 → WebAudio
  if (m.type === 'interrupt' || m.type === 'cut_audio') stopAudioNow();
  if (m.type === 'avatar_audio_start') alignVideoTo(m.pts_offset_ms, m.lead_ms);
};
// 麦克风:AudioWorklet 重采样到 16 kHz,每 1280 样本 ws.send(int16Buffer)
ws.send(JSON.stringify({ type: 'text', text: '你好' }));      // 或直接文字提问