docs(voice): 收回 r2 修掉的旧语义注释 + 让关闭态变异真能打红(r3 P2-1/P2-2)
P2-1(两处注释在描述已被推翻的旧语义,会把 r2 的修复写回去):
- voice.go 的 voiceReplySink 文档:原写「personaID = env.PersonaID,会话无绑定时为空」
⇒ 实际传的是 turnPersona.Effective,**未绑定 ≠ 空**(有全局默认人格时非空)。
改为写明「生效人格(会话绑定 → 全局默认人格;两者都没有才是空)」,并显式指出
它不是 env.PersonaID、把空当"没有角色"会让主会话落到 default(正是 r2 修掉的)。
- voice.go 的 synthesizeVoiceReply:原写「空 personaID = 会话无绑定 ⇒ 走 default」
⇒ 反了。改为「空 = 连全局默认人格都没有 ⇒ 才走 voice-tts.json 的 default」。
- 同族 format 残留措辞:client_test.go 的「非法格式的回落」(该行为已不存在)、
cmd/cyrene/wire_voice_tts_test.go 的「角色/格式/超时按文件来」都改写。
- nit:voice.go 的 `env, turnPersona := ...` 遮蔽同包类型名,改成 `tp`。
P2-2(我上轮那条「摘掉 PrimaryActive 闸 ⇒ 用例红」不成立,复审实测仍绿):
根因是 audioStubVoice 不实现 PrimaryActive,拦住 persona 读取的只有类型断言。
按塔选的第二种收口(把测试做强):
- audioStubVoice 补 `PrimaryActive() bool { return true }`(它是"生效中的 persona
voice"替身),并在注释里说明它存在的理由;
- 关闭态用例改用新替身 inactivePersonaVoice(实现 personaVoice 但报 false),
于是 PrimaryActive 成为关闭态唯一的闸;
- 重跑变异(摘掉 `&& pv.PrimaryActive()`)**实测打红**:
「未生效的 personaVoice 却查了 1 次会话人格(关闭态行为变了)」。
未动:功能面、错误码、500 状态码、白名单、session 可读、隐私/format/Stats/redactURL。
go build ./... + go vet ./... + go test -count=1 -race 覆盖 tts/runtimeconfig/gateway/
cmd/cyrene 全绿。
This commit is contained in:
1 parent
e684061e56
commit
1c2944fcf4
5 files changed
+47
-16
No files matched your search
@@ -232,14 +232,19 @@ func TestVoiceOutputFrameVoiceFollowsTurnPersona(t *testing.T) {
|
||||
// TestSpeechEndpointDisabledVoiceDoesNotReadPersona 是**关闭态零行为变化**的自证
|
||||
// (复审要求在当前 tip 上重跑的那一条,而我这一版动了 `gateway/audio.go`):
|
||||
//
|
||||
// 注入的语音服务不是"按 persona 选发音"的那一类(既有链 / stub)时,语音合成
|
||||
// **一次都不查会话人格** —— 与引入本切片之前逐字同行为;反之(真 emotionTTS 门面)
|
||||
// 才会查,说明这个闸不是"永远不查"。
|
||||
// 注入的语音服务**实现了 personaVoice 却报告未生效**(`inactivePersonaVoice`,
|
||||
// 模拟"门面在、这一级关着")时,语音合成**一次都不查会话人格** —— 与引入本切片
|
||||
// 之前逐字同行为;反之(真 emotionTTS 门面,PrimaryActive=true)才会查,说明这个闸
|
||||
// 不是"永远不查"。
|
||||
//
|
||||
// 变异:去掉 handleSpeech 里的 `pv.PrimaryActive()` 闸 ⇒ 本用例第一段红。
|
||||
// 变异:去掉 handleSpeech 里的 `&& pv.PrimaryActive()`(只留类型断言)⇒ 本用例
|
||||
// 第一段红。🔴 这条变异此前**打不红**,因为替身不实现 `PrimaryActive`,拦住读库的
|
||||
// 只有类型断言(复审 r3 P2-2);现在替身实现了它,布尔值才是唯一的闸。
|
||||
func TestSpeechEndpointDisabledVoiceDoesNotReadPersona(t *testing.T) {
|
||||
env := newEnv(t, nil, false)
|
||||
stub := &audioStubVoice{ttsAudio: mvoice.Audio{Format: "wav", Data: fakeWAV("chain")}}
|
||||
stub := &inactivePersonaVoice{audioStubVoice: audioStubVoice{
|
||||
ttsAudio: mvoice.Audio{Format: "wav", Data: fakeWAV("chain")},
|
||||
}}
|
||||
env.srv.SetVoice(stub)
|
||||
counter := &countingHistory{fakeHistory: &fakeHistory{}}
|
||||
env.srv.envResolver = turnEnvResolver{history: counter, log: env.srv.log}
|
||||
@@ -252,7 +257,7 @@ func TestSpeechEndpointDisabledVoiceDoesNotReadPersona(t *testing.T) {
|
||||
t.Fatalf("status = %d, body = %s", resp.StatusCode, body)
|
||||
}
|
||||
if got := counter.Calls(); got != 0 {
|
||||
t.Fatalf("非 persona 语音服务却查了 %d 次会话人格(关闭态行为变了)", got)
|
||||
t.Fatalf("未生效的 personaVoice 却查了 %d 次会话人格(关闭态行为变了)", got)
|
||||
}
|
||||
if string(body) != string(fakeWAV("chain")) {
|
||||
t.Fatalf("关闭态的音频不是既有链产物: %q", string(body))
|
||||
@@ -295,6 +300,14 @@ func (h *countingHistory) Calls() int {
|
||||
return h.calls
|
||||
}
|
||||
|
||||
// inactivePersonaVoice 是"**实现了** personaVoice 但报告这一级未生效"的替身
|
||||
// (模拟 emotionTTS 门面存在、`enabled` 关着的情形)。它让 `PrimaryActive()` 成为
|
||||
// 关闭态唯一的闸 —— 这样把 `&& pv.PrimaryActive()` 摘掉的变异才会打红。
|
||||
type inactivePersonaVoice struct{ audioStubVoice }
|
||||
|
||||
// PrimaryActive 报告"按 persona 选发音"的那一级**未**生效。
|
||||
func (i *inactivePersonaVoice) PrimaryActive() bool { return false }
|
||||
|
||||
// asString 安全地取一个 string 值(帧载荷是 map[string]any)。
|
||||
func asString(v any) string {
|
||||
s, _ := v.(string)
|
||||
|
||||
Reference in new issue
Block a user