difyTTS.py 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # -*- coding: utf-8 -*-
  2. from ..builder import TTSEngines
  3. from ..engineBase import BaseTTSEngine
  4. import base64
  5. import httpx
  6. from digitalHuman.protocol import *
  7. from digitalHuman.utils import logger, mp3ToWav
  8. __all__ = ["DifyApiTts"]
  9. @TTSEngines.register("Dify")
  10. class DifyApiTts(BaseTTSEngine):
  11. def setup(self):
  12. """初始化 HTTP 客户端,优化连接池和超时设置"""
  13. super().setup()
  14. # 创建专用的 HTTP 客户端,优化连接池和超时设置
  15. # 使用连接池复用连接,减少连接建立时间
  16. # 设置合理的超时时间:连接超时 5s,读取超时 30s(TTS 可能需要一些时间)
  17. self._client = httpx.AsyncClient(
  18. timeout=httpx.Timeout(connect=5.0, read=30.0, write=10.0, pool=5.0),
  19. limits=httpx.Limits(max_keepalive_connections=10, max_connections=20),
  20. # 注意:如需启用 HTTP/2,请安装 httpx[http2]:pip install httpx[http2]
  21. # http2=True, # 暂时禁用,避免缺少 h2 包的错误
  22. )
  23. def release(self):
  24. """释放 HTTP 客户端资源"""
  25. super().release()
  26. # 注意:httpx.AsyncClient 会在程序退出时自动关闭
  27. # 如果需要立即关闭,可以在异步上下文中调用 await self._client.aclose()
  28. # 这里只做标记,避免在 release 中处理异步操作
  29. if hasattr(self, '_client'):
  30. self._client = None
  31. async def run(self, input: TextMessage, **kwargs) -> AudioMessage:
  32. # 参数校验
  33. paramters = self.checkParameter(**kwargs)
  34. API_SERVER = paramters["api_server"]
  35. API_KEY = paramters["api_key"]
  36. API_USERNAME = paramters["username"]
  37. headers = {
  38. 'Authorization': f'Bearer {API_KEY}',
  39. 'Content-Type': 'application/json',
  40. 'Accept': 'audio/*', # 明确指定接受音频类型
  41. }
  42. payload = {
  43. "text": input.data,
  44. "user": API_USERNAME,
  45. }
  46. logger.debug(f"[TTS] Engine input: {input.data[:50]}..." if len(input.data) > 50 else f"[TTS] Engine input: {input.data}")
  47. try:
  48. # 使用优化的客户端发送请求
  49. response = await self._client.post(
  50. API_SERVER.rstrip('/') + "/text-to-audio",
  51. json=payload,
  52. headers=headers,
  53. follow_redirects=True, # 自动跟随重定向
  54. )
  55. if response.status_code != 200:
  56. error_msg = f"DifyAPI tts api error: {response.status_code}"
  57. if response.text:
  58. error_msg += f", response: {response.text[:200]}"
  59. raise RuntimeError(error_msg)
  60. # 直接使用响应内容,无需额外转换
  61. audio_content = response.content
  62. message = AudioMessage(
  63. data=base64.b64encode(audio_content).decode('utf-8'),
  64. sampleRate=16000,
  65. sampleWidth=2,
  66. )
  67. logger.debug(f"[TTS] Successfully generated audio, size: {len(audio_content)} bytes")
  68. return message
  69. except httpx.TimeoutException as e:
  70. logger.error(f"[TTS] Request timeout: {e}")
  71. raise RuntimeError(f"DifyAPI tts request timeout: {e}")
  72. except httpx.RequestError as e:
  73. logger.error(f"[TTS] Request error: {e}")
  74. raise RuntimeError(f"DifyAPI tts request error: {e}")
  75. except Exception as e:
  76. logger.error(f"[TTS] Unexpected error: {e}")
  77. raise