Skip to content

feat: add HuoShanV3 HTTP Chunked TTS adapter#579

Open
shenenqing wants to merge 1 commit intoLianjiaTech:developfrom
shenenqing:feat/huoshan-v2-tts
Open

feat: add HuoShanV3 HTTP Chunked TTS adapter#579
shenenqing wants to merge 1 commit intoLianjiaTech:developfrom
shenenqing:feat/huoshan-v2-tts

Conversation

@shenenqing
Copy link
Copy Markdown
Collaborator

Summary

  • 新增火山引擎 V3 HTTP Chunked 流式 TTS 适配器,对接 openspeech.bytedance.com/api/v3/tts/unidirectional 接口
  • 支持流式和非流式 TTS,使用 X-Api-* 自定义 Header 认证
  • 通过 BellaStreamCallback 实现 HTTP Chunked 逐行 JSON 解析和 base64 音频解码

新增文件

  • HuoShanV3Property.java — channel 配置 DTO(appId, accessKey, resourceId)
  • HuoShanV3Request.java — V3 请求体结构,支持 extra_body 透传
  • HuoShanV3Response.java — V3 响应体,含错误码映射
  • HuoShanV3Adaptor.java — TTS 适配器(Component: HuoShanV3Tts
  • HuoShanV3StreamTtsCallback.java — 流式回调,按行拆分 JSON 并解码音频

🤖 Generated with Claude Code

@szl97
Copy link
Copy Markdown
Collaborator

szl97 commented Apr 1, 2026

@claude 检查提交的代码,使用中文回复

@szl97
Copy link
Copy Markdown
Collaborator

szl97 commented Apr 1, 2026

Claude finished @szl97's task —— View job


火山引擎 V3 TTS 适配器代码审查报告

  • 分析 HuoShanV3Property.java - 配置属性类
  • 审查 HuoShanV3Request.java - 请求体结构
  • 检查 HuoShanV3Response.java - 响应体结构
  • 评估 HuoShanV3Adaptor.java - 主适配器实现
  • 审查 HuoShanV3StreamTtsCallback.java - 流式回调实现
  • 提供综合代码审查反馈

总体评价

这个 PR 实现了火山引擎 V3 HTTP Chunked TTS 适配器,代码架构合理,遵循了现有的适配器模式。整体实现质量较高,但有几个方面需要注意和改进。

具体审查意见

🟢 优点

  1. 架构设计良好

    • 严格遵循了 TtsAdaptor 接口规范
    • 使用了标准的协议适配器模式,与现有 HuoShanAdaptor 保持一致性
    • 正确实现了流式和非流式两种模式
  2. 代码组织清晰

    • 文件结构合理,DTO 类与适配器实现分离
    • 使用了合适的 Lombok 注解减少样板代码
    • Jackson 注解使用恰当,支持动态字段透传
  3. 功能实现完整

    • 支持 extra_body 参数透传,增强了灵活性
    • 正确实现了 HTTP Chunked 逐行 JSON 解析
    • Base64 音频解码处理正确

🟡 需要注意的问题

  1. HuoShanV3Adaptor.java:36 命名不一致

    HuoShanV3Request v2Request = HuoShanV3Request.from(request, property);

    变量名 v2Request 应该改为 v3Request,与实际版本保持一致。

  2. 错误处理可以优化

    • HuoShanV3StreamTtsCallback.java:70-72 中对错误响应只记录了警告日志,但没有抛出异常
    • 建议参考 AudioCollector 的错误处理方式,在流式回调中也要正确传播错误
  3. 内存管理

    • AudioCollector 类中使用了多个 ByteArrayOutputStream,对于大文件可能有内存压力
    • 建议考虑添加大小限制或优化内存使用

🟠 潜在改进点

  1. 代码复用

    • HuoShanV3AdaptorHuoShanV3StreamTtsCallback 中都有相似的行解析逻辑
    • 建议提取公共的 JSON 行解析方法
  2. 配置验证

    • HuoShanV3Property 缺少字段验证,建议添加 @NotNull 等注解
  3. 常量定义

    • 建议将 "application/json" 等常量提取到类常量中

🔵 具体修改建议

HuoShanV3Adaptor.java

// 第36行和第46行
HuoShanV3Request v3Request = HuoShanV3Request.from(request, property);

HuoShanV3StreamTtsCallback.java

// 第70-73行,改进错误处理
if (!response.isSuccess()) {
    log.warn("HuoShanV3 stream error: code={}, message={}", response.getCode(), response.getMessage());
    HttpStatus status = mapErrorCode(response.getCode()); // 需要添加此方法或注入适配器
    BellaException error = new BellaException.ChannelException(status.value(), status.getReasonPhrase(), response.getMessage());
    finish(error);
    return;
}

HuoShanV3Property.java

@Data
@EqualsAndHashCode(callSuper = true)
public class HuoShanV3Property extends TtsProperty {
    @NotNull
    String appId;
    @NotNull
    String accessKey;  
    @NotNull
    String resourceId;
}

安全性检查

✅ 没有发现明显的安全漏洞
✅ API 密钥通过 Header 传递,符合安全规范
✅ 输入验证适当,支持透传但有类型检查

结论

这是一个高质量的实现,代码结构合理,功能完整。主要问题是一些小的命名不一致和错误处理细节。建议在合并前修复这些小问题,整体上可以合并。


@shenenqing shenenqing force-pushed the feat/huoshan-v2-tts branch from f4283ff to 3dd6d1e Compare April 1, 2026 04:11
@shenenqing shenenqing requested a review from fengyizhu April 1, 2026 04:11
@fengyizhu fengyizhu requested a review from muverystrong April 3, 2026 03:30

public byte[] getAudioBytes() {
try {
doneFuture.get();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里如果,stream流在一开始初始化连接的时候就失败了,是不是存在一直block在doneFuture.get(),线程会一直block在这里。是不是应该用带超时的阻塞获取方法doneFuture.get(30, TimeUnit.SECONDS)。

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

分析了一下调用链路:

  • 连接阶段失败BellaStreamCallback.onFailure()connectionInitFuture.completeExceptionally()HttpUtils.streamRequest() 直接抛异常,不会走到 doneFuture.get()
  • 读取阶段失败BellaStreamCallback.onResponse() 中 IOException 会调 callback.finish(exception),进而 doneFuture.complete(null)

正常路径下 doneFuture 不会永远阻塞。但作为防御性编程,已改为 doneFuture.get(30, TimeUnit.SECONDS),超时抛出 GATEWAY_TIMEOUT 异常。

}
}

private void processLine() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AudioCollector感觉应是HuoShanV3StreamTtsCallback的一个子类。本质上是用HuoShanV3StreamTtsCallback的能力实现了collector的能力,这样processLine的方法就可以共用了。

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

认同,已重构。AudioCollector 不再重复行解析逻辑,改为:

  • 构造一个自定义 Sender(将 send(byte[]) 写入 audioBufferclose() 完成 doneFuture
  • 内部创建 HuoShanV3StreamTtsCallback 子类作为 delegate,override finish() 跳过 metrics/logger,只做 flushLineBuffer() + sender.close()
  • HuoShanV3StreamTtsCallback 中提取了 flushLineBuffer() protected 方法供子类复用

这样 processLine 的行解析、JSON 反序列化、base64 解码、错误处理逻辑只维护一份。

实现火山引擎 V3 HTTP Chunked 流式 TTS 适配器,对接
openspeech.bytedance.com/api/v3/tts/unidirectional 接口。
支持流式和非流式 TTS,使用 X-Api-* 自定义 Header 认证。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@shenenqing shenenqing force-pushed the feat/huoshan-v2-tts branch from 3dd6d1e to bdc4cd3 Compare April 7, 2026 07:54
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class HuoShanV3Request implements IMemoryClearable, ITransfer {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里如果不继承TtsRequest,会不会复用现在的计费有问题。

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不会有问题。计费逻辑(TtsLogHandler)只依赖统一的 TtsRequest DTO,不涉及 provider 特定的 Request 类:

  1. 优先路径StageLoggingProcessor.extractTtsMetrics 在请求进入适配器前,从 TtsRequest.getInput().length() 预计算 inputLength 存到 processData.requestMetrics
  2. 回退路径:从 processData.getRequestRaw() 反序列化为 TtsRequestinput.length()

两条路径的 instanceof 检查都是 TtsRequest(统一入口 DTO),不会检查 provider 特定类。现有的 HuoShanRequest(V1)也没有继承 TtsRequest,采用相同模式。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants