realtime_dialog_client.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import gzip
  2. import json
  3. from typing import Dict, Any
  4. import websockets
  5. import config
  6. import protocol
  7. class RealtimeDialogClient:
  8. def __init__(self, config: Dict[str, Any], session_id: str, output_audio_format: str = "pcm",
  9. mod: str = "audio", recv_timeout: int = 10) -> None:
  10. self.config = config
  11. self.logid = ""
  12. self.session_id = session_id
  13. self.output_audio_format = output_audio_format
  14. self.mod = mod
  15. self.recv_timeout = recv_timeout
  16. self.ws = None
  17. async def connect(self) -> None:
  18. """建立WebSocket连接"""
  19. print(f"url: {self.config['base_url']}, headers: {self.config['headers']}")
  20. self.ws = await websockets.connect(
  21. self.config['base_url'],
  22. extra_headers=self.config['headers'],
  23. ping_interval=None
  24. )
  25. # 在新版本的websockets库中,response_headers属性已被移除
  26. # 使用安全的方式获取logid,如果无法获取则使用空字符串
  27. try:
  28. if hasattr(self.ws, 'response_headers'):
  29. self.logid = self.ws.response_headers.get("X-Tt-Logid", "")
  30. elif hasattr(self.ws, 'response') and hasattr(self.ws.response, 'headers'):
  31. self.logid = self.ws.response.headers.get("X-Tt-Logid", "")
  32. else:
  33. self.logid = ""
  34. except AttributeError:
  35. self.logid = ""
  36. print(f"dialog server response logid: {self.logid}")
  37. # StartConnection request
  38. start_connection_request = bytearray(protocol.generate_header())
  39. start_connection_request.extend(int(1).to_bytes(4, 'big'))
  40. payload_bytes = str.encode("{}")
  41. payload_bytes = gzip.compress(payload_bytes)
  42. start_connection_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  43. start_connection_request.extend(payload_bytes)
  44. await self.ws.send(start_connection_request)
  45. response = await self.ws.recv()
  46. print(f"StartConnection response: {protocol.parse_response(response)}")
  47. # 扩大这个参数,可以在一段时间内保持静默,主要用于text模式,参数范围[10,120]
  48. config.start_session_req["dialog"]["extra"]["recv_timeout"] = self.recv_timeout
  49. # 这个参数,在text或者audio_file模式,可以在一段时间内保持静默
  50. config.start_session_req["dialog"]["extra"]["input_mod"] = self.mod
  51. # StartSession request
  52. if self.output_audio_format == "pcm_s16le":
  53. config.start_session_req["tts"]["audio_config"]["format"] = "pcm_s16le"
  54. request_params = config.start_session_req
  55. payload_bytes = str.encode(json.dumps(request_params))
  56. payload_bytes = gzip.compress(payload_bytes)
  57. start_session_request = bytearray(protocol.generate_header())
  58. start_session_request.extend(int(100).to_bytes(4, 'big'))
  59. start_session_request.extend((len(self.session_id)).to_bytes(4, 'big'))
  60. start_session_request.extend(str.encode(self.session_id))
  61. start_session_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  62. start_session_request.extend(payload_bytes)
  63. await self.ws.send(start_session_request)
  64. response = await self.ws.recv()
  65. print(f"StartSession response: {protocol.parse_response(response)}")
  66. async def say_hello(self) -> None:
  67. """发送Hello消息"""
  68. payload = {
  69. "content": "你好,我是豆包,有什么可以帮助你的?",
  70. }
  71. hello_request = bytearray(protocol.generate_header())
  72. hello_request.extend(int(300).to_bytes(4, 'big'))
  73. payload_bytes = str.encode(json.dumps(payload))
  74. payload_bytes = gzip.compress(payload_bytes)
  75. hello_request.extend((len(self.session_id)).to_bytes(4, 'big'))
  76. hello_request.extend(str.encode(self.session_id))
  77. hello_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  78. hello_request.extend(payload_bytes)
  79. await self.ws.send(hello_request)
  80. async def chat_text_query(self, content: str) -> None:
  81. """发送Chat Text Query消息"""
  82. payload = {
  83. "content": content,
  84. }
  85. chat_text_query_request = bytearray(protocol.generate_header())
  86. chat_text_query_request.extend(int(501).to_bytes(4, 'big'))
  87. payload_bytes = str.encode(json.dumps(payload))
  88. payload_bytes = gzip.compress(payload_bytes)
  89. chat_text_query_request.extend((len(self.session_id)).to_bytes(4, 'big'))
  90. chat_text_query_request.extend(str.encode(self.session_id))
  91. chat_text_query_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  92. chat_text_query_request.extend(payload_bytes)
  93. await self.ws.send(chat_text_query_request)
  94. async def chat_tts_text(self, is_user_querying: bool, start: bool, end: bool, content: str) -> None:
  95. if is_user_querying:
  96. return
  97. """发送Chat TTS Text消息"""
  98. payload = {
  99. "start": start,
  100. "end": end,
  101. "content": content,
  102. }
  103. print(f"ChatTTSTextRequest payload: {payload}")
  104. payload_bytes = str.encode(json.dumps(payload))
  105. payload_bytes = gzip.compress(payload_bytes)
  106. chat_tts_text_request = bytearray(protocol.generate_header())
  107. chat_tts_text_request.extend(int(500).to_bytes(4, 'big'))
  108. chat_tts_text_request.extend((len(self.session_id)).to_bytes(4, 'big'))
  109. chat_tts_text_request.extend(str.encode(self.session_id))
  110. chat_tts_text_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  111. chat_tts_text_request.extend(payload_bytes)
  112. await self.ws.send(chat_tts_text_request)
  113. async def chat_rag_text(self, is_user_querying: bool, external_rag: str) -> None:
  114. if is_user_querying:
  115. return
  116. """发送Chat TTS Text消息"""
  117. payload = {
  118. "external_rag": external_rag,
  119. }
  120. print(f"ChatRAGTextRequest payload: {payload}")
  121. payload_bytes = str.encode(json.dumps(payload))
  122. payload_bytes = gzip.compress(payload_bytes)
  123. chat_rag_text_request = bytearray(protocol.generate_header())
  124. chat_rag_text_request.extend(int(502).to_bytes(4, 'big'))
  125. chat_rag_text_request.extend((len(self.session_id)).to_bytes(4, 'big'))
  126. chat_rag_text_request.extend(str.encode(self.session_id))
  127. chat_rag_text_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  128. chat_rag_text_request.extend(payload_bytes)
  129. await self.ws.send(chat_rag_text_request)
  130. async def task_request(self, audio: bytes) -> None:
  131. task_request = bytearray(
  132. protocol.generate_header(message_type=protocol.CLIENT_AUDIO_ONLY_REQUEST,
  133. serial_method=protocol.NO_SERIALIZATION))
  134. task_request.extend(int(200).to_bytes(4, 'big'))
  135. task_request.extend((len(self.session_id)).to_bytes(4, 'big'))
  136. task_request.extend(str.encode(self.session_id))
  137. payload_bytes = gzip.compress(audio)
  138. task_request.extend((len(payload_bytes)).to_bytes(4, 'big')) # payload size(4 bytes)
  139. task_request.extend(payload_bytes)
  140. await self.ws.send(task_request)
  141. async def receive_server_response(self) -> Dict[str, Any]:
  142. try:
  143. response = await self.ws.recv()
  144. data = protocol.parse_response(response)
  145. return data
  146. except Exception as e:
  147. raise Exception(f"Failed to receive message: {e}")
  148. async def finish_session(self):
  149. finish_session_request = bytearray(protocol.generate_header())
  150. finish_session_request.extend(int(102).to_bytes(4, 'big'))
  151. payload_bytes = str.encode("{}")
  152. payload_bytes = gzip.compress(payload_bytes)
  153. finish_session_request.extend((len(self.session_id)).to_bytes(4, 'big'))
  154. finish_session_request.extend(str.encode(self.session_id))
  155. finish_session_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  156. finish_session_request.extend(payload_bytes)
  157. await self.ws.send(finish_session_request)
  158. async def finish_connection(self):
  159. finish_connection_request = bytearray(protocol.generate_header())
  160. finish_connection_request.extend(int(2).to_bytes(4, 'big'))
  161. payload_bytes = str.encode("{}")
  162. payload_bytes = gzip.compress(payload_bytes)
  163. finish_connection_request.extend((len(payload_bytes)).to_bytes(4, 'big'))
  164. finish_connection_request.extend(payload_bytes)
  165. await self.ws.send(finish_connection_request)
  166. response = await self.ws.recv()
  167. print(f"FinishConnection response: {protocol.parse_response(response)}")
  168. async def close(self) -> None:
  169. """关闭WebSocket连接"""
  170. if self.ws:
  171. print(f"Closing WebSocket connection...")
  172. await self.ws.close()