docs(voice): 收口「没会话/没绑定就走 default」的镜像注释(r4)

唯一 finding:audio.go 的 speechRequest.SessionID 注释原本写「省略它时本口没有会话
概念 ⇒ 走 emotionTTS 的 default 分支」——**反了**。真实链路是
effectivePersona(ctx,"") → sessionPersona("") 为空**不是终点** → Effective =
defaultPersonaID() → 常规安装必有全局默认人格 ⇒ personaId 非空 ⇒ 走 personas 映射。
照那句话改的人会写出 `if req.SessionID == "" { personaID = "" }`,正是 r2 修掉的病。
改为:省略时按**全局默认人格**选发音人(会话绑定 → 全局默认人格 → 两者都没有才落到
default 字符),并显式写了"不要写成 if SessionID == \"\" { personaID = \"\" }"。

顺手同根三处(不留第四轮):
- tts/config.go 的 Config.Default 注释:成因表「会话无绑定 / 读取失败 / 该出口本身
  没有会话」已过时 ⇒ 改为「空串的唯一合法成因是连全局默认人格都没有」,并写明不是
  "会话未绑定"、也不是"出站口没有会话"。
- runtimeconfig/voicetts.go 的 Default 字段注释与文件头同族措辞同步;
- selection.go 的 WithPersonaID / Resolve 分支表、service.go 的分支小标把
  「解析不到」统一改成「连全局默认人格都没有」;
- voice_emotiontts_test.go 的 TestSpeechEndpointNoSessionUsesDefault 注释同步(该用例
  没接人格库,所以确实落到 default —— 但成因写对了);
- wire_voice_tts_test.go nit:改成「已废弃的全局 `character` 不生效」(墓碑键仍在)。

纯注释:`git diff` 里非注释改动行为空。go build/vet 通过;四包 go test -count=1 全绿。
This commit is contained in:
AskaEth committed 2026-10-26 13:40:00 +08:00
1 parent 1c2944fcf4
commit b90811fae4
8 files changed
+41 -22

No files matched your search

+11 -4
View File
@@ -30,10 +30,17 @@ type transcribeRequest struct {
// 每请求情绪/语气覆盖,用于克隆音色(声音复刻);系统音色
// (longxiaochun_v3)忽略它,绝不接收自由形式的指令。
//
// SessionID 是**可选**的会话标识(切片 M35,纯增量):发音人按
// 「该会话绑定的人格」解析(`chat_sessions.persona_id`)。省略它时本口没有会话
// 概念 ⇒ 走 emotionTTS 的 default 分支(没配 default 就不合成)。客户端**绝不**
// 通过它选音色:音色由服务端的 persona 映射决定,请求只声明"这是哪个会话"。
// SessionID 是**可选**的会话标识(切片 M35,纯增量):发音人按该会话的**生效
// 人格**解析 —— 会话绑定 → **全局默认人格** → 两者都没有才是空。
//
// 🔴 省略它**不等于**"没有角色":本口此时按**全局默认人格**选发音人(常规安装必
// 有全局默认人格 ⇒ 仍走 `personas` 映射);只有连全局默认人格都没有时,才落到
// `voice-tts.json` 的 `default` 字符(没配则不合成)。因此**不要**写成
// `if req.SessionID == "" { personaID = "" }` —— 那会让本口永远用 `default` 那个
// 声音,正是主人在意的「换人格还用老角色」(复审 r2/r4)。
//
// 客户端**绝不**通过它选音色:音色由服务端的 persona 映射决定,请求只声明
// "这是哪个会话"。
type speechRequest struct {
Text string `json:"text"`
Voice string `json:"voice,omitempty"`
@@ -168,7 +168,8 @@ func TestSpeechEndpointUnmappedPersonaRefuses(t *testing.T) {
}
// TestSpeechEndpointNoSessionUsesDefault 钉住"这条口没有会话时"的诚实处置:
// 省略 sessionId ⇒ persona 解析不到 ⇒ 用配置的 default(没配则同样不合成)。
// 省略 sessionId ⇒ 生效人格只能来自**全局默认人格**;本用例没有接人格库,
// 因此连默认人格都没有 ⇒ 才落到配置的 default(没配则同样不合成)。
func TestSpeechEndpointNoSessionUsesDefault(t *testing.T) {
env := newEnv(t, nil, false)
rec := &vocalRecorder{}