7.7 KiB
TTS 接口契约与适配边界
状态:Phase 0 审计基线,已补充旧 TTS 工程只读核查事实
审计日期:2026-09-08
重要说明:本文件将“已核实事实”和“目标契约”分开。已授权只读检查外部工程 E:\RD\Kaotings\kts_site\KT26-0903_Big-TTS,未复制或修改该工程。
1. 现有接口事实
| 项目 | 结论 | 证据/处理 |
|---|---|---|
| 上游地址 | 代码默认:http://43.248.188.28:44480,路径 /v1/audio/speech |
ReadMe.md 和 server.js 初始 SQLite 配置;不是实际运行地址证明 |
| 协议和方法 | POST,OpenAI 兼容 JSON 请求 |
旧工程 /api/tts 使用 fetch |
| 鉴权 | 配置有 API Key 时发送 Authorization: Bearer <key>;默认 Key 为空 |
Key 只存旧工程服务端 SQLite,配置接口返回掩码 |
| 输入文本字段 | input |
旧工程将业务文本映射为 input |
| 音色 ID/语言 | voice;默认 default,逗号分隔配置;无真实语言清单 |
旧工程 tts_config 和前端 /api/config |
| 参数范围 | response_format: wav/mp3;speed: 0.5 至 2.0;文本 1 至 5000 字符 |
server.js 服务端校验 |
| 模型 | 默认 qwen3-tts,可由旧工程管理员配置 |
ReadMe.md、tts_config |
| 同步/异步 | 同步 | 单次 fetch 等待完整响应,无 task ID |
| 结果格式 | 音频字节流;WAV audio/wav,MP3 audio/mpeg |
旧工程返回完整 arrayBuffer |
| 错误语义 | 输入错误 400;未登录 401;上游非 2xx/超时统一 502 |
旧工程业务层映射 |
| 超时和大小限制 | 上游超时 120000ms;请求体最多 1 MiB;文本最多 5000 字符;未见上游响应大小限制 |
body() 与 AbortSignal.timeout(120000) |
| 幂等支持 | 未实现 | 无幂等键、provider task ID 或重复请求去重 |
代码契约已核实;首次无鉴权请求返回 401 Unauthorized,配置服务端凭据后最小文本请求已返回 200 audio/wav。当前测试服务器没有 TTS 上游监听端口,业务 API 通过固定环境地址访问外部上游。
旧工程自身的业务接口为:登录后 GET /api/config 获取模型/音色元数据,POST /api/tts 同步返回音频,GET /api/history 返回当前用户最多 100 条历史;管理员配置接口为 /api/admin/config。Phase 3 只复用上游边界,不复制旧工程用户体系或整套工程。
www_site 不复用旧工程的网页端管理员配置接口。上游主机、API Key、超时和相关秘密只由服务端环境配置注入;网站用户和管理员均不能通过网页修改上游连接配置。
2. 业务层目标契约
以下是依据 V1_PLAN.md 第 9、10 章拟定的业务层边界。它是 Phase 2/3 的实现输入,不是上游协议。
2.1 公开业务 API
| 方法与路径 | 用途 | 认证 |
|---|---|---|
GET /api/v1/tts/voices |
返回当前可用音色及安全参数元数据 | 可公开,实际策略待定 |
POST /api/v1/tts/tasks |
创建 TTS 任务,返回 202 和 task_id |
登录、限流、额度和并发校验 |
GET /api/v1/tts/tasks |
当前用户分页历史 | 当前用户 |
GET /api/v1/tts/tasks/{id} |
查询状态和结果元数据 | 所有者或管理员 |
GET /api/v1/tts/tasks/{id}/audio |
受控播放 | 所有者或管理员 |
GET /api/v1/tts/tasks/{id}/download |
受控下载 | 所有者或管理员 |
认证、账户和管理接口以 V1_PLAN.md 第 9 章为准。业务层统一使用同源 /api/v1,浏览器不直接访问上游 TTS 地址。
2.2 创建任务请求
下面是脱敏的目标样例,仅用于说明业务层字段;字段名、音色和参数必须在上游联调后固定。
{
"text": "待转换文本",
"voice_id": "configured-voice-id",
"parameters": {
"format": "mp3"
},
"idempotency_key": "client-generated-opaque-key"
}
服务端必须重新计算 Unicode 码点计量、校验文本长度/音色/参数/权益/并发和频率,不能信任客户端计数或隐藏字段。旧工程会把完整原始文本写入 SQLite 历史,Phase 3 不直接复制该隐私行为。
2.3 创建成功响应
{
"task_id": "uuid",
"status": "queued",
"text_length": 123,
"reserved_amount": 123,
"request_id": "request-id"
}
返回 202 Accepted 表示任务已持久化并冻结额度,不表示上游已成功生成音频。额度冻结和任务创建必须在同一数据库事务中完成。
2.4 状态响应
{
"task_id": "uuid",
"status": "queued|running|succeeded|failed",
"text_length": 123,
"voice_id": "configured-voice-id",
"audio": {
"available": false,
"duration_ms": null,
"expires_at": null
},
"error": null,
"request_id": "request-id"
}
成功时 audio 只返回受控业务地址或短期授权结果,不返回存储桶内部路径。管理员默认不展开完整输入文本。
3. 适配器目标边界
services/worker 通过配置固定的上游主机和端点调用适配器;用户输入不能成为 URL、Host、路径或凭据。适配器负责:
- 将业务层标准请求映射为真实上游请求,并从真实响应提取 provider task ID、音频数据/地址和计量信息。
- 对上游鉴权、连接超时、读取超时、响应大小、MIME 和音频可解析性做服务端校验。
- 将真实上游错误映射到内部错误码,不向浏览器泄露内部地址、密钥、模型路径或堆栈。
- 在上游语义明确时实现有限重试;超时且结果不明时先保留
running并按 provider task ID 核对,禁止盲目重复生成。 - 将音频写入私有对象存储后,才允许任务转为
succeeded并把冻结额度转为消费。
适配器不负责用户授权、额度决策、跨用户资源判断或 CMS 权限;这些由 API/业务服务负责。
4. 失败语义基线
以下是业务层目标分类,不是对未核实上游返回码的断言:
| 内部错误码 | HTTP 建议 | 处理 |
|---|---|---|
AUTH_REQUIRED |
401 | 不创建任务,不冻结额度 |
FORBIDDEN |
403 | 不泄露资源存在性 |
INVALID_INPUT |
422 | 文本、音色或参数不合法 |
QUOTA_EXCEEDED |
409 | 不创建任务,不产生负余额 |
RATE_LIMITED |
429 | 返回安全的重试提示 |
UPSTREAM_UNAVAILABLE |
503 | 按任务状态处理,不盲目重试不确定请求 |
UPSTREAM_REJECTED |
502 | 记录脱敏上游分类,失败时释放冻结 |
AUDIO_PERSIST_FAILED |
500/503 | 不标记成功,执行对账/释放策略 |
TASK_FAILED |
500 | 任务失败,保留可追踪错误码 |
AUDIO_EXPIRED |
410 | 保留历史元信息,不返回内部路径 |
错误响应统一使用:
{
"error": {
"code": "INVALID_INPUT",
"message": "请求参数不可用",
"request_id": "request-id"
}
}
不记录密码、会话令牌、验证码、上游密钥或完整原始文本。
5. 待确认清单和验证顺序
- 确认
43.248.188.28:44480是否仍是获授权的测试上游;代码默认值不作为部署事实。 - 提供该上游测试环境的 API Key 或其他鉴权凭据注入方式;本次无鉴权最小请求返回
401,未保存或输出响应内容。 - 获取当前音色 ID、语言、参数和计量补充资料;旧工程只有
default示例,没有真实清单。 - 确认上游是否支持幂等、取消、重试和 provider task ID 查询;当前旧工程不提供这些能力。
- 在测试环境执行最小成功、参数拒绝、超时、上游失败、重复请求和响应不明测试;不得用 Mock 结果替代。
- 依据真实结果固定
TTSVoice、TTSTask、错误映射、超时、重试、音频校验和额度计量配置。
在上述步骤完成前,Phase 3 不得宣称“真实 TTS 已接入”。