models.py 3.2 KB

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