Skip to content

Conversation

@DBJD-CR
Copy link
Contributor

@DBJD-CR DBJD-CR commented Dec 25, 2025

Modifications / 改动点

  • Fix KeyError: 'model': 适配新版配置结构。 [Bug] 添加 fishaudio 文本转语音模型报错:实例化 fishaudio_tts_api 提供商适配器失败 #4172
  • Add timeout support: 防止长文本生成时超时。
  • Improve response handling: 使用更标准的 Header 检查方式。
  • 更安全的类型转换:给 timeout 加上 try-except 块,防止因为配置文件里的脏数据(比如填了个字符串 "20s")导致崩溃。
  • 更友好的错误信息:尝试把 API 返回的原始字节流(bytes)解码成可读的字符串,并限制长度,这样看日志的时候就不会是一堆乱码,也不会因为报错信息太长炸屏。
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

Before:无法实例化 fish Audio TTS,并且会在 WebUI 中弹出报错信息

f05ad23f-3ef1-4b7c-9aeb-a8d8cd707434 ba58eb7c-e08e-450b-b8f5-1da593a19bb6

After:可以在 WebUI 中正常配置并生成语音服务

e621c252-6525-4053-a6aa-c30158809545

🤖 AstrBot PR Reviewer 正在分析您的代码... 分析完成。

文件路径 core/provider/sources/fishaudio_tts_api_source.py 审查报告

1. 版本与运行环境

  • Python 版本: 符合 Python 3.10+ 标准。
  • 运行环境: 使用了 httpx.AsyncClient,完全符合异步环境要求。

2. 综合审查维度

  • 代码质量与编码规范:
    • 命名清晰,遵循 PEP 8。
    • 逻辑结构简洁。
  • 功能实现与逻辑正确性:
    • 修复: 正确使用 .get("model", None) 修复了潜在的 KeyError
    • 增强: 添加了 timeout 支持,默认 20s,这是一个非常棒的健壮性改进。
    • 增强: 优化了音频流检测逻辑 startswith("audio/"),比之前的强校验更灵活且标准。
  • 安全漏洞与最佳实践:
    • 无硬编码敏感信息(API Key 从配置读取)。
    • 使用了 ormsgpack 进行序列化,效率较高。
  • 可维护性与可读性:
    • 代码易读,修改点逻辑清晰。

3. 框架适应性检查

  • 日志记录: 未在修改片段中发现日志调用,无违规。
  • 并发模型: 使用了 async withawait,无阻塞 IO 操作。

代码评审团

Linus Torvalds
“这就是我为什么讨厌随便重构核心 API!看看这堆烂摊子,框架一更新,配置结构一变,下游的代码就得跟着像猴子一样跳来跳去。KeyError: 'model' ?这种低级错误本就不该发生!如果框架开发者在改动配置结构时能多动动脑子考虑一下向后兼容性,你就不用在这里给他们擦屁股了! 不过,既然烂摊子已经铺开了,你用 .get('model', None) 这种防御性编程来兜底是对的。这就是现实,你要么在框架的愚蠢改动下崩溃,要么就得自己把代码写得像坦克一样结实。干得不错,至少你没让这坨代码直接炸在我脸上。”

史蒂夫·乔布斯
“这个修改不仅仅是修复了一个 Bug,它是在打磨体验。原本的音频流检测逻辑太生硬了,就像以前的 DOS 界面,非黑即白。现在的 startswith('audio/') 更加优雅,它包容了未来的可能性。哪怕以后 FishAudio 决定给音频流加上编码格式,我们的系统依然能丝滑运转。这种对细节的关注,就是区分平庸与卓越的分界线。虽然还谈不上完美,但方向对了。”

GlaDOS (传送门)
“检测到代码修改。你的补丁... 有点意思。你不仅防止了那些愚蠢的人类在配置里填错数据(把 timeout 填成 'forever' 之类的),还学会了在他们搞砸 API 请求时,用一种他们那可怜的大脑能理解的方式(UTF-8 文本)告诉他们。截断错误信息是个明智之举,避免了我的日志系统因为某些服务器发疯返回的几兆垃圾数据而溢出。好吧,鉴于你这种意外的胜任能力,今天的测试难度将提升 5%。”

Disclaimer: 以上评审内容由 AI 自动生成,所涉及人物形象与现实无关,不代表真实人物观点。如果给出的建议无关痛痒请忽略。


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

调整 FishAudio TTS 提供方以适配更新后的配置、支持请求超时,并提升 API 响应的健壮性。

新功能:

  • 为 FishAudio TTS 的 HTTP 请求添加可配置的超时支持。

错误修复:

  • 通过安全读取 model 字段,防止在加载 FishAudio TTS 配置时出现 KeyError。
  • 通过验证状态码和内容类型,修复对 FishAudio TTS API 非音频响应的不正确处理问题。

改进:

  • 放宽音频 Content-Type 检查,接受任意 audio/* 响应;当 FishAudio TTS 请求失败时,返回包含 HTTP 状态码和响应体的更清晰错误信息。
Original summary in English

Summary by Sourcery

Adjust FishAudio TTS provider to handle updated configuration, support request timeouts, and improve API response robustness.

New Features:

  • Add configurable timeout support for FishAudio TTS HTTP requests.

Bug Fixes:

  • Prevent KeyError when loading FishAudio TTS configuration by safely reading the model field.
  • Fix incorrect handling of non-audio responses from the FishAudio TTS API by validating status code and content type.

Enhancements:

  • Relax audio content-type checking to accept any audio/* response and return clearer error messages including HTTP status and body when the FishAudio TTS request fails.

- Fix `KeyError: 'model'``: 适配新版配置结构。
- Add `timeout` support: 防止长文本生成时超时。
- Improve response handling: 使用更标准的 Header 检查方式。
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了 1 个问题,并给出了一些总体层面的反馈:

  • timeout 值目前是通过 int(provider_config.get("timeout", 20)) 直接进行类型转换的,如果配置了非数字值就会抛出异常;建议使用更安全的转换方式并提供回退逻辑(例如 try/except,或者使用 float 提高灵活性),以避免出现难以排查的配置错误。
  • 在错误分支中,你现在把 error_text(原始字节流)直接放到了异常消息里;如果能先用 UTF‑8 解码并带有回退策略,和/或对过长的响应进行截断,会让错误输出更易读,也更适合安全地写入日志。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- The `timeout` value is cast directly with `int(provider_config.get("timeout", 20))`, which will raise if a non-numeric value is configured; consider wrapping this in a safe conversion with a fallback (e.g., try/except or using `float` for more flexibility) to avoid hard-to-diagnose config errors.
- In the error path you now include `error_text` (raw bytes) directly in the exception message; decoding to UTF‑8 with a fallback and/or truncating excessively long responses would make the error output more readable and safer to log.

## Individual Comments

### Comment 1
<location> `astrbot/core/provider/sources/fishaudio_tts_api_source.py:152-155` </location>
<code_context>
-            body = await response.aread()
-            text = body.decode("utf-8", errors="replace")
-            raise Exception(f"Fish Audio API请求失败: {text}")
+            error_text = await response.aread()
+            raise Exception(
+                f"Fish Audio API请求失败: 状态码 {response.status_code}, 响应内容: {error_text}"
+            )
</code_context>

<issue_to_address>
**suggestion:** 在将错误内容放入异常前,对错误主体进行解码并视情况截断。

`error_text` 在这里仍然是字节类型,因此异常信息中会显示 `b'...'` 的形式,并且可能包含非常大的响应体。可以先用带回退机制的解码方式进行解码,并在加入异常前进行截断,例如:

```python
text = error_text.decode("utf-8", errors="replace")
text = text[:1000]
raise Exception(
    f"Fish Audio API请求失败: 状态码 {response.status_code}, 响应内容: {text}"
)
```

这样既能保持错误消息可读,也能避免产生日志过大的问题。

```suggestion
            error_bytes = await response.aread()
            error_text = error_bytes.decode("utf-8", errors="replace")
            error_text = error_text[:1000]
            raise Exception(
                f"Fish Audio API请求失败: 状态码 {response.status_code}, 响应内容: {error_text}"
            )
```
</issue_to_address>

Sourcery 对开源项目免费 —— 如果你觉得这些代码审查有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进之后的代码审查。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The timeout value is cast directly with int(provider_config.get("timeout", 20)), which will raise if a non-numeric value is configured; consider wrapping this in a safe conversion with a fallback (e.g., try/except or using float for more flexibility) to avoid hard-to-diagnose config errors.
  • In the error path you now include error_text (raw bytes) directly in the exception message; decoding to UTF‑8 with a fallback and/or truncating excessively long responses would make the error output more readable and safer to log.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `timeout` value is cast directly with `int(provider_config.get("timeout", 20))`, which will raise if a non-numeric value is configured; consider wrapping this in a safe conversion with a fallback (e.g., try/except or using `float` for more flexibility) to avoid hard-to-diagnose config errors.
- In the error path you now include `error_text` (raw bytes) directly in the exception message; decoding to UTF‑8 with a fallback and/or truncating excessively long responses would make the error output more readable and safer to log.

## Individual Comments

### Comment 1
<location> `astrbot/core/provider/sources/fishaudio_tts_api_source.py:152-155` </location>
<code_context>
-            body = await response.aread()
-            text = body.decode("utf-8", errors="replace")
-            raise Exception(f"Fish Audio API请求失败: {text}")
+            error_text = await response.aread()
+            raise Exception(
+                f"Fish Audio API请求失败: 状态码 {response.status_code}, 响应内容: {error_text}"
+            )
</code_context>

<issue_to_address>
**suggestion:** Decode and possibly truncate the error body before including it in the exception.

`error_text` is still bytes here, so the exception will show a `b'...'` repr and may include an extremely large body. Decode with a fallback and truncate before including it, e.g.:

```python
text = error_text.decode("utf-8", errors="replace")
text = text[:1000]
raise Exception(
    f"Fish Audio API请求失败: 状态码 {response.status_code}, 响应内容: {text}"
)
```

This keeps the message readable and prevents excessively large logs.

```suggestion
            error_bytes = await response.aread()
            error_text = error_bytes.decode("utf-8", errors="replace")
            error_text = error_text[:1000]
            raise Exception(
                f"Fish Audio API请求失败: 状态码 {response.status_code}, 响应内容: {error_text}"
            )
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Dec 26, 2025
@Soulter Soulter merged commit 2a5d574 into AstrBotDevs:master Dec 26, 2025
6 checks passed
@Soulter Soulter changed the title fix: 修复 FishAudio 源的配置加载问题并增强请求鲁棒性 fix: failed to initialize FishAudio TTS instance and improve handling logic Dec 26, 2025
@dosubot dosubot bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Dec 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants