Files
AskaEth b0fbb8bc0d
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
docs(comments): 注释中文化 · internal/runtime/multimodal(21 文件,纯替换)
- 只翻注释/文档字符串,代码零变化:go/scanner 词法流比对 21 文件 token+字面量全等,增删行数 1:1(467/467)
- 刻意保留非散文英文:JSON Schema description、工具 Description() 返回值、adapter prompt、API 路径与命令示例、环境变量名
- 逐行确认代码前缀未变,未触碰其他模块
2026-10-04 12:28:16 +08:00

97 lines
3.2 KiB
Go

//go:build dashscope_live
package voice
import (
"bytes"
"context"
"encoding/binary"
"os"
"testing"
"git.yeij.top/AskaEth/Cyrene_v2/internal/config"
)
// TestDashscopeLive 端到端验证真实的阿里百炼 STT + TTS。它
// 被排除在默认测试集之外(构建标签 dashscope_live),使 `go test ./...`
// 绝不触碰付费 API。用从 .env 取到的 key 显式运行:
//
// set -a; source .env; set +a
// go test -tags dashscope_live -run TestDashscopeLive -v ./internal/runtime/multimodal/voice/
func TestDashscopeLive(t *testing.T) {
key := os.Getenv("DASHSCOPE_API_KEY")
if key == "" {
t.Skip("DASHSCOPE_API_KEY not set")
}
cfg := config.VoiceConfig{DashscopeAPIKey: key}
// TTS:为一句短文本合成语音。
tts := NewDashscopeTTS(cfg)
audio, err := tts.TTS(context.Background(), "你好,我是昔涟")
if err != nil {
t.Fatalf("TTS error: %v", err)
}
t.Logf("TTS: format=%q bytes=%d", audio.Format, len(audio.Data))
if len(audio.Data) == 0 {
t.Fatal("TTS returned empty audio")
}
// STT 闭环:把合成的语音转写回文字。
stt := NewDashscopeSTT(cfg)
text, err := stt.STT(context.Background(), Audio{Format: "wav", Data: audio.Data})
if err != nil {
t.Fatalf("STT error: %v", err)
}
t.Logf("STT (of TTS audio): text=%q", text)
// 对生成的静音 wav 做 STT,确认空音频路径可被接受。
silence := silentWAVBytes(1, 16000)
sttText, sttErr := stt.STT(context.Background(), Audio{Format: "wav", Data: silence})
if sttErr != nil {
t.Logf("STT (silence): error=%v", sttErr)
} else {
t.Logf("STT (silence): text=%q", sttText)
}
// 走一遍降级链(网关接线的就是同一个对象),证明它
// 返回真实的字节/文字,而不是降级到静默末端一级。
ch := New(cfg)
chTTS, chTTSErr := ch.TTS(context.Background(), "你好,我是昔涟")
if chTTSErr != nil {
t.Fatalf("chain TTS error: %v", chTTSErr)
}
t.Logf("chain TTS: format=%q bytes=%d", chTTS.Format, len(chTTS.Data))
if len(chTTS.Data) == 0 {
t.Fatal("chain TTS degraded to empty (silent) audio")
}
chText, chSTTErr := ch.STT(context.Background(), Audio{Format: "wav", Data: chTTS.Data})
if chSTTErr != nil {
t.Fatalf("chain STT error: %v", chSTTErr)
}
t.Logf("chain STT: text=%q", chText)
if chText == "" {
t.Fatal("chain STT degraded to empty (silent) text")
}
}
// silentWAVBytes 构造 durS 秒的单声道 16 位 16 kHz 静音 WAV。
func silentWAVBytes(durS, sampleRate int) []byte {
dataLen := durS * sampleRate * 2
buf := &bytes.Buffer{}
// RIFF 头 + fmt 块 + data 块。
buf.WriteString("RIFF")
_ = binary.Write(buf, binary.LittleEndian, uint32(36+dataLen))
buf.WriteString("WAVEfmt ")
_ = binary.Write(buf, binary.LittleEndian, uint32(16))
_ = binary.Write(buf, binary.LittleEndian, uint16(1)) // PCM
_ = binary.Write(buf, binary.LittleEndian, uint16(1)) // 单声道
_ = binary.Write(buf, binary.LittleEndian, uint32(sampleRate))
_ = binary.Write(buf, binary.LittleEndian, uint32(sampleRate*2))
_ = binary.Write(buf, binary.LittleEndian, uint16(2)) // 块对齐
_ = binary.Write(buf, binary.LittleEndian, uint16(16)) // 每样本位数
buf.WriteString("data")
_ = binary.Write(buf, binary.LittleEndian, uint32(dataLen))
buf.Write(make([]byte, dataLen))
return buf.Bytes()
}