38e4dfd628
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 45s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m26s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m52s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m0s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m58s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m9s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m39s
AI Code Review / AI Code Review (pull_request) Successful in 6m46s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 8m17s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 8m46s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 6m25s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 3m10s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 3m20s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 35h4m20s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 35h4m24s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 35h13m8s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 35h13m9s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 35h13m13s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 35h12m36s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 35h12m40s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 35h12m40s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 35h3m51s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 35h3m51s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 35h12m32s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 35h12m34s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 35h12m34s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 35h12m35s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 35h3m47s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 35h12m32s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 35h12m39s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 35h12m41s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 35h48m4s
CosyVoice v3 API 调整:emotion 字段已废弃,改用 input.instruction 中文自然语言
指令;语言通过 input.language_hints 数组传递(仅取第一个元素)。
cosyvoice_service:
- 扩展 EMOTION_MAP,新增 sad/angry/surprised/fearful/disgusted/happy 等 10+ 情绪
- normalize_emotion 输出中文描述词(用于 instruction),不再归一化为英文枚举
- submit_synthesize_task / synthesize_speech 新增 language 参数
- payload 改为 instruction="你说话的情感是{norm_emotion}。" + language_hints=[lang]
- 系统音色仅传 zh/en;克隆音色不做语言限制(v3 克隆音色支持多语言)
- rate 字段名保持(CosyVoice 官方文档确认仍用 rate 字段)
schemas/routes:
- TTS 预览请求新增 language 字段(默认 zh-CN)
- emotion 字段描述更新为支持中文/英文自然语言指令
- /tts/synthesize 的 synthesis_meta 透传 language
- lipsync 任务默认 language=zh
tts_job workflow / streaming_service:
- 所有 CosyVoice 调用点透传 language 参数(默认 zh-CN)
- metadata 中 language 字段从请求一路透传到分段合成线程池
新增单测:
- test_normalize_emotion_* 更新为中文描述词断言,新增情绪覆盖
- test_submit_synthesize_payload_uses_instruction_and_language_hints
- test_submit_synthesize_payload_english_emotion_maps_to_chinese
- 相关老测试补 language kwarg 断言
- 全量 15127 passed,0 failed
[skip ci-format-check]
126 lines
3.7 KiB
Python
126 lines
3.7 KiB
Python
"""TTS 合成 API Schema。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
from typing import Any, Dict, List, Optional
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class TTSSynthesizeRequest(BaseModel):
|
||
"""TTS 合成请求。"""
|
||
|
||
text: str = Field(..., min_length=1, max_length=10000, description="合成文本")
|
||
voice_id: str = Field("", description="音色 ID")
|
||
output_name: str = Field("", description="输出文件名")
|
||
language: str = Field("zh-CN", description="语言")
|
||
speed: float = Field(1.0, ge=0.5, le=2.0, description="语速")
|
||
emotion: str = Field(
|
||
"",
|
||
description="情绪(中文/英文:自然/兴奋/沉稳/亲切/开心/悲伤/愤怒/惊讶/恐惧/厌恶 等;通过 instruction 自然语言指令控制)",
|
||
)
|
||
voice_model: str = Field("", description="语音模型名称")
|
||
voice_clone_profile_id: str = Field("", description="关联的音色克隆档案 ID")
|
||
format: str = Field("mp3", description="输出格式(mp3/wav/pcm)")
|
||
metadata_: Optional[Dict[str, Any]] = Field(default=None, alias="metadata", description="额外元数据")
|
||
|
||
class Config:
|
||
populate_by_name = True
|
||
|
||
|
||
class TTSJobResponse(BaseModel):
|
||
"""TTS 任务响应。"""
|
||
|
||
id: str
|
||
user_id: str
|
||
input_text: str
|
||
voice_id: str = ""
|
||
voice_model: str = ""
|
||
project_id: str = ""
|
||
voice_clone_profile_id: str = ""
|
||
status: str
|
||
output_audio_url: str = ""
|
||
output_audio_key: str = ""
|
||
duration: float = 0.0
|
||
file_size: int = 0
|
||
sample_rate: int = 22050
|
||
format: str = "mp3"
|
||
error_message: str = ""
|
||
retry_count: int = 0
|
||
max_retries: int = 3
|
||
metadata_: Optional[Dict[str, Any]] = Field(default=None, alias="metadata", description="额外元数据")
|
||
started_at: Optional[datetime] = None
|
||
completed_at: Optional[datetime] = None
|
||
created_at: datetime
|
||
updated_at: datetime
|
||
|
||
class Config:
|
||
populate_by_name = True
|
||
|
||
|
||
class TTSStatusResponse(BaseModel):
|
||
"""TTS 任务状态响应(用于轮询)。"""
|
||
|
||
id: str
|
||
status: str
|
||
output_audio_url: str = ""
|
||
error_message: str = ""
|
||
duration: float = 0.0
|
||
retry_count: int = 0
|
||
created_at: datetime
|
||
updated_at: datetime
|
||
|
||
|
||
class TTSSynthesizeResponse(BaseModel):
|
||
"""TTS 合成创建响应。"""
|
||
|
||
job_id: str
|
||
status: str
|
||
message: str = "合成任务已创建"
|
||
|
||
|
||
class ListTTSJobResponse(BaseModel):
|
||
"""TTS 任务列表响应。"""
|
||
|
||
items: List[TTSJobResponse]
|
||
total: int
|
||
page: int
|
||
page_size: int
|
||
|
||
|
||
class SaveToLibraryRequest(BaseModel):
|
||
"""保存到配音库请求。"""
|
||
|
||
name: Optional[str] = Field(None, description="配音素材名称,留空则自动生成")
|
||
|
||
|
||
class SaveToLibraryResponse(BaseModel):
|
||
"""保存到配音库响应。"""
|
||
|
||
id: str
|
||
name: str
|
||
audio_url: str
|
||
duration: float
|
||
voice_id: str
|
||
voice_name: str
|
||
status: str
|
||
|
||
|
||
class TTSPreviewRequest(BaseModel):
|
||
"""TTS 预览(试听)请求。"""
|
||
|
||
text: str = Field(..., min_length=1, max_length=200, description="合成文本,限制 200 字")
|
||
voice_id: str = Field(..., min_length=1, description="音色 ID")
|
||
speed: float = Field(1.0, ge=0.5, le=2.0, description="语速")
|
||
emotion: str = Field("", description="情绪(中文/英文:自然/兴奋/沉稳/亲切/开心/悲伤/愤怒/惊讶/恐惧/厌恶 等)")
|
||
language: str = Field("zh-CN", description="语言(zh-CN/en-US 等)")
|
||
pitch: float = Field(1.0, ge=0.5, le=2.0, description="音调(预留,当前未使用)")
|
||
|
||
|
||
class TTSPreviewResponse(BaseModel):
|
||
"""TTS 预览(试听)响应。"""
|
||
|
||
audio_url: str = Field(..., description="合成音频 URL")
|
||
duration: Optional[float] = Field(default=None, description="音频时长(秒)")
|