models.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. """
  2. 数据模型定义
  3. """
  4. from typing import Literal, Optional, Dict
  5. from pydantic import BaseModel
  6. # 消息类型
  7. MessageType = Literal[
  8. "audio_chunk",
  9. "audio_end",
  10. "cancel",
  11. "text_result",
  12. "emotion",
  13. "audio_response",
  14. "error",
  15. "ping",
  16. "pong",
  17. "energy_stone_spawn",
  18. "energy_stone_event",
  19. "user_level",
  20. ]
  21. # 情感类型
  22. EmotionType = Literal[
  23. # 基础情感
  24. "neutral", # 中性/平静
  25. "happy", # 开心/愉悦
  26. "sad", # 难过/悲伤
  27. "excited", # 兴奋/激动
  28. "angry", # 生气/愤怒
  29. "surprised", # 惊讶/意外
  30. "thoughtful", # 深思/沉思
  31. "encouraging", # 鼓励/支持
  32. # 扩展情感
  33. "confused", # 困惑/疑惑
  34. "worried", # 担忧/焦虑
  35. "satisfied", # 满意/满足
  36. "friendly", # 友好/亲切
  37. "sorry", # 抱歉/歉意
  38. ]
  39. # 错误代码
  40. ErrorCode = Literal[
  41. "STT_ERROR",
  42. "LLM_ERROR",
  43. "TTS_ERROR",
  44. "NETWORK_ERROR",
  45. "RATE_LIMIT",
  46. "INVALID_AUDIO",
  47. "CANCELLED",
  48. ]
  49. class BaseMessage(BaseModel):
  50. """基础消息"""
  51. type: MessageType
  52. requestId: str
  53. timestamp: int
  54. class AudioChunkMessage(BaseMessage):
  55. """音频数据块消息"""
  56. type: Literal["audio_chunk"] = "audio_chunk"
  57. data: str
  58. sampleRate: int = 16000
  59. channels: int = 1
  60. bitDepth: int = 16
  61. chunkIndex: int
  62. class AudioEndMessage(BaseMessage):
  63. """音频结束消息"""
  64. type: Literal["audio_end"] = "audio_end"
  65. totalChunks: int
  66. speechDuration: Optional[int] = None # 说话时长(毫秒)
  67. class CancelMessage(BaseMessage):
  68. """取消消息"""
  69. type: Literal["cancel"] = "cancel"
  70. class TextResultMessage(BaseMessage):
  71. """文本结果消息"""
  72. type: Literal["text_result"] = "text_result"
  73. text: str
  74. isFinal: bool
  75. class EmotionMessage(BaseMessage):
  76. """情感消息"""
  77. type: Literal["emotion"] = "emotion"
  78. emotion: EmotionType
  79. intensity: Optional[float] = None
  80. class AudioResponseMessage(BaseMessage):
  81. """音频响应消息"""
  82. type: Literal["audio_response"] = "audio_response"
  83. data: str
  84. sampleRate: int = 24000 # 流式 TTS 使用 24kHz
  85. channels: int = 1
  86. bitDepth: int = 16
  87. format: Literal["pcm", "wav"] = "pcm" # 音频格式:pcm(流式)或 wav(完整)
  88. chunkIndex: int
  89. isLast: bool
  90. class ErrorMessage(BaseMessage):
  91. """错误消息"""
  92. type: Literal["error"] = "error"
  93. code: ErrorCode
  94. message: str
  95. class PingMessage(BaseMessage):
  96. """Ping消息"""
  97. type: Literal["ping"] = "ping"
  98. class PongMessage(BaseMessage):
  99. """Pong消息"""
  100. type: Literal["pong"] = "pong"
  101. StoneType = Literal["mission", "conversation"]
  102. EnergyStoneEvent = Literal["click", "timeout"]
  103. class EnergyStoneSpawnMessage(BaseMessage):
  104. """能量石生成消息"""
  105. type: Literal["energy_stone_spawn"] = "energy_stone_spawn"
  106. stoneId: str
  107. stoneType: StoneType
  108. ttlSeconds: int
  109. appearAt: int
  110. metadata: Optional[Dict] = None
  111. class EnergyStoneEventMessage(BaseMessage):
  112. """能量石交互事件"""
  113. type: Literal["energy_stone_event"] = "energy_stone_event"
  114. stoneId: str
  115. event: EnergyStoneEvent
  116. clientTs: Optional[int] = None
  117. position: Optional[Dict] = None
  118. class UserLevelMessage(BaseMessage):
  119. """用户等级更新消息"""
  120. type: Literal["user_level"] = "user_level"
  121. level: int
  122. totalDurationMs: int