protocol.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. """
  2. 豆包API协议模块
  3. 复制自官方demo,用于处理豆包API的消息协议
  4. """
  5. import gzip
  6. import json
  7. PROTOCOL_VERSION = 0b0001
  8. DEFAULT_HEADER_SIZE = 0b0001
  9. PROTOCOL_VERSION_BITS = 4
  10. HEADER_BITS = 4
  11. MESSAGE_TYPE_BITS = 4
  12. MESSAGE_TYPE_SPECIFIC_FLAGS_BITS = 4
  13. MESSAGE_SERIALIZATION_BITS = 4
  14. MESSAGE_COMPRESSION_BITS = 4
  15. RESERVED_BITS = 8
  16. # Message Type:
  17. CLIENT_FULL_REQUEST = 0b0001
  18. CLIENT_AUDIO_ONLY_REQUEST = 0b0010
  19. SERVER_FULL_RESPONSE = 0b1001
  20. SERVER_ACK = 0b1011
  21. SERVER_ERROR_RESPONSE = 0b1111
  22. # Message Type Specific Flags
  23. NO_SEQUENCE = 0b0000 # no check sequence
  24. POS_SEQUENCE = 0b0001
  25. NEG_SEQUENCE = 0b0010
  26. NEG_SEQUENCE_1 = 0b0011
  27. MSG_WITH_EVENT = 0b0100
  28. # Message Serialization
  29. NO_SERIALIZATION = 0b0000
  30. JSON = 0b0001
  31. THRIFT = 0b0011
  32. CUSTOM_TYPE = 0b1111
  33. # Message Compression
  34. NO_COMPRESSION = 0b0000
  35. GZIP = 0b0001
  36. CUSTOM_COMPRESSION = 0b1111
  37. def generate_header(
  38. version=PROTOCOL_VERSION,
  39. message_type=CLIENT_FULL_REQUEST,
  40. message_type_specific_flags=MSG_WITH_EVENT,
  41. serial_method=JSON,
  42. compression_type=GZIP,
  43. reserved_data=0x00,
  44. extension_header=bytes()
  45. ):
  46. """
  47. 生成协议头
  48. protocol_version(4 bits), header_size(4 bits),
  49. message_type(4 bits), message_type_specific_flags(4 bits)
  50. serialization_method(4 bits) message_compression(4 bits)
  51. reserved (8bits) 保留字段
  52. header_extensions 扩展头(大小等于 8 * 4 * (header_size - 1) )
  53. """
  54. header = bytearray()
  55. header_size = int(len(extension_header) / 4) + 1
  56. header.append((version << 4) | header_size)
  57. header.append((message_type << 4) | message_type_specific_flags)
  58. header.append((serial_method << 4) | compression_type)
  59. header.append(reserved_data)
  60. header.extend(extension_header)
  61. return header
  62. def parse_response(res):
  63. """
  64. 解析响应消息
  65. - header
  66. - (4bytes)header
  67. - (4bits)version(v1) + (4bits)header_size
  68. - (4bits)messageType + (4bits)messageTypeFlags
  69. -- 0001 CompleteClient | -- 0001 hasSequence
  70. -- 0010 audioonly | -- 0010 isTailPacket
  71. | -- 0100 hasEvent
  72. - (4bits)payloadFormat + (4bits)compression
  73. - (8bits) reserve
  74. - payload
  75. - [optional 4 bytes] event
  76. - [optional] session ID
  77. -- (4 bytes)session ID len
  78. -- session ID data
  79. - (4 bytes)data len
  80. - data
  81. """
  82. if isinstance(res, str):
  83. return {}
  84. protocol_version = res[0] >> 4
  85. header_size = res[0] & 0x0f
  86. message_type = res[1] >> 4
  87. message_type_specific_flags = res[1] & 0x0f
  88. serialization_method = res[2] >> 4
  89. message_compression = res[2] & 0x0f
  90. reserved = res[3]
  91. header_extensions = res[4:header_size * 4]
  92. payload = res[header_size * 4:]
  93. result = {}
  94. payload_msg = None
  95. payload_size = 0
  96. start = 0
  97. if message_type == SERVER_FULL_RESPONSE or message_type == SERVER_ACK:
  98. result['message_type'] = 'SERVER_FULL_RESPONSE'
  99. if message_type == SERVER_ACK:
  100. result['message_type'] = 'SERVER_ACK'
  101. if message_type_specific_flags & NEG_SEQUENCE > 0:
  102. result['seq'] = int.from_bytes(payload[:4], "big", signed=False)
  103. start += 4
  104. if message_type_specific_flags & MSG_WITH_EVENT > 0:
  105. result['event'] = int.from_bytes(payload[:4], "big", signed=False)
  106. start += 4
  107. payload = payload[start:]
  108. session_id_size = int.from_bytes(payload[:4], "big", signed=True)
  109. session_id = payload[4:session_id_size+4]
  110. result['session_id'] = str(session_id)
  111. payload = payload[4 + session_id_size:]
  112. payload_size = int.from_bytes(payload[:4], "big", signed=False)
  113. payload_msg = payload[4:]
  114. elif message_type == SERVER_ERROR_RESPONSE:
  115. code = int.from_bytes(payload[:4], "big", signed=False)
  116. result['code'] = code
  117. payload_size = int.from_bytes(payload[4:8], "big", signed=False)
  118. payload_msg = payload[8:]
  119. if payload_msg is None:
  120. return result
  121. if message_compression == GZIP:
  122. payload_msg = gzip.decompress(payload_msg)
  123. if serialization_method == JSON:
  124. payload_msg = json.loads(str(payload_msg, "utf-8"))
  125. elif serialization_method != NO_SERIALIZATION:
  126. payload_msg = str(payload_msg, "utf-8")
  127. result['payload_msg'] = payload_msg
  128. result['payload_size'] = payload_size
  129. return result