feat(volcano-tts): add support for v3 model parameter
1. 新增BYTEDANCE_TTS_MODEL环境变量支持,为火山TTS v3 API配置子模型版本 2. 为复刻音色添加默认兜底模型,修复55000000报错问题 3. 更新文档说明v3 API调用规则与环境变量配置 4. 修正语速转换的范围限制,匹配官方文档规范
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
|||||||
|
# Go build cache
|
||||||
|
.gocache/
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Editor / OS
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
@@ -65,6 +65,7 @@ tts_server.exe
|
|||||||
| `BYTEDANCE_TTS_API_KEY` | 火山引擎新版控制台 API Key | `your_api_key_here` |
|
| `BYTEDANCE_TTS_API_KEY` | 火山引擎新版控制台 API Key | `your_api_key_here` |
|
||||||
| `BYTEDANCE_TTS_RESOURCE_ID` | 资源ID,决定模型版本 | `seed-tts-1.0` |
|
| `BYTEDANCE_TTS_RESOURCE_ID` | 资源ID,决定模型版本 | `seed-tts-1.0` |
|
||||||
| `BYTEDANCE_TTS_SPEAKER` | 发音人(音色)ID | `zh_female_qingxin` |
|
| `BYTEDANCE_TTS_SPEAKER` | 发音人(音色)ID | `zh_female_qingxin` |
|
||||||
|
| `BYTEDANCE_TTS_MODEL` | 模型子版本(复刻音色必填,不设默认 `seed-tts-2.0-standard`) | `seed-tts-2.0-standard` |
|
||||||
|
|
||||||
### 可选参数
|
### 可选参数
|
||||||
|
|
||||||
@@ -90,6 +91,18 @@ tts_server.exe
|
|||||||
|
|
||||||
**注意:** 1.0音色只能搭配 `seed-tts-1.0` Resource ID,2.0音色只能搭配 `seed-tts-2.0` Resource ID。
|
**注意:** 1.0音色只能搭配 `seed-tts-1.0` Resource ID,2.0音色只能搭配 `seed-tts-2.0` Resource ID。
|
||||||
|
|
||||||
|
### v3 API 调用说明
|
||||||
|
|
||||||
|
本项目按火山 v3 单向流式 TTS API 实现([官方文档](https://www.volcengine.com/docs/6561/2528925)),相比 v1/v2 有以下关键差异:
|
||||||
|
|
||||||
|
- **不再使用业务集群**(`cluster` 字段在 v3 已废弃),改用 `X-Api-Resource-Id` HTTP header 路由模型
|
||||||
|
- **鉴权 header 只有** `X-Api-Key` 一个,无 `Authorization`,无 app 对象
|
||||||
|
- **`req_params.model` 字段**:v3 必须显式传子模型版本。可选值:
|
||||||
|
- `seed-tts-2.0-standard`(默认,标准版,常规音色/复刻音色通用)
|
||||||
|
- `seed-tts-2.0-expressive`(表现力增强版,部分复刻音色推荐)
|
||||||
|
- 留空时会用 `seed-tts-2.0-standard` 作为兜底
|
||||||
|
- **复刻音色(`S_` 开头的 speaker)必须显式传 model**,否则可能因默认模型与复刻音色不匹配返回 `55000000`
|
||||||
|
|
||||||
## CORS 跨域配置
|
## CORS 跨域配置
|
||||||
|
|
||||||
跨域请求由 `ALLOWED_ORIGINS` 环境变量控制,按**完整 origin**(含协议 + 域名 + 端口)精确匹配:
|
跨域请求由 `ALLOWED_ORIGINS` 环境变量控制,按**完整 origin**(含协议 + 域名 + 端口)精确匹配:
|
||||||
@@ -232,6 +245,7 @@ OPENAI_TTS_API_KEY=sk-key1,sk-key2,sk-key3
|
|||||||
2. 用控制台的在线体验/调试试一下同一对 `BYTEDANCE_TTS_RESOURCE_ID` + 音色
|
2. 用控制台的在线体验/调试试一下同一对 `BYTEDANCE_TTS_RESOURCE_ID` + 音色
|
||||||
3. 控制台能合成的组合才是正确的
|
3. 控制台能合成的组合才是正确的
|
||||||
4. 把控制台显示的**实际资源 ID 字符串**(通常是 `volc.megatts.*` 格式)填到 Zeabur 的 `BYTEDANCE_TTS_RESOURCE_ID`
|
4. 把控制台显示的**实际资源 ID 字符串**(通常是 `volc.megatts.*` 格式)填到 Zeabur 的 `BYTEDANCE_TTS_RESOURCE_ID`
|
||||||
|
5. 如果你用的是**声音复刻**音色(speaker 以 `S_` 开头),同时确认设置了 `BYTEDANCE_TTS_MODEL`(推荐 `seed-tts-2.0-standard` 或 `seed-tts-2.0-expressive`)。复刻音色不传 `model` 字段是 55000000 的常见原因之一
|
||||||
|
|
||||||
### 5. PowerShell 下 `curl` 命令被解释错
|
### 5. PowerShell 下 `curl` 命令被解释错
|
||||||
|
|
||||||
@@ -334,3 +348,4 @@ sudo systemctl start tts-server
|
|||||||
5. ALLOWED_ORIGINS 是否包含前端完整 origin(含 https://)
|
5. ALLOWED_ORIGINS 是否包含前端完整 origin(含 https://)
|
||||||
6. 客户端请求 URL 是否以 https:// 开头
|
6. 客户端请求 URL 是否以 https:// 开头
|
||||||
7. 生产环境凭据是否定期轮换(API Key 明文出现在日志/对话中时立刻重置)
|
7. 生产环境凭据是否定期轮换(API Key 明文出现在日志/对话中时立刻重置)
|
||||||
|
8. 复刻音色(speaker 以 `S_` 开头)是否设置了 `BYTEDANCE_TTS_MODEL`(默认 `seed-tts-2.0-standard`)
|
||||||
|
|||||||
@@ -49,13 +49,16 @@ func (h *HTTPClient) PostStream(url string, headers map[string]string, body []by
|
|||||||
return h.client.Do(req)
|
return h.client.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertSpeedToSpeechRate 把 OpenAI 风格的 speed(倍率)转成火山 v3 的 speech_rate(百分比)。
|
||||||
|
// 文档规定 speech_rate 范围 [-50, 100],对应 0.5x ~ 2.0x 倍速。
|
||||||
|
// 输入超出范围会被截断到边界值。
|
||||||
func convertSpeedToSpeechRate(speed float64) int {
|
func convertSpeedToSpeechRate(speed float64) int {
|
||||||
rate := int((speed - 1.0) * 100)
|
rate := int((speed - 1.0) * 100)
|
||||||
if rate < -200 {
|
if rate < -50 {
|
||||||
rate = -200
|
rate = -50
|
||||||
}
|
}
|
||||||
if rate > 500 {
|
if rate > 100 {
|
||||||
rate = 500
|
rate = 100
|
||||||
}
|
}
|
||||||
return rate
|
return rate
|
||||||
}
|
}
|
||||||
@@ -69,14 +72,23 @@ func Synthesis(config *dto.ByteDanceTTSConfig, httpClient *HTTPClient, text stri
|
|||||||
speaker = voice
|
speaker = voice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model := config.Model
|
||||||
|
if model == "" {
|
||||||
|
model = "seed-tts-2.0-standard" // 文档默认值 复刻音色可设为 seed-tts-2.0-expressive
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求体结构严格按火山 v3 单向流式 API 文档构造
|
||||||
|
// https://www.volcengine.com/docs/6561/2528925
|
||||||
|
// v3 鉴权只依赖 X-Api-Key 一个 header,不再需要业务集群参数
|
||||||
params := map[string]interface{}{
|
params := map[string]interface{}{
|
||||||
"user": map[string]interface{}{
|
"user": map[string]interface{}{
|
||||||
"uid": "uid",
|
"uid": reqID, // 文档要求随机字符串,这里复用请求级 UUID
|
||||||
},
|
},
|
||||||
"namespace": "UnidirectionalTTS",
|
"namespace": "UnidirectionalTTS",
|
||||||
"req_params": map[string]interface{}{
|
"req_params": map[string]interface{}{
|
||||||
"text": text,
|
"text": text,
|
||||||
"speaker": speaker,
|
"speaker": speaker,
|
||||||
|
"model": model, // 复刻音色必填
|
||||||
"audio_params": map[string]interface{}{
|
"audio_params": map[string]interface{}{
|
||||||
"format": "wav",
|
"format": "wav",
|
||||||
"sample_rate": 24000,
|
"sample_rate": 24000,
|
||||||
@@ -88,9 +100,9 @@ func Synthesis(config *dto.ByteDanceTTSConfig, httpClient *HTTPClient, text stri
|
|||||||
headers := map[string]string{
|
headers := map[string]string{
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
"X-Api-Resource-Id": config.ResourceId,
|
"X-Api-Resource-Id": config.ResourceId, // 模型路由(seed-tts-2.0 / seed-icl-2.0)
|
||||||
"X-Api-Request-Id": reqID,
|
"X-Api-Request-Id": reqID,
|
||||||
"X-Api-Key": config.ApiKey,
|
"X-Api-Key": config.ApiKey, // v3 鉴权 key
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyStr, err := json.Marshal(params)
|
bodyStr, err := json.Marshal(params)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type ByteDanceTTSConfig struct {
|
|||||||
ApiKey string
|
ApiKey string
|
||||||
ResourceId string
|
ResourceId string
|
||||||
Speaker string
|
Speaker string
|
||||||
|
Model string // v3 声音复刻/语音大模型 子模型版本,复刻音色必填
|
||||||
URL string
|
URL string
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ func InitTTSConfig() error {
|
|||||||
apiKey := os.Getenv("BYTEDANCE_TTS_API_KEY")
|
apiKey := os.Getenv("BYTEDANCE_TTS_API_KEY")
|
||||||
resourceId := os.Getenv("BYTEDANCE_TTS_RESOURCE_ID")
|
resourceId := os.Getenv("BYTEDANCE_TTS_RESOURCE_ID")
|
||||||
speaker := os.Getenv("BYTEDANCE_TTS_SPEAKER")
|
speaker := os.Getenv("BYTEDANCE_TTS_SPEAKER")
|
||||||
|
model := os.Getenv("BYTEDANCE_TTS_MODEL")
|
||||||
|
if model == "" {
|
||||||
|
model = "seed-tts-2.0-standard" // 文档默认值 复刻音色可设为 seed-tts-2.0-expressive
|
||||||
|
}
|
||||||
|
|
||||||
missingVars := []string{}
|
missingVars := []string{}
|
||||||
if apiKey == "" {
|
if apiKey == "" {
|
||||||
@@ -50,6 +54,7 @@ func InitTTSConfig() error {
|
|||||||
ApiKey: apiKey,
|
ApiKey: apiKey,
|
||||||
ResourceId: resourceId,
|
ResourceId: resourceId,
|
||||||
Speaker: speaker,
|
Speaker: speaker,
|
||||||
|
Model: model,
|
||||||
URL: url,
|
URL: url,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
}
|
}
|
||||||
@@ -72,6 +77,7 @@ func CheckEnvironmentVariables() map[string]interface{} {
|
|||||||
|
|
||||||
optionalVars := map[string]bool{
|
optionalVars := map[string]bool{
|
||||||
"BYTEDANCE_TTS_TIMEOUT": os.Getenv("BYTEDANCE_TTS_TIMEOUT") != "",
|
"BYTEDANCE_TTS_TIMEOUT": os.Getenv("BYTEDANCE_TTS_TIMEOUT") != "",
|
||||||
|
"BYTEDANCE_TTS_MODEL": os.Getenv("BYTEDANCE_TTS_MODEL") != "",
|
||||||
"OPENAI_TTS_API_KEY": os.Getenv("OPENAI_TTS_API_KEY") != "",
|
"OPENAI_TTS_API_KEY": os.Getenv("OPENAI_TTS_API_KEY") != "",
|
||||||
"PORT": os.Getenv("PORT") != "",
|
"PORT": os.Getenv("PORT") != "",
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user