mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-10-25 17:36:29 +08:00
fix(linkai_voice): return an ERROR reply on ASR/TTS failure instead of None
LinkAI was the only voice provider that returned None on a failed
transcription or synthesis (voice/linkai/linkai_voice.py, 4 sites).
Every sibling (ali, openai, mimo) returns Reply(ReplyType.ERROR, ...),
so the user hears/sees a failure message. A None bubbles up through
bridge.fetch_voice_to_text with no Reply at all and the turn goes silent
(or the caller dereferences a None reply).
Replace the four `return None` with ReplyType.ERROR replies, reusing the
same Chinese strings the other providers use ("抱歉,语音识别失败" /
"抱歉,语音合成失败").
Tests: the four failure exits (ASR/TTS, HTTP error / exception) now
return an ERROR reply instead of None.
This commit is contained in:
1 parent
901c17dfb5
commit
dfbb8083c7
2 files changed
+83
-4
No files matched your search
@@ -0,0 +1,79 @@
|
||||
# encoding:utf-8
|
||||
"""
|
||||
Regression tests for ``voice/linkai/linkai_voice.py``.
|
||||
|
||||
When LinkAI ASR/TTS fails, the old code returned ``None``. Every sibling
|
||||
provider (ali, openai, mimo) returns a ``Reply(ReplyType.ERROR, ...)``
|
||||
instead, so the user gets a failure message; LinkAI's ``None`` means the
|
||||
bridge returns no Reply at all (``bridge/bridge.py::fetch_voice_to_text``)
|
||||
and the turn goes silent. These tests assert the four failure exits now
|
||||
produce an ERROR reply.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
from bridge.reply import ReplyType
|
||||
from voice.linkai.linkai_voice import LinkAIVoice
|
||||
|
||||
|
||||
class TestLinkAIVoiceErrorReply(unittest.TestCase):
|
||||
def _voice(self):
|
||||
return LinkAIVoice()
|
||||
|
||||
def _audio(self):
|
||||
p = Path(__file__).with_suffix(".mp3")
|
||||
p.write_bytes(b"fake-audio-bytes")
|
||||
return p
|
||||
|
||||
def test_voiceToText_http_error_returns_error_reply(self):
|
||||
v = self._voice()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 500
|
||||
resp.json.return_value = {"message": "boom"}
|
||||
audio = self._audio()
|
||||
with patch("voice.linkai.linkai_voice.requests.post", return_value=resp):
|
||||
reply = v.voiceToText(str(audio))
|
||||
audio.unlink()
|
||||
self.assertIsNotNone(reply)
|
||||
self.assertEqual(reply.type, ReplyType.ERROR)
|
||||
self.assertIn("识别", reply.content)
|
||||
|
||||
def test_voiceToText_exception_returns_error_reply(self):
|
||||
v = self._voice()
|
||||
audio = self._audio()
|
||||
with patch(
|
||||
"voice.linkai.linkai_voice.requests.post", side_effect=RuntimeError("net down")
|
||||
):
|
||||
reply = v.voiceToText(str(audio))
|
||||
audio.unlink()
|
||||
self.assertIsNotNone(reply)
|
||||
self.assertEqual(reply.type, ReplyType.ERROR)
|
||||
|
||||
def test_textToVoice_http_error_returns_error_reply(self):
|
||||
v = self._voice()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 500
|
||||
resp.json.return_value = {"message": "boom"}
|
||||
with patch("voice.linkai.linkai_voice.requests.post", return_value=resp):
|
||||
reply = v.textToVoice("hello")
|
||||
self.assertIsNotNone(reply)
|
||||
self.assertEqual(reply.type, ReplyType.ERROR)
|
||||
self.assertIn("合成", reply.content)
|
||||
|
||||
def test_textToVoice_exception_returns_error_reply(self):
|
||||
v = self._voice()
|
||||
with patch(
|
||||
"voice.linkai.linkai_voice.requests.post", side_effect=RuntimeError("net down")
|
||||
):
|
||||
reply = v.textToVoice("hello")
|
||||
self.assertIsNotNone(reply)
|
||||
self.assertEqual(reply.type, ReplyType.ERROR)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -56,13 +56,13 @@ class LinkAIVoice(Voice):
|
||||
except Exception:
|
||||
pass
|
||||
logger.error(f"[LinkVoice] voiceToText error, status_code={res.status_code}, msg={msg}")
|
||||
return None
|
||||
return Reply(ReplyType.ERROR, "抱歉,语音识别失败")
|
||||
text = res.json().get("text")
|
||||
logger.info(f"[LinkVoice] voiceToText success, text={text}, file name={voice_file}")
|
||||
return Reply(ReplyType.TEXT, text)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return None
|
||||
return Reply(ReplyType.ERROR, "抱歉,语音识别失败")
|
||||
|
||||
def textToVoice(self, text):
|
||||
try:
|
||||
@@ -88,7 +88,7 @@ class LinkAIVoice(Voice):
|
||||
except Exception:
|
||||
pass
|
||||
logger.error(f"[LinkVoice] textToVoice error, status_code={res.status_code}, msg={msg}")
|
||||
return None
|
||||
return Reply(ReplyType.ERROR, "抱歉,语音合成失败")
|
||||
tmp_file_name = TmpDir().path() + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + str(random.randint(0, 1000)) + ".mp3"
|
||||
with open(tmp_file_name, 'wb') as f:
|
||||
f.write(res.content)
|
||||
@@ -96,4 +96,4 @@ class LinkAIVoice(Voice):
|
||||
return Reply(ReplyType.VOICE, tmp_file_name)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return None
|
||||
return Reply(ReplyType.ERROR, "抱歉,语音合成失败")
|
||||
Reference in new issue
Block a user