dv-pipecat-ai 0.0.74.dev770__py3-none-any.whl → 0.0.82.dev776__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dv-pipecat-ai might be problematic. Click here for more details.

Files changed (244) hide show
  1. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/METADATA +137 -93
  2. dv_pipecat_ai-0.0.82.dev776.dist-info/RECORD +340 -0
  3. pipecat/__init__.py +17 -0
  4. pipecat/adapters/base_llm_adapter.py +36 -1
  5. pipecat/adapters/schemas/direct_function.py +296 -0
  6. pipecat/adapters/schemas/function_schema.py +15 -6
  7. pipecat/adapters/schemas/tools_schema.py +55 -7
  8. pipecat/adapters/services/anthropic_adapter.py +22 -3
  9. pipecat/adapters/services/aws_nova_sonic_adapter.py +23 -3
  10. pipecat/adapters/services/bedrock_adapter.py +22 -3
  11. pipecat/adapters/services/gemini_adapter.py +16 -3
  12. pipecat/adapters/services/open_ai_adapter.py +17 -2
  13. pipecat/adapters/services/open_ai_realtime_adapter.py +23 -3
  14. pipecat/audio/filters/base_audio_filter.py +30 -6
  15. pipecat/audio/filters/koala_filter.py +37 -2
  16. pipecat/audio/filters/krisp_filter.py +59 -6
  17. pipecat/audio/filters/noisereduce_filter.py +37 -0
  18. pipecat/audio/interruptions/base_interruption_strategy.py +25 -5
  19. pipecat/audio/interruptions/min_words_interruption_strategy.py +21 -4
  20. pipecat/audio/mixers/base_audio_mixer.py +30 -7
  21. pipecat/audio/mixers/soundfile_mixer.py +53 -6
  22. pipecat/audio/resamplers/base_audio_resampler.py +17 -9
  23. pipecat/audio/resamplers/resampy_resampler.py +26 -1
  24. pipecat/audio/resamplers/soxr_resampler.py +32 -1
  25. pipecat/audio/resamplers/soxr_stream_resampler.py +101 -0
  26. pipecat/audio/utils.py +194 -1
  27. pipecat/audio/vad/silero.py +60 -3
  28. pipecat/audio/vad/vad_analyzer.py +114 -30
  29. pipecat/clocks/base_clock.py +19 -0
  30. pipecat/clocks/system_clock.py +25 -0
  31. pipecat/extensions/voicemail/__init__.py +0 -0
  32. pipecat/extensions/voicemail/voicemail_detector.py +707 -0
  33. pipecat/frames/frames.py +590 -156
  34. pipecat/metrics/metrics.py +64 -1
  35. pipecat/observers/base_observer.py +58 -19
  36. pipecat/observers/loggers/debug_log_observer.py +56 -64
  37. pipecat/observers/loggers/llm_log_observer.py +8 -1
  38. pipecat/observers/loggers/transcription_log_observer.py +19 -7
  39. pipecat/observers/loggers/user_bot_latency_log_observer.py +32 -5
  40. pipecat/observers/turn_tracking_observer.py +26 -1
  41. pipecat/pipeline/base_pipeline.py +5 -7
  42. pipecat/pipeline/base_task.py +52 -9
  43. pipecat/pipeline/parallel_pipeline.py +121 -177
  44. pipecat/pipeline/pipeline.py +129 -20
  45. pipecat/pipeline/runner.py +50 -1
  46. pipecat/pipeline/sync_parallel_pipeline.py +132 -32
  47. pipecat/pipeline/task.py +263 -280
  48. pipecat/pipeline/task_observer.py +85 -34
  49. pipecat/pipeline/to_be_updated/merge_pipeline.py +32 -2
  50. pipecat/processors/aggregators/dtmf_aggregator.py +29 -22
  51. pipecat/processors/aggregators/gated.py +25 -24
  52. pipecat/processors/aggregators/gated_openai_llm_context.py +22 -2
  53. pipecat/processors/aggregators/llm_response.py +398 -89
  54. pipecat/processors/aggregators/openai_llm_context.py +161 -13
  55. pipecat/processors/aggregators/sentence.py +25 -14
  56. pipecat/processors/aggregators/user_response.py +28 -3
  57. pipecat/processors/aggregators/vision_image_frame.py +24 -14
  58. pipecat/processors/async_generator.py +28 -0
  59. pipecat/processors/audio/audio_buffer_processor.py +78 -37
  60. pipecat/processors/consumer_processor.py +25 -6
  61. pipecat/processors/filters/frame_filter.py +23 -0
  62. pipecat/processors/filters/function_filter.py +30 -0
  63. pipecat/processors/filters/identity_filter.py +17 -2
  64. pipecat/processors/filters/null_filter.py +24 -1
  65. pipecat/processors/filters/stt_mute_filter.py +56 -21
  66. pipecat/processors/filters/wake_check_filter.py +46 -3
  67. pipecat/processors/filters/wake_notifier_filter.py +21 -3
  68. pipecat/processors/frame_processor.py +488 -131
  69. pipecat/processors/frameworks/langchain.py +38 -3
  70. pipecat/processors/frameworks/rtvi.py +719 -34
  71. pipecat/processors/gstreamer/pipeline_source.py +41 -0
  72. pipecat/processors/idle_frame_processor.py +26 -3
  73. pipecat/processors/logger.py +23 -0
  74. pipecat/processors/metrics/frame_processor_metrics.py +77 -4
  75. pipecat/processors/metrics/sentry.py +42 -4
  76. pipecat/processors/producer_processor.py +34 -14
  77. pipecat/processors/text_transformer.py +22 -10
  78. pipecat/processors/transcript_processor.py +48 -29
  79. pipecat/processors/user_idle_processor.py +31 -21
  80. pipecat/runner/__init__.py +1 -0
  81. pipecat/runner/daily.py +132 -0
  82. pipecat/runner/livekit.py +148 -0
  83. pipecat/runner/run.py +543 -0
  84. pipecat/runner/types.py +67 -0
  85. pipecat/runner/utils.py +515 -0
  86. pipecat/serializers/base_serializer.py +42 -0
  87. pipecat/serializers/exotel.py +17 -6
  88. pipecat/serializers/genesys.py +95 -0
  89. pipecat/serializers/livekit.py +33 -0
  90. pipecat/serializers/plivo.py +16 -15
  91. pipecat/serializers/protobuf.py +37 -1
  92. pipecat/serializers/telnyx.py +18 -17
  93. pipecat/serializers/twilio.py +32 -16
  94. pipecat/services/ai_service.py +5 -3
  95. pipecat/services/anthropic/llm.py +113 -43
  96. pipecat/services/assemblyai/models.py +63 -5
  97. pipecat/services/assemblyai/stt.py +64 -11
  98. pipecat/services/asyncai/__init__.py +0 -0
  99. pipecat/services/asyncai/tts.py +501 -0
  100. pipecat/services/aws/llm.py +185 -111
  101. pipecat/services/aws/stt.py +217 -23
  102. pipecat/services/aws/tts.py +118 -52
  103. pipecat/services/aws/utils.py +101 -5
  104. pipecat/services/aws_nova_sonic/aws.py +82 -64
  105. pipecat/services/aws_nova_sonic/context.py +15 -6
  106. pipecat/services/azure/common.py +10 -2
  107. pipecat/services/azure/image.py +32 -0
  108. pipecat/services/azure/llm.py +9 -7
  109. pipecat/services/azure/stt.py +65 -2
  110. pipecat/services/azure/tts.py +154 -23
  111. pipecat/services/cartesia/stt.py +125 -8
  112. pipecat/services/cartesia/tts.py +102 -38
  113. pipecat/services/cerebras/llm.py +15 -23
  114. pipecat/services/deepgram/stt.py +19 -11
  115. pipecat/services/deepgram/tts.py +36 -0
  116. pipecat/services/deepseek/llm.py +14 -23
  117. pipecat/services/elevenlabs/tts.py +330 -64
  118. pipecat/services/fal/image.py +43 -0
  119. pipecat/services/fal/stt.py +48 -10
  120. pipecat/services/fireworks/llm.py +14 -21
  121. pipecat/services/fish/tts.py +109 -9
  122. pipecat/services/gemini_multimodal_live/__init__.py +1 -0
  123. pipecat/services/gemini_multimodal_live/events.py +83 -2
  124. pipecat/services/gemini_multimodal_live/file_api.py +189 -0
  125. pipecat/services/gemini_multimodal_live/gemini.py +218 -21
  126. pipecat/services/gladia/config.py +17 -10
  127. pipecat/services/gladia/stt.py +82 -36
  128. pipecat/services/google/frames.py +40 -0
  129. pipecat/services/google/google.py +2 -0
  130. pipecat/services/google/image.py +39 -2
  131. pipecat/services/google/llm.py +176 -58
  132. pipecat/services/google/llm_openai.py +26 -4
  133. pipecat/services/google/llm_vertex.py +37 -15
  134. pipecat/services/google/rtvi.py +41 -0
  135. pipecat/services/google/stt.py +65 -17
  136. pipecat/services/google/test-google-chirp.py +45 -0
  137. pipecat/services/google/tts.py +390 -19
  138. pipecat/services/grok/llm.py +8 -6
  139. pipecat/services/groq/llm.py +8 -6
  140. pipecat/services/groq/stt.py +13 -9
  141. pipecat/services/groq/tts.py +40 -0
  142. pipecat/services/hamsa/__init__.py +9 -0
  143. pipecat/services/hamsa/stt.py +241 -0
  144. pipecat/services/heygen/__init__.py +5 -0
  145. pipecat/services/heygen/api.py +281 -0
  146. pipecat/services/heygen/client.py +620 -0
  147. pipecat/services/heygen/video.py +338 -0
  148. pipecat/services/image_service.py +5 -3
  149. pipecat/services/inworld/__init__.py +1 -0
  150. pipecat/services/inworld/tts.py +592 -0
  151. pipecat/services/llm_service.py +127 -45
  152. pipecat/services/lmnt/tts.py +80 -7
  153. pipecat/services/mcp_service.py +85 -44
  154. pipecat/services/mem0/memory.py +42 -13
  155. pipecat/services/minimax/tts.py +74 -15
  156. pipecat/services/mistral/__init__.py +0 -0
  157. pipecat/services/mistral/llm.py +185 -0
  158. pipecat/services/moondream/vision.py +55 -10
  159. pipecat/services/neuphonic/tts.py +275 -48
  160. pipecat/services/nim/llm.py +8 -6
  161. pipecat/services/ollama/llm.py +27 -7
  162. pipecat/services/openai/base_llm.py +54 -16
  163. pipecat/services/openai/image.py +30 -0
  164. pipecat/services/openai/llm.py +7 -5
  165. pipecat/services/openai/stt.py +13 -9
  166. pipecat/services/openai/tts.py +42 -10
  167. pipecat/services/openai_realtime_beta/azure.py +11 -9
  168. pipecat/services/openai_realtime_beta/context.py +7 -5
  169. pipecat/services/openai_realtime_beta/events.py +10 -7
  170. pipecat/services/openai_realtime_beta/openai.py +37 -18
  171. pipecat/services/openpipe/llm.py +30 -24
  172. pipecat/services/openrouter/llm.py +9 -7
  173. pipecat/services/perplexity/llm.py +15 -19
  174. pipecat/services/piper/tts.py +26 -12
  175. pipecat/services/playht/tts.py +227 -65
  176. pipecat/services/qwen/llm.py +8 -6
  177. pipecat/services/rime/tts.py +128 -17
  178. pipecat/services/riva/stt.py +160 -22
  179. pipecat/services/riva/tts.py +67 -2
  180. pipecat/services/sambanova/llm.py +19 -17
  181. pipecat/services/sambanova/stt.py +14 -8
  182. pipecat/services/sarvam/tts.py +60 -13
  183. pipecat/services/simli/video.py +82 -21
  184. pipecat/services/soniox/__init__.py +0 -0
  185. pipecat/services/soniox/stt.py +398 -0
  186. pipecat/services/speechmatics/stt.py +29 -17
  187. pipecat/services/stt_service.py +47 -11
  188. pipecat/services/tavus/video.py +94 -25
  189. pipecat/services/together/llm.py +8 -6
  190. pipecat/services/tts_service.py +77 -53
  191. pipecat/services/ultravox/stt.py +46 -43
  192. pipecat/services/vision_service.py +5 -3
  193. pipecat/services/websocket_service.py +12 -11
  194. pipecat/services/whisper/base_stt.py +58 -12
  195. pipecat/services/whisper/stt.py +69 -58
  196. pipecat/services/xtts/tts.py +59 -2
  197. pipecat/sync/base_notifier.py +19 -0
  198. pipecat/sync/event_notifier.py +24 -0
  199. pipecat/tests/utils.py +73 -5
  200. pipecat/transcriptions/language.py +24 -0
  201. pipecat/transports/base_input.py +112 -8
  202. pipecat/transports/base_output.py +235 -13
  203. pipecat/transports/base_transport.py +119 -0
  204. pipecat/transports/local/audio.py +76 -0
  205. pipecat/transports/local/tk.py +84 -0
  206. pipecat/transports/network/fastapi_websocket.py +174 -15
  207. pipecat/transports/network/small_webrtc.py +383 -39
  208. pipecat/transports/network/webrtc_connection.py +214 -8
  209. pipecat/transports/network/websocket_client.py +171 -1
  210. pipecat/transports/network/websocket_server.py +147 -9
  211. pipecat/transports/services/daily.py +792 -70
  212. pipecat/transports/services/helpers/daily_rest.py +122 -129
  213. pipecat/transports/services/livekit.py +339 -4
  214. pipecat/transports/services/tavus.py +273 -38
  215. pipecat/utils/asyncio/task_manager.py +92 -186
  216. pipecat/utils/base_object.py +83 -1
  217. pipecat/utils/network.py +2 -0
  218. pipecat/utils/string.py +114 -58
  219. pipecat/utils/text/base_text_aggregator.py +44 -13
  220. pipecat/utils/text/base_text_filter.py +46 -0
  221. pipecat/utils/text/markdown_text_filter.py +70 -14
  222. pipecat/utils/text/pattern_pair_aggregator.py +18 -14
  223. pipecat/utils/text/simple_text_aggregator.py +43 -2
  224. pipecat/utils/text/skip_tags_aggregator.py +21 -13
  225. pipecat/utils/time.py +36 -0
  226. pipecat/utils/tracing/class_decorators.py +32 -7
  227. pipecat/utils/tracing/conversation_context_provider.py +12 -2
  228. pipecat/utils/tracing/service_attributes.py +80 -64
  229. pipecat/utils/tracing/service_decorators.py +48 -21
  230. pipecat/utils/tracing/setup.py +13 -7
  231. pipecat/utils/tracing/turn_context_provider.py +12 -2
  232. pipecat/utils/tracing/turn_trace_observer.py +27 -0
  233. pipecat/utils/utils.py +14 -14
  234. dv_pipecat_ai-0.0.74.dev770.dist-info/RECORD +0 -319
  235. pipecat/examples/daily_runner.py +0 -64
  236. pipecat/examples/run.py +0 -265
  237. pipecat/utils/asyncio/watchdog_async_iterator.py +0 -72
  238. pipecat/utils/asyncio/watchdog_event.py +0 -42
  239. pipecat/utils/asyncio/watchdog_priority_queue.py +0 -48
  240. pipecat/utils/asyncio/watchdog_queue.py +0 -48
  241. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/WHEEL +0 -0
  242. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/licenses/LICENSE +0 -0
  243. {dv_pipecat_ai-0.0.74.dev770.dist-info → dv_pipecat_ai-0.0.82.dev776.dist-info}/top_level.txt +0 -0
  244. /pipecat/{examples → extensions}/__init__.py +0 -0
@@ -4,6 +4,13 @@
4
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
5
  #
6
6
 
7
+ """RTVI (Real-Time Voice Interface) protocol implementation for Pipecat.
8
+
9
+ This module provides the RTVI protocol implementation for real-time voice interactions
10
+ between clients and AI agents. It includes message handling, action processing,
11
+ and frame observation for the RTVI protocol.
12
+ """
13
+
7
14
  import asyncio
8
15
  import base64
9
16
  from dataclasses import dataclass
@@ -37,6 +44,7 @@ from pipecat.frames.frames import (
37
44
  InterimTranscriptionFrame,
38
45
  LLMFullResponseEndFrame,
39
46
  LLMFullResponseStartFrame,
47
+ LLMMessagesAppendFrame,
40
48
  LLMTextFrame,
41
49
  MetricsFrame,
42
50
  StartFrame,
@@ -67,10 +75,9 @@ from pipecat.services.llm_service import (
67
75
  from pipecat.transports.base_input import BaseInputTransport
68
76
  from pipecat.transports.base_output import BaseOutputTransport
69
77
  from pipecat.transports.base_transport import BaseTransport
70
- from pipecat.utils.asyncio.watchdog_queue import WatchdogQueue
71
78
  from pipecat.utils.string import match_endofsentence
72
79
 
73
- RTVI_PROTOCOL_VERSION = "0.3.0"
80
+ RTVI_PROTOCOL_VERSION = "1.0.0"
74
81
 
75
82
  RTVI_MESSAGE_LABEL = "rtvi-ai"
76
83
  RTVIMessageLiteral = Literal["rtvi-ai"]
@@ -79,6 +86,16 @@ ActionResult = Union[bool, int, float, str, list, dict]
79
86
 
80
87
 
81
88
  class RTVIServiceOption(BaseModel):
89
+ """Configuration option for an RTVI service.
90
+
91
+ Defines a configurable option that can be set for an RTVI service,
92
+ including its name, type, and handler function.
93
+
94
+ .. deprecated:: 0.0.75
95
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
96
+ Use custom client and server messages instead.
97
+ """
98
+
82
99
  name: str
83
100
  type: Literal["bool", "number", "string", "array", "object"]
84
101
  handler: Callable[["RTVIProcessor", str, "RTVIServiceOptionConfig"], Awaitable[None]] = Field(
@@ -87,11 +104,22 @@ class RTVIServiceOption(BaseModel):
87
104
 
88
105
 
89
106
  class RTVIService(BaseModel):
107
+ """An RTVI service definition.
108
+
109
+ Represents a service that can be configured and used within the RTVI protocol,
110
+ containing a name and list of configurable options.
111
+
112
+ .. deprecated:: 0.0.75
113
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
114
+ Use custom client and server messages instead.
115
+ """
116
+
90
117
  name: str
91
118
  options: List[RTVIServiceOption]
92
119
  _options_dict: Dict[str, RTVIServiceOption] = PrivateAttr(default={})
93
120
 
94
121
  def model_post_init(self, __context: Any) -> None:
122
+ """Initialize the options dictionary after model creation."""
95
123
  self._options_dict = {}
96
124
  for option in self.options:
97
125
  self._options_dict[option.name] = option
@@ -99,16 +127,44 @@ class RTVIService(BaseModel):
99
127
 
100
128
 
101
129
  class RTVIActionArgumentData(BaseModel):
130
+ """Data for an RTVI action argument.
131
+
132
+ Contains the name and value of an argument passed to an RTVI action.
133
+
134
+ .. deprecated:: 0.0.75
135
+ Actions have been removed as part of the RTVI protocol 1.0.0.
136
+ Use custom client and server messages instead.
137
+ """
138
+
102
139
  name: str
103
140
  value: Any
104
141
 
105
142
 
106
143
  class RTVIActionArgument(BaseModel):
144
+ """Definition of an RTVI action argument.
145
+
146
+ Specifies the name and expected type of an argument for an RTVI action.
147
+
148
+ .. deprecated:: 0.0.75
149
+ Actions have been removed as part of the RTVI protocol 1.0.0.
150
+ Use custom client and server messages instead.
151
+ """
152
+
107
153
  name: str
108
154
  type: Literal["bool", "number", "string", "array", "object"]
109
155
 
110
156
 
111
157
  class RTVIAction(BaseModel):
158
+ """An RTVI action definition.
159
+
160
+ Represents an action that can be executed within the RTVI protocol,
161
+ including its service, name, arguments, and handler function.
162
+
163
+ .. deprecated:: 0.0.75
164
+ Actions have been removed as part of the RTVI protocol 1.0.0.
165
+ Use custom client and server messages instead.
166
+ """
167
+
112
168
  service: str
113
169
  action: str
114
170
  arguments: List[RTVIActionArgument] = Field(default_factory=list)
@@ -119,6 +175,7 @@ class RTVIAction(BaseModel):
119
175
  _arguments_dict: Dict[str, RTVIActionArgument] = PrivateAttr(default={})
120
176
 
121
177
  def model_post_init(self, __context: Any) -> None:
178
+ """Initialize the arguments dictionary after model creation."""
122
179
  self._arguments_dict = {}
123
180
  for arg in self.arguments:
124
181
  self._arguments_dict[arg.name] = arg
@@ -126,16 +183,43 @@ class RTVIAction(BaseModel):
126
183
 
127
184
 
128
185
  class RTVIServiceOptionConfig(BaseModel):
186
+ """Configuration value for an RTVI service option.
187
+
188
+ Contains the name and value to set for a specific service option.
189
+
190
+ .. deprecated:: 0.0.75
191
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
192
+ Use custom client and server messages instead.
193
+ """
194
+
129
195
  name: str
130
196
  value: Any
131
197
 
132
198
 
133
199
  class RTVIServiceConfig(BaseModel):
200
+ """Configuration for an RTVI service.
201
+
202
+ Contains the service name and list of option configurations to apply.
203
+
204
+ .. deprecated:: 0.0.75
205
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
206
+ Use custom client and server messages instead.
207
+ """
208
+
134
209
  service: str
135
210
  options: List[RTVIServiceOptionConfig]
136
211
 
137
212
 
138
213
  class RTVIConfig(BaseModel):
214
+ """Complete RTVI configuration.
215
+
216
+ Contains the full configuration for all RTVI services.
217
+
218
+ .. deprecated:: 0.0.75
219
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
220
+ Use custom client and server messages instead.
221
+ """
222
+
139
223
  config: List[RTVIServiceConfig]
140
224
 
141
225
 
@@ -144,17 +228,45 @@ class RTVIConfig(BaseModel):
144
228
  #
145
229
 
146
230
 
231
+ # deprecated
147
232
  class RTVIUpdateConfig(BaseModel):
233
+ """Request to update RTVI configuration.
234
+
235
+ Contains new configuration settings and whether to interrupt the bot.
236
+
237
+ .. deprecated:: 0.0.75
238
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
239
+ Use custom client and server messages instead.
240
+ """
241
+
148
242
  config: List[RTVIServiceConfig]
149
243
  interrupt: bool = False
150
244
 
151
245
 
152
246
  class RTVIActionRunArgument(BaseModel):
247
+ """Argument for running an RTVI action.
248
+
249
+ Contains the name and value of an argument to pass to an action.
250
+
251
+ .. deprecated:: 0.0.75
252
+ Actions have been removed as part of the RTVI protocol 1.0.0.
253
+ Use custom client and server messages instead.
254
+ """
255
+
153
256
  name: str
154
257
  value: Any
155
258
 
156
259
 
157
260
  class RTVIActionRun(BaseModel):
261
+ """Request to run an RTVI action.
262
+
263
+ Contains the service, action name, and optional arguments.
264
+
265
+ .. deprecated:: 0.0.75
266
+ Actions have been removed as part of the RTVI protocol 1.0.0.
267
+ Use custom client and server messages instead.
268
+ """
269
+
158
270
  service: str
159
271
  action: str
160
272
  arguments: Optional[List[RTVIActionRunArgument]] = None
@@ -162,11 +274,91 @@ class RTVIActionRun(BaseModel):
162
274
 
163
275
  @dataclass
164
276
  class RTVIActionFrame(DataFrame):
277
+ """Frame containing an RTVI action to execute.
278
+
279
+ Parameters:
280
+ rtvi_action_run: The action to execute.
281
+ message_id: Optional message ID for response correlation.
282
+
283
+ .. deprecated:: 0.0.75
284
+ Actions have been removed as part of the RTVI protocol 1.0.0.
285
+ Use custom client and server messages instead.
286
+ """
287
+
165
288
  rtvi_action_run: RTVIActionRun
166
289
  message_id: Optional[str] = None
167
290
 
168
291
 
292
+ class RTVIRawClientMessageData(BaseModel):
293
+ """Data structure expected from client messages sent to the RTVI server."""
294
+
295
+ t: str
296
+ d: Optional[Any] = None
297
+
298
+
299
+ class RTVIClientMessage(BaseModel):
300
+ """Cleansed data structure for client messages for handling."""
301
+
302
+ msg_id: str
303
+ type: str
304
+ data: Optional[Any] = None
305
+
306
+
307
+ @dataclass
308
+ class RTVIClientMessageFrame(SystemFrame):
309
+ """A frame for sending messages from the client to the RTVI server.
310
+
311
+ This frame is meant for custom messaging from the client to the server
312
+ and expects a server-response message.
313
+ """
314
+
315
+ msg_id: str
316
+ type: str
317
+ data: Optional[Any] = None
318
+
319
+
320
+ @dataclass
321
+ class RTVIServerResponseFrame(SystemFrame):
322
+ """A frame for responding to a client RTVI message.
323
+
324
+ This frame should be sent in response to an RTVIClientMessageFrame
325
+ and include the original RTVIClientMessageFrame to ensure the response
326
+ is properly attributed to the original request. To respond with an error,
327
+ set the `error` field to a string describing the error. This will result
328
+ in the client receiving a `response-error` message instead of a
329
+ `server-response` message.
330
+ """
331
+
332
+ client_msg: RTVIClientMessageFrame
333
+ data: Optional[Any] = None
334
+ error: Optional[str] = None
335
+
336
+
337
+ class RTVIRawServerResponseData(BaseModel):
338
+ """Data structure for server responses to client messages."""
339
+
340
+ t: str
341
+ d: Optional[Any] = None
342
+
343
+
344
+ class RTVIServerResponse(BaseModel):
345
+ """The RTVI-formatted message response from the server to the client.
346
+
347
+ This message is used to respond to custom messages sent by the client.
348
+ """
349
+
350
+ label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
351
+ type: Literal["server-response"] = "server-response"
352
+ id: str
353
+ data: RTVIRawServerResponseData
354
+
355
+
169
356
  class RTVIMessage(BaseModel):
357
+ """Base RTVI message structure.
358
+
359
+ Represents the standard format for RTVI protocol messages.
360
+ """
361
+
170
362
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
171
363
  type: str
172
364
  id: str
@@ -179,10 +371,20 @@ class RTVIMessage(BaseModel):
179
371
 
180
372
 
181
373
  class RTVIErrorResponseData(BaseModel):
374
+ """Data for an RTVI error response.
375
+
376
+ Contains the error message to send back to the client.
377
+ """
378
+
182
379
  error: str
183
380
 
184
381
 
185
382
  class RTVIErrorResponse(BaseModel):
383
+ """RTVI error response message.
384
+
385
+ RTVI Formatted error response message for relaying failed client requests.
386
+ """
387
+
186
388
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
187
389
  type: Literal["error-response"] = "error-response"
188
390
  id: str
@@ -190,21 +392,49 @@ class RTVIErrorResponse(BaseModel):
190
392
 
191
393
 
192
394
  class RTVIErrorData(BaseModel):
395
+ """Data for an RTVI error event.
396
+
397
+ Contains error information including whether it's fatal.
398
+ """
399
+
193
400
  error: str
194
- fatal: bool
401
+ fatal: bool # Indicates the pipeline has stopped due to this error
195
402
 
196
403
 
197
404
  class RTVIError(BaseModel):
405
+ """RTVI error event message.
406
+
407
+ RTVI Formatted error message for relaying errors in the pipeline.
408
+ """
409
+
198
410
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
199
411
  type: Literal["error"] = "error"
200
412
  data: RTVIErrorData
201
413
 
202
414
 
203
415
  class RTVIDescribeConfigData(BaseModel):
416
+ """Data for describing available RTVI configuration.
417
+
418
+ Contains the list of available services and their options.
419
+
420
+ .. deprecated:: 0.0.75
421
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
422
+ Use custom client and server messages instead.
423
+ """
424
+
204
425
  config: List[RTVIService]
205
426
 
206
427
 
207
428
  class RTVIDescribeConfig(BaseModel):
429
+ """Message describing available RTVI configuration.
430
+
431
+ Sent in response to a describe-config request.
432
+
433
+ .. deprecated:: 0.0.75
434
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
435
+ Use custom client and server messages instead.
436
+ """
437
+
208
438
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
209
439
  type: Literal["config-available"] = "config-available"
210
440
  id: str
@@ -212,10 +442,28 @@ class RTVIDescribeConfig(BaseModel):
212
442
 
213
443
 
214
444
  class RTVIDescribeActionsData(BaseModel):
445
+ """Data for describing available RTVI actions.
446
+
447
+ Contains the list of available actions that can be executed.
448
+
449
+ .. deprecated:: 0.0.75
450
+ Actions have been removed as part of the RTVI protocol 1.0.0.
451
+ Use custom client and server messages instead.
452
+ """
453
+
215
454
  actions: List[RTVIAction]
216
455
 
217
456
 
218
457
  class RTVIDescribeActions(BaseModel):
458
+ """Message describing available RTVI actions.
459
+
460
+ Sent in response to a describe-actions request.
461
+
462
+ .. deprecated:: 0.0.75
463
+ Actions have been removed as part of the RTVI protocol 1.0.0.
464
+ Use custom client and server messages instead.
465
+ """
466
+
219
467
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
220
468
  type: Literal["actions-available"] = "actions-available"
221
469
  id: str
@@ -223,6 +471,15 @@ class RTVIDescribeActions(BaseModel):
223
471
 
224
472
 
225
473
  class RTVIConfigResponse(BaseModel):
474
+ """Response containing current RTVI configuration.
475
+
476
+ Sent in response to a get-config request.
477
+
478
+ .. deprecated:: 0.0.75
479
+ Pipeline Configuration has been removed as part of the RTVI protocol 1.0.0.
480
+ Use custom client and server messages instead.
481
+ """
482
+
226
483
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
227
484
  type: Literal["config"] = "config"
228
485
  id: str
@@ -230,22 +487,77 @@ class RTVIConfigResponse(BaseModel):
230
487
 
231
488
 
232
489
  class RTVIActionResponseData(BaseModel):
490
+ """Data for an RTVI action response.
491
+
492
+ Contains the result of executing an action.
493
+
494
+ .. deprecated:: 0.0.75
495
+ Actions have been removed as part of the RTVI protocol 1.0.0.
496
+ Use custom client and server messages instead.
497
+ """
498
+
233
499
  result: ActionResult
234
500
 
235
501
 
236
502
  class RTVIActionResponse(BaseModel):
503
+ """Response to an RTVI action execution.
504
+
505
+ Sent after successfully executing an action.
506
+
507
+ .. deprecated:: 0.0.75
508
+ Actions have been removed as part of the RTVI protocol 1.0.0.
509
+ Use custom client and server messages instead.
510
+ """
511
+
237
512
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
238
513
  type: Literal["action-response"] = "action-response"
239
514
  id: str
240
515
  data: RTVIActionResponseData
241
516
 
242
517
 
518
+ class AboutClientData(BaseModel):
519
+ """Data about the RTVI client.
520
+
521
+ Contains information about the client, including which RTVI library it
522
+ is using, what platform it is on and any additional details, if available.
523
+ """
524
+
525
+ library: str
526
+ library_version: Optional[str] = None
527
+ platform: Optional[str] = None
528
+ platform_version: Optional[str] = None
529
+ platform_details: Optional[Any] = None
530
+
531
+
532
+ class RTVIClientReadyData(BaseModel):
533
+ """Data format of client ready messages.
534
+
535
+ Contains the RTVIprotocol version and client information.
536
+ """
537
+
538
+ version: str
539
+ about: AboutClientData
540
+
541
+
243
542
  class RTVIBotReadyData(BaseModel):
543
+ """Data for bot ready notification.
544
+
545
+ Contains protocol version and initial configuration.
546
+ """
547
+
244
548
  version: str
245
- config: List[RTVIServiceConfig]
549
+ # The config field is deprecated and will not be included if
550
+ # the client's rtvi version is 1.0.0 or higher.
551
+ config: Optional[List[RTVIServiceConfig]] = None
552
+ about: Optional[Mapping[str, Any]] = None
246
553
 
247
554
 
248
555
  class RTVIBotReady(BaseModel):
556
+ """Message indicating bot is ready for interaction.
557
+
558
+ Sent after bot initialization is complete.
559
+ """
560
+
249
561
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
250
562
  type: Literal["bot-ready"] = "bot-ready"
251
563
  id: str
@@ -253,28 +565,72 @@ class RTVIBotReady(BaseModel):
253
565
 
254
566
 
255
567
  class RTVILLMFunctionCallMessageData(BaseModel):
568
+ """Data for LLM function call notification.
569
+
570
+ Contains function call details including name, ID, and arguments.
571
+ """
572
+
256
573
  function_name: str
257
574
  tool_call_id: str
258
575
  args: Mapping[str, Any]
259
576
 
260
577
 
261
578
  class RTVILLMFunctionCallMessage(BaseModel):
579
+ """Message notifying of an LLM function call.
580
+
581
+ Sent when the LLM makes a function call.
582
+ """
583
+
262
584
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
263
585
  type: Literal["llm-function-call"] = "llm-function-call"
264
586
  data: RTVILLMFunctionCallMessageData
265
587
 
266
588
 
589
+ class RTVIAppendToContextData(BaseModel):
590
+ """Data format for appending messages to the context.
591
+
592
+ Contains the role, content, and whether to run the message immediately.
593
+ """
594
+
595
+ role: Literal["user", "assistant"] | str
596
+ content: Any
597
+ run_immediately: bool = False
598
+
599
+
600
+ class RTVIAppendToContext(BaseModel):
601
+ """RTVI Message format to append content to the LLM context."""
602
+
603
+ label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
604
+ type: Literal["append-to-context"] = "append-to-context"
605
+ data: RTVIAppendToContextData
606
+
607
+
267
608
  class RTVILLMFunctionCallStartMessageData(BaseModel):
609
+ """Data for LLM function call start notification.
610
+
611
+ Contains the function name being called.
612
+ """
613
+
268
614
  function_name: str
269
615
 
270
616
 
271
617
  class RTVILLMFunctionCallStartMessage(BaseModel):
618
+ """Message notifying that an LLM function call has started.
619
+
620
+ Sent when the LLM begins a function call.
621
+ """
622
+
272
623
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
273
624
  type: Literal["llm-function-call-start"] = "llm-function-call-start"
274
625
  data: RTVILLMFunctionCallStartMessageData
275
626
 
276
627
 
277
628
  class RTVILLMFunctionCallResultData(BaseModel):
629
+ """Data for LLM function call result.
630
+
631
+ Contains function call details and result.
632
+ """
633
+
278
634
  function_name: str
279
635
  tool_call_id: str
280
636
  arguments: dict
@@ -282,60 +638,103 @@ class RTVILLMFunctionCallResultData(BaseModel):
282
638
 
283
639
 
284
640
  class RTVIBotLLMStartedMessage(BaseModel):
641
+ """Message indicating bot LLM processing has started."""
642
+
285
643
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
286
644
  type: Literal["bot-llm-started"] = "bot-llm-started"
287
645
 
288
646
 
289
647
  class RTVIBotLLMStoppedMessage(BaseModel):
648
+ """Message indicating bot LLM processing has stopped."""
649
+
290
650
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
291
651
  type: Literal["bot-llm-stopped"] = "bot-llm-stopped"
292
652
 
293
653
 
294
654
  class RTVIBotTTSStartedMessage(BaseModel):
655
+ """Message indicating bot TTS processing has started."""
656
+
295
657
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
296
658
  type: Literal["bot-tts-started"] = "bot-tts-started"
297
659
 
298
660
 
299
661
  class RTVIBotTTSStoppedMessage(BaseModel):
662
+ """Message indicating bot TTS processing has stopped."""
663
+
300
664
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
301
665
  type: Literal["bot-tts-stopped"] = "bot-tts-stopped"
302
666
 
303
667
 
304
668
  class RTVITextMessageData(BaseModel):
669
+ """Data for text-based RTVI messages.
670
+
671
+ Contains text content.
672
+ """
673
+
305
674
  text: str
306
675
 
307
676
 
308
677
  class RTVIBotTranscriptionMessage(BaseModel):
678
+ """Message containing bot transcription text.
679
+
680
+ Sent when the bot's speech is transcribed.
681
+ """
682
+
309
683
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
310
684
  type: Literal["bot-transcription"] = "bot-transcription"
311
685
  data: RTVITextMessageData
312
686
 
313
687
 
314
688
  class RTVIBotLLMTextMessage(BaseModel):
689
+ """Message containing bot LLM text output.
690
+
691
+ Sent when the bot's LLM generates text.
692
+ """
693
+
315
694
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
316
695
  type: Literal["bot-llm-text"] = "bot-llm-text"
317
696
  data: RTVITextMessageData
318
697
 
319
698
 
320
699
  class RTVIBotTTSTextMessage(BaseModel):
700
+ """Message containing bot TTS text output.
701
+
702
+ Sent when text is being processed by TTS.
703
+ """
704
+
321
705
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
322
706
  type: Literal["bot-tts-text"] = "bot-tts-text"
323
707
  data: RTVITextMessageData
324
708
 
325
709
 
326
710
  class RTVIAudioMessageData(BaseModel):
711
+ """Data for audio-based RTVI messages.
712
+
713
+ Contains audio data and metadata.
714
+ """
715
+
327
716
  audio: str
328
717
  sample_rate: int
329
718
  num_channels: int
330
719
 
331
720
 
332
721
  class RTVIBotTTSAudioMessage(BaseModel):
722
+ """Message containing bot TTS audio output.
723
+
724
+ Sent when the bot's TTS generates audio.
725
+ """
726
+
333
727
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
334
728
  type: Literal["bot-tts-audio"] = "bot-tts-audio"
335
729
  data: RTVIAudioMessageData
336
730
 
337
731
 
338
732
  class RTVIUserTranscriptionMessageData(BaseModel):
733
+ """Data for user transcription messages.
734
+
735
+ Contains transcription text and metadata.
736
+ """
737
+
339
738
  text: str
340
739
  user_id: str
341
740
  timestamp: str
@@ -343,44 +742,72 @@ class RTVIUserTranscriptionMessageData(BaseModel):
343
742
 
344
743
 
345
744
  class RTVIUserTranscriptionMessage(BaseModel):
745
+ """Message containing user transcription.
746
+
747
+ Sent when user speech is transcribed.
748
+ """
749
+
346
750
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
347
751
  type: Literal["user-transcription"] = "user-transcription"
348
752
  data: RTVIUserTranscriptionMessageData
349
753
 
350
754
 
351
755
  class RTVIUserLLMTextMessage(BaseModel):
756
+ """Message containing user text input for LLM.
757
+
758
+ Sent when user text is processed by the LLM.
759
+ """
760
+
352
761
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
353
762
  type: Literal["user-llm-text"] = "user-llm-text"
354
763
  data: RTVITextMessageData
355
764
 
356
765
 
357
766
  class RTVIUserStartedSpeakingMessage(BaseModel):
767
+ """Message indicating user has started speaking."""
768
+
358
769
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
359
770
  type: Literal["user-started-speaking"] = "user-started-speaking"
360
771
 
361
772
 
362
773
  class RTVIUserStoppedSpeakingMessage(BaseModel):
774
+ """Message indicating user has stopped speaking."""
775
+
363
776
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
364
777
  type: Literal["user-stopped-speaking"] = "user-stopped-speaking"
365
778
 
366
779
 
367
780
  class RTVIBotStartedSpeakingMessage(BaseModel):
781
+ """Message indicating bot has started speaking."""
782
+
368
783
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
369
784
  type: Literal["bot-started-speaking"] = "bot-started-speaking"
370
785
 
371
786
 
372
787
  class RTVIBotStoppedSpeakingMessage(BaseModel):
788
+ """Message indicating bot has stopped speaking."""
789
+
373
790
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
374
791
  type: Literal["bot-stopped-speaking"] = "bot-stopped-speaking"
375
792
 
376
793
 
377
794
  class RTVIMetricsMessage(BaseModel):
795
+ """Message containing performance metrics.
796
+
797
+ Sent to provide performance and usage metrics.
798
+ """
799
+
378
800
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
379
801
  type: Literal["metrics"] = "metrics"
380
802
  data: Mapping[str, Any]
381
803
 
382
804
 
383
805
  class RTVIServerMessage(BaseModel):
806
+ """Generic server message.
807
+
808
+ Used for custom server-to-client messages.
809
+ """
810
+
384
811
  label: RTVIMessageLiteral = RTVI_MESSAGE_LABEL
385
812
  type: Literal["server-message"] = "server-message"
386
813
  data: Any
@@ -388,28 +815,32 @@ class RTVIServerMessage(BaseModel):
388
815
 
389
816
  @dataclass
390
817
  class RTVIServerMessageFrame(SystemFrame):
391
- """A frame for sending server messages to the client."""
818
+ """A frame for sending server messages to the client.
819
+
820
+ Parameters:
821
+ data: The message data to send to the client.
822
+ """
392
823
 
393
824
  data: Any
394
825
 
395
826
  def __str__(self):
827
+ """String representation of the RTVI server message frame."""
396
828
  return f"{self.name}(data: {self.data})"
397
829
 
398
830
 
399
831
  @dataclass
400
832
  class RTVIObserverParams:
401
- """
402
- Parameters for configuring RTVI Observer behavior.
403
-
404
- Attributes:
405
- bot_llm_enabled (bool): Indicates if the bot's LLM messages should be sent.
406
- bot_tts_enabled (bool): Indicates if the bot's TTS messages should be sent.
407
- bot_speaking_enabled (bool): Indicates if the bot's started/stopped speaking messages should be sent.
408
- user_llm_enabled (bool): Indicates if the user's LLM input messages should be sent.
409
- user_speaking_enabled (bool): Indicates if the user's started/stopped speaking messages should be sent.
410
- user_transcription_enabled (bool): Indicates if user's transcription messages should be sent.
411
- metrics_enabled (bool): Indicates if metrics messages should be sent.
412
- errors_enabled (bool): Indicates if errors messages should be sent.
833
+ """Parameters for configuring RTVI Observer behavior.
834
+
835
+ Parameters:
836
+ bot_llm_enabled: Indicates if the bot's LLM messages should be sent.
837
+ bot_tts_enabled: Indicates if the bot's TTS messages should be sent.
838
+ bot_speaking_enabled: Indicates if the bot's started/stopped speaking messages should be sent.
839
+ user_llm_enabled: Indicates if the user's LLM input messages should be sent.
840
+ user_speaking_enabled: Indicates if the user's started/stopped speaking messages should be sent.
841
+ user_transcription_enabled: Indicates if user's transcription messages should be sent.
842
+ metrics_enabled: Indicates if metrics messages should be sent.
843
+ errors_enabled: Indicates if errors messages should be sent.
413
844
  """
414
845
 
415
846
  bot_llm_enabled: bool = True
@@ -432,15 +863,18 @@ class RTVIObserver(BaseObserver):
432
863
  Note:
433
864
  This observer only handles outgoing messages. Incoming RTVI client messages
434
865
  are handled by the RTVIProcessor.
435
-
436
- Args:
437
- rtvi (RTVIProcessor): The RTVI processor to push frames to.
438
- params (RTVIObserverParams): Settings to enable/disable specific messages.
439
866
  """
440
867
 
441
868
  def __init__(
442
869
  self, rtvi: "RTVIProcessor", *, params: Optional[RTVIObserverParams] = None, **kwargs
443
870
  ):
871
+ """Initialize the RTVI observer.
872
+
873
+ Args:
874
+ rtvi: The RTVI processor to push frames to.
875
+ params: Settings to enable/disable specific messages.
876
+ **kwargs: Additional arguments passed to parent class.
877
+ """
444
878
  super().__init__(**kwargs)
445
879
  self._rtvi = rtvi
446
880
  self._params = params or RTVIObserverParams()
@@ -452,11 +886,7 @@ class RTVIObserver(BaseObserver):
452
886
  """Process a frame being pushed through the pipeline.
453
887
 
454
888
  Args:
455
- src: Source processor pushing the frame
456
- dst: Destination processor receiving the frame
457
- frame: The frame being pushed
458
- direction: Direction of frame flow in pipeline
459
- timestamp: Time when frame was pushed
889
+ data: Frame push event data containing source, frame, direction, and timestamp.
460
890
  """
461
891
  src = data.source
462
892
  frame = data.frame
@@ -509,6 +939,11 @@ class RTVIObserver(BaseObserver):
509
939
  elif isinstance(frame, RTVIServerMessageFrame):
510
940
  message = RTVIServerMessage(data=frame.data)
511
941
  await self.push_transport_message_urgent(message)
942
+ elif isinstance(frame, RTVIServerResponseFrame):
943
+ if frame.error is not None:
944
+ await self._send_error_response(frame)
945
+ else:
946
+ await self._send_server_response(frame)
512
947
 
513
948
  if mark_as_seen:
514
949
  self._frames_seen.add(frame.id)
@@ -517,13 +952,14 @@ class RTVIObserver(BaseObserver):
517
952
  """Push an urgent transport message to the RTVI processor.
518
953
 
519
954
  Args:
520
- model: The message model to send
521
- exclude_none: Whether to exclude None values from the model dump
955
+ model: The message model to send.
956
+ exclude_none: Whether to exclude None values from the model dump.
522
957
  """
523
958
  frame = TransportMessageUrgentFrame(message=model.model_dump(exclude_none=exclude_none))
524
959
  await self._rtvi.push_frame(frame)
525
960
 
526
961
  async def _push_bot_transcription(self):
962
+ """Push accumulated bot transcription as a message."""
527
963
  if len(self._bot_transcription) > 0:
528
964
  message = RTVIBotTranscriptionMessage(
529
965
  data=RTVITextMessageData(text=self._bot_transcription)
@@ -532,6 +968,7 @@ class RTVIObserver(BaseObserver):
532
968
  self._bot_transcription = ""
533
969
 
534
970
  async def _handle_interruptions(self, frame: Frame):
971
+ """Handle user speaking interruption frames."""
535
972
  message = None
536
973
  if isinstance(frame, UserStartedSpeakingFrame):
537
974
  message = RTVIUserStartedSpeakingMessage()
@@ -542,6 +979,7 @@ class RTVIObserver(BaseObserver):
542
979
  await self.push_transport_message_urgent(message)
543
980
 
544
981
  async def _handle_bot_speaking(self, frame: Frame):
982
+ """Handle bot speaking event frames."""
545
983
  message = None
546
984
  if isinstance(frame, BotStartedSpeakingFrame):
547
985
  message = RTVIBotStartedSpeakingMessage()
@@ -552,6 +990,7 @@ class RTVIObserver(BaseObserver):
552
990
  await self.push_transport_message_urgent(message)
553
991
 
554
992
  async def _handle_llm_text_frame(self, frame: LLMTextFrame):
993
+ """Handle LLM text output frames."""
555
994
  message = RTVIBotLLMTextMessage(data=RTVITextMessageData(text=frame.text))
556
995
  await self.push_transport_message_urgent(message)
557
996
 
@@ -560,6 +999,7 @@ class RTVIObserver(BaseObserver):
560
999
  await self._push_bot_transcription()
561
1000
 
562
1001
  async def _handle_user_transcriptions(self, frame: Frame):
1002
+ """Handle user transcription frames."""
563
1003
  message = None
564
1004
  if isinstance(frame, TranscriptionFrame):
565
1005
  message = RTVIUserTranscriptionMessage(
@@ -608,6 +1048,7 @@ class RTVIObserver(BaseObserver):
608
1048
  logger.warning(f"Caught an error while trying to handle context: {e}")
609
1049
 
610
1050
  async def _handle_metrics(self, frame: MetricsFrame):
1051
+ """Handle metrics frames and convert to RTVI metrics messages."""
611
1052
  metrics = {}
612
1053
  for d in frame.data:
613
1054
  if isinstance(d, TTFBMetricsData):
@@ -630,8 +1071,31 @@ class RTVIObserver(BaseObserver):
630
1071
  message = RTVIMetricsMessage(data=metrics)
631
1072
  await self.push_transport_message_urgent(message)
632
1073
 
1074
+ async def _send_server_response(self, frame: RTVIServerResponseFrame):
1075
+ """Send a response to the client for a specific request."""
1076
+ message = RTVIServerResponse(
1077
+ id=str(frame.client_msg.msg_id),
1078
+ data=RTVIRawServerResponseData(t=frame.client_msg.type, d=frame.data),
1079
+ )
1080
+ await self.push_transport_message_urgent(message)
1081
+
1082
+ async def _send_error_response(self, frame: RTVIServerResponseFrame):
1083
+ """Send a response to the client for a specific request."""
1084
+ if self._params.errors_enabled:
1085
+ message = RTVIErrorResponse(
1086
+ id=str(frame.client_msg.msg_id), data=RTVIErrorResponseData(error=frame.error)
1087
+ )
1088
+ await self.push_transport_message_urgent(message)
1089
+
633
1090
 
634
1091
  class RTVIProcessor(FrameProcessor):
1092
+ """Main processor for handling RTVI protocol messages and actions.
1093
+
1094
+ This processor manages the RTVI protocol communication including client-server
1095
+ handshaking, configuration management, action execution, and message routing.
1096
+ It serves as the central hub for RTVI protocol operations.
1097
+ """
1098
+
635
1099
  def __init__(
636
1100
  self,
637
1101
  *,
@@ -639,12 +1103,20 @@ class RTVIProcessor(FrameProcessor):
639
1103
  transport: Optional[BaseTransport] = None,
640
1104
  **kwargs,
641
1105
  ):
1106
+ """Initialize the RTVI processor.
1107
+
1108
+ Args:
1109
+ config: Initial RTVI configuration.
1110
+ transport: Transport layer for communication.
1111
+ **kwargs: Additional arguments passed to parent class.
1112
+ """
642
1113
  super().__init__(**kwargs)
643
1114
  self._config = config or RTVIConfig(config=[])
644
1115
 
645
1116
  self._bot_ready = False
646
1117
  self._client_ready = False
647
1118
  self._client_ready_id = ""
1119
+ self._client_version = []
648
1120
  self._errors_enabled = True
649
1121
 
650
1122
  self._registered_actions: Dict[str, RTVIAction] = {}
@@ -658,6 +1130,7 @@ class RTVIProcessor(FrameProcessor):
658
1130
 
659
1131
  self._register_event_handler("on_bot_started")
660
1132
  self._register_event_handler("on_client_ready")
1133
+ self._register_event_handler("on_client_message")
661
1134
 
662
1135
  self._input_transport = None
663
1136
  self._transport = transport
@@ -668,34 +1141,101 @@ class RTVIProcessor(FrameProcessor):
668
1141
  self._input_transport.enable_audio_in_stream_on_start(False)
669
1142
 
670
1143
  def register_action(self, action: RTVIAction):
1144
+ """Register an action that can be executed via RTVI.
1145
+
1146
+ Args:
1147
+ action: The action to register.
1148
+ """
1149
+ import warnings
1150
+
1151
+ with warnings.catch_warnings():
1152
+ warnings.simplefilter("always")
1153
+ warnings.warn(
1154
+ "The actions API is deprecated, use server and client messages instead.",
1155
+ DeprecationWarning,
1156
+ )
1157
+
671
1158
  id = self._action_id(action.service, action.action)
672
1159
  self._registered_actions[id] = action
673
1160
 
674
1161
  def register_service(self, service: RTVIService):
1162
+ """Register a service that can be configured via RTVI.
1163
+
1164
+ Args:
1165
+ service: The service to register.
1166
+ """
1167
+ import warnings
1168
+
1169
+ with warnings.catch_warnings():
1170
+ warnings.simplefilter("always")
1171
+ warnings.warn(
1172
+ "The actions API is deprecated, use server and client messages instead.",
1173
+ DeprecationWarning,
1174
+ )
1175
+
675
1176
  self._registered_services[service.name] = service
676
1177
 
677
1178
  async def set_client_ready(self):
1179
+ """Mark the client as ready and trigger the ready event."""
678
1180
  self._client_ready = True
679
1181
  await self._call_event_handler("on_client_ready")
680
1182
 
681
1183
  async def set_bot_ready(self):
1184
+ """Mark the bot as ready and send the bot-ready message."""
682
1185
  self._bot_ready = True
683
1186
  await self._update_config(self._config, False)
684
1187
  await self._send_bot_ready()
685
1188
 
686
1189
  def set_errors_enabled(self, enabled: bool):
1190
+ """Enable or disable error message sending.
1191
+
1192
+ Args:
1193
+ enabled: Whether to send error messages.
1194
+ """
687
1195
  self._errors_enabled = enabled
688
1196
 
689
1197
  async def interrupt_bot(self):
1198
+ """Send a bot interruption frame upstream."""
690
1199
  await self.push_frame(BotInterruptionFrame(), FrameDirection.UPSTREAM)
691
1200
 
1201
+ async def send_server_message(self, data: Any):
1202
+ """Send a server message to the client."""
1203
+ message = RTVIServerMessage(data=data)
1204
+ await self._send_server_message(message)
1205
+
1206
+ async def send_server_response(self, client_msg: RTVIClientMessage, data: Any):
1207
+ """Send a server response for a given client message."""
1208
+ message = RTVIServerResponse(
1209
+ id=client_msg.msg_id, data=RTVIRawServerResponseData(t=client_msg.type, d=data)
1210
+ )
1211
+ await self._send_server_message(message)
1212
+
1213
+ async def send_error_response(self, client_msg: RTVIClientMessage, error: str):
1214
+ """Send an error response for a given client message."""
1215
+ await self._send_error_response(id=client_msg.msg_id, error=error)
1216
+
692
1217
  async def send_error(self, error: str):
1218
+ """Send an error message to the client.
1219
+
1220
+ Args:
1221
+ error: The error message to send.
1222
+ """
693
1223
  await self._send_error_frame(ErrorFrame(error=error))
694
1224
 
695
1225
  async def handle_message(self, message: RTVIMessage):
1226
+ """Handle an incoming RTVI message.
1227
+
1228
+ Args:
1229
+ message: The RTVI message to handle.
1230
+ """
696
1231
  await self._message_queue.put(message)
697
1232
 
698
1233
  async def handle_function_call(self, params: FunctionCallParams):
1234
+ """Handle a function call from the LLM.
1235
+
1236
+ Args:
1237
+ params: The function call parameters.
1238
+ """
699
1239
  fn = RTVILLMFunctionCallMessageData(
700
1240
  function_name=params.function_name,
701
1241
  tool_call_id=params.tool_call_id,
@@ -707,6 +1247,17 @@ class RTVIProcessor(FrameProcessor):
707
1247
  async def handle_function_call_start(
708
1248
  self, function_name: str, llm: FrameProcessor, context: OpenAILLMContext
709
1249
  ):
1250
+ """Handle the start of a function call from the LLM.
1251
+
1252
+ .. deprecated:: 0.0.66
1253
+ This method is deprecated and will be removed in a future version.
1254
+ Use `RTVIProcessor.handle_function_call()` instead.
1255
+
1256
+ Args:
1257
+ function_name: Name of the function being called.
1258
+ llm: The LLM processor making the call.
1259
+ context: The LLM context.
1260
+ """
710
1261
  import warnings
711
1262
 
712
1263
  with warnings.catch_warnings():
@@ -721,6 +1272,12 @@ class RTVIProcessor(FrameProcessor):
721
1272
  await self._push_transport_message(message, exclude_none=False)
722
1273
 
723
1274
  async def process_frame(self, frame: Frame, direction: FrameDirection):
1275
+ """Process incoming frames through the RTVI processor.
1276
+
1277
+ Args:
1278
+ frame: The frame to process.
1279
+ direction: The direction of frame flow.
1280
+ """
724
1281
  await super().process_frame(frame, direction)
725
1282
 
726
1283
  # Specific system frames
@@ -754,21 +1311,25 @@ class RTVIProcessor(FrameProcessor):
754
1311
  await self.push_frame(frame, direction)
755
1312
 
756
1313
  async def _start(self, frame: StartFrame):
1314
+ """Start the RTVI processor tasks."""
757
1315
  if not self._action_task:
758
- self._action_queue = WatchdogQueue(self.task_manager)
1316
+ self._action_queue = asyncio.Queue()
759
1317
  self._action_task = self.create_task(self._action_task_handler())
760
1318
  if not self._message_task:
761
- self._message_queue = WatchdogQueue(self.task_manager)
1319
+ self._message_queue = asyncio.Queue()
762
1320
  self._message_task = self.create_task(self._message_task_handler())
763
1321
  await self._call_event_handler("on_bot_started")
764
1322
 
765
1323
  async def _stop(self, frame: EndFrame):
1324
+ """Stop the RTVI processor tasks."""
766
1325
  await self._cancel_tasks()
767
1326
 
768
1327
  async def _cancel(self, frame: CancelFrame):
1328
+ """Cancel the RTVI processor tasks."""
769
1329
  await self._cancel_tasks()
770
1330
 
771
1331
  async def _cancel_tasks(self):
1332
+ """Cancel all running tasks."""
772
1333
  if self._action_task:
773
1334
  await self.cancel_task(self._action_task)
774
1335
  self._action_task = None
@@ -778,22 +1339,26 @@ class RTVIProcessor(FrameProcessor):
778
1339
  self._message_task = None
779
1340
 
780
1341
  async def _push_transport_message(self, model: BaseModel, exclude_none: bool = True):
1342
+ """Push a transport message frame."""
781
1343
  frame = TransportMessageUrgentFrame(message=model.model_dump(exclude_none=exclude_none))
782
1344
  await self.push_frame(frame)
783
1345
 
784
1346
  async def _action_task_handler(self):
1347
+ """Handle incoming action frames."""
785
1348
  while True:
786
1349
  frame = await self._action_queue.get()
787
1350
  await self._handle_action(frame.message_id, frame.rtvi_action_run)
788
1351
  self._action_queue.task_done()
789
1352
 
790
1353
  async def _message_task_handler(self):
1354
+ """Handle incoming transport messages."""
791
1355
  while True:
792
1356
  message = await self._message_queue.get()
793
1357
  await self._handle_message(message)
794
1358
  self._message_queue.task_done()
795
1359
 
796
1360
  async def _handle_transport_message(self, frame: TransportMessageUrgentFrame):
1361
+ """Handle an incoming transport message frame."""
797
1362
  try:
798
1363
  transport_message = frame.message
799
1364
  if transport_message.get("label") != RTVI_MESSAGE_LABEL:
@@ -806,10 +1371,19 @@ class RTVIProcessor(FrameProcessor):
806
1371
  logger.warning(f"Invalid RTVI transport message: {e}")
807
1372
 
808
1373
  async def _handle_message(self, message: RTVIMessage):
1374
+ """Handle a parsed RTVI message."""
809
1375
  try:
810
1376
  match message.type:
811
1377
  case "client-ready":
812
- await self._handle_client_ready(message.id)
1378
+ data = None
1379
+ try:
1380
+ data = RTVIClientReadyData.model_validate(message.data)
1381
+ except ValidationError:
1382
+ # Not all clients have been updated to RTVI 1.0.0.
1383
+ # For now, that's okay, we just log their info as unknown.
1384
+ data = None
1385
+ pass
1386
+ await self._handle_client_ready(message.id, data)
813
1387
  case "describe-actions":
814
1388
  await self._handle_describe_actions(message.id)
815
1389
  case "describe-config":
@@ -821,6 +1395,9 @@ class RTVIProcessor(FrameProcessor):
821
1395
  await self._handle_update_config(message.id, update_config)
822
1396
  case "disconnect-bot":
823
1397
  await self.push_frame(EndTaskFrame(), FrameDirection.UPSTREAM)
1398
+ case "client-message":
1399
+ data = RTVIRawClientMessageData.model_validate(message.data)
1400
+ await self._handle_client_message(message.id, data)
824
1401
  case "action":
825
1402
  action = RTVIActionRun.model_validate(message.data)
826
1403
  action_frame = RTVIActionFrame(message_id=message.id, rtvi_action_run=action)
@@ -828,6 +1405,9 @@ class RTVIProcessor(FrameProcessor):
828
1405
  case "llm-function-call-result":
829
1406
  data = RTVILLMFunctionCallResultData.model_validate(message.data)
830
1407
  await self._handle_function_call_result(data)
1408
+ case "append-to-context":
1409
+ data = RTVIAppendToContextData.model_validate(message.data)
1410
+ await self._handle_update_context(data)
831
1411
  case "raw-audio" | "raw-audio-batch":
832
1412
  await self._handle_audio_buffer(message.data)
833
1413
 
@@ -841,8 +1421,20 @@ class RTVIProcessor(FrameProcessor):
841
1421
  await self._send_error_response(message.id, f"Exception processing message: {e}")
842
1422
  logger.warning(f"Exception processing message: {e}")
843
1423
 
844
- async def _handle_client_ready(self, request_id: str):
845
- logger.debug("Received client-ready")
1424
+ async def _handle_client_ready(self, request_id: str, data: RTVIClientReadyData | None):
1425
+ """Handle the client-ready message from the client."""
1426
+ version = data.version if data else "unknown"
1427
+ logger.debug(f"Received client-ready: version {version}")
1428
+ if version == "unknown":
1429
+ self._client_version = [0, 3, 0] # Default to 0.3.0 if unknown
1430
+ else:
1431
+ try:
1432
+ self._client_version = [int(v) for v in version.split(".")]
1433
+ except ValueError:
1434
+ logger.warning(f"Invalid client version format: {version}")
1435
+ self._client_version = [0, 3, 0]
1436
+ about = data.about if data else {"library": "unknown"}
1437
+ logger.debug(f"Client Details: {about}")
846
1438
  if self._input_transport:
847
1439
  await self._input_transport.start_audio_in_streaming()
848
1440
 
@@ -850,6 +1442,7 @@ class RTVIProcessor(FrameProcessor):
850
1442
  await self.set_client_ready()
851
1443
 
852
1444
  async def _handle_audio_buffer(self, data):
1445
+ """Handle incoming audio buffer data."""
853
1446
  if not self._input_transport:
854
1447
  return
855
1448
 
@@ -871,20 +1464,51 @@ class RTVIProcessor(FrameProcessor):
871
1464
  logger.error(f"Error processing audio buffer: {e}")
872
1465
 
873
1466
  async def _handle_describe_config(self, request_id: str):
1467
+ """Handle a describe-config request."""
1468
+ import warnings
1469
+
1470
+ with warnings.catch_warnings():
1471
+ warnings.simplefilter("always")
1472
+ warnings.warn(
1473
+ "Configuration helpers are deprecated. If your application needs this behavior, use custom server and client messages.",
1474
+ DeprecationWarning,
1475
+ )
1476
+
874
1477
  services = list(self._registered_services.values())
875
1478
  message = RTVIDescribeConfig(id=request_id, data=RTVIDescribeConfigData(config=services))
876
1479
  await self._push_transport_message(message)
877
1480
 
878
1481
  async def _handle_describe_actions(self, request_id: str):
1482
+ """Handle a describe-actions request."""
1483
+ import warnings
1484
+
1485
+ with warnings.catch_warnings():
1486
+ warnings.simplefilter("always")
1487
+ warnings.warn(
1488
+ "The Actions API is deprecated, use custom server and client messages instead.",
1489
+ DeprecationWarning,
1490
+ )
1491
+
879
1492
  actions = list(self._registered_actions.values())
880
1493
  message = RTVIDescribeActions(id=request_id, data=RTVIDescribeActionsData(actions=actions))
881
1494
  await self._push_transport_message(message)
882
1495
 
883
1496
  async def _handle_get_config(self, request_id: str):
1497
+ """Handle a get-config request."""
1498
+ import warnings
1499
+
1500
+ with warnings.catch_warnings():
1501
+ warnings.simplefilter("always")
1502
+ warnings.warn(
1503
+ "Configuration helpers are deprecated. If your application needs this behavior, use custom server and client messages.",
1504
+ DeprecationWarning,
1505
+ )
1506
+
884
1507
  message = RTVIConfigResponse(id=request_id, data=self._config)
885
1508
  await self._push_transport_message(message)
886
1509
 
887
1510
  def _update_config_option(self, service: str, config: RTVIServiceOptionConfig):
1511
+ """Update a specific configuration option."""
888
1512
  for service_config in self._config.config:
889
1513
  if service_config.service == service:
890
1514
  for option_config in service_config.options:
@@ -896,6 +1520,16 @@ class RTVIProcessor(FrameProcessor):
896
1520
  service_config.options.append(config)
897
1521
 
898
1522
  async def _update_service_config(self, config: RTVIServiceConfig):
1523
+ """Update configuration for a specific service."""
1524
+ import warnings
1525
+
1526
+ with warnings.catch_warnings():
1527
+ warnings.simplefilter("always")
1528
+ warnings.warn(
1529
+ "Configuration helpers are deprecated. If your application needs this behavior, use custom server and client messages.",
1530
+ DeprecationWarning,
1531
+ )
1532
+
899
1533
  service = self._registered_services[config.service]
900
1534
  for option in config.options:
901
1535
  handler = service._options_dict[option.name].handler
@@ -903,16 +1537,55 @@ class RTVIProcessor(FrameProcessor):
903
1537
  self._update_config_option(service.name, option)
904
1538
 
905
1539
  async def _update_config(self, data: RTVIConfig, interrupt: bool):
1540
+ """Update the RTVI configuration."""
1541
+ import warnings
1542
+
1543
+ with warnings.catch_warnings():
1544
+ warnings.simplefilter("always")
1545
+ warnings.warn(
1546
+ "Configuration helpers are deprecated. If your application needs this behavior, use custom server and client messages.",
1547
+ DeprecationWarning,
1548
+ )
1549
+
906
1550
  if interrupt:
907
1551
  await self.interrupt_bot()
908
1552
  for service_config in data.config:
909
1553
  await self._update_service_config(service_config)
910
1554
 
911
1555
  async def _handle_update_config(self, request_id: str, data: RTVIUpdateConfig):
1556
+ """Handle an update-config request."""
912
1557
  await self._update_config(RTVIConfig(config=data.config), data.interrupt)
913
1558
  await self._handle_get_config(request_id)
914
1559
 
1560
+ async def _handle_update_context(self, data: RTVIAppendToContextData):
1561
+ if data.run_immediately:
1562
+ await self.interrupt_bot()
1563
+ frame = LLMMessagesAppendFrame(
1564
+ messages=[{"role": data.role, "content": data.content}],
1565
+ run_llm=data.run_immediately,
1566
+ )
1567
+ await self.push_frame(frame)
1568
+
1569
+ async def _handle_client_message(self, msg_id: str, data: RTVIRawClientMessageData):
1570
+ """Handle a client message frame."""
1571
+ if not data:
1572
+ await self._send_error_response(msg_id, "Malformed client message")
1573
+ return
1574
+
1575
+ # Create a RTVIClientMessageFrame to push the message
1576
+ frame = RTVIClientMessageFrame(msg_id=msg_id, type=data.t, data=data.d)
1577
+ await self.push_frame(frame)
1578
+ await self._call_event_handler(
1579
+ "on_client_message",
1580
+ RTVIClientMessage(
1581
+ msg_id=msg_id,
1582
+ type=data.t,
1583
+ data=data.d,
1584
+ ),
1585
+ )
1586
+
915
1587
  async def _handle_function_call_result(self, data):
1588
+ """Handle a function call result from the client."""
916
1589
  frame = FunctionCallResultFrame(
917
1590
  function_name=data.function_name,
918
1591
  tool_call_id=data.tool_call_id,
@@ -922,6 +1595,7 @@ class RTVIProcessor(FrameProcessor):
922
1595
  await self.push_frame(frame)
923
1596
 
924
1597
  async def _handle_action(self, request_id: Optional[str], data: RTVIActionRun):
1598
+ """Handle an action execution request."""
925
1599
  action_id = self._action_id(data.service, data.action)
926
1600
  if action_id not in self._registered_actions:
927
1601
  await self._send_error_response(request_id, f"Action {action_id} not registered")
@@ -939,21 +1613,32 @@ class RTVIProcessor(FrameProcessor):
939
1613
  await self._push_transport_message(message)
940
1614
 
941
1615
  async def _send_bot_ready(self):
1616
+ """Send the bot-ready message to the client."""
1617
+ config = None
1618
+ if self._client_version[0] < 1:
1619
+ config = self._config.config
942
1620
  message = RTVIBotReady(
943
1621
  id=self._client_ready_id,
944
- data=RTVIBotReadyData(version=RTVI_PROTOCOL_VERSION, config=self._config.config),
1622
+ data=RTVIBotReadyData(version=RTVI_PROTOCOL_VERSION, config=config),
945
1623
  )
946
1624
  await self._push_transport_message(message)
947
1625
 
1626
+ async def _send_server_message(self, message: RTVIServerMessage | RTVIServerResponse):
1627
+ """Send a message or response to the client."""
1628
+ await self._push_transport_message(message)
1629
+
948
1630
  async def _send_error_frame(self, frame: ErrorFrame):
1631
+ """Send an error frame as an RTVI error message."""
949
1632
  if self._errors_enabled:
950
1633
  message = RTVIError(data=RTVIErrorData(error=frame.error, fatal=frame.fatal))
951
1634
  await self._push_transport_message(message)
952
1635
 
953
1636
  async def _send_error_response(self, id: str, error: str):
1637
+ """Send an error response message."""
954
1638
  if self._errors_enabled:
955
1639
  message = RTVIErrorResponse(id=id, data=RTVIErrorResponseData(error=error))
956
1640
  await self._push_transport_message(message)
957
1641
 
958
1642
  def _action_id(self, service: str, action: str) -> str:
1643
+ """Generate an action ID from service and action names."""
959
1644
  return f"{service}:{action}"