models.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. "SESSION_INVALIDATED",
  49. ]
  50. class BaseMessage(BaseModel):
  51. """基础消息"""
  52. type: MessageType
  53. requestId: str
  54. timestamp: int
  55. token: Optional[str] = None # 会话token(客户端->服务端鉴权)
  56. class AudioChunkMessage(BaseMessage):
  57. """音频数据块消息"""
  58. type: Literal["audio_chunk"] = "audio_chunk"
  59. data: str
  60. sampleRate: int = 16000
  61. channels: int = 1
  62. bitDepth: int = 16
  63. chunkIndex: int
  64. class AudioEndMessage(BaseMessage):
  65. """音频结束消息"""
  66. type: Literal["audio_end"] = "audio_end"
  67. totalChunks: int
  68. speechDuration: Optional[int] = None # 说话时长(毫秒)
  69. class CancelMessage(BaseMessage):
  70. """取消消息"""
  71. type: Literal["cancel"] = "cancel"
  72. class TextResultMessage(BaseMessage):
  73. """文本结果消息"""
  74. type: Literal["text_result"] = "text_result"
  75. text: str
  76. isFinal: bool
  77. class EmotionMessage(BaseMessage):
  78. """情感消息"""
  79. type: Literal["emotion"] = "emotion"
  80. emotion: EmotionType
  81. intensity: Optional[float] = None
  82. class AudioResponseMessage(BaseMessage):
  83. """音频响应消息"""
  84. type: Literal["audio_response"] = "audio_response"
  85. data: str
  86. sampleRate: int = 24000 # 流式 TTS 使用 24kHz
  87. channels: int = 1
  88. bitDepth: int = 16
  89. format: Literal["pcm", "wav"] = "pcm" # 音频格式:pcm(流式)或 wav(完整)
  90. chunkIndex: int
  91. isLast: bool
  92. class ErrorMessage(BaseMessage):
  93. """错误消息"""
  94. type: Literal["error"] = "error"
  95. code: ErrorCode
  96. message: str
  97. class PingMessage(BaseMessage):
  98. """Ping消息"""
  99. type: Literal["ping"] = "ping"
  100. class PongMessage(BaseMessage):
  101. """Pong消息"""
  102. type: Literal["pong"] = "pong"
  103. StoneType = Literal["mission", "conversation"]
  104. EnergyStoneEvent = Literal["click", "timeout"]
  105. class EnergyStoneSpawnMessage(BaseMessage):
  106. """能量石生成消息"""
  107. type: Literal["energy_stone_spawn"] = "energy_stone_spawn"
  108. stoneId: str
  109. stoneType: StoneType
  110. ttlSeconds: int
  111. appearAt: int
  112. metadata: Optional[Dict] = None
  113. class EnergyStoneEventMessage(BaseMessage):
  114. """能量石交互事件"""
  115. type: Literal["energy_stone_event"] = "energy_stone_event"
  116. stoneId: str
  117. event: EnergyStoneEvent
  118. clientTs: Optional[int] = None
  119. position: Optional[Dict] = None
  120. class UserLevelMessage(BaseMessage):
  121. """用户等级更新消息"""
  122. type: Literal["user_level"] = "user_level"
  123. level: int
  124. totalDurationMs: int