diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2fc5abde4..466b3f28b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,79 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
+- Added `lexicon_names` parameter to `AWSPollyTTSService.InputParams`.
+
+- Added reconnection logic and audio buffer management to `GladiaSTTService`.
+
+- Added Polish support to `AWSTranscribeSTTService`.
+
+- Added new frames `FrameProcessorPauseFrame` and `FrameProcessorResumeFrame`
+ which allow pausing and resuming frame processing for a given frame
+ processor. These are control frames, so they are ordered. Pausing frame
+ processor will keep old frames in the internal queues until resume takes
+ place. Frames being pushed while a frame processor is paused will be pushed to
+ the queues. When frame processing is resumed all queued frames will be
+ processed in order. Also added `FrameProcessorPauseUrgentFrame` and
+ `FrameProcessorResumeUrgentFrame` which are system frames and therefore they
+ have high priority.
+
+- Added a property called `has_function_calls_in_progress` in
+ `LLMAssistantContextAggregator` that exposes whether a function call is in
+ progress.
+
+### Changed
+
+- The `PipelineParams` arg `allow_interruptions` now defaults to `True`.
+
+- `TavusTransport` and `TavusVideoService` now send audio to Tavus using WebRTC
+ audio tracks instead of `app-messages` over WebSocket. This should improve the
+ overall audio quality.
+
+- Upgraded `daily-python` to 0.19.3.
+
+### Fixed
+
+- Fixed function calling in `AWSNovaSonicLLMService`.
+
+- Fixed an issue that would cause multiple `PipelineTask.on_idle_timeout`
+ events to be triggered repeatedly.
+
+- Fixed an issue that was causing user and bot speech to not be synchronized
+ during recordings.
+
+- Fixed an issue where voice settings weren't applied to ElevenLabsTTSService.
+
+- Fixed an issue with `GroqTTSService` where it was not properly parsing the
+ WAV file header.
+
+- Fixed an issue with `GoogleSTTService` where it was constantly reconnecting
+ before starting to receive audio from the user.
+
+- Fixed an issue where `GoogleLLMService`'s TTFB value was incorrect.
+
+### Deprecated
+
+- `AudioBufferProcessor` parameter `user_continuos_stream` is deprecated.
+
+### Other
+
+- Rename `14e-function-calling-gemini.py` to `14e-function-calling-google.py`.
+
+## [0.0.71] - 2025-06-10
+
+### Added
+
+- Adds a parameter called `additional_span_attributes` to PipelineTask that
+ lets you add any additional attributes you'd like to the conversation span.
+
+### Fixed
+
+- Fixed an issue with `CartesiaSTTService` initialization.
+
+## [0.0.70] - 2025-06-10
+
+### Added
+
- Added `ExotelFrameSerializer` to handle telephony calls via Exotel.
- Added the option `informal` to `TranslationConfig` on Gladia config.
diff --git a/README.md b/README.md
index f906cb8bb..7ec4c6000 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ You can connect to Pipecat from any platform using our official SDKs:
| Category | Services |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/server/services/stt/assemblyai), [AWS](https://docs.pipecat.ai/server/services/stt/aws), [Azure](https://docs.pipecat.ai/server/services/stt/azure), Cartesia, [Deepgram](https://docs.pipecat.ai/server/services/stt/deepgram), [Fal Wizper](https://docs.pipecat.ai/server/services/stt/fal), [Gladia](https://docs.pipecat.ai/server/services/stt/gladia), [Google](https://docs.pipecat.ai/server/services/stt/google), [Groq (Whisper)](https://docs.pipecat.ai/server/services/stt/groq), [OpenAI (Whisper)](https://docs.pipecat.ai/server/services/stt/openai), [Parakeet (NVIDIA)](https://docs.pipecat.ai/server/services/stt/parakeet), [Ultravox](https://docs.pipecat.ai/server/services/stt/ultravox), [Whisper](https://docs.pipecat.ai/server/services/stt/whisper) |
+| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/server/services/stt/assemblyai), [AWS](https://docs.pipecat.ai/server/services/stt/aws), [Azure](https://docs.pipecat.ai/server/services/stt/azure), [Cartesia](https://docs.pipecat.ai/server/services/stt/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/stt/deepgram), [Fal Wizper](https://docs.pipecat.ai/server/services/stt/fal), [Gladia](https://docs.pipecat.ai/server/services/stt/gladia), [Google](https://docs.pipecat.ai/server/services/stt/google), [Groq (Whisper)](https://docs.pipecat.ai/server/services/stt/groq), [OpenAI (Whisper)](https://docs.pipecat.ai/server/services/stt/openai), [Parakeet (NVIDIA)](https://docs.pipecat.ai/server/services/stt/parakeet), [Ultravox](https://docs.pipecat.ai/server/services/stt/ultravox), [Whisper](https://docs.pipecat.ai/server/services/stt/whisper) |
| LLMs | [Anthropic](https://docs.pipecat.ai/server/services/llm/anthropic), [AWS](https://docs.pipecat.ai/server/services/llm/aws), [Azure](https://docs.pipecat.ai/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/server/services/llm/cerebras), [DeepSeek](https://docs.pipecat.ai/server/services/llm/deepseek), [Fireworks AI](https://docs.pipecat.ai/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/server/services/llm/grok), [Groq](https://docs.pipecat.ai/server/services/llm/groq), [NVIDIA NIM](https://docs.pipecat.ai/server/services/llm/nim), [Ollama](https://docs.pipecat.ai/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/server/services/llm/openai), [OpenRouter](https://docs.pipecat.ai/server/services/llm/openrouter), [Perplexity](https://docs.pipecat.ai/server/services/llm/perplexity), [Qwen](https://docs.pipecat.ai/server/services/llm/qwen), [Together AI](https://docs.pipecat.ai/server/services/llm/together) |
| Text-to-Speech | [AWS](https://docs.pipecat.ai/server/services/tts/aws), [Azure](https://docs.pipecat.ai/server/services/tts/azure), [Cartesia](https://docs.pipecat.ai/server/services/tts/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/tts/deepgram), [ElevenLabs](https://docs.pipecat.ai/server/services/tts/elevenlabs), [FastPitch (NVIDIA)](https://docs.pipecat.ai/server/services/tts/fastpitch), [Fish](https://docs.pipecat.ai/server/services/tts/fish), [Google](https://docs.pipecat.ai/server/services/tts/google), [LMNT](https://docs.pipecat.ai/server/services/tts/lmnt), [MiniMax](https://docs.pipecat.ai/server/services/tts/minimax), [Neuphonic](https://docs.pipecat.ai/server/services/tts/neuphonic), [OpenAI](https://docs.pipecat.ai/server/services/tts/openai), [Piper](https://docs.pipecat.ai/server/services/tts/piper), [PlayHT](https://docs.pipecat.ai/server/services/tts/playht), [Rime](https://docs.pipecat.ai/server/services/tts/rime), [Sarvam](https://docs.pipecat.ai/server/services/tts/sarvam), [XTTS](https://docs.pipecat.ai/server/services/tts/xtts) |
| Speech-to-Speech | [AWS Nova Sonic](https://docs.pipecat.ai/server/services/s2s/aws), [Gemini Multimodal Live](https://docs.pipecat.ai/server/services/s2s/gemini), [OpenAI Realtime](https://docs.pipecat.ai/server/services/s2s/openai) |
diff --git a/dev-requirements.txt b/dev-requirements.txt
index af1c35721..2bab71684 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -3,11 +3,11 @@ coverage~=7.6.12
grpcio-tools~=1.67.1
pip-tools~=7.4.1
pre-commit~=4.0.1
-pyright~=1.1.397
+pyright~=1.1.400
pytest~=8.3.4
pytest-asyncio~=0.25.3
pytest-aiohttp==1.1.0
-ruff~=0.11.1
+ruff~=0.11.13
setuptools~=70.0.0
setuptools_scm~=8.1.0
python-dotenv~=1.0.1
diff --git a/examples/chatbot-audio-recording/bot.py b/examples/chatbot-audio-recording/bot.py
index 128c97c7e..a6c61cba8 100644
--- a/examples/chatbot-audio-recording/bot.py
+++ b/examples/chatbot-audio-recording/bot.py
@@ -133,7 +133,8 @@ async def main():
params=PipelineParams(
audio_in_sample_rate=16000,
audio_out_sample_rate=16000,
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/daily-custom-tracks/bot.py b/examples/daily-custom-tracks/bot.py
index 17d0e2dfa..1b0b3b64d 100644
--- a/examples/daily-custom-tracks/bot.py
+++ b/examples/daily-custom-tracks/bot.py
@@ -71,6 +71,8 @@ async def main():
params=PipelineParams(
audio_in_sample_rate=16000,
audio_out_sample_rate=16000,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/daily-multi-translation/bot.py b/examples/daily-multi-translation/bot.py
index 5f5037584..67a11863a 100644
--- a/examples/daily-multi-translation/bot.py
+++ b/examples/daily-multi-translation/bot.py
@@ -148,10 +148,8 @@ async def main():
params=PipelineParams(
audio_in_sample_rate=16000,
audio_out_sample_rate=16000,
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
observers=[TranscriptionLogObserver()],
)
diff --git a/examples/deployment/flyio-example/bot.py b/examples/deployment/flyio-example/bot.py
index 45d94a9e3..25b30f1b3 100644
--- a/examples/deployment/flyio-example/bot.py
+++ b/examples/deployment/flyio-example/bot.py
@@ -75,7 +75,13 @@ async def main(room_url: str, token: str):
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant):
diff --git a/examples/deployment/modal-example/server/src/bot_gemini.py b/examples/deployment/modal-example/server/src/bot_gemini.py
index 6c19c9923..39e4d0958 100644
--- a/examples/deployment/modal-example/server/src/bot_gemini.py
+++ b/examples/deployment/modal-example/server/src/bot_gemini.py
@@ -170,7 +170,6 @@ async def run_bot(room_url: str, token: str):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/deployment/modal-example/server/src/bot_openai.py b/examples/deployment/modal-example/server/src/bot_openai.py
index f85e61a81..5d920bbba 100644
--- a/examples/deployment/modal-example/server/src/bot_openai.py
+++ b/examples/deployment/modal-example/server/src/bot_openai.py
@@ -198,7 +198,6 @@ async def run_bot(room_url: str, token: str):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/deployment/modal-example/server/src/bot_vllm.py b/examples/deployment/modal-example/server/src/bot_vllm.py
index 3572fae88..af6e4c0a7 100644
--- a/examples/deployment/modal-example/server/src/bot_vllm.py
+++ b/examples/deployment/modal-example/server/src/bot_vllm.py
@@ -211,7 +211,6 @@ async def run_bot(room_url: str, token: str):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/deployment/pipecat-cloud-example/bot.py b/examples/deployment/pipecat-cloud-example/bot.py
index 5f355b881..c521954fc 100644
--- a/examples/deployment/pipecat-cloud-example/bot.py
+++ b/examples/deployment/pipecat-cloud-example/bot.py
@@ -67,10 +67,8 @@ async def main(transport: DailyTransport):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/fal-smart-turn/server/bot.py b/examples/fal-smart-turn/server/bot.py
index 8c3810535..1b94f3e31 100644
--- a/examples/fal-smart-turn/server/bot.py
+++ b/examples/fal-smart-turn/server/bot.py
@@ -192,7 +192,6 @@ async def main(transport: DailyTransport):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/03b-still-frame-imagen.py b/examples/foundational/03b-still-frame-imagen.py
index 83c68ef19..0414fe8de 100644
--- a/examples/foundational/03b-still-frame-imagen.py
+++ b/examples/foundational/03b-still-frame-imagen.py
@@ -47,7 +47,10 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
Pipeline([imagegen, transport.output()]),
- params=PipelineParams(enable_metrics=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
# Register an event handler so we can play the audio when the client joins
diff --git a/examples/foundational/04-transports-small-webrtc.py b/examples/foundational/04-transports-small-webrtc.py
index 627c13ad1..ffb1d9c9b 100644
--- a/examples/foundational/04-transports-small-webrtc.py
+++ b/examples/foundational/04-transports-small-webrtc.py
@@ -93,10 +93,8 @@ async def run_example(webrtc_connection: SmallWebRTCConnection):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/04a-transports-daily.py b/examples/foundational/04a-transports-daily.py
index a968c3abb..e330b53a9 100644
--- a/examples/foundational/04a-transports-daily.py
+++ b/examples/foundational/04a-transports-daily.py
@@ -75,10 +75,8 @@ async def main():
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/04b-transports-livekit.py b/examples/foundational/04b-transports-livekit.py
index 3cd5c7eea..b7c4887c3 100644
--- a/examples/foundational/04b-transports-livekit.py
+++ b/examples/foundational/04b-transports-livekit.py
@@ -158,7 +158,8 @@ async def main():
],
),
params=PipelineParams(
- allow_interruptions=True, enable_metrics=True, enable_usage_metrics=True
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/06a-image-sync.py b/examples/foundational/06a-image-sync.py
index 61ba1c6f4..d8b2e7c04 100644
--- a/examples/foundational/06a-image-sync.py
+++ b/examples/foundational/06a-image-sync.py
@@ -133,10 +133,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07-interruptible-cartesia-http.py b/examples/foundational/07-interruptible-cartesia-http.py
index 75f8a0ddb..eff98b1e7 100644
--- a/examples/foundational/07-interruptible-cartesia-http.py
+++ b/examples/foundational/07-interruptible-cartesia-http.py
@@ -84,10 +84,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py
index 9429f5a33..b69aaba92 100644
--- a/examples/foundational/07-interruptible.py
+++ b/examples/foundational/07-interruptible.py
@@ -83,10 +83,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py
index 2a5e72c1a..55bfa6cdf 100644
--- a/examples/foundational/07b-interruptible-langchain.py
+++ b/examples/foundational/07b-interruptible-langchain.py
@@ -113,10 +113,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py
index f228dcecd..86cb363e2 100644
--- a/examples/foundational/07c-interruptible-deepgram-vad.py
+++ b/examples/foundational/07c-interruptible-deepgram-vad.py
@@ -87,10 +87,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py
index f523f2aa5..cce7c245d 100644
--- a/examples/foundational/07c-interruptible-deepgram.py
+++ b/examples/foundational/07c-interruptible-deepgram.py
@@ -81,10 +81,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07d-interruptible-elevenlabs-http.py b/examples/foundational/07d-interruptible-elevenlabs-http.py
index eb7e03bf5..395b9331c 100644
--- a/examples/foundational/07d-interruptible-elevenlabs-http.py
+++ b/examples/foundational/07d-interruptible-elevenlabs-http.py
@@ -88,10 +88,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py
index 945c0e47b..491f15c4d 100644
--- a/examples/foundational/07d-interruptible-elevenlabs.py
+++ b/examples/foundational/07d-interruptible-elevenlabs.py
@@ -84,10 +84,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07e-interruptible-playht-http.py b/examples/foundational/07e-interruptible-playht-http.py
index 534cd6927..d628714c2 100644
--- a/examples/foundational/07e-interruptible-playht-http.py
+++ b/examples/foundational/07e-interruptible-playht-http.py
@@ -84,10 +84,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07e-interruptible-playht.py b/examples/foundational/07e-interruptible-playht.py
index 1bbb04fe2..d5b24dc49 100644
--- a/examples/foundational/07e-interruptible-playht.py
+++ b/examples/foundational/07e-interruptible-playht.py
@@ -86,10 +86,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py
index 0da653f4d..1e8e7d7f3 100644
--- a/examples/foundational/07f-interruptible-azure.py
+++ b/examples/foundational/07f-interruptible-azure.py
@@ -90,10 +90,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07g-interruptible-openai.py b/examples/foundational/07g-interruptible-openai.py
index 5d028603e..fb85c1af4 100644
--- a/examples/foundational/07g-interruptible-openai.py
+++ b/examples/foundational/07g-interruptible-openai.py
@@ -84,11 +84,9 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
audio_out_sample_rate=24000,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py
index e488ad6db..719067b21 100644
--- a/examples/foundational/07h-interruptible-openpipe.py
+++ b/examples/foundational/07h-interruptible-openpipe.py
@@ -89,10 +89,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py
index 32c7a6348..32dddd965 100644
--- a/examples/foundational/07i-interruptible-xtts.py
+++ b/examples/foundational/07i-interruptible-xtts.py
@@ -87,10 +87,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py
index 6975cfec7..4cf7b2f04 100644
--- a/examples/foundational/07j-interruptible-gladia.py
+++ b/examples/foundational/07j-interruptible-gladia.py
@@ -92,10 +92,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py
index c3c46ab15..3d4616276 100644
--- a/examples/foundational/07k-interruptible-lmnt.py
+++ b/examples/foundational/07k-interruptible-lmnt.py
@@ -80,10 +80,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07l-interruptible-groq.py b/examples/foundational/07l-interruptible-groq.py
index 3e73194ab..6acccee63 100644
--- a/examples/foundational/07l-interruptible-groq.py
+++ b/examples/foundational/07l-interruptible-groq.py
@@ -85,7 +85,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/07m-interruptible-aws.py b/examples/foundational/07m-interruptible-aws.py
index 78ef705a1..5cd1a579a 100644
--- a/examples/foundational/07m-interruptible-aws.py
+++ b/examples/foundational/07m-interruptible-aws.py
@@ -87,10 +87,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py
index 7e032513c..e8613e082 100644
--- a/examples/foundational/07n-interruptible-google.py
+++ b/examples/foundational/07n-interruptible-google.py
@@ -88,10 +88,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py
index 72963c0dc..2a1a8da8d 100644
--- a/examples/foundational/07o-interruptible-assemblyai.py
+++ b/examples/foundational/07o-interruptible-assemblyai.py
@@ -86,10 +86,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py
index 21046a3b9..167749e77 100644
--- a/examples/foundational/07p-interruptible-krisp.py
+++ b/examples/foundational/07p-interruptible-krisp.py
@@ -84,10 +84,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07q-interruptible-rime-http.py b/examples/foundational/07q-interruptible-rime-http.py
index 132b743ab..7790a1c82 100644
--- a/examples/foundational/07q-interruptible-rime-http.py
+++ b/examples/foundational/07q-interruptible-rime-http.py
@@ -89,10 +89,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py
index d859b83aa..fb5e453fd 100644
--- a/examples/foundational/07q-interruptible-rime.py
+++ b/examples/foundational/07q-interruptible-rime.py
@@ -83,10 +83,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07r-interruptible-riva-nim.py b/examples/foundational/07r-interruptible-riva-nim.py
index 1d202bcb5..7092f6633 100644
--- a/examples/foundational/07r-interruptible-riva-nim.py
+++ b/examples/foundational/07r-interruptible-riva-nim.py
@@ -80,10 +80,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py
index 9f8c9fbbf..c10ca02d0 100644
--- a/examples/foundational/07s-interruptible-google-audio-in.py
+++ b/examples/foundational/07s-interruptible-google-audio-in.py
@@ -258,7 +258,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py
index 50eee52ec..69babfc05 100644
--- a/examples/foundational/07t-interruptible-fish.py
+++ b/examples/foundational/07t-interruptible-fish.py
@@ -84,10 +84,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07u-interruptible-ultravox.py b/examples/foundational/07u-interruptible-ultravox.py
index 23bc409f3..b81f43461 100644
--- a/examples/foundational/07u-interruptible-ultravox.py
+++ b/examples/foundational/07u-interruptible-ultravox.py
@@ -77,8 +77,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/07v-interruptible-neuphonic-http.py b/examples/foundational/07v-interruptible-neuphonic-http.py
index 453caa00d..fda109c8f 100644
--- a/examples/foundational/07v-interruptible-neuphonic-http.py
+++ b/examples/foundational/07v-interruptible-neuphonic-http.py
@@ -84,10 +84,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07v-interruptible-neuphonic.py b/examples/foundational/07v-interruptible-neuphonic.py
index 934875db4..34aee1977 100644
--- a/examples/foundational/07v-interruptible-neuphonic.py
+++ b/examples/foundational/07v-interruptible-neuphonic.py
@@ -83,10 +83,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07w-interruptible-fal.py b/examples/foundational/07w-interruptible-fal.py
index 2b00c4b27..2ef942ad2 100644
--- a/examples/foundational/07w-interruptible-fal.py
+++ b/examples/foundational/07w-interruptible-fal.py
@@ -86,10 +86,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07x-interruptible-local.py b/examples/foundational/07x-interruptible-local.py
index 94097d7c9..48725d9bd 100644
--- a/examples/foundational/07x-interruptible-local.py
+++ b/examples/foundational/07x-interruptible-local.py
@@ -70,10 +70,8 @@ async def main():
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07y-interruptible-minimax.py b/examples/foundational/07y-interruptible-minimax.py
index b01a51b57..97bb655d3 100644
--- a/examples/foundational/07y-interruptible-minimax.py
+++ b/examples/foundational/07y-interruptible-minimax.py
@@ -90,10 +90,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/07z-interruptible-sarvam.py b/examples/foundational/07z-interruptible-sarvam.py
index 02225b209..2fdf1634a 100644
--- a/examples/foundational/07z-interruptible-sarvam.py
+++ b/examples/foundational/07z-interruptible-sarvam.py
@@ -89,10 +89,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py
index 186ff97aa..fb72e6a2a 100644
--- a/examples/foundational/10-wake-phrase.py
+++ b/examples/foundational/10-wake-phrase.py
@@ -85,7 +85,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/12a-describe-video-gemini-flash.py b/examples/foundational/12a-describe-video-gemini-flash.py
index fa2d9aec6..1c762e067 100644
--- a/examples/foundational/12a-describe-video-gemini-flash.py
+++ b/examples/foundational/12a-describe-video-gemini-flash.py
@@ -101,7 +101,10 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
@transport.event_handler("on_client_connected")
diff --git a/examples/foundational/12b-describe-video-gpt-4o.py b/examples/foundational/12b-describe-video-gpt-4o.py
index 3c7937f54..c5492c022 100644
--- a/examples/foundational/12b-describe-video-gpt-4o.py
+++ b/examples/foundational/12b-describe-video-gpt-4o.py
@@ -101,7 +101,10 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
@transport.event_handler("on_client_connected")
diff --git a/examples/foundational/12c-describe-video-anthropic.py b/examples/foundational/12c-describe-video-anthropic.py
index 0e82f1077..34b7d6bed 100644
--- a/examples/foundational/12c-describe-video-anthropic.py
+++ b/examples/foundational/12c-describe-video-anthropic.py
@@ -101,7 +101,10 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
@transport.event_handler("on_client_connected")
diff --git a/examples/foundational/13e-whisper-mlx.py b/examples/foundational/13e-whisper-mlx.py
index 5d7dc53e3..d06788570 100644
--- a/examples/foundational/13e-whisper-mlx.py
+++ b/examples/foundational/13e-whisper-mlx.py
@@ -84,7 +84,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
pipeline,
params=PipelineParams(
enable_metrics=True,
- report_only_initial_ttfb=False,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/14-function-calling.py b/examples/foundational/14-function-calling.py
index b4e09c99a..b8fddd227 100644
--- a/examples/foundational/14-function-calling.py
+++ b/examples/foundational/14-function-calling.py
@@ -134,10 +134,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/14a-function-calling-anthropic.py b/examples/foundational/14a-function-calling-anthropic.py
index bf34e211c..cb0766262 100644
--- a/examples/foundational/14a-function-calling-anthropic.py
+++ b/examples/foundational/14a-function-calling-anthropic.py
@@ -127,8 +127,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/14b-function-calling-anthropic-video.py b/examples/foundational/14b-function-calling-anthropic-video.py
index 04049984f..b9d9b2f71 100644
--- a/examples/foundational/14b-function-calling-anthropic-video.py
+++ b/examples/foundational/14b-function-calling-anthropic-video.py
@@ -172,8 +172,8 @@ If you need to use a tool, simply use the tool. Do not tell the user the tool yo
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/14c-function-calling-together.py b/examples/foundational/14c-function-calling-together.py
index 1188f32e2..e1fc55eb3 100644
--- a/examples/foundational/14c-function-calling-together.py
+++ b/examples/foundational/14c-function-calling-together.py
@@ -16,7 +16,7 @@ from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import TTSSpeakFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
-from pipecat.pipeline.task import PipelineTask
+from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
from pipecat.services.cartesia.tts import CartesiaTTSService
from pipecat.services.deepgram.stt import DeepgramSTTService
@@ -116,7 +116,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline)
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/14d-function-calling-video.py b/examples/foundational/14d-function-calling-video.py
index 7761e1be2..a6f21922d 100644
--- a/examples/foundational/14d-function-calling-video.py
+++ b/examples/foundational/14d-function-calling-video.py
@@ -17,7 +17,7 @@ from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
-from pipecat.pipeline.task import PipelineTask
+from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
from pipecat.services.cartesia.tts import CartesiaTTSService
from pipecat.services.deepgram.stt import DeepgramSTTService
@@ -158,7 +158,13 @@ indicate you should use the get_image tool are:
]
)
- task = PipelineTask(pipeline)
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/14e-function-calling-gemini.py b/examples/foundational/14e-function-calling-google.py
similarity index 99%
rename from examples/foundational/14e-function-calling-gemini.py
rename to examples/foundational/14e-function-calling-google.py
index ccbe952fb..92ebe4135 100644
--- a/examples/foundational/14e-function-calling-gemini.py
+++ b/examples/foundational/14e-function-calling-google.py
@@ -183,7 +183,6 @@ indicate you should use the get_image tool are:
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14f-function-calling-groq.py b/examples/foundational/14f-function-calling-groq.py
index 3ff56a915..d527642d0 100644
--- a/examples/foundational/14f-function-calling-groq.py
+++ b/examples/foundational/14f-function-calling-groq.py
@@ -121,7 +121,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14g-function-calling-grok.py b/examples/foundational/14g-function-calling-grok.py
index ce3349ecb..00aa32aa2 100644
--- a/examples/foundational/14g-function-calling-grok.py
+++ b/examples/foundational/14g-function-calling-grok.py
@@ -111,7 +111,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14h-function-calling-azure.py b/examples/foundational/14h-function-calling-azure.py
index 25cf7defa..cf8480c75 100644
--- a/examples/foundational/14h-function-calling-azure.py
+++ b/examples/foundational/14h-function-calling-azure.py
@@ -120,7 +120,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14i-function-calling-fireworks.py b/examples/foundational/14i-function-calling-fireworks.py
index 028d9fa64..0921d73d6 100644
--- a/examples/foundational/14i-function-calling-fireworks.py
+++ b/examples/foundational/14i-function-calling-fireworks.py
@@ -119,7 +119,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14j-function-calling-nim.py b/examples/foundational/14j-function-calling-nim.py
index d254e0d4f..0365a4369 100644
--- a/examples/foundational/14j-function-calling-nim.py
+++ b/examples/foundational/14j-function-calling-nim.py
@@ -117,7 +117,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14k-function-calling-cerebras.py b/examples/foundational/14k-function-calling-cerebras.py
index 70ffceffd..5a6bcf91c 100644
--- a/examples/foundational/14k-function-calling-cerebras.py
+++ b/examples/foundational/14k-function-calling-cerebras.py
@@ -126,10 +126,8 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/14l-function-calling-deepseek.py b/examples/foundational/14l-function-calling-deepseek.py
index 7e992942d..814a4147d 100644
--- a/examples/foundational/14l-function-calling-deepseek.py
+++ b/examples/foundational/14l-function-calling-deepseek.py
@@ -126,10 +126,8 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/14m-function-calling-openrouter.py b/examples/foundational/14m-function-calling-openrouter.py
index 023f725f6..aac0923ca 100644
--- a/examples/foundational/14m-function-calling-openrouter.py
+++ b/examples/foundational/14m-function-calling-openrouter.py
@@ -120,10 +120,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/14n-function-calling-perplexity.py b/examples/foundational/14n-function-calling-perplexity.py
index c4a70d627..144c3a3df 100644
--- a/examples/foundational/14n-function-calling-perplexity.py
+++ b/examples/foundational/14n-function-calling-perplexity.py
@@ -90,10 +90,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/14o-function-calling-gemini-openai-format.py b/examples/foundational/14o-function-calling-gemini-openai-format.py
index 8f0036e07..7d236bf4c 100644
--- a/examples/foundational/14o-function-calling-gemini-openai-format.py
+++ b/examples/foundational/14o-function-calling-gemini-openai-format.py
@@ -116,7 +116,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py
index 4348b663a..1fc6b0fc4 100644
--- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py
+++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py
@@ -122,7 +122,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/14q-function-calling-qwen.py b/examples/foundational/14q-function-calling-qwen.py
index 6e07a97e1..7cf2182c0 100644
--- a/examples/foundational/14q-function-calling-qwen.py
+++ b/examples/foundational/14q-function-calling-qwen.py
@@ -118,10 +118,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/14r-function-calling-aws.py b/examples/foundational/14r-function-calling-aws.py
index 4443bec27..a14c37cf9 100644
--- a/examples/foundational/14r-function-calling-aws.py
+++ b/examples/foundational/14r-function-calling-aws.py
@@ -134,10 +134,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py
index 0735b62c0..ce2a358af 100644
--- a/examples/foundational/15-switch-voices.py
+++ b/examples/foundational/15-switch-voices.py
@@ -147,7 +147,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py
index 48bc7e63b..61cc2e3bd 100644
--- a/examples/foundational/15a-switch-languages.py
+++ b/examples/foundational/15a-switch-languages.py
@@ -135,7 +135,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py
index c13c2bbff..5d9eff072 100644
--- a/examples/foundational/16-gpu-container-local-bot.py
+++ b/examples/foundational/16-gpu-container-local-bot.py
@@ -90,8 +90,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py
index 8f5e3ba1d..bb6975829 100644
--- a/examples/foundational/17-detect-user-idle.py
+++ b/examples/foundational/17-detect-user-idle.py
@@ -117,9 +117,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
- report_only_initial_ttfb=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/19-openai-realtime-beta.py b/examples/foundational/19-openai-realtime-beta.py
index 9eb816432..71b20eb0f 100644
--- a/examples/foundational/19-openai-realtime-beta.py
+++ b/examples/foundational/19-openai-realtime-beta.py
@@ -186,10 +186,8 @@ Remember, your responses should be short. Just one or two sentences, usually."""
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/19a-azure-realtime-beta.py b/examples/foundational/19a-azure-realtime-beta.py
index 54f0302e5..81017188c 100644
--- a/examples/foundational/19a-azure-realtime-beta.py
+++ b/examples/foundational/19a-azure-realtime-beta.py
@@ -179,10 +179,8 @@ Remember, your responses should be short. Just one or two sentences, usually."""
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py
index 360d5f659..9dd6ba42c 100644
--- a/examples/foundational/20a-persistent-context-openai.py
+++ b/examples/foundational/20a-persistent-context-openai.py
@@ -223,10 +223,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py
index a1ea1f4c5..2315254cb 100644
--- a/examples/foundational/20b-persistent-context-openai-realtime.py
+++ b/examples/foundational/20b-persistent-context-openai-realtime.py
@@ -233,10 +233,8 @@ Remember, your responses should be short. Just one or two sentences, usually."""
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py
index 686bed5a7..1c5f2c5a0 100644
--- a/examples/foundational/20c-persistent-context-anthropic.py
+++ b/examples/foundational/20c-persistent-context-anthropic.py
@@ -222,10 +222,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- # report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py
index 5219e3a2e..c9489539d 100644
--- a/examples/foundational/20d-persistent-context-gemini.py
+++ b/examples/foundational/20d-persistent-context-gemini.py
@@ -275,10 +275,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- # report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py
index 86630ce6d..7fa4c7e54 100644
--- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py
+++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py
@@ -242,10 +242,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/21-tavus-transport.py b/examples/foundational/21-tavus-transport.py
index 68614a854..8c6bb03fc 100644
--- a/examples/foundational/21-tavus-transport.py
+++ b/examples/foundational/21-tavus-transport.py
@@ -79,10 +79,8 @@ async def main():
params=PipelineParams(
audio_in_sample_rate=16000,
audio_out_sample_rate=24000,
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/21a-tavus-video-service.py b/examples/foundational/21a-tavus-video-service.py
index db79d343d..532cce847 100644
--- a/examples/foundational/21a-tavus-video-service.py
+++ b/examples/foundational/21a-tavus-video-service.py
@@ -96,10 +96,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
params=PipelineParams(
audio_in_sample_rate=16000,
audio_out_sample_rate=24000,
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py
index 0a49f141c..e3662ac85 100644
--- a/examples/foundational/22-natural-conversation.py
+++ b/examples/foundational/22-natural-conversation.py
@@ -147,10 +147,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/22b-natural-conversation-proposal.py b/examples/foundational/22b-natural-conversation-proposal.py
index 1b03f341e..74f280124 100644
--- a/examples/foundational/22b-natural-conversation-proposal.py
+++ b/examples/foundational/22b-natural-conversation-proposal.py
@@ -312,9 +312,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
# to start the conversation.
bot_output_gate = OutputGate(notifier=notifier, start_open=True)
- async def block_user_stopped_speaking(frame):
- return not isinstance(frame, UserStoppedSpeakingFrame)
-
async def pass_only_llm_trigger_frames(frame):
return (
isinstance(frame, OpenAILLMContextFrame)
@@ -331,11 +328,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
stt,
context_aggregator.user(),
ParallelPipeline(
- [
- # Pass everything except UserStoppedSpeaking to the elements after
- # this ParallelPipeline
- FunctionFilter(filter=block_user_stopped_speaking),
- ],
[
# Ignore everything except an OpenAILLMContextFrame. Pass a specially constructed
# LLMMessagesFrame to the statement classifier LLM. The only frame this
@@ -361,10 +353,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/22c-natural-conversation-mixed-llms.py b/examples/foundational/22c-natural-conversation-mixed-llms.py
index 0bcbf75bd..00d799cc8 100644
--- a/examples/foundational/22c-natural-conversation-mixed-llms.py
+++ b/examples/foundational/22c-natural-conversation-mixed-llms.py
@@ -564,7 +564,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/22d-natural-conversation-gemini-audio.py b/examples/foundational/22d-natural-conversation-gemini-audio.py
index b6b66db0c..b8e21266f 100644
--- a/examples/foundational/22d-natural-conversation-gemini-audio.py
+++ b/examples/foundational/22d-natural-conversation-gemini-audio.py
@@ -746,7 +746,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py
index 098a62f86..a74b81f65 100644
--- a/examples/foundational/23-bot-background-sound.py
+++ b/examples/foundational/23-bot-background-sound.py
@@ -103,10 +103,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/24-stt-mute-filter.py b/examples/foundational/24-stt-mute-filter.py
index c1c4580fd..2706bebaf 100644
--- a/examples/foundational/24-stt-mute-filter.py
+++ b/examples/foundational/24-stt-mute-filter.py
@@ -121,7 +121,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py
index 83005a3ed..e0c834c23 100644
--- a/examples/foundational/25-google-audio-in.py
+++ b/examples/foundational/25-google-audio-in.py
@@ -357,7 +357,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py
index 03c063982..7d9a19995 100644
--- a/examples/foundational/26-gemini-multimodal-live.py
+++ b/examples/foundational/26-gemini-multimodal-live.py
@@ -83,7 +83,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py
index 9da336459..a919f6297 100644
--- a/examples/foundational/26a-gemini-multimodal-live-transcription.py
+++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py
@@ -107,7 +107,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py
index 15db2faa0..c21559b12 100644
--- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py
+++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py
@@ -151,7 +151,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py
index c39aeb63b..50c283aae 100644
--- a/examples/foundational/26c-gemini-multimodal-live-video.py
+++ b/examples/foundational/26c-gemini-multimodal-live-video.py
@@ -82,7 +82,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py
index 036e383b9..f5d0f3630 100644
--- a/examples/foundational/26d-gemini-multimodal-live-text.py
+++ b/examples/foundational/26d-gemini-multimodal-live-text.py
@@ -119,7 +119,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py
index 8638dfc11..a9a2bc384 100644
--- a/examples/foundational/26e-gemini-multimodal-google-search.py
+++ b/examples/foundational/26e-gemini-multimodal-google-search.py
@@ -107,7 +107,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py
index 9f7f6aa38..40f6b0c3d 100644
--- a/examples/foundational/27-simli-layer.py
+++ b/examples/foundational/27-simli-layer.py
@@ -92,8 +92,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/28-transcription-processor.py b/examples/foundational/28-transcription-processor.py
index d8211071c..82ca13bbc 100644
--- a/examples/foundational/28-transcription-processor.py
+++ b/examples/foundational/28-transcription-processor.py
@@ -152,7 +152,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/29-turn-tracking-observer.py b/examples/foundational/29-turn-tracking-observer.py
index 987ef372f..3467f044c 100644
--- a/examples/foundational/29-turn-tracking-observer.py
+++ b/examples/foundational/29-turn-tracking-observer.py
@@ -84,10 +84,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
observers=[UserBotLatencyLogObserver()],
)
diff --git a/examples/foundational/30-observer.py b/examples/foundational/30-observer.py
index c8172d2fa..161031415 100644
--- a/examples/foundational/30-observer.py
+++ b/examples/foundational/30-observer.py
@@ -133,10 +133,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
observers=[
CustomObserver(),
diff --git a/examples/foundational/32-gemini-grounding-metadata.py b/examples/foundational/32-gemini-grounding-metadata.py
index 47b94b979..e4729bfd5 100644
--- a/examples/foundational/32-gemini-grounding-metadata.py
+++ b/examples/foundational/32-gemini-grounding-metadata.py
@@ -130,7 +130,10 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
observers=[LLMSearchLoggerObserver()],
)
diff --git a/examples/foundational/33-gemini-rag.py b/examples/foundational/33-gemini-rag.py
index b1c88f289..dcb4c9a8c 100644
--- a/examples/foundational/33-gemini-rag.py
+++ b/examples/foundational/33-gemini-rag.py
@@ -239,7 +239,6 @@ Your response will be turned into speech so use only simple words and punctuatio
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/34-audio-recording.py b/examples/foundational/34-audio-recording.py
index 5c6b60d72..692904d0d 100644
--- a/examples/foundational/34-audio-recording.py
+++ b/examples/foundational/34-audio-recording.py
@@ -146,7 +146,13 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
diff --git a/examples/foundational/35-pattern-pair-voice-switching.py b/examples/foundational/35-pattern-pair-voice-switching.py
index 2663380be..07860f98b 100644
--- a/examples/foundational/35-pattern-pair-voice-switching.py
+++ b/examples/foundational/35-pattern-pair-voice-switching.py
@@ -211,10 +211,8 @@ Remember: Use narrator voice for EVERYTHING except the actual quoted dialogue.""
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/36-user-email-gathering.py b/examples/foundational/36-user-email-gathering.py
index 47996a492..528200182 100644
--- a/examples/foundational/36-user-email-gathering.py
+++ b/examples/foundational/36-user-email-gathering.py
@@ -129,10 +129,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/37-mem0.py b/examples/foundational/37-mem0.py
index 654a53a80..b7ceb885e 100644
--- a/examples/foundational/37-mem0.py
+++ b/examples/foundational/37-mem0.py
@@ -257,7 +257,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/38-smart-turn-fal.py b/examples/foundational/38-smart-turn-fal.py
index 1fa8a2891..120fe67dc 100644
--- a/examples/foundational/38-smart-turn-fal.py
+++ b/examples/foundational/38-smart-turn-fal.py
@@ -97,10 +97,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/38a-smart-turn-local-coreml.py b/examples/foundational/38a-smart-turn-local-coreml.py
index ceedfcfbe..043e2e0bd 100644
--- a/examples/foundational/38a-smart-turn-local-coreml.py
+++ b/examples/foundational/38a-smart-turn-local-coreml.py
@@ -112,10 +112,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/38b-smart-turn-local.py b/examples/foundational/38b-smart-turn-local.py
index 42fec18ad..08f21f875 100644
--- a/examples/foundational/38b-smart-turn-local.py
+++ b/examples/foundational/38b-smart-turn-local.py
@@ -112,10 +112,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
),
)
diff --git a/examples/foundational/39-mcp-stdio.py b/examples/foundational/39-mcp-stdio.py
index 235a38523..634ed8393 100644
--- a/examples/foundational/39-mcp-stdio.py
+++ b/examples/foundational/39-mcp-stdio.py
@@ -170,8 +170,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/39a-mcp-run-sse.py b/examples/foundational/39a-mcp-run-sse.py
index c02245174..8b43b3022 100644
--- a/examples/foundational/39a-mcp-run-sse.py
+++ b/examples/foundational/39a-mcp-run-sse.py
@@ -101,8 +101,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/39b-multiple-mcp.py b/examples/foundational/39b-multiple-mcp.py
index fe2869a52..3ba90d580 100644
--- a/examples/foundational/39b-multiple-mcp.py
+++ b/examples/foundational/39b-multiple-mcp.py
@@ -180,8 +180,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py
index 38fc4c189..ef01f7a47 100644
--- a/examples/foundational/40-aws-nova-sonic.py
+++ b/examples/foundational/40-aws-nova-sonic.py
@@ -144,7 +144,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/foundational/41a-text-only-webrtc.py b/examples/foundational/41a-text-only-webrtc.py
index bfd7a6051..2441b6380 100644
--- a/examples/foundational/41a-text-only-webrtc.py
+++ b/examples/foundational/41a-text-only-webrtc.py
@@ -112,8 +112,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
observers=[RTVIObserver(rtvi)],
)
diff --git a/examples/foundational/41b-text-and-audio-webrtc.py b/examples/foundational/41b-text-and-audio-webrtc.py
index 5ac250286..4e8f10605 100644
--- a/examples/foundational/41b-text-and-audio-webrtc.py
+++ b/examples/foundational/41b-text-and-audio-webrtc.py
@@ -130,8 +130,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
observers=[RTVIObserver(rtvi)],
)
diff --git a/examples/foundational/42-interruption-config.py b/examples/foundational/42-interruption-config.py
index 3cb64c204..9b57cc926 100644
--- a/examples/foundational/42-interruption-config.py
+++ b/examples/foundational/42-interruption-config.py
@@ -88,10 +88,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
- report_only_initial_ttfb=True,
interruption_strategies=[MinWordsInterruptionStrategy(min_words=3)],
),
)
diff --git a/examples/instant-voice/server/src/single_bot.py b/examples/instant-voice/server/src/single_bot.py
index 6c4e2b1b4..23a6008cb 100644
--- a/examples/instant-voice/server/src/single_bot.py
+++ b/examples/instant-voice/server/src/single_bot.py
@@ -90,7 +90,10 @@ async def main():
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
observers=[RTVIObserver(rtvi)],
)
diff --git a/examples/news-chatbot/server/news_bot.py b/examples/news-chatbot/server/news_bot.py
index c78752dfb..a24143051 100644
--- a/examples/news-chatbot/server/news_bot.py
+++ b/examples/news-chatbot/server/news_bot.py
@@ -140,7 +140,10 @@ async def main():
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
observers=[GoogleRTVIObserver(rtvi)],
)
diff --git a/examples/open-telemetry/jaeger/bot.py b/examples/open-telemetry/jaeger/bot.py
index 0f3084fd3..980245a02 100644
--- a/examples/open-telemetry/jaeger/bot.py
+++ b/examples/open-telemetry/jaeger/bot.py
@@ -139,7 +139,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/open-telemetry/langfuse/bot.py b/examples/open-telemetry/langfuse/bot.py
index 9c4f34695..08139ad19 100644
--- a/examples/open-telemetry/langfuse/bot.py
+++ b/examples/open-telemetry/langfuse/bot.py
@@ -136,7 +136,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/p2p-webrtc/daily-interop-bridge/bot.py b/examples/p2p-webrtc/daily-interop-bridge/bot.py
index 659d3fcef..e8783ab71 100644
--- a/examples/p2p-webrtc/daily-interop-bridge/bot.py
+++ b/examples/p2p-webrtc/daily-interop-bridge/bot.py
@@ -97,7 +97,8 @@ async def run_bot(webrtc_connection):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=False,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/p2p-webrtc/video-transform/server/bot.py b/examples/p2p-webrtc/video-transform/server/bot.py
index 8b11ffe21..cd2ea2709 100644
--- a/examples/p2p-webrtc/video-transform/server/bot.py
+++ b/examples/p2p-webrtc/video-transform/server/bot.py
@@ -121,7 +121,8 @@ async def run_bot(webrtc_connection):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
observers=[RTVIObserver(rtvi)],
)
diff --git a/examples/p2p-webrtc/voice-agent/bot.py b/examples/p2p-webrtc/voice-agent/bot.py
index 505a768e6..42dd712b4 100644
--- a/examples/p2p-webrtc/voice-agent/bot.py
+++ b/examples/p2p-webrtc/voice-agent/bot.py
@@ -73,7 +73,8 @@ async def run_bot(webrtc_connection):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/patient-intake/bot.py b/examples/patient-intake/bot.py
index 62a50fa77..a51456246 100644
--- a/examples/patient-intake/bot.py
+++ b/examples/patient-intake/bot.py
@@ -350,7 +350,13 @@ async def main():
]
)
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=False))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
@transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant):
diff --git a/examples/phone-chatbot/daily-pstn-advanced-voicemail-detection/bot.py b/examples/phone-chatbot/daily-pstn-advanced-voicemail-detection/bot.py
index 7eeaef39e..5ede2a59a 100644
--- a/examples/phone-chatbot/daily-pstn-advanced-voicemail-detection/bot.py
+++ b/examples/phone-chatbot/daily-pstn-advanced-voicemail-detection/bot.py
@@ -326,7 +326,10 @@ async def run_bot(
# Create pipeline task
voicemail_detection_pipeline_task = PipelineTask(
voicemail_detection_pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
# ------------ RETRY LOGIC VARIABLES ------------
@@ -471,7 +474,10 @@ async def run_bot(
# Create pipeline task
human_conversation_pipeline_task = PipelineTask(
human_conversation_pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
# Update participant left handler for human conversation phase
diff --git a/examples/phone-chatbot/daily-pstn-call-transfer/bot.py b/examples/phone-chatbot/daily-pstn-call-transfer/bot.py
index 9613be832..cd4e3a70a 100644
--- a/examples/phone-chatbot/daily-pstn-call-transfer/bot.py
+++ b/examples/phone-chatbot/daily-pstn-call-transfer/bot.py
@@ -454,7 +454,10 @@ async def run_bot(
# Create pipeline task
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
# ------------ EVENT HANDLERS ------------
diff --git a/examples/phone-chatbot/daily-pstn-dial-in/bot.py b/examples/phone-chatbot/daily-pstn-dial-in/bot.py
index 26310b052..112ce1ba6 100644
--- a/examples/phone-chatbot/daily-pstn-dial-in/bot.py
+++ b/examples/phone-chatbot/daily-pstn-dial-in/bot.py
@@ -148,7 +148,8 @@ async def run_bot(
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True # Enable barge-in so callers can interrupt the bot
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
logger.debug("setup task")
diff --git a/examples/phone-chatbot/daily-pstn-dial-out/bot.py b/examples/phone-chatbot/daily-pstn-dial-out/bot.py
index 752c4941d..3021a5cbf 100644
--- a/examples/phone-chatbot/daily-pstn-dial-out/bot.py
+++ b/examples/phone-chatbot/daily-pstn-dial-out/bot.py
@@ -140,7 +140,13 @@ async def run_bot(
)
# Create pipeline task
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
# ------------ RETRY LOGIC VARIABLES ------------
max_retries = 5
diff --git a/examples/phone-chatbot/daily-pstn-simple-voicemail-detection/bot.py b/examples/phone-chatbot/daily-pstn-simple-voicemail-detection/bot.py
index 46d8051f5..d4c99831c 100644
--- a/examples/phone-chatbot/daily-pstn-simple-voicemail-detection/bot.py
+++ b/examples/phone-chatbot/daily-pstn-simple-voicemail-detection/bot.py
@@ -107,7 +107,6 @@ async def run_bot(
params: FunctionCallParams,
):
"""Function the bot can call to terminate the call."""
-
await params.llm.queue_frame(EndTaskFrame(), FrameDirection.UPSTREAM)
tools = [
@@ -211,7 +210,13 @@ async def run_bot(
)
# Create pipeline task
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
# ------------ RETRY LOGIC VARIABLES ------------
max_retries = 5
diff --git a/examples/phone-chatbot/daily-twilio-sip-dial-in/bot.py b/examples/phone-chatbot/daily-twilio-sip-dial-in/bot.py
index 3d4afbfcd..0b5f935aa 100644
--- a/examples/phone-chatbot/daily-twilio-sip-dial-in/bot.py
+++ b/examples/phone-chatbot/daily-twilio-sip-dial-in/bot.py
@@ -95,7 +95,8 @@ async def run_bot(room_url: str, token: str, call_id: str, sip_uri: str) -> None
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True # Enable barge-in so callers can interrupt the bot
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/phone-chatbot/daily-twilio-sip-dial-out/bot.py b/examples/phone-chatbot/daily-twilio-sip-dial-out/bot.py
index 5f65a821e..e1056dcb8 100644
--- a/examples/phone-chatbot/daily-twilio-sip-dial-out/bot.py
+++ b/examples/phone-chatbot/daily-twilio-sip-dial-out/bot.py
@@ -134,7 +134,13 @@ async def run_bot(
)
# Create pipeline task
- task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
+ task = PipelineTask(
+ pipeline,
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
+ )
# ------------ RETRY LOGIC VARIABLES ------------
max_retries = 5
diff --git a/examples/plivo-chatbot/bot.py b/examples/plivo-chatbot/bot.py
index 5ce4be0c6..88ce2c120 100644
--- a/examples/plivo-chatbot/bot.py
+++ b/examples/plivo-chatbot/bot.py
@@ -88,7 +88,8 @@ async def run_bot(websocket_client: WebSocket, stream_id: str, call_id: Optional
params=PipelineParams(
audio_in_sample_rate=8000,
audio_out_sample_rate=8000,
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/sentry-metrics/bot.py b/examples/sentry-metrics/bot.py
index 0980fdbf2..8ff412bb7 100644
--- a/examples/sentry-metrics/bot.py
+++ b/examples/sentry-metrics/bot.py
@@ -87,7 +87,10 @@ async def main():
task = PipelineTask(
pipeline,
- params=PipelineParams(allow_interruptions=True, enable_metrics=True),
+ params=PipelineParams(
+ enable_metrics=True,
+ enable_usage_metrics=True,
+ ),
)
@transport.event_handler("on_first_participant_joined")
diff --git a/examples/simple-chatbot/server/bot-gemini.py b/examples/simple-chatbot/server/bot-gemini.py
index a38dc11d3..27db5ebde 100644
--- a/examples/simple-chatbot/server/bot-gemini.py
+++ b/examples/simple-chatbot/server/bot-gemini.py
@@ -171,7 +171,6 @@ async def main():
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/simple-chatbot/server/bot-openai.py b/examples/simple-chatbot/server/bot-openai.py
index 63226396e..8316d3918 100644
--- a/examples/simple-chatbot/server/bot-openai.py
+++ b/examples/simple-chatbot/server/bot-openai.py
@@ -199,7 +199,6 @@ async def main():
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/storytelling-chatbot/server/bot.py b/examples/storytelling-chatbot/server/bot.py
index 47f948a3c..999ed8afc 100644
--- a/examples/storytelling-chatbot/server/bot.py
+++ b/examples/storytelling-chatbot/server/bot.py
@@ -106,7 +106,6 @@ async def main(room_url, token=None):
main_task = PipelineTask(
main_pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/studypal/studypal.py b/examples/studypal/studypal.py
index 0117134cb..c175d50c1 100644
--- a/examples/studypal/studypal.py
+++ b/examples/studypal/studypal.py
@@ -157,8 +157,8 @@ Your task is to help the user understand and learn from this article in 2 senten
pipeline,
params=PipelineParams(
audio_out_sample_rate=44100,
- allow_interruptions=True,
enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/telnyx-chatbot/bot.py b/examples/telnyx-chatbot/bot.py
index 1abbbd348..790e3a81a 100644
--- a/examples/telnyx-chatbot/bot.py
+++ b/examples/telnyx-chatbot/bot.py
@@ -92,7 +92,8 @@ async def run_bot(
params=PipelineParams(
audio_in_sample_rate=8000,
audio_out_sample_rate=8000,
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/translation-chatbot/bot.py b/examples/translation-chatbot/bot.py
index d68261cd0..e845b6d07 100644
--- a/examples/translation-chatbot/bot.py
+++ b/examples/translation-chatbot/bot.py
@@ -26,6 +26,7 @@ from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
+from pipecat.processors.filters.stt_mute_filter import STTMuteConfig, STTMuteFilter, STTMuteStrategy
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
from pipecat.processors.frameworks.rtvi import RTVIObserver, RTVIProcessor
from pipecat.processors.transcript_processor import TranscriptProcessor
@@ -140,6 +141,14 @@ async def main():
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
+ stt_mute_processor = STTMuteFilter(
+ config=STTMuteConfig(
+ strategies={
+ STTMuteStrategy.ALWAYS,
+ }
+ ),
+ )
+
tts = CartesiaTTSService(
api_key=os.getenv("CARTESIA_API_KEY"),
voice_id="34dbb662-8e98-413c-a1ef-1a3407675fe7", # Spanish Narrator Man
@@ -169,6 +178,7 @@ async def main():
[
transport.input(),
rtvi,
+ stt_mute_processor, # We don't want to interrupt the translator bot
stt,
transcript.user(), # User transcripts
tp,
@@ -183,7 +193,6 @@ async def main():
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=False, # We don't want to interrupt the translator bot
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/twilio-chatbot/README.md b/examples/twilio-chatbot/README.md
index d06fd7f85..9c7a4be95 100644
--- a/examples/twilio-chatbot/README.md
+++ b/examples/twilio-chatbot/README.md
@@ -110,31 +110,6 @@ To start a call, simply make a call to your configured Twilio phone number. The
## Testing
-It is also possible to automatically test the server without making phone calls by using a software client.
-
-First, update `templates/streams.xml` to point to your server's websocket endpoint. For example:
-
-```
-
-
-
-
-
-
-
-```
-
-Then, start the server with `-t` to indicate we are testing:
-
-```sh
-# Make sure you’re in the project directory and your virtual environment is activated
-python server.py -t
-```
-
-Finally, just point the client to the server's URL:
-
-```sh
-python client.py -u http://localhost:8765 -c 2
-```
-
-where `-c` allows you to create multiple concurrent clients.
+It is also possible to test the server without making phone calls by using one of these clients.
+- [python](client/python/README.md): This Python client enables automated testing of the server via WebSocket without the need to make actual phone calls.
+- [typescript](client/typescript/README.md): This typescript client enables manual testing of the server via WebSocket without the need to make actual phone calls.
\ No newline at end of file
diff --git a/examples/twilio-chatbot/bot.py b/examples/twilio-chatbot/bot.py
index 8aa73a2be..04aa8e806 100644
--- a/examples/twilio-chatbot/bot.py
+++ b/examples/twilio-chatbot/bot.py
@@ -95,7 +95,7 @@ async def run_bot(websocket_client: WebSocket, stream_sid: str, call_sid: str, t
# NOTE: Watch out! This will save all the conversation in memory. You can
# pass `buffer_size` to get periodic callbacks.
- audiobuffer = AudioBufferProcessor(user_continuous_stream=not testing)
+ audiobuffer = AudioBufferProcessor()
pipeline = Pipeline(
[
@@ -115,7 +115,8 @@ async def run_bot(websocket_client: WebSocket, stream_sid: str, call_sid: str, t
params=PipelineParams(
audio_in_sample_rate=8000,
audio_out_sample_rate=8000,
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/twilio-chatbot/client/python/README.md b/examples/twilio-chatbot/client/python/README.md
new file mode 100644
index 000000000..18e695382
--- /dev/null
+++ b/examples/twilio-chatbot/client/python/README.md
@@ -0,0 +1,39 @@
+# Python Client for Server Testing
+
+This Python client enables automated testing of the server via WebSocket without the need to make actual phone calls.
+
+## Setup Instructions
+
+### 1. Configure the Stream Template
+
+Edit the `templates/streams.xml` file to point to your server’s WebSocket endpoint. For example:
+
+```xml
+
+
+
+
+
+
+
+```
+
+### 2. Start the Server in Test Mode
+
+Run the server with the `-t` flag to indicate test mode:
+
+```sh
+# Ensure you're in the project directory and your virtual environment is activated
+python server.py -t
+```
+
+### 3. Run the Client
+
+Start the client and point it to the server URL:
+
+```sh
+python client.py -u http://localhost:8765 -c 2
+```
+
+- `-u`: Server URL (default is `http://localhost:8765`)
+- `-c`: Number of concurrent client connections (e.g., 2)
diff --git a/examples/twilio-chatbot/client.py b/examples/twilio-chatbot/client/python/client.py
similarity index 98%
rename from examples/twilio-chatbot/client.py
rename to examples/twilio-chatbot/client/python/client.py
index 33592da0a..fc9adecac 100644
--- a/examples/twilio-chatbot/client.py
+++ b/examples/twilio-chatbot/client/python/client.py
@@ -119,7 +119,7 @@ async def run_client(client_name: str, server_url: str, duration_secs: int):
# NOTE: Watch out! This will save all the conversation in memory. You can
# pass `buffer_size` to get periodic callbacks.
- audiobuffer = AudioBufferProcessor(user_continuous_stream=False)
+ audiobuffer = AudioBufferProcessor()
pipeline = Pipeline(
[
@@ -139,7 +139,8 @@ async def run_client(client_name: str, server_url: str, duration_secs: int):
params=PipelineParams(
audio_in_sample_rate=8000,
audio_out_sample_rate=8000,
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
)
diff --git a/examples/twilio-chatbot/client/typescript/README.md b/examples/twilio-chatbot/client/typescript/README.md
new file mode 100644
index 000000000..a2dd7b05b
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/README.md
@@ -0,0 +1,27 @@
+# Typescript Client for Server Testing
+
+This typescript client enables manual testing of the server via WebSocket without the need to make actual phone calls.
+
+## Setup
+
+1. Run the bot server. See the [server README](../../README).
+
+2. Navigate to the `client/typescript` directory:
+
+```bash
+cd client/typescript
+```
+
+3. Install dependencies:
+
+```bash
+npm install
+```
+
+4. Run the client app:
+
+```
+npm run dev
+```
+
+5. Visit http://localhost:5173 in your browser.
diff --git a/examples/twilio-chatbot/client/typescript/index.html b/examples/twilio-chatbot/client/typescript/index.html
new file mode 100644
index 000000000..83c24031a
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ AI Chatbot
+
+
+
+
+
+
+ Transport: Disconnected
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/twilio-chatbot/client/typescript/package-lock.json b/examples/twilio-chatbot/client/typescript/package-lock.json
new file mode 100644
index 000000000..d7e58ef56
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/package-lock.json
@@ -0,0 +1,1793 @@
+{
+ "name": "client",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "client",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "@pipecat-ai/client-js": "^0.4.0",
+ "@pipecat-ai/websocket-transport": "^0.4.2"
+ },
+ "devDependencies": {
+ "@types/node": "^22.13.1",
+ "@types/protobufjs": "^6.0.0",
+ "@vitejs/plugin-react-swc": "^3.7.2",
+ "typescript": "^5.7.3",
+ "vite": "^6.0.2"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.27.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
+ "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bufbuild/protobuf": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.5.2.tgz",
+ "integrity": "sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg==",
+ "license": "(Apache-2.0 AND BSD-3-Clause)"
+ },
+ "node_modules/@bufbuild/protoplugin": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.5.2.tgz",
+ "integrity": "sha512-7d/NUae/ugs/qgHEYOwkVWGDE3Bf/xjuGviVFs38+MLRdwiHNTiuvzPVwuIPo/1wuZCZn3Nax1cg1owLuY72xw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@bufbuild/protobuf": "2.5.2",
+ "@typescript/vfs": "^1.5.2",
+ "typescript": "5.4.5"
+ }
+ },
+ "node_modules/@bufbuild/protoplugin/node_modules/typescript": {
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/@daily-co/daily-js": {
+ "version": "0.79.0",
+ "resolved": "https://registry.npmjs.org/@daily-co/daily-js/-/daily-js-0.79.0.tgz",
+ "integrity": "sha512-Ii/Zi6cfTl2EZBpX8msRPNkkCHcajA+ErXpbN2Xe2KySd1Nb4IzC/QWJlSl9VA9pIlYPQicRTDoZnoym/0uEAw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "@sentry/browser": "^8.33.1",
+ "bowser": "^2.8.1",
+ "dequal": "^2.0.3",
+ "events": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
+ "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
+ "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
+ "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
+ "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
+ "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
+ "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
+ "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
+ "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
+ "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
+ "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
+ "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
+ "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
+ "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
+ "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
+ "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
+ "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
+ "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
+ "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
+ "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
+ "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
+ "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
+ "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@pipecat-ai/client-js": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@pipecat-ai/client-js/-/client-js-0.4.1.tgz",
+ "integrity": "sha512-3jLKRzeryqLxtkqvr4Bvxe2OxoI7mdOFecm6iolZizXnk/BE480SEg2oAKyov3b5oT6+jmPlT+1HRBlTzEtL7A==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@types/events": "^3.0.3",
+ "clone-deep": "^4.0.1",
+ "events": "^3.3.0",
+ "typed-emitter": "^2.1.0",
+ "uuid": "^10.0.0"
+ }
+ },
+ "node_modules/@pipecat-ai/websocket-transport": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@pipecat-ai/websocket-transport/-/websocket-transport-0.4.2.tgz",
+ "integrity": "sha512-mOYnw9n60usODrE35D+uhFbJXl0DqXV32pAqSHu1of049s128mex6Qv+W49DBMVr8h5W6pLGrXhm+XDAtN5leg==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@daily-co/daily-js": "^0.79.0",
+ "@protobuf-ts/plugin": "^2.11.0",
+ "@protobuf-ts/runtime": "^2.11.0",
+ "x-law": "^0.3.1"
+ },
+ "peerDependencies": {
+ "@pipecat-ai/client-js": "~0.4.0"
+ }
+ },
+ "node_modules/@protobuf-ts/plugin": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.11.0.tgz",
+ "integrity": "sha512-Y+p4Axrk3thxws4BVSIO+x4CKWH2c8k3K+QPrp6Oq8agdsXPL/uwsMTIdpTdXIzTaUEZFASJL9LU56pob5GTHg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@bufbuild/protobuf": "^2.4.0",
+ "@bufbuild/protoplugin": "^2.4.0",
+ "@protobuf-ts/protoc": "^2.11.0",
+ "@protobuf-ts/runtime": "^2.11.0",
+ "@protobuf-ts/runtime-rpc": "^2.11.0",
+ "typescript": "^3.9"
+ },
+ "bin": {
+ "protoc-gen-dump": "bin/protoc-gen-dump",
+ "protoc-gen-ts": "bin/protoc-gen-ts"
+ }
+ },
+ "node_modules/@protobuf-ts/plugin/node_modules/typescript": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
+ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/@protobuf-ts/protoc": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.11.0.tgz",
+ "integrity": "sha512-GYfmv1rjZ/7MWzUqMszhdXiuoa4Js/j6zCbcxFmeThBBUhbrXdPU42vY+QVCHL9PvAMXO+wEhUfPWYdd1YgnlA==",
+ "license": "Apache-2.0",
+ "bin": {
+ "protoc": "protoc.js"
+ }
+ },
+ "node_modules/@protobuf-ts/runtime": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.11.0.tgz",
+ "integrity": "sha512-DfpRpUiNvPC3Kj48CmlU4HaIEY1Myh++PIumMmohBAk8/k0d2CkxYxJfPyUAxfuUfl97F4AvuCu1gXmfOG7OJQ==",
+ "license": "(Apache-2.0 AND BSD-3-Clause)"
+ },
+ "node_modules/@protobuf-ts/runtime-rpc": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.11.0.tgz",
+ "integrity": "sha512-g/oMPym5LjVyCc3nlQc6cHer0R3CyleBos4p7CjRNzdKuH/FlRXzfQYo6EN5uv8vLtn7zEK9Cy4YBKvHStIaag==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@protobuf-ts/runtime": "^2.11.0"
+ }
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.11",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.11.tgz",
+ "integrity": "sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz",
+ "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz",
+ "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz",
+ "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz",
+ "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz",
+ "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz",
+ "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz",
+ "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz",
+ "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz",
+ "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz",
+ "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz",
+ "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz",
+ "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz",
+ "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz",
+ "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz",
+ "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz",
+ "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz",
+ "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz",
+ "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz",
+ "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz",
+ "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@sentry-internal/browser-utils": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.55.0.tgz",
+ "integrity": "sha512-ROgqtQfpH/82AQIpESPqPQe0UyWywKJsmVIqi3c5Fh+zkds5LUxnssTj3yNd1x+kxaPDVB023jAP+3ibNgeNDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/core": "8.55.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/@sentry-internal/feedback": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.55.0.tgz",
+ "integrity": "sha512-cP3BD/Q6pquVQ+YL+rwCnorKuTXiS9KXW8HNKu4nmmBAyf7urjs+F6Hr1k9MXP5yQ8W3yK7jRWd09Yu6DHWOiw==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry/core": "8.55.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/@sentry-internal/replay": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.55.0.tgz",
+ "integrity": "sha512-roCDEGkORwolxBn8xAKedybY+Jlefq3xYmgN2fr3BTnsXjSYOPC7D1/mYqINBat99nDtvgFvNfRcZPiwwZ1hSw==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry-internal/browser-utils": "8.55.0",
+ "@sentry/core": "8.55.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/@sentry-internal/replay-canvas": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.55.0.tgz",
+ "integrity": "sha512-nIkfgRWk1091zHdu4NbocQsxZF1rv1f7bbp3tTIlZYbrH62XVZosx5iHAuZG0Zc48AETLE7K4AX9VGjvQj8i9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry-internal/replay": "8.55.0",
+ "@sentry/core": "8.55.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/@sentry/browser": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.55.0.tgz",
+ "integrity": "sha512-1A31mCEWCjaMxJt6qGUK+aDnLDcK6AwLAZnqpSchNysGni1pSn1RWSmk9TBF8qyTds5FH8B31H480uxMPUJ7Cw==",
+ "license": "MIT",
+ "dependencies": {
+ "@sentry-internal/browser-utils": "8.55.0",
+ "@sentry-internal/feedback": "8.55.0",
+ "@sentry-internal/replay": "8.55.0",
+ "@sentry-internal/replay-canvas": "8.55.0",
+ "@sentry/core": "8.55.0"
+ },
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/@sentry/core": {
+ "version": "8.55.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.55.0.tgz",
+ "integrity": "sha512-6g7jpbefjHYs821Z+EBJ8r4Z7LT5h80YSWRJaylGS4nW5W5Z2KXzpdnyFarv37O7QjauzVC2E+PABmpkw5/JGA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
+ "node_modules/@swc/core": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.0.tgz",
+ "integrity": "sha512-/C0kiMHPY/HnLfqXYGMGxGck3A5Y3mqwxfv+EwHTPHGjAVRfHpWAEEBTSTF5C88vVY6CvwBEkhR2TX7t8Mahcw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/counter": "^0.1.3",
+ "@swc/types": "^0.1.22"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/swc"
+ },
+ "optionalDependencies": {
+ "@swc/core-darwin-arm64": "1.12.0",
+ "@swc/core-darwin-x64": "1.12.0",
+ "@swc/core-linux-arm-gnueabihf": "1.12.0",
+ "@swc/core-linux-arm64-gnu": "1.12.0",
+ "@swc/core-linux-arm64-musl": "1.12.0",
+ "@swc/core-linux-x64-gnu": "1.12.0",
+ "@swc/core-linux-x64-musl": "1.12.0",
+ "@swc/core-win32-arm64-msvc": "1.12.0",
+ "@swc/core-win32-ia32-msvc": "1.12.0",
+ "@swc/core-win32-x64-msvc": "1.12.0"
+ },
+ "peerDependencies": {
+ "@swc/helpers": ">=0.5.17"
+ },
+ "peerDependenciesMeta": {
+ "@swc/helpers": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@swc/core-darwin-arm64": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.0.tgz",
+ "integrity": "sha512-usLr8kC80GDv3pwH2zoEaS279kxtWY0MY3blbMFw7zA8fAjqxa8IDxm3WcgyNLNWckWn4asFfguEwz/Weem3nA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-darwin-x64": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.0.tgz",
+ "integrity": "sha512-Cvv4sqDcTY7QF2Dh1vn2Xbt/1ENYQcpmrGHzITJrXzxA2aBopsz/n4yQDiyRxTR0t802m4xu0CzMoZIHvVruWQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm-gnueabihf": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.0.tgz",
+ "integrity": "sha512-seM4/XMJMOupkzfLfHl8sRa3NdhsVZp+XgwA/vVeYZYJE4wuWUxVzhCYzwmNftVY32eF2IiRaWnhG6ho6jusnQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-gnu": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.0.tgz",
+ "integrity": "sha512-Al0x33gUVxNY5tutEYpSyv7mze6qQS1ONa0HEwoRxcK9WXsX0NHLTiOSGZoCUS1SsXM37ONlbA6/Bsp1MQyP+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-musl": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.0.tgz",
+ "integrity": "sha512-OeFHz/5Hl9v75J9TYA5jQxNIYAZMqaiPpd9dYSTK2Xyqa/ZGgTtNyPhIwVfxx+9mHBf6+9c1mTlXUtACMtHmaQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-gnu": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.0.tgz",
+ "integrity": "sha512-ltIvqNi7H0c5pRawyqjeYSKEIfZP4vv/datT3mwT6BW7muJtd1+KIDCPFLMIQ4wm/h76YQwPocsin3fzmnFdNA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-musl": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.0.tgz",
+ "integrity": "sha512-Z/DhpjehaTK0uf+MhNB7mV9SuewpGs3P/q9/8+UsJeYoFr7yuOoPbAvrD6AqZkf6Bh7MRZ5OtG+KQgG5L+goiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-arm64-msvc": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.0.tgz",
+ "integrity": "sha512-wHnvbfHIh2gfSbvuFT7qP97YCMUDh+fuiso+pcC6ug8IsMxuViNapHET4o0ZdFNWHhXJ7/s0e6w7mkOalsqQiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-ia32-msvc": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.0.tgz",
+ "integrity": "sha512-88umlXwK+7J2p4DjfWHXQpmlZgCf1ayt6Ssj+PYlAfMCR0aBiJoAMwHWrvDXEozyOrsyP1j2X6WxbmA861vL5Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-x64-msvc": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.0.tgz",
+ "integrity": "sha512-KR9TSRp+FEVOhbgTU6c94p/AYpsyBk7dIvlKQiDp8oKScUoyHG5yjmMBFN/BqUyTq4kj6zlgsY2rFE4R8/yqWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@swc/types": {
+ "version": "0.1.23",
+ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz",
+ "integrity": "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/counter": "^0.1.3"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/events": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
+ "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.15.31",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz",
+ "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/protobufjs": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@types/protobufjs/-/protobufjs-6.0.0.tgz",
+ "integrity": "sha512-A27RDExpAf3rdDjIrHKiJK6x8kqqJ4CmoChwtipfhVAn1p7+wviQFFP7dppn8FslSbHtQeVPvi8wNKkDjSYjHw==",
+ "deprecated": "This is a stub types definition for protobufjs (https://github.com/dcodeIO/ProtoBuf.js). protobufjs provides its own type definitions, so you don't need @types/protobufjs installed!",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "protobufjs": "*"
+ }
+ },
+ "node_modules/@typescript/vfs": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz",
+ "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ }
+ },
+ "node_modules/@vitejs/plugin-react-swc": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.10.2.tgz",
+ "integrity": "sha512-xD3Rdvrt5LgANug7WekBn1KhcvLn1H3jNBfJRL3reeOIua/WnZOEV5qi5qIBq5T8R0jUDmRtxuvk4bPhzGHDWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-beta.11",
+ "@swc/core": "^1.11.31"
+ },
+ "peerDependencies": {
+ "vite": "^4 || ^5 || ^6 || ^7.0.0-beta.0"
+ }
+ },
+ "node_modules/bowser": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
+ "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==",
+ "license": "MIT"
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
+ "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.5",
+ "@esbuild/android-arm": "0.25.5",
+ "@esbuild/android-arm64": "0.25.5",
+ "@esbuild/android-x64": "0.25.5",
+ "@esbuild/darwin-arm64": "0.25.5",
+ "@esbuild/darwin-x64": "0.25.5",
+ "@esbuild/freebsd-arm64": "0.25.5",
+ "@esbuild/freebsd-x64": "0.25.5",
+ "@esbuild/linux-arm": "0.25.5",
+ "@esbuild/linux-arm64": "0.25.5",
+ "@esbuild/linux-ia32": "0.25.5",
+ "@esbuild/linux-loong64": "0.25.5",
+ "@esbuild/linux-mips64el": "0.25.5",
+ "@esbuild/linux-ppc64": "0.25.5",
+ "@esbuild/linux-riscv64": "0.25.5",
+ "@esbuild/linux-s390x": "0.25.5",
+ "@esbuild/linux-x64": "0.25.5",
+ "@esbuild/netbsd-arm64": "0.25.5",
+ "@esbuild/netbsd-x64": "0.25.5",
+ "@esbuild/openbsd-arm64": "0.25.5",
+ "@esbuild/openbsd-x64": "0.25.5",
+ "@esbuild/sunos-x64": "0.25.5",
+ "@esbuild/win32-arm64": "0.25.5",
+ "@esbuild/win32-ia32": "0.25.5",
+ "@esbuild/win32-x64": "0.25.5"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "license": "MIT",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.5",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
+ "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/protobufjs": {
+ "version": "7.5.3",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz",
+ "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz",
+ "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.7"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.43.0",
+ "@rollup/rollup-android-arm64": "4.43.0",
+ "@rollup/rollup-darwin-arm64": "4.43.0",
+ "@rollup/rollup-darwin-x64": "4.43.0",
+ "@rollup/rollup-freebsd-arm64": "4.43.0",
+ "@rollup/rollup-freebsd-x64": "4.43.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.43.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.43.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.43.0",
+ "@rollup/rollup-linux-arm64-musl": "4.43.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.43.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.43.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.43.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.43.0",
+ "@rollup/rollup-linux-x64-gnu": "4.43.0",
+ "@rollup/rollup-linux-x64-musl": "4.43.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.43.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.43.0",
+ "@rollup/rollup-win32-x64-msvc": "4.43.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/typed-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz",
+ "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==",
+ "license": "MIT",
+ "optionalDependencies": {
+ "rxjs": "*"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
+ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/x-law": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/x-law/-/x-law-0.3.1.tgz",
+ "integrity": "sha512-Nvo6OKj6UL2LuzAc08uJkwIDkK2PsTEdpLiY82NkwMptuRpAA1V7arUl7ZY12BcgRYNq8uh1pdAu7G6VeQn7Hg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ }
+ }
+}
diff --git a/examples/twilio-chatbot/client/typescript/package.json b/examples/twilio-chatbot/client/typescript/package.json
new file mode 100644
index 000000000..4f107e789
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "client",
+ "version": "1.0.0",
+ "main": "index.js",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "devDependencies": {
+ "@types/node": "^22.13.1",
+ "@types/protobufjs": "^6.0.0",
+ "@vitejs/plugin-react-swc": "^3.7.2",
+ "typescript": "^5.7.3",
+ "vite": "^6.0.2"
+ },
+ "dependencies": {
+ "@pipecat-ai/client-js": "^0.4.0",
+ "@pipecat-ai/websocket-transport": "^0.4.2"
+ }
+}
diff --git a/examples/twilio-chatbot/client/typescript/src/app.ts b/examples/twilio-chatbot/client/typescript/src/app.ts
new file mode 100644
index 000000000..e52c6ebe3
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/src/app.ts
@@ -0,0 +1,247 @@
+/**
+ * Copyright (c) 2024–2025, Daily
+ *
+ * SPDX-License-Identifier: BSD 2-Clause License
+ */
+
+import {
+ RTVIClient,
+ RTVIClientOptions,
+ RTVIEvent,
+} from '@pipecat-ai/client-js';
+import {
+ WebSocketTransport,
+ TwilioSerializer,
+} from "@pipecat-ai/websocket-transport";
+
+class WebsocketClientApp {
+
+ private static STREAM_SID = "ws_mock_stream_sid"
+ private static CALL_SID = "ws_mock_call_sid"
+
+ private rtviClient: RTVIClient | null = null;
+ private connectBtn: HTMLButtonElement | null = null;
+ private disconnectBtn: HTMLButtonElement | null = null;
+ private statusSpan: HTMLElement | null = null;
+ private debugLog: HTMLElement | null = null;
+ private botAudio: HTMLAudioElement;
+
+ constructor() {
+ this.botAudio = document.createElement('audio');
+ this.botAudio.autoplay = true;
+ document.body.appendChild(this.botAudio);
+ this.setupDOMElements();
+ this.setupEventListeners();
+ }
+
+ /**
+ * Set up references to DOM elements and create necessary media elements
+ */
+ private setupDOMElements(): void {
+ this.connectBtn = document.getElementById('connect-btn') as HTMLButtonElement;
+ this.disconnectBtn = document.getElementById('disconnect-btn') as HTMLButtonElement;
+ this.statusSpan = document.getElementById('connection-status');
+ this.debugLog = document.getElementById('debug-log');
+ }
+
+ /**
+ * Set up event listeners for connect/disconnect buttons
+ */
+ private setupEventListeners(): void {
+ this.connectBtn?.addEventListener('click', () => this.connect());
+ this.disconnectBtn?.addEventListener('click', () => this.disconnect());
+ }
+
+ /**
+ * Add a timestamped message to the debug log
+ */
+ private log(message: string): void {
+ if (!this.debugLog) return;
+ const entry = document.createElement('div');
+ entry.textContent = `${new Date().toISOString()} - ${message}`;
+ if (message.startsWith('User: ')) {
+ entry.style.color = '#2196F3';
+ } else if (message.startsWith('Bot: ')) {
+ entry.style.color = '#4CAF50';
+ }
+ this.debugLog.appendChild(entry);
+ this.debugLog.scrollTop = this.debugLog.scrollHeight;
+ console.log(message);
+ }
+
+ /**
+ * Update the connection status display
+ */
+ private updateStatus(status: string): void {
+ if (this.statusSpan) {
+ this.statusSpan.textContent = status;
+ }
+ this.log(`Status: ${status}`);
+ }
+
+ private async emulateTwilioMessages() {
+ const connectedMessage={"event": "connected", "protocol": "Call", "version": "1.0.0"}
+
+ const websocketTransport = this.rtviClient?.transport as WebSocketTransport
+ void websocketTransport?.sendRawMessage(connectedMessage)
+
+ const startMessage={"event": "start", "start": {"streamSid": WebsocketClientApp.STREAM_SID, "callSid": WebsocketClientApp.CALL_SID}}
+ void websocketTransport?.sendRawMessage(startMessage)
+ }
+
+ /**
+ * Check for available media tracks and set them up if present
+ * This is called when the bot is ready or when the transport state changes to ready
+ */
+ setupMediaTracks() {
+ if (!this.rtviClient) return;
+ const tracks = this.rtviClient.tracks();
+ if (tracks.bot?.audio) {
+ this.setupAudioTrack(tracks.bot.audio);
+ }
+ }
+
+ /**
+ * Set up listeners for track events (start/stop)
+ * This handles new tracks being added during the session
+ */
+ setupTrackListeners() {
+ if (!this.rtviClient) return;
+
+ // Listen for new tracks starting
+ this.rtviClient.on(RTVIEvent.TrackStarted, (track, participant) => {
+ // Only handle non-local (bot) tracks
+ if (!participant?.local && track.kind === 'audio') {
+ this.setupAudioTrack(track);
+ }
+ });
+
+ // Listen for tracks stopping
+ this.rtviClient.on(RTVIEvent.TrackStopped, (track, participant) => {
+ this.log(`Track stopped: ${track.kind} from ${participant?.name || 'unknown'}`);
+ });
+ }
+
+ /**
+ * Set up an audio track for playback
+ * Handles both initial setup and track updates
+ */
+ private setupAudioTrack(track: MediaStreamTrack): void {
+ this.log('Setting up audio track');
+ if (this.botAudio.srcObject && "getAudioTracks" in this.botAudio.srcObject) {
+ const oldTrack = this.botAudio.srcObject.getAudioTracks()[0];
+ if (oldTrack?.id === track.id) return;
+ }
+ this.botAudio.srcObject = new MediaStream([track]);
+ }
+
+ /**
+ * Initialize and connect to the bot
+ * This sets up the RTVI client, initializes devices, and establishes the connection
+ */
+ public async connect(): Promise {
+ try {
+ const startTime = Date.now();
+
+ const transport = new WebSocketTransport({
+ serializer: new TwilioSerializer(),
+ recorderSampleRate: 8000,
+ playerSampleRate: 8000
+ });
+ const RTVIConfig: RTVIClientOptions = {
+ transport,
+ params: {
+ // The baseURL and endpoint of your bot server that the client will connect to
+ baseUrl: 'http://localhost:8765',
+ endpoints: { connect: '/' },
+ },
+ enableMic: true,
+ enableCam: false,
+ callbacks: {
+ onConnected: () => {
+ this.emulateTwilioMessages()
+ this.updateStatus('Connected');
+ if (this.connectBtn) this.connectBtn.disabled = true;
+ if (this.disconnectBtn) this.disconnectBtn.disabled = false;
+ },
+ onDisconnected: () => {
+ this.updateStatus('Disconnected');
+ if (this.connectBtn) this.connectBtn.disabled = false;
+ if (this.disconnectBtn) this.disconnectBtn.disabled = true;
+ this.log('Client disconnected');
+ },
+ onBotReady: (data) => {
+ this.log(`Bot ready: ${JSON.stringify(data)}`);
+ this.setupMediaTracks();
+ },
+ onUserTranscript: (data) => {
+ if (data.final) {
+ this.log(`User: ${data.text}`);
+ }
+ },
+ onBotTranscript: (data) => this.log(`Bot: ${data.text}`),
+ onMessageError: (error) => console.error('Message error:', error),
+ onError: (error) => console.error('Error:', error),
+ },
+ }
+ // @ts-ignore
+ RTVIConfig.customConnectHandler = () => Promise.resolve(
+ {
+ ws_url: "/ws",
+ }
+ );
+ this.rtviClient = new RTVIClient(RTVIConfig);
+ this.setupTrackListeners();
+
+ this.log('Initializing devices...');
+ await this.rtviClient.initDevices();
+
+ this.log('Connecting to bot...');
+ await this.rtviClient.connect();
+
+ const timeTaken = Date.now() - startTime;
+ this.log(`Connection complete, timeTaken: ${timeTaken}`);
+ } catch (error) {
+ this.log(`Error connecting: ${(error as Error).message}`);
+ this.updateStatus('Error');
+ // Clean up if there's an error
+ if (this.rtviClient) {
+ try {
+ await this.rtviClient.disconnect();
+ } catch (disconnectError) {
+ this.log(`Error during disconnect: ${disconnectError}`);
+ }
+ }
+ }
+ }
+
+ /**
+ * Disconnect from the bot and clean up media resources
+ */
+ public async disconnect(): Promise {
+ if (this.rtviClient) {
+ try {
+ await this.rtviClient.disconnect();
+ this.rtviClient = null;
+ if (this.botAudio.srcObject && "getAudioTracks" in this.botAudio.srcObject) {
+ this.botAudio.srcObject.getAudioTracks().forEach((track) => track.stop());
+ this.botAudio.srcObject = null;
+ }
+ } catch (error) {
+ this.log(`Error disconnecting: ${(error as Error).message}`);
+ }
+ }
+ }
+
+}
+
+declare global {
+ interface Window {
+ WebsocketClientApp: typeof WebsocketClientApp;
+ }
+}
+
+window.addEventListener('DOMContentLoaded', () => {
+ window.WebsocketClientApp = WebsocketClientApp;
+ new WebsocketClientApp();
+});
diff --git a/examples/twilio-chatbot/client/typescript/src/style.css b/examples/twilio-chatbot/client/typescript/src/style.css
new file mode 100644
index 000000000..9c147266e
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/src/style.css
@@ -0,0 +1,98 @@
+body {
+ margin: 0;
+ padding: 20px;
+ font-family: Arial, sans-serif;
+ background-color: #f0f0f0;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.status-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px;
+ background-color: #fff;
+ border-radius: 8px;
+ margin-bottom: 20px;
+}
+
+.controls button {
+ padding: 8px 16px;
+ margin-left: 10px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+#connect-btn {
+ background-color: #4caf50;
+ color: white;
+}
+
+#disconnect-btn {
+ background-color: #f44336;
+ color: white;
+}
+
+button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.main-content {
+ background-color: #fff;
+ border-radius: 8px;
+ padding: 20px;
+ margin-bottom: 20px;
+}
+
+.bot-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+#bot-video-container {
+ width: 640px;
+ height: 360px;
+ background-color: #e0e0e0;
+ border-radius: 8px;
+ margin: 20px auto;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+#bot-video-container video {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.debug-panel {
+ background-color: #fff;
+ border-radius: 8px;
+ padding: 20px;
+}
+
+.debug-panel h3 {
+ margin: 0 0 10px 0;
+ font-size: 16px;
+ font-weight: bold;
+}
+
+#debug-log {
+ height: 500px;
+ overflow-y: auto;
+ background-color: #f8f8f8;
+ padding: 10px;
+ border-radius: 4px;
+ font-family: monospace;
+ font-size: 12px;
+ line-height: 1.4;
+}
diff --git a/examples/twilio-chatbot/client/typescript/tsconfig.json b/examples/twilio-chatbot/client/typescript/tsconfig.json
new file mode 100644
index 000000000..c9c555d96
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/tsconfig.json
@@ -0,0 +1,111 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ // "noUncheckedSideEffectImports": true, /* Check side effect imports. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/examples/twilio-chatbot/client/typescript/vite.config.js b/examples/twilio-chatbot/client/typescript/vite.config.js
new file mode 100644
index 000000000..6bcaa3bc8
--- /dev/null
+++ b/examples/twilio-chatbot/client/typescript/vite.config.js
@@ -0,0 +1,15 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react-swc';
+
+export default defineConfig({
+ base: "./", //Use relative paths so it works at any mount path
+ plugins: [react()],
+ server: {
+ proxy: {
+ '/ws': {
+ target: 'ws://0.0.0.0:8765', // Replace with your backend URL
+ changeOrigin: true,
+ },
+ },
+ },
+});
diff --git a/examples/websocket/client/package-lock.json b/examples/websocket/client/package-lock.json
index f8157d4e1..bccbf27b4 100644
--- a/examples/websocket/client/package-lock.json
+++ b/examples/websocket/client/package-lock.json
@@ -10,7 +10,7 @@
"license": "ISC",
"dependencies": {
"@pipecat-ai/client-js": "^0.4.0",
- "@pipecat-ai/websocket-transport": "^0.4.1",
+ "@pipecat-ai/websocket-transport": "^0.4.2",
"protobufjs": "^7.4.0"
},
"devDependencies": {
@@ -502,9 +502,9 @@
}
},
"node_modules/@pipecat-ai/client-js": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/@pipecat-ai/client-js/-/client-js-0.4.0.tgz",
- "integrity": "sha512-O2EgCqt2cAmp21Z6dXz88zgW845HcsfE//qZghaKOt0Z8xPbhidbVbuOX5iajrYgGRqlnXInYiJ9nN2zY6CUJw==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@pipecat-ai/client-js/-/client-js-0.4.1.tgz",
+ "integrity": "sha512-3jLKRzeryqLxtkqvr4Bvxe2OxoI7mdOFecm6iolZizXnk/BE480SEg2oAKyov3b5oT6+jmPlT+1HRBlTzEtL7A==",
"license": "BSD-2-Clause",
"dependencies": {
"@types/events": "^3.0.3",
@@ -515,14 +515,15 @@
}
},
"node_modules/@pipecat-ai/websocket-transport": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@pipecat-ai/websocket-transport/-/websocket-transport-0.4.1.tgz",
- "integrity": "sha512-/qdMz1IGV+rJ0qi4UE84XKVZu2VqyIh9J7RgNkzS8nEZiUVwaclrVMjKFgwPqwqKi3ik3h2oucPa/u+8s7Tleg==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@pipecat-ai/websocket-transport/-/websocket-transport-0.4.2.tgz",
+ "integrity": "sha512-mOYnw9n60usODrE35D+uhFbJXl0DqXV32pAqSHu1of049s128mex6Qv+W49DBMVr8h5W6pLGrXhm+XDAtN5leg==",
"license": "BSD-2-Clause",
"dependencies": {
"@daily-co/daily-js": "^0.79.0",
"@protobuf-ts/plugin": "^2.11.0",
- "@protobuf-ts/runtime": "^2.11.0"
+ "@protobuf-ts/runtime": "^2.11.0",
+ "x-law": "^0.3.1"
},
"peerDependencies": {
"@pipecat-ai/client-js": "~0.4.0"
@@ -648,16 +649,16 @@
"license": "BSD-3-Clause"
},
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.9",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.9.tgz",
- "integrity": "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==",
+ "version": "1.0.0-beta.11",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.11.tgz",
+ "integrity": "sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz",
- "integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz",
+ "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==",
"cpu": [
"arm"
],
@@ -669,9 +670,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz",
- "integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz",
+ "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==",
"cpu": [
"arm64"
],
@@ -683,9 +684,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz",
- "integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz",
+ "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==",
"cpu": [
"arm64"
],
@@ -697,9 +698,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz",
- "integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz",
+ "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==",
"cpu": [
"x64"
],
@@ -711,9 +712,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz",
- "integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz",
+ "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==",
"cpu": [
"arm64"
],
@@ -725,9 +726,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz",
- "integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz",
+ "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==",
"cpu": [
"x64"
],
@@ -739,9 +740,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz",
- "integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz",
+ "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==",
"cpu": [
"arm"
],
@@ -753,9 +754,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz",
- "integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz",
+ "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==",
"cpu": [
"arm"
],
@@ -767,9 +768,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz",
- "integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz",
+ "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==",
"cpu": [
"arm64"
],
@@ -781,9 +782,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz",
- "integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz",
+ "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==",
"cpu": [
"arm64"
],
@@ -795,9 +796,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz",
- "integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz",
+ "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==",
"cpu": [
"loong64"
],
@@ -809,9 +810,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz",
- "integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz",
+ "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==",
"cpu": [
"ppc64"
],
@@ -823,9 +824,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz",
- "integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz",
+ "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==",
"cpu": [
"riscv64"
],
@@ -837,9 +838,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz",
- "integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz",
+ "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==",
"cpu": [
"riscv64"
],
@@ -851,9 +852,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz",
- "integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz",
+ "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==",
"cpu": [
"s390x"
],
@@ -865,9 +866,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz",
- "integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz",
+ "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==",
"cpu": [
"x64"
],
@@ -879,9 +880,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz",
- "integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz",
+ "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==",
"cpu": [
"x64"
],
@@ -893,9 +894,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz",
- "integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz",
+ "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==",
"cpu": [
"arm64"
],
@@ -907,9 +908,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz",
- "integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz",
+ "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==",
"cpu": [
"ia32"
],
@@ -921,9 +922,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz",
- "integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz",
+ "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==",
"cpu": [
"x64"
],
@@ -1010,15 +1011,15 @@
}
},
"node_modules/@swc/core": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.31.tgz",
- "integrity": "sha512-mAby9aUnKRjMEA7v8cVZS9Ah4duoRBnX7X6r5qrhTxErx+68MoY1TPrVwj/66/SWN3Bl+jijqAqoB8Qx0QE34A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.0.tgz",
+ "integrity": "sha512-/C0kiMHPY/HnLfqXYGMGxGck3A5Y3mqwxfv+EwHTPHGjAVRfHpWAEEBTSTF5C88vVY6CvwBEkhR2TX7t8Mahcw==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3",
- "@swc/types": "^0.1.21"
+ "@swc/types": "^0.1.22"
},
"engines": {
"node": ">=10"
@@ -1028,16 +1029,16 @@
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
- "@swc/core-darwin-arm64": "1.11.31",
- "@swc/core-darwin-x64": "1.11.31",
- "@swc/core-linux-arm-gnueabihf": "1.11.31",
- "@swc/core-linux-arm64-gnu": "1.11.31",
- "@swc/core-linux-arm64-musl": "1.11.31",
- "@swc/core-linux-x64-gnu": "1.11.31",
- "@swc/core-linux-x64-musl": "1.11.31",
- "@swc/core-win32-arm64-msvc": "1.11.31",
- "@swc/core-win32-ia32-msvc": "1.11.31",
- "@swc/core-win32-x64-msvc": "1.11.31"
+ "@swc/core-darwin-arm64": "1.12.0",
+ "@swc/core-darwin-x64": "1.12.0",
+ "@swc/core-linux-arm-gnueabihf": "1.12.0",
+ "@swc/core-linux-arm64-gnu": "1.12.0",
+ "@swc/core-linux-arm64-musl": "1.12.0",
+ "@swc/core-linux-x64-gnu": "1.12.0",
+ "@swc/core-linux-x64-musl": "1.12.0",
+ "@swc/core-win32-arm64-msvc": "1.12.0",
+ "@swc/core-win32-ia32-msvc": "1.12.0",
+ "@swc/core-win32-x64-msvc": "1.12.0"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
@@ -1049,9 +1050,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.31.tgz",
- "integrity": "sha512-NTEaYOts0OGSbJZc0O74xsji+64JrF1stmBii6D5EevWEtrY4wlZhm8SiP/qPrOB+HqtAihxWIukWkP2aSdGSQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.0.tgz",
+ "integrity": "sha512-usLr8kC80GDv3pwH2zoEaS279kxtWY0MY3blbMFw7zA8fAjqxa8IDxm3WcgyNLNWckWn4asFfguEwz/Weem3nA==",
"cpu": [
"arm64"
],
@@ -1066,9 +1067,9 @@
}
},
"node_modules/@swc/core-darwin-x64": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.31.tgz",
- "integrity": "sha512-THSGaSwT96JwXDwuXQ6yFBbn+xDMdyw7OmBpnweAWsh5DhZmQkALEm1DgdQO3+rrE99MkmzwAfclc0UmYro/OA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.0.tgz",
+ "integrity": "sha512-Cvv4sqDcTY7QF2Dh1vn2Xbt/1ENYQcpmrGHzITJrXzxA2aBopsz/n4yQDiyRxTR0t802m4xu0CzMoZIHvVruWQ==",
"cpu": [
"x64"
],
@@ -1083,9 +1084,9 @@
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.31.tgz",
- "integrity": "sha512-laKtQFnW7KHgE57Hx32os2SNAogcuIDxYE+3DYIOmDMqD7/1DCfJe6Rln2N9WcOw6HuDbDpyQavIwZNfSAa8vQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.0.tgz",
+ "integrity": "sha512-seM4/XMJMOupkzfLfHl8sRa3NdhsVZp+XgwA/vVeYZYJE4wuWUxVzhCYzwmNftVY32eF2IiRaWnhG6ho6jusnQ==",
"cpu": [
"arm"
],
@@ -1100,9 +1101,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.31.tgz",
- "integrity": "sha512-T+vGw9aPE1YVyRxRr1n7NAdkbgzBzrXCCJ95xAZc/0+WUwmL77Z+js0J5v1KKTRxw4FvrslNCOXzMWrSLdwPSA==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.0.tgz",
+ "integrity": "sha512-Al0x33gUVxNY5tutEYpSyv7mze6qQS1ONa0HEwoRxcK9WXsX0NHLTiOSGZoCUS1SsXM37ONlbA6/Bsp1MQyP+g==",
"cpu": [
"arm64"
],
@@ -1117,9 +1118,9 @@
}
},
"node_modules/@swc/core-linux-arm64-musl": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.31.tgz",
- "integrity": "sha512-Mztp5NZkyd5MrOAG+kl+QSn0lL4Uawd4CK4J7wm97Hs44N9DHGIG5nOz7Qve1KZo407Y25lTxi/PqzPKHo61zQ==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.0.tgz",
+ "integrity": "sha512-OeFHz/5Hl9v75J9TYA5jQxNIYAZMqaiPpd9dYSTK2Xyqa/ZGgTtNyPhIwVfxx+9mHBf6+9c1mTlXUtACMtHmaQ==",
"cpu": [
"arm64"
],
@@ -1134,9 +1135,9 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.31.tgz",
- "integrity": "sha512-DDVE0LZcXOWwOqFU1Xi7gdtiUg3FHA0vbGb3trjWCuI1ZtDZHEQYL4M3/2FjqKZtIwASrDvO96w91okZbXhvMg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.0.tgz",
+ "integrity": "sha512-ltIvqNi7H0c5pRawyqjeYSKEIfZP4vv/datT3mwT6BW7muJtd1+KIDCPFLMIQ4wm/h76YQwPocsin3fzmnFdNA==",
"cpu": [
"x64"
],
@@ -1151,9 +1152,9 @@
}
},
"node_modules/@swc/core-linux-x64-musl": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.31.tgz",
- "integrity": "sha512-mJA1MzPPRIfaBUHZi0xJQ4vwL09MNWDeFtxXb0r4Yzpf0v5Lue9ymumcBPmw/h6TKWms+Non4+TDquAsweuKSw==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.0.tgz",
+ "integrity": "sha512-Z/DhpjehaTK0uf+MhNB7mV9SuewpGs3P/q9/8+UsJeYoFr7yuOoPbAvrD6AqZkf6Bh7MRZ5OtG+KQgG5L+goiA==",
"cpu": [
"x64"
],
@@ -1168,9 +1169,9 @@
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.31.tgz",
- "integrity": "sha512-RdtakUkNVAb/FFIMw3LnfNdlH1/ep6KgiPDRlmyUfd0WdIQ3OACmeBegEFNFTzi7gEuzy2Yxg4LWf4IUVk8/bg==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.0.tgz",
+ "integrity": "sha512-wHnvbfHIh2gfSbvuFT7qP97YCMUDh+fuiso+pcC6ug8IsMxuViNapHET4o0ZdFNWHhXJ7/s0e6w7mkOalsqQiQ==",
"cpu": [
"arm64"
],
@@ -1185,9 +1186,9 @@
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.31.tgz",
- "integrity": "sha512-hErXdCGsg7swWdG1fossuL8542I59xV+all751mYlBoZ8kOghLSKObGQTkBbuNvc0sUKWfWg1X0iBuIhAYar+w==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.0.tgz",
+ "integrity": "sha512-88umlXwK+7J2p4DjfWHXQpmlZgCf1ayt6Ssj+PYlAfMCR0aBiJoAMwHWrvDXEozyOrsyP1j2X6WxbmA861vL5Q==",
"cpu": [
"ia32"
],
@@ -1202,9 +1203,9 @@
}
},
"node_modules/@swc/core-win32-x64-msvc": {
- "version": "1.11.31",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.31.tgz",
- "integrity": "sha512-5t7SGjUBMMhF9b5j17ml/f/498kiBJNf4vZFNM421UGUEETdtjPN9jZIuQrowBkoFGJTCVL/ECM4YRtTH30u/A==",
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.0.tgz",
+ "integrity": "sha512-KR9TSRp+FEVOhbgTU6c94p/AYpsyBk7dIvlKQiDp8oKScUoyHG5yjmMBFN/BqUyTq4kj6zlgsY2rFE4R8/yqWg==",
"cpu": [
"x64"
],
@@ -1226,9 +1227,9 @@
"license": "Apache-2.0"
},
"node_modules/@swc/types": {
- "version": "0.1.22",
- "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.22.tgz",
- "integrity": "sha512-D13mY/ZA4PPEFSy6acki9eBT/3WgjMoRqNcdpIvjaYLQ44Xk5BdaL7UkDxAh6Z9UOe7tCCp67BVmZCojYp9owg==",
+ "version": "0.1.23",
+ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz",
+ "integrity": "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -1249,9 +1250,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.15.30",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz",
- "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==",
+ "version": "22.15.31",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz",
+ "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
@@ -1281,17 +1282,17 @@
}
},
"node_modules/@vitejs/plugin-react-swc": {
- "version": "3.10.1",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.10.1.tgz",
- "integrity": "sha512-FmQvN3yZGyD9XW6IyxE86Kaa/DnxSsrDQX1xCR1qojNpBLaUop+nLYFvhCkJsq8zOupNjCRA9jyhPGOJsSkutA==",
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.10.2.tgz",
+ "integrity": "sha512-xD3Rdvrt5LgANug7WekBn1KhcvLn1H3jNBfJRL3reeOIua/WnZOEV5qi5qIBq5T8R0jUDmRtxuvk4bPhzGHDWw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@rolldown/pluginutils": "1.0.0-beta.9",
- "@swc/core": "^1.11.22"
+ "@rolldown/pluginutils": "1.0.0-beta.11",
+ "@swc/core": "^1.11.31"
},
"peerDependencies": {
- "vite": "^4 || ^5 || ^6"
+ "vite": "^4 || ^5 || ^6 || ^7.0.0-beta.0"
}
},
"node_modules/bowser": {
@@ -1391,9 +1392,9 @@
}
},
"node_modules/fdir": {
- "version": "6.4.5",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
- "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -1451,9 +1452,9 @@
}
},
"node_modules/long": {
- "version": "5.2.4",
- "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz",
- "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
"license": "Apache-2.0"
},
"node_modules/ms": {
@@ -1502,9 +1503,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.4",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz",
- "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==",
+ "version": "8.5.5",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
+ "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
"dev": true,
"funding": [
{
@@ -1531,9 +1532,9 @@
}
},
"node_modules/protobufjs": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
- "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+ "version": "7.5.3",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz",
+ "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -1555,9 +1556,9 @@
}
},
"node_modules/rollup": {
- "version": "4.42.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz",
- "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==",
+ "version": "4.43.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz",
+ "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1571,33 +1572,33 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.42.0",
- "@rollup/rollup-android-arm64": "4.42.0",
- "@rollup/rollup-darwin-arm64": "4.42.0",
- "@rollup/rollup-darwin-x64": "4.42.0",
- "@rollup/rollup-freebsd-arm64": "4.42.0",
- "@rollup/rollup-freebsd-x64": "4.42.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.42.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.42.0",
- "@rollup/rollup-linux-arm64-gnu": "4.42.0",
- "@rollup/rollup-linux-arm64-musl": "4.42.0",
- "@rollup/rollup-linux-loongarch64-gnu": "4.42.0",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.42.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.42.0",
- "@rollup/rollup-linux-riscv64-musl": "4.42.0",
- "@rollup/rollup-linux-s390x-gnu": "4.42.0",
- "@rollup/rollup-linux-x64-gnu": "4.42.0",
- "@rollup/rollup-linux-x64-musl": "4.42.0",
- "@rollup/rollup-win32-arm64-msvc": "4.42.0",
- "@rollup/rollup-win32-ia32-msvc": "4.42.0",
- "@rollup/rollup-win32-x64-msvc": "4.42.0",
+ "@rollup/rollup-android-arm-eabi": "4.43.0",
+ "@rollup/rollup-android-arm64": "4.43.0",
+ "@rollup/rollup-darwin-arm64": "4.43.0",
+ "@rollup/rollup-darwin-x64": "4.43.0",
+ "@rollup/rollup-freebsd-arm64": "4.43.0",
+ "@rollup/rollup-freebsd-x64": "4.43.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.43.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.43.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.43.0",
+ "@rollup/rollup-linux-arm64-musl": "4.43.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.43.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.43.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.43.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.43.0",
+ "@rollup/rollup-linux-x64-gnu": "4.43.0",
+ "@rollup/rollup-linux-x64-musl": "4.43.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.43.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.43.0",
+ "@rollup/rollup-win32-x64-msvc": "4.43.0",
"fsevents": "~2.3.2"
}
},
"node_modules/rxjs": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
- "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
@@ -1765,6 +1766,15 @@
"optional": true
}
}
+ },
+ "node_modules/x-law": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/x-law/-/x-law-0.3.1.tgz",
+ "integrity": "sha512-Nvo6OKj6UL2LuzAc08uJkwIDkK2PsTEdpLiY82NkwMptuRpAA1V7arUl7ZY12BcgRYNq8uh1pdAu7G6VeQn7Hg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
}
}
}
diff --git a/examples/websocket/client/package.json b/examples/websocket/client/package.json
index d2df048f5..21a8dc95c 100644
--- a/examples/websocket/client/package.json
+++ b/examples/websocket/client/package.json
@@ -20,7 +20,7 @@
},
"dependencies": {
"@pipecat-ai/client-js": "^0.4.0",
- "@pipecat-ai/websocket-transport": "^0.4.1",
+ "@pipecat-ai/websocket-transport": "^0.4.2",
"protobufjs": "^7.4.0"
}
}
diff --git a/examples/websocket/server/bot_fast_api.py b/examples/websocket/server/bot_fast_api.py
index 421fe8dbf..fa1005136 100644
--- a/examples/websocket/server/bot_fast_api.py
+++ b/examples/websocket/server/bot_fast_api.py
@@ -85,7 +85,8 @@ async def run_bot(websocket_client):
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
observers=[RTVIObserver(rtvi)],
)
diff --git a/examples/websocket/server/bot_websocket_server.py b/examples/websocket/server/bot_websocket_server.py
index 5274c11f6..e88ec60dd 100644
--- a/examples/websocket/server/bot_websocket_server.py
+++ b/examples/websocket/server/bot_websocket_server.py
@@ -78,7 +78,8 @@ async def run_bot_websocket_server():
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
+ enable_metrics=True,
+ enable_usage_metrics=True,
),
observers=[RTVIObserver(rtvi)],
)
diff --git a/examples/word-wrangler-gemini-live/server/bot.py b/examples/word-wrangler-gemini-live/server/bot.py
index 7a0527636..f5086a5c1 100644
--- a/examples/word-wrangler-gemini-live/server/bot.py
+++ b/examples/word-wrangler-gemini-live/server/bot.py
@@ -135,7 +135,6 @@ Important guidelines:
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/word-wrangler-gemini-live/server/bot_phone_local.py b/examples/word-wrangler-gemini-live/server/bot_phone_local.py
index 84f15ea30..0e2739411 100644
--- a/examples/word-wrangler-gemini-live/server/bot_phone_local.py
+++ b/examples/word-wrangler-gemini-live/server/bot_phone_local.py
@@ -678,7 +678,6 @@ Important guidelines:
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=False,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/examples/word-wrangler-gemini-live/server/bot_phone_twilio.py b/examples/word-wrangler-gemini-live/server/bot_phone_twilio.py
index 2af8b2d4a..0b0c12e1e 100644
--- a/examples/word-wrangler-gemini-live/server/bot_phone_twilio.py
+++ b/examples/word-wrangler-gemini-live/server/bot_phone_twilio.py
@@ -695,7 +695,6 @@ Important guidelines:
pipeline,
params=PipelineParams(
audio_out_sample_rate=8000,
- allow_interruptions=False,
enable_metrics=True,
enable_usage_metrics=True,
),
diff --git a/pyproject.toml b/pyproject.toml
index 4652b684a..b5c874919 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -47,7 +47,7 @@ azure = [ "azure-cognitiveservices-speech~=1.42.0"]
cartesia = [ "cartesia~=2.0.3", "websockets~=13.1" ]
cerebras = []
deepseek = []
-daily = [ "daily-python~=0.19.2" ]
+daily = [ "daily-python~=0.19.3" ]
deepgram = [ "deepgram-sdk~=4.1.0" ]
elevenlabs = [ "websockets~=13.1" ]
fal = [ "fal-client~=0.5.9" ]
@@ -58,7 +58,7 @@ google = [ "google-cloud-speech~=2.32.0", "google-cloud-texttospeech~=2.26.0", "
grok = []
groq = [ "groq~=0.23.0" ]
gstreamer = [ "pygobject~=3.50.0" ]
-krisp = [ "pipecat-ai-krisp~=0.3.0" ]
+krisp = [ "pipecat-ai-krisp~=0.4.0" ]
koala = [ "pvkoala~=2.0.3" ]
langchain = [ "langchain~=0.3.20", "langchain-community~=0.3.20", "langchain-openai~=0.3.9" ]
livekit = [ "livekit~=0.22.0", "livekit-api~=0.8.2", "tenacity~=9.0.0" ]
diff --git a/scripts/daily/test_tavus_transport.py b/scripts/daily/test_tavus_transport.py
new file mode 100644
index 000000000..fa8afe835
--- /dev/null
+++ b/scripts/daily/test_tavus_transport.py
@@ -0,0 +1,177 @@
+import asyncio
+import os
+import signal
+
+from daily import *
+from dotenv import load_dotenv
+from loguru import logger
+
+load_dotenv(override=True)
+
+
+def completion_callback(future):
+ def _callback(*args):
+ def set_result(future, *args):
+ try:
+ if len(args) > 1:
+ future.set_result(args)
+ else:
+ future.set_result(*args)
+ except asyncio.InvalidStateError:
+ pass
+
+ future.get_loop().call_soon_threadsafe(set_result, future, *args)
+
+ return _callback
+
+
+class DailyProxyApp(EventHandler):
+ # This is necessary to override EventHandler's __new__ method.
+ def __new__(cls, *args, **kwargs):
+ return super().__new__(cls)
+
+ def __init__(self, sample_rate: int):
+ super().__init__()
+ self._sample_rate = sample_rate
+ self._loop = None
+ self._audio_queue: asyncio.Queue | None = None
+ self._audio_task: asyncio.Task | None = None
+
+ self._client: CallClient = CallClient(event_handler=self)
+ self._client.update_subscription_profiles(
+ {"base": {"camera": "unsubscribed", "microphone": "subscribed"}}
+ )
+
+ self._audio_source = CustomAudioSource(self._sample_rate, 1)
+ self._audio_track = CustomAudioTrack(self._audio_source)
+
+ def on_joined(self, data, error):
+ logger.debug("Local participant Joined!")
+ if error:
+ print(f"Unable to join meeting: {error}")
+ self._loop.call_soon_threadsafe(self._loop.stop)
+
+ def run(self, meeting_url: str):
+ self._loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(self._loop)
+ self._create_audio_task()
+
+ def handle_exit():
+ logger.info("Ctrl+C pressed. Leaving the meeting...")
+ self._loop.call_soon_threadsafe(self._loop.stop)
+
+ for sig in (signal.SIGINT, signal.SIGTERM):
+ self._loop.add_signal_handler(sig, handle_exit)
+
+ self._client.set_user_name("TestTavusTransport")
+ self._client.join(
+ meeting_url,
+ completion=self.on_joined,
+ client_settings={
+ "inputs": {
+ "microphone": {
+ "isEnabled": True,
+ "settings": {"customTrack": {"id": self._audio_track.id}},
+ },
+ }
+ },
+ )
+
+ try:
+ self._loop.run_forever()
+ finally:
+ self.leave()
+
+ def leave(self):
+ if self._audio_task:
+ self._loop.run_until_complete(self._cancel_audio_task())
+
+ self._client.leave()
+ self._client.release()
+
+ async def update_subscriptions(self, participant_settings=None, profile_settings=None):
+ logger.info(f"Updating subscriptions participant_settings: {participant_settings}")
+ future = asyncio.get_running_loop().create_future()
+ self._client.update_subscriptions(
+ participant_settings=participant_settings,
+ profile_settings=profile_settings,
+ completion=completion_callback(future),
+ )
+ await future
+
+ def _create_audio_task(self):
+ if not self._audio_task:
+ self._audio_queue = asyncio.Queue()
+ self._audio_task = self._loop.create_task(self._audio_task_handler())
+
+ async def _cancel_audio_task(self):
+ if self._audio_task:
+ self._audio_task.cancel()
+ try:
+ # Waits for it to finish
+ await self._audio_task
+ except asyncio.CancelledError:
+ pass
+ self._audio_task = None
+ self._audio_queue = None
+
+ async def capture_participant_audio(self, participant_id: str):
+ logger.info(f"Capturing participant audio: {participant_id}")
+ # Receiving from this custom track
+ # audio_source: str = "microphone"
+ audio_source: str = "stream"
+ media = {"media": {"customAudio": {audio_source: "subscribed"}}}
+ await self.update_subscriptions(participant_settings={participant_id: media})
+
+ self._client.set_audio_renderer(
+ participant_id,
+ self._audio_data_received,
+ audio_source=audio_source,
+ sample_rate=self._sample_rate,
+ callback_interval_ms=20,
+ )
+
+ async def send_audio(self, audio: AudioData):
+ future = asyncio.get_running_loop().create_future()
+ self._audio_source.write_frames(audio.audio_frames, completion=completion_callback(future))
+ await future
+
+ async def queue_audio(self, audio: AudioData):
+ await self._audio_queue.put(audio)
+
+ def _audio_data_received(self, participant_id: str, audio_data: AudioData, audio_source: str):
+ # logger.info(f"Received audio data for {participant_id}, audio_source: {audio_source}")
+ asyncio.run_coroutine_threadsafe(self.queue_audio(audio_data), self._loop)
+
+ async def _audio_task_handler(self):
+ while True:
+ audio = await self._audio_queue.get()
+ await self.send_audio(audio)
+
+ #
+ # Daily (EventHandler)
+ #
+
+ def on_participant_joined(self, participant):
+ participant_name = participant["info"]["userName"]
+ logger.info(f"Participant {participant_name} joined")
+ if participant_name != "Pipecat":
+ # We are only subscribing for audios from Pipecat.
+ return
+ asyncio.run_coroutine_threadsafe(
+ self.capture_participant_audio(participant_id=participant["id"]), self._loop
+ )
+
+ def on_participant_left(self, participant, reason):
+ logger.info(f"Participant {participant['id']} left {reason}")
+
+
+def main():
+ Daily.init()
+ room_url = os.getenv("TAVUS_SAMPLE_ROOM_URL")
+ app = DailyProxyApp(sample_rate=24000)
+ app.run(room_url)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/evals/eval.py b/scripts/evals/eval.py
index 1ad165a4a..743b62222 100644
--- a/scripts/evals/eval.py
+++ b/scripts/evals/eval.py
@@ -262,7 +262,6 @@ async def run_eval_pipeline(
task = PipelineTask(
pipeline,
params=PipelineParams(
- allow_interruptions=True,
audio_in_sample_rate=16000,
audio_out_sample_rate=16000,
),
diff --git a/scripts/fix-ruff.sh b/scripts/fix-ruff.sh
index 892f6d405..6bd24300d 100755
--- a/scripts/fix-ruff.sh
+++ b/scripts/fix-ruff.sh
@@ -1,4 +1,5 @@
ruff format src
ruff format examples
ruff format tests
+ruff format scripts
ruff check --select I --fix
\ No newline at end of file
diff --git a/src/pipecat/examples/run.py b/src/pipecat/examples/run.py
index 117ca285d..24f673e06 100644
--- a/src/pipecat/examples/run.py
+++ b/src/pipecat/examples/run.py
@@ -64,7 +64,7 @@ async def maybe_capture_participant_screen(
def run_example_daily(
run_example: Callable,
args: argparse.Namespace,
- params: DailyParams,
+ transport_params: Mapping[str, Callable] = {},
):
logger.info("Running example with DailyTransport...")
@@ -75,6 +75,7 @@ def run_example_daily(
(room_url, token) = await configure(session)
# Run example function with DailyTransport transport arguments.
+ params: DailyParams = transport_params[args.transport]()
transport = DailyTransport(room_url, token, "Pipecat", params=params)
await run_example(transport, args, True)
@@ -84,7 +85,7 @@ def run_example_daily(
def run_example_webrtc(
run_example: Callable,
args: argparse.Namespace,
- params: TransportParams,
+ transport_params: Mapping[str, Callable] = {},
):
logger.info("Running example with SmallWebRTCTransport...")
@@ -130,6 +131,7 @@ def run_example_webrtc(
pcs_map.pop(webrtc_connection.pc_id, None)
# Run example function with SmallWebRTC transport arguments.
+ params: TransportParams = transport_params[args.transport]()
transport = SmallWebRTCTransport(params=params, webrtc_connection=pipecat_connection)
background_tasks.add_task(run_example, transport, args, False)
@@ -152,7 +154,7 @@ def run_example_webrtc(
def run_example_twilio(
run_example: Callable,
args: argparse.Namespace,
- params: FastAPIWebsocketParams,
+ transport_params: Mapping[str, Callable] = {},
):
logger.info("Running example with FastAPIWebsocketTransport (Twilio)...")
@@ -195,6 +197,7 @@ def run_example_twilio(
call_sid = call_data["start"]["callSid"]
# Create websocket transport and update params.
+ params: FastAPIWebsocketParams = transport_params[args.transport]()
params.add_wav_header = False
params.serializer = TwilioFrameSerializer(
stream_sid=stream_sid,
@@ -217,14 +220,13 @@ def run_main(
logger.error(f"Transport '{args.transport}' not supported by this example")
return
- params = transport_params[args.transport]()
match args.transport:
case "daily":
- run_example_daily(run_example, args, params)
+ run_example_daily(run_example, args, transport_params)
case "webrtc":
- run_example_webrtc(run_example, args, params)
+ run_example_webrtc(run_example, args, transport_params)
case "twilio":
- run_example_twilio(run_example, args, params)
+ run_example_twilio(run_example, args, transport_params)
def main(
diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py
index 63caf9a09..ec368789d 100644
--- a/src/pipecat/frames/frames.py
+++ b/src/pipecat/frames/frames.py
@@ -527,6 +527,29 @@ class StopTaskFrame(SystemFrame):
pass
+@dataclass
+class FrameProcessorPauseUrgentFrame(SystemFrame):
+ """This processor is used to pause frame processing for the given processor
+ as fast as possible. Pausing frame processing will keep frames in the
+ internal queue which will then be processed when frame processing is resumed
+ with `FrameProcessorResumeFrame`.
+
+ """
+
+ processor: str
+
+
+@dataclass
+class FrameProcessorResumeUrgentFrame(SystemFrame):
+ """This processor is used to resume frame processing for the given processor
+ if it was previously paused as fast as possible. After resuming frame
+ processing all queued frames will be processed in the order received.
+
+ """
+
+ processor: str
+
+
@dataclass
class StartInterruptionFrame(SystemFrame):
"""Emitted by VAD to indicate that a user has started speaking (i.e. is
@@ -854,6 +877,27 @@ class StopFrame(ControlFrame):
pass
+@dataclass
+class FrameProcessorPauseFrame(ControlFrame):
+ """This processor is used to pause frame processing for the given
+ processor. Pausing frame processing will keep frames in the internal queue
+ which will then be processed when frame processing is resumed with
+ `FrameProcessorResumeFrame`."""
+
+ processor: str
+
+
+@dataclass
+class FrameProcessorResumeFrame(ControlFrame):
+ """This processor is used to resume frame processing for the given processor
+ if it was previously paused. After resuming frame processing all queued
+ frames will be processed in the order received.
+
+ """
+
+ processor: str
+
+
@dataclass
class LLMFullResponseStartFrame(ControlFrame):
"""Used to indicate the beginning of an LLM response. Following by one or
diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py
index 6bf5dd688..3f13e7eb2 100644
--- a/src/pipecat/pipeline/task.py
+++ b/src/pipecat/pipeline/task.py
@@ -64,7 +64,7 @@ class PipelineParams(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
- allow_interruptions: bool = False
+ allow_interruptions: bool = True
audio_in_sample_rate: int = 16000
audio_out_sample_rate: int = 24000
enable_heartbeats: bool = False
@@ -184,7 +184,9 @@ class PipelineTask(BaseTask):
the idle timeout is reached.
enable_turn_tracking: Whether to enable turn tracking.
enable_turn_tracing: Whether to enable turn tracing.
-
+ conversation_id: Optional custom ID for the conversation.
+ additional_span_attributes: Optional dictionary of attributes to propagate as
+ OpenTelemetry conversation span attributes.
"""
def __init__(
@@ -205,6 +207,7 @@ class PipelineTask(BaseTask):
enable_turn_tracking: bool = True,
enable_tracing: bool = False,
conversation_id: Optional[str] = None,
+ additional_span_attributes: Optional[dict] = None,
):
super().__init__()
self._pipeline = pipeline
@@ -217,6 +220,7 @@ class PipelineTask(BaseTask):
self._enable_turn_tracking = enable_turn_tracking
self._enable_tracing = enable_tracing and is_tracing_available()
self._conversation_id = conversation_id
+ self._additional_span_attributes = additional_span_attributes or {}
if self._params.observers:
import warnings
@@ -235,7 +239,9 @@ class PipelineTask(BaseTask):
observers.append(self._turn_tracking_observer)
if self._enable_tracing and self._turn_tracking_observer:
self._turn_trace_observer = TurnTraceObserver(
- self._turn_tracking_observer, conversation_id=self._conversation_id
+ self._turn_tracking_observer,
+ conversation_id=self._conversation_id,
+ additional_span_attributes=self._additional_span_attributes,
)
observers.append(self._turn_trace_observer)
self._finished = False
@@ -657,6 +663,11 @@ class PipelineTask(BaseTask):
diff_time = time.time() - last_frame_time
if diff_time >= self._idle_timeout_secs:
running = await self._idle_timeout_detected()
+ # Reset `last_frame_time` so we don't trigger another
+ # immediate idle timeout if we are not cancelling. For
+ # example, we might want to force the bot to say goodbye
+ # and then clean nicely with an `EndFrame`.
+ last_frame_time = time.time()
self._idle_queue.task_done()
except asyncio.TimeoutError:
diff --git a/src/pipecat/processors/aggregators/llm_response.py b/src/pipecat/processors/aggregators/llm_response.py
index 479199550..e9aebb2a0 100644
--- a/src/pipecat/processors/aggregators/llm_response.py
+++ b/src/pipecat/processors/aggregators/llm_response.py
@@ -504,6 +504,15 @@ class LLMAssistantContextAggregator(LLMContextResponseAggregator):
self._function_calls_in_progress: Dict[str, Optional[FunctionCallInProgressFrame]] = {}
self._context_updated_tasks: Set[asyncio.Task] = set()
+ @property
+ def has_function_calls_in_progress(self) -> bool:
+ """Check if there are any function calls currently in progress.
+
+ Returns:
+ bool: True if function calls are in progress, False otherwise
+ """
+ return bool(self._function_calls_in_progress)
+
async def handle_aggregation(self, aggregation: str):
self._context.add_message({"role": "assistant", "content": aggregation})
diff --git a/src/pipecat/processors/audio/audio_buffer_processor.py b/src/pipecat/processors/audio/audio_buffer_processor.py
index c1b2eb810..13d5a84bc 100644
--- a/src/pipecat/processors/audio/audio_buffer_processor.py
+++ b/src/pipecat/processors/audio/audio_buffer_processor.py
@@ -41,7 +41,6 @@ class AudioBufferProcessor(FrameProcessor):
sample_rate (Optional[int]): Desired output sample rate. If None, uses source rate
num_channels (int): Number of channels (1 for mono, 2 for stereo). Defaults to 1
buffer_size (int): Size of buffer before triggering events. 0 for no buffering
- user_continuous_stream (bool): Whether user audio is continuous or speech-only
enable_turn_audio (bool): Whether turn audio event handlers should be triggered
Audio handling:
@@ -50,10 +49,6 @@ class AudioBufferProcessor(FrameProcessor):
- Automatic resampling of incoming audio to match desired sample_rate
- Silence insertion for non-continuous audio streams
- Buffer synchronization between user and bot audio
-
- Note:
- When user_continuous_stream is False, the processor expects only speech
- segments and will handle silence insertion between segments automatically.
"""
def __init__(
@@ -62,7 +57,7 @@ class AudioBufferProcessor(FrameProcessor):
sample_rate: Optional[int] = None,
num_channels: int = 1,
buffer_size: int = 0,
- user_continuous_stream: bool = True,
+ user_continuous_stream: Optional[bool] = None,
enable_turn_audio: bool = False,
**kwargs,
):
@@ -72,9 +67,18 @@ class AudioBufferProcessor(FrameProcessor):
self._audio_buffer_size_1s = 0
self._num_channels = num_channels
self._buffer_size = buffer_size
- self._user_continuous_stream = user_continuous_stream
self._enable_turn_audio = enable_turn_audio
+ if user_continuous_stream is not None:
+ import warnings
+
+ with warnings.catch_warnings():
+ warnings.simplefilter("always")
+ warnings.warn(
+ "Parameter `user_continuous_stream` is deprecated.",
+ DeprecationWarning,
+ )
+
self._user_audio_buffer = bytearray()
self._bot_audio_buffer = bytearray()
@@ -181,10 +185,24 @@ class AudioBufferProcessor(FrameProcessor):
self._audio_buffer_size_1s = self._sample_rate * 2
async def _process_recording(self, frame: Frame):
- if self._user_continuous_stream:
- await self._handle_continuous_stream(frame)
- else:
- await self._handle_intermittent_stream(frame)
+ if isinstance(frame, InputAudioRawFrame):
+ # Add silence if we need to.
+ silence = self._compute_silence(self._last_user_frame_at)
+ self._user_audio_buffer.extend(silence)
+ # Add user audio.
+ resampled = await self._resample_audio(frame)
+ self._user_audio_buffer.extend(resampled)
+ # Save time of frame so we can compute silence.
+ self._last_user_frame_at = time.time()
+ elif self._recording and isinstance(frame, OutputAudioRawFrame):
+ # Add silence if we need to.
+ silence = self._compute_silence(self._last_bot_frame_at)
+ self._bot_audio_buffer.extend(silence)
+ # Add bot audio.
+ resampled = await self._resample_audio(frame)
+ self._bot_audio_buffer.extend(resampled)
+ # Save time of frame so we can compute silence.
+ self._last_bot_frame_at = time.time()
if self._buffer_size > 0 and len(self._user_audio_buffer) > self._buffer_size:
await self._call_on_audio_data_handler()
@@ -223,41 +241,6 @@ class AudioBufferProcessor(FrameProcessor):
resampled = await self._resample_audio(frame)
self._bot_turn_audio_buffer += resampled
- async def _handle_continuous_stream(self, frame: Frame):
- if isinstance(frame, InputAudioRawFrame):
- # Add user audio.
- resampled = await self._resample_audio(frame)
- self._user_audio_buffer.extend(resampled)
- # Sync the bot's buffer to the user's buffer by adding silence if needed
- if len(self._user_audio_buffer) > len(self._bot_audio_buffer):
- silence_size = len(self._user_audio_buffer) - len(self._bot_audio_buffer)
- silence = b"\x00" * silence_size
- self._bot_audio_buffer.extend(silence)
- elif self._recording and isinstance(frame, OutputAudioRawFrame):
- # Add bot audio.
- resampled = await self._resample_audio(frame)
- self._bot_audio_buffer.extend(resampled)
-
- async def _handle_intermittent_stream(self, frame: Frame):
- if isinstance(frame, InputAudioRawFrame):
- # Add silence if we need to.
- silence = self._compute_silence(self._last_user_frame_at)
- self._user_audio_buffer.extend(silence)
- # Add user audio.
- resampled = await self._resample_audio(frame)
- self._user_audio_buffer.extend(resampled)
- # Save time of frame so we can compute silence.
- self._last_user_frame_at = time.time()
- elif self._recording and isinstance(frame, OutputAudioRawFrame):
- # Add silence if we need to.
- silence = self._compute_silence(self._last_bot_frame_at)
- self._bot_audio_buffer.extend(silence)
- # Add bot audio.
- resampled = await self._resample_audio(frame)
- self._bot_audio_buffer.extend(resampled)
- # Save time of frame so we can compute silence.
- self._last_bot_frame_at = time.time()
-
async def _call_on_audio_data_handler(self):
if not self.has_audio() or not self._recording:
return
diff --git a/src/pipecat/processors/frame_processor.py b/src/pipecat/processors/frame_processor.py
index 3b66973dd..1d2f066ed 100644
--- a/src/pipecat/processors/frame_processor.py
+++ b/src/pipecat/processors/frame_processor.py
@@ -17,6 +17,10 @@ from pipecat.frames.frames import (
CancelFrame,
ErrorFrame,
Frame,
+ FrameProcessorPauseFrame,
+ FrameProcessorPauseUrgentFrame,
+ FrameProcessorResumeFrame,
+ FrameProcessorResumeUrgentFrame,
StartFrame,
StartInterruptionFrame,
StopInterruptionFrame,
@@ -259,6 +263,10 @@ class FrameProcessor(BaseObject):
self._should_report_ttfb = True
elif isinstance(frame, CancelFrame):
await self.__cancel(frame)
+ elif isinstance(frame, (FrameProcessorPauseFrame, FrameProcessorPauseUrgentFrame)):
+ await self.__pause(frame)
+ elif isinstance(frame, (FrameProcessorResumeFrame, FrameProcessorResumeUrgentFrame)):
+ await self.__resume(frame)
async def push_error(self, error: ErrorFrame):
await self.push_frame(error, FrameDirection.UPSTREAM)
@@ -287,6 +295,14 @@ class FrameProcessor(BaseObject):
await self.__cancel_input_task()
await self.__cancel_push_task()
+ async def __pause(self, frame: FrameProcessorPauseFrame | FrameProcessorPauseUrgentFrame):
+ if frame.name == self.name:
+ await self.pause_processing_frames()
+
+ async def __resume(self, frame: FrameProcessorResumeFrame | FrameProcessorResumeUrgentFrame):
+ if frame.name == self.name:
+ await self.resume_processing_frames()
+
#
# Handle interruptions
#
diff --git a/src/pipecat/services/aws/stt.py b/src/pipecat/services/aws/stt.py
index 4490f3795..d6aa1fa5d 100644
--- a/src/pipecat/services/aws/stt.py
+++ b/src/pipecat/services/aws/stt.py
@@ -266,6 +266,7 @@ class AWSTranscribeSTTService(STTService):
Language.JA: "ja-JP",
Language.KO: "ko-KR",
Language.ZH: "zh-CN",
+ Language.PL: "pl-PL",
}
return language_map.get(language)
diff --git a/src/pipecat/services/aws/tts.py b/src/pipecat/services/aws/tts.py
index 0159096d1..762e8b9e4 100644
--- a/src/pipecat/services/aws/tts.py
+++ b/src/pipecat/services/aws/tts.py
@@ -6,7 +6,7 @@
import asyncio
import os
-from typing import AsyncGenerator, Optional
+from typing import AsyncGenerator, List, Optional
from loguru import logger
from pydantic import BaseModel
@@ -115,6 +115,7 @@ class AWSPollyTTSService(TTSService):
pitch: Optional[str] = None
rate: Optional[str] = None
volume: Optional[str] = None
+ lexicon_names: Optional[List[str]] = None
def __init__(
self,
@@ -147,6 +148,7 @@ class AWSPollyTTSService(TTSService):
"pitch": params.pitch,
"rate": params.rate,
"volume": params.volume,
+ "lexicon_names": params.lexicon_names,
}
self._resampler = create_default_resampler()
@@ -235,6 +237,7 @@ class AWSPollyTTSService(TTSService):
"Engine": self._settings["engine"],
# AWS only supports 8000 and 16000 for PCM. We select 16000.
"SampleRate": "16000",
+ "LexiconNames": self._settings["lexicon_names"],
}
# Filter out None values
diff --git a/src/pipecat/services/aws_nova_sonic/aws.py b/src/pipecat/services/aws_nova_sonic/aws.py
index 2a342539e..d1332c5c4 100644
--- a/src/pipecat/services/aws_nova_sonic/aws.py
+++ b/src/pipecat/services/aws_nova_sonic/aws.py
@@ -25,6 +25,7 @@ from pipecat.frames.frames import (
CancelFrame,
EndFrame,
Frame,
+ FunctionCallFromLLM,
InputAudioRawFrame,
InterimTranscriptionFrame,
LLMFullResponseEndFrame,
@@ -804,12 +805,16 @@ class AWSNovaSonicLLMService(LLMService):
# Call tool function
if self.has_function(function_name):
if function_name in self._functions.keys() or None in self._functions.keys():
- await self.call_function(
- context=self._context,
- tool_call_id=tool_call_id,
- function_name=function_name,
- arguments=arguments,
- )
+ function_calls_llm = [
+ FunctionCallFromLLM(
+ context=self._context,
+ tool_call_id=tool_call_id,
+ function_name=function_name,
+ arguments=arguments,
+ )
+ ]
+
+ await self.run_function_calls(function_calls_llm)
else:
raise AWSNovaSonicUnhandledFunctionException(
f"The LLM tried to call a function named '{function_name}', but there isn't a callback registered for that function."
diff --git a/src/pipecat/services/cartesia/stt.py b/src/pipecat/services/cartesia/stt.py
index 618908ed9..104e4b2c5 100644
--- a/src/pipecat/services/cartesia/stt.py
+++ b/src/pipecat/services/cartesia/stt.py
@@ -101,7 +101,7 @@ class CartesiaSTTService(STTService):
)
self._settings = merged_options
- self.set_model_name(merged_options["model"])
+ self.set_model_name(merged_options.model)
self._api_key = api_key
self._base_url = base_url or "api.cartesia.ai"
self._connection = None
diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py
index 37261a4ef..e0301360e 100644
--- a/src/pipecat/services/elevenlabs/tts.py
+++ b/src/pipecat/services/elevenlabs/tts.py
@@ -428,26 +428,9 @@ class ElevenLabsTTSService(AudioContextWordTTSService):
break
async def _send_text(self, text: str):
- if self._websocket:
- if not self._context_id:
- # First message for a new context - need a space to initialize
- msg = {"text": " ", "context_id": str(uuid.uuid4())}
-
- # Add voice settings only in first message for a context
- if self._voice_settings:
- msg["voice_settings"] = self._voice_settings
-
- await self._websocket.send(json.dumps(msg))
- self._context_id = msg["context_id"]
- logger.trace(f"Created new context {self._context_id}")
-
- # Now send the actual text content
- msg = {"text": text, "context_id": self._context_id}
- await self._websocket.send(json.dumps(msg))
- else:
- # Continuing with an existing context
- msg = {"text": text, "context_id": self._context_id}
- await self._websocket.send(json.dumps(msg))
+ if self._websocket and self._context_id:
+ msg = {"text": text, "context_id": self._context_id}
+ await self._websocket.send(json.dumps(msg))
@traced_tts
async def run_tts(self, text: str) -> AsyncGenerator[Frame, None]:
@@ -475,8 +458,17 @@ class ElevenLabsTTSService(AudioContextWordTTSService):
self._context_id = str(uuid.uuid4())
await self.create_audio_context(self._context_id)
- await self._send_text(text)
- await self.start_tts_usage_metrics(text)
+ # Initialize context with voice settings
+ msg = {"text": " ", "context_id": self._context_id}
+ if self._voice_settings:
+ msg["voice_settings"] = self._voice_settings
+ await self._websocket.send(json.dumps(msg))
+ logger.trace(f"Created new context {self._context_id} with voice settings")
+
+ await self._send_text(text)
+ await self.start_tts_usage_metrics(text)
+ else:
+ await self._send_text(text)
except Exception as e:
logger.error(f"{self} error sending message: {e}")
yield TTSStoppedFrame()
diff --git a/src/pipecat/services/gladia/config.py b/src/pipecat/services/gladia/config.py
index 4bcde35bb..662996f1d 100644
--- a/src/pipecat/services/gladia/config.py
+++ b/src/pipecat/services/gladia/config.py
@@ -74,12 +74,18 @@ class TranslationConfig(BaseModel):
target_languages: List of target language codes for translation
model: Translation model to use ("base" or "enhanced")
match_original_utterances: Whether to align translations with original utterances
+ lipsync: Whether to enable lip-sync optimization for translations
+ context_adaptation: Whether to enable context-aware translation adaptation
+ context: Additional context to help with translation accuracy
informal: Force informal language forms when available
"""
target_languages: Optional[List[str]] = None
model: Optional[str] = None
match_original_utterances: Optional[bool] = None
+ lipsync: Optional[bool] = None
+ context_adaptation: Optional[bool] = None
+ context: Optional[str] = None
informal: Optional[bool] = None
diff --git a/src/pipecat/services/gladia/stt.py b/src/pipecat/services/gladia/stt.py
index 6ac5edad9..8e1296f0c 100644
--- a/src/pipecat/services/gladia/stt.py
+++ b/src/pipecat/services/gladia/stt.py
@@ -195,6 +195,9 @@ class GladiaSTTService(STTService):
sample_rate: Optional[int] = None,
model: str = "solaria-1",
params: Optional[GladiaInputParams] = None,
+ max_reconnection_attempts: int = 5,
+ reconnection_delay: float = 1.0,
+ max_buffer_size: int = 1024 * 1024 * 20, # 20MB default buffer
**kwargs,
):
"""Initialize the Gladia STT service.
@@ -204,9 +207,11 @@ class GladiaSTTService(STTService):
url: Gladia API URL
confidence: Minimum confidence threshold for transcriptions
sample_rate: Audio sample rate in Hz
- model: Model to use ("solaria-1", "solaria-mini-1", "fast",
- or "accurate")
+ model: Model to use ("solaria-1")
params: Additional configuration parameters
+ max_reconnection_attempts: Maximum number of reconnection attempts
+ reconnection_delay: Initial delay between reconnection attempts (exponential backoff)
+ max_buffer_size: Maximum size of audio buffer in bytes
**kwargs: Additional arguments passed to the STTService
"""
super().__init__(sample_rate=sample_rate, **kwargs)
@@ -232,6 +237,23 @@ class GladiaSTTService(STTService):
self._keepalive_task = None
self._settings = {}
+ # Reconnection settings
+ self._max_reconnection_attempts = max_reconnection_attempts
+ self._reconnection_delay = reconnection_delay
+ self._reconnection_attempts = 0
+ self._session_url = None
+ self._connection_active = False
+
+ # Audio buffer management
+ self._audio_buffer = bytearray()
+ self._bytes_sent = 0
+ self._max_buffer_size = max_buffer_size
+ self._buffer_lock = asyncio.Lock()
+
+ # Connection management
+ self._connection_task = None
+ self._should_reconnect = True
+
def can_generate_metrics(self) -> bool:
return True
@@ -293,36 +315,116 @@ class GladiaSTTService(STTService):
async def start(self, frame: StartFrame):
"""Start the Gladia STT websocket connection."""
await super().start(frame)
- if self._websocket:
+ if self._connection_task:
return
- settings = self._prepare_settings()
- response = await self._setup_gladia(settings)
- self._websocket = await websockets.connect(response["url"])
- if self._websocket and not self._receive_task:
- self._receive_task = self.create_task(self._receive_task_handler())
- if self._websocket and not self._keepalive_task:
- self._keepalive_task = self.create_task(self._keepalive_task_handler())
+
+ self._should_reconnect = True
+ self._connection_task = self.create_task(self._connection_handler())
async def stop(self, frame: EndFrame):
"""Stop the Gladia STT websocket connection."""
await super().stop(frame)
+ self._should_reconnect = False
await self._send_stop_recording()
- if self._keepalive_task:
- await self.cancel_task(self._keepalive_task)
- self._keepalive_task = None
+ if self._connection_task:
+ await self.cancel_task(self._connection_task)
+ self._connection_task = None
- if self._websocket:
- await self._websocket.close()
- self._websocket = None
-
- if self._receive_task:
- await self.wait_for_task(self._receive_task)
- self._receive_task = None
+ await self._cleanup_connection()
async def cancel(self, frame: CancelFrame):
"""Cancel the Gladia STT websocket connection."""
await super().cancel(frame)
+ self._should_reconnect = False
+
+ if self._connection_task:
+ await self.cancel_task(self._connection_task)
+ self._connection_task = None
+
+ await self._cleanup_connection()
+
+ async def run_stt(self, audio: bytes) -> AsyncGenerator[Frame, None]:
+ """Run speech-to-text on audio data."""
+ await self.start_ttfb_metrics()
+ await self.start_processing_metrics()
+
+ # Add audio to buffer
+ async with self._buffer_lock:
+ self._audio_buffer.extend(audio)
+ # Trim buffer if it exceeds max size
+ if len(self._audio_buffer) > self._max_buffer_size:
+ trim_size = len(self._audio_buffer) - self._max_buffer_size
+ self._audio_buffer = self._audio_buffer[trim_size:]
+ self._bytes_sent = max(0, self._bytes_sent - trim_size)
+ logger.warning(f"Audio buffer exceeded max size, trimmed {trim_size} bytes")
+
+ # Send audio if connected
+ if self._connection_active and self._websocket and not self._websocket.closed:
+ try:
+ await self._send_audio(audio)
+ except websockets.exceptions.ConnectionClosed as e:
+ logger.warning(f"Websocket closed while sending audio chunk: {e}")
+ self._connection_active = False
+
+ yield None
+
+ async def _connection_handler(self):
+ """Handle WebSocket connection with automatic reconnection."""
+ while self._should_reconnect:
+ try:
+ # Initialize session if needed
+ if not self._session_url:
+ settings = self._prepare_settings()
+ response = await self._setup_gladia(settings)
+ self._session_url = response["url"]
+ self._reconnection_attempts = 0
+
+ # Connect with automatic reconnection
+ async with websockets.connect(self._session_url) as websocket:
+ try:
+ self._websocket = websocket
+ self._connection_active = True
+ logger.info("Connected to Gladia WebSocket")
+
+ # Send buffered audio if any
+ await self._send_buffered_audio()
+
+ # Start tasks
+ self._receive_task = asyncio.create_task(self._receive_task_handler())
+ self._keepalive_task = asyncio.create_task(self._keepalive_task_handler())
+
+ # Wait for tasks to complete
+ await asyncio.gather(self._receive_task, self._keepalive_task)
+
+ except websockets.exceptions.ConnectionClosed as e:
+ logger.warning(f"WebSocket connection closed: {e}")
+ self._connection_active = False
+
+ # Clean up tasks
+ if self._receive_task:
+ self._receive_task.cancel()
+ if self._keepalive_task:
+ self._keepalive_task.cancel()
+
+ # Attempt reconnect using helper
+ if not await self._maybe_reconnect():
+ break
+
+ except Exception as e:
+ logger.error(f"Error in connection handler: {e}")
+ self._connection_active = False
+
+ if not self._should_reconnect:
+ break
+
+ # Reset session URL to get a new one
+ self._session_url = None
+ await asyncio.sleep(self._reconnection_delay)
+
+ async def _cleanup_connection(self):
+ """Clean up connection resources."""
+ self._connection_active = False
if self._keepalive_task:
await self.cancel_task(self._keepalive_task)
@@ -336,13 +438,6 @@ class GladiaSTTService(STTService):
await self.cancel_task(self._receive_task)
self._receive_task = None
- async def run_stt(self, audio: bytes) -> AsyncGenerator[Frame, None]:
- """Run speech-to-text on audio data."""
- await self.start_ttfb_metrics()
- await self.start_processing_metrics()
- await self._send_audio(audio)
- yield None
-
async def _setup_gladia(self, settings: Dict[str, Any]):
async with aiohttp.ClientSession() as session:
async with session.post(
@@ -369,9 +464,18 @@ class GladiaSTTService(STTService):
await self.stop_processing_metrics()
async def _send_audio(self, audio: bytes):
- data = base64.b64encode(audio).decode("utf-8")
- message = {"type": "audio_chunk", "data": {"chunk": data}}
- await self._websocket.send(json.dumps(message))
+ """Send audio chunk with proper message format."""
+ if self._websocket and not self._websocket.closed:
+ data = base64.b64encode(audio).decode("utf-8")
+ message = {"type": "audio_chunk", "data": {"chunk": data}}
+ await self._websocket.send(json.dumps(message))
+
+ async def _send_buffered_audio(self):
+ """Send any buffered audio after reconnection."""
+ async with self._buffer_lock:
+ if self._audio_buffer:
+ logger.info(f"Sending {len(self._audio_buffer)} bytes of buffered audio")
+ await self._send_audio(bytes(self._audio_buffer))
async def _send_stop_recording(self):
if self._websocket and not self._websocket.closed:
@@ -380,7 +484,7 @@ class GladiaSTTService(STTService):
async def _keepalive_task_handler(self):
"""Send periodic empty audio chunks to keep the connection alive."""
try:
- while True:
+ while self._connection_active:
# Send keepalive every 20 seconds (Gladia times out after 30 seconds)
await asyncio.sleep(20)
if self._websocket and not self._websocket.closed:
@@ -399,7 +503,19 @@ class GladiaSTTService(STTService):
try:
async for message in self._websocket:
content = json.loads(message)
- if content["type"] == "transcript":
+
+ # Handle audio chunk acknowledgments
+ if content["type"] == "audio_chunk" and content.get("acknowledged"):
+ byte_range = content["data"]["byte_range"]
+ async with self._buffer_lock:
+ # Update bytes sent and trim acknowledged data from buffer
+ end_byte = byte_range[1]
+ if end_byte > self._bytes_sent:
+ trim_size = end_byte - self._bytes_sent
+ self._audio_buffer = self._audio_buffer[trim_size:]
+ self._bytes_sent = end_byte
+
+ elif content["type"] == "transcript":
utterance = content["data"]["utterance"]
confidence = utterance.get("confidence", 0)
language = utterance["language"]
@@ -448,3 +564,19 @@ class GladiaSTTService(STTService):
pass
except Exception as e:
logger.error(f"Error in Gladia WebSocket handler: {e}")
+
+ async def _maybe_reconnect(self) -> bool:
+ """Handle exponential backoff reconnection logic."""
+ if not self._should_reconnect:
+ return False
+ self._reconnection_attempts += 1
+ if self._reconnection_attempts > self._max_reconnection_attempts:
+ logger.error(f"Max reconnection attempts ({self._max_reconnection_attempts}) reached")
+ self._should_reconnect = False
+ return False
+ delay = self._reconnection_delay * (2 ** (self._reconnection_attempts - 1))
+ logger.info(
+ f"Reconnecting in {delay} seconds (attempt {self._reconnection_attempts}/{self._max_reconnection_attempts})"
+ )
+ await asyncio.sleep(delay)
+ return True
diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py
index 8960bda31..f983b7342 100644
--- a/src/pipecat/services/google/llm.py
+++ b/src/pipecat/services/google/llm.py
@@ -555,10 +555,11 @@ class GoogleLLMService(LLMService):
contents=messages,
config=generation_config,
)
- await self.stop_ttfb_metrics()
function_calls = []
async for chunk in response:
+ # Stop TTFB metrics after the first chunk
+ await self.stop_ttfb_metrics()
if chunk.usage_metadata:
prompt_tokens += chunk.usage_metadata.prompt_token_count or 0
completion_tokens += chunk.usage_metadata.candidates_token_count or 0
diff --git a/src/pipecat/services/google/stt.py b/src/pipecat/services/google/stt.py
index 4fd129af3..bf60541f5 100644
--- a/src/pipecat/services/google/stt.py
+++ b/src/pipecat/services/google/stt.py
@@ -747,6 +747,11 @@ class GoogleSTTService(STTService):
try:
while True:
try:
+ if self._request_queue.empty():
+ # wait for 10ms in case we don't have audio
+ await asyncio.sleep(0.01)
+ continue
+
# Start bi-directional streaming
streaming_recognize = await self._client.streaming_recognize(
requests=self._request_generator()
diff --git a/src/pipecat/services/groq/tts.py b/src/pipecat/services/groq/tts.py
index 6f73b1629..33fd3ce34 100644
--- a/src/pipecat/services/groq/tts.py
+++ b/src/pipecat/services/groq/tts.py
@@ -4,6 +4,8 @@
# SPDX-License-Identifier: BSD 2-Clause License
#
+import io
+import wave
from typing import AsyncGenerator, Optional
from loguru import logger
@@ -78,22 +80,26 @@ class GroqTTSService(TTSService):
await self.start_ttfb_metrics()
yield TTSStartedFrame()
- response = await self._client.audio.speech.create(
- model=self._model_name,
- voice=self._voice_id,
- response_format=self._output_format,
- input=text,
- )
+ try:
+ response = await self._client.audio.speech.create(
+ model=self._model_name,
+ voice=self._voice_id,
+ response_format=self._output_format,
+ input=text,
+ )
- async for data in response.iter_bytes():
- if measuring_ttfb:
- await self.stop_ttfb_metrics()
- measuring_ttfb = False
- # remove wav header if present
- if data.startswith(b"RIFF"):
- data = data[44:]
- if len(data) == 0:
- continue
- yield TTSAudioRawFrame(data, self.sample_rate, 1)
+ async for data in response.iter_bytes():
+ if measuring_ttfb:
+ await self.stop_ttfb_metrics()
+ measuring_ttfb = False
+
+ with wave.open(io.BytesIO(data)) as w:
+ channels = w.getnchannels()
+ frame_rate = w.getframerate()
+ num_frames = w.getnframes()
+ bytes = w.readframes(num_frames)
+ yield TTSAudioRawFrame(bytes, frame_rate, channels)
+ except Exception as e:
+ logger.error(f"{self} exception: {e}")
yield TTSStoppedFrame()
diff --git a/src/pipecat/services/tavus/video.py b/src/pipecat/services/tavus/video.py
index 0b59514e7..e6c78813d 100644
--- a/src/pipecat/services/tavus/video.py
+++ b/src/pipecat/services/tavus/video.py
@@ -7,7 +7,6 @@
"""This module implements Tavus as a sink transport layer"""
import asyncio
-import time
from typing import Optional
import aiohttp
@@ -29,9 +28,6 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessorSet
from pipecat.services.ai_service import AIService
from pipecat.transports.services.tavus import TavusCallbacks, TavusParams, TavusTransportClient
-# Using the same values that we do in the BaseOutputTransport
-BOT_VAD_STOP_SECS = 0.35
-
class TavusVideoService(AIService):
"""
@@ -48,7 +44,7 @@ class TavusVideoService(AIService):
Args:
api_key (str): Tavus API key used for authentication.
replica_id (str): ID of the Tavus voice replica to use for speech synthesis.
- persona_id (str): ID of the Tavus persona. Defaults to "pipecat0" to use the Pipecat TTS voice.
+ persona_id (str): ID of the Tavus persona. Defaults to "pipecat-stream" to use the Pipecat TTS voice.
session (aiohttp.ClientSession): Async HTTP session used for communication with Tavus.
**kwargs: Additional arguments passed to the parent `AIService` class.
"""
@@ -58,7 +54,7 @@ class TavusVideoService(AIService):
*,
api_key: str,
replica_id: str,
- persona_id: str = "pipecat0", # Use `pipecat0` so that your TTS voice is used in place of the Tavus persona
+ persona_id: str = "pipecat-stream",
session: aiohttp.ClientSession,
**kwargs,
) -> None:
@@ -77,6 +73,8 @@ class TavusVideoService(AIService):
self._audio_buffer = bytearray()
self._queue = asyncio.Queue()
self._send_task: Optional[asyncio.Task] = None
+ # This is the custom track destination expected by Tavus
+ self._transport_destination: Optional[str] = "stream"
async def setup(self, setup: FrameProcessorSetup):
await super().setup(setup)
@@ -94,6 +92,8 @@ class TavusVideoService(AIService):
params=TavusParams(
audio_in_enabled=True,
video_in_enabled=True,
+ audio_out_enabled=True,
+ microphone_out_enabled=False,
),
)
await self._client.setup(setup)
@@ -152,6 +152,8 @@ class TavusVideoService(AIService):
async def start(self, frame: StartFrame):
await super().start(frame)
await self._client.start(frame)
+ if self._transport_destination:
+ await self._client.register_audio_destination(self._transport_destination)
await self._create_send_task()
async def stop(self, frame: EndFrame):
@@ -171,7 +173,7 @@ class TavusVideoService(AIService):
await self._handle_interruptions()
await self.push_frame(frame, direction)
elif isinstance(frame, TTSAudioRawFrame):
- await self._queue.put(frame)
+ await self._handle_audio_frame(frame)
else:
await self.push_frame(frame, direction)
@@ -194,60 +196,26 @@ class TavusVideoService(AIService):
await self.cancel_task(self._send_task)
self._send_task = None
- async def _send_task_handler(self):
- # Daily app-messages have a 4kb limit and also a rate limit of 20
- # messages per second. Below, we only consider the rate limit because 1
- # second of a 24000 sample rate would be 48000 bytes (16-bit samples and
- # 1 channel). So, that is 48000 / 20 = 2400, which is below the 4kb
- # limit (even including base64 encoding). For a sample rate of 16000,
- # that would be 32000 / 20 = 1600.
+ async def _handle_audio_frame(self, frame: OutputAudioRawFrame):
sample_rate = self._client.out_sample_rate
- # 50 ms of audio
- MAX_CHUNK_SIZE = int((sample_rate * 2) / 20)
-
- audio_buffer = bytearray()
- current_idx_str = None
- silence = b"\x00" * MAX_CHUNK_SIZE
- samples_sent = 0
- start_time = None
+ # 40 ms of audio
+ chunk_size = int((sample_rate * 2) / 25)
+ # We might need to resample if incoming audio doesn't match the
+ # transport sample rate.
+ resampled = await self._resampler.resample(frame.audio, frame.sample_rate, sample_rate)
+ self._audio_buffer.extend(resampled)
+ while len(self._audio_buffer) >= chunk_size:
+ chunk = OutputAudioRawFrame(
+ bytes(self._audio_buffer[:chunk_size]),
+ sample_rate=sample_rate,
+ num_channels=frame.num_channels,
+ )
+ chunk.transport_destination = self._transport_destination
+ await self._queue.put(chunk)
+ self._audio_buffer = self._audio_buffer[chunk_size:]
+ async def _send_task_handler(self):
while True:
- try:
- frame = await asyncio.wait_for(self._queue.get(), timeout=BOT_VAD_STOP_SECS)
- if isinstance(frame, TTSAudioRawFrame):
- # starting the new inference
- if current_idx_str is None:
- current_idx_str = str(frame.id)
- samples_sent = 0
- start_time = time.time()
-
- audio = await self._resampler.resample(
- frame.audio, frame.sample_rate, sample_rate
- )
- audio_buffer.extend(audio)
- while len(audio_buffer) >= MAX_CHUNK_SIZE:
- chunk = audio_buffer[:MAX_CHUNK_SIZE]
- audio_buffer = audio_buffer[MAX_CHUNK_SIZE:]
-
- # Compute wait time for synchronization
- wait = start_time + (samples_sent / sample_rate) - time.time()
- if wait > 0:
- logger.trace(f"TavusVideoService _send_task_handler wait: {wait}")
- await asyncio.sleep(wait)
-
- await self._client.encode_audio_and_send(
- bytes(chunk), False, current_idx_str
- )
-
- # Update timestamp based on number of samples sent
- samples_sent += len(chunk) // 2 # 2 bytes per sample (16-bit)
- except asyncio.TimeoutError:
- # Bot has stopped speaking
- # Send any remaining audio.
- if len(audio_buffer) > 0:
- await self._client.encode_audio_and_send(
- bytes(audio_buffer), False, current_idx_str
- )
- await self._client.encode_audio_and_send(silence, True, current_idx_str)
- audio_buffer.clear()
- current_idx_str = None
+ frame = await self._queue.get()
+ if isinstance(frame, OutputAudioRawFrame):
+ await self._client.write_audio_frame(frame)
diff --git a/src/pipecat/transports/services/daily.py b/src/pipecat/transports/services/daily.py
index 78bf62062..776da1693 100644
--- a/src/pipecat/transports/services/daily.py
+++ b/src/pipecat/transports/services/daily.py
@@ -767,6 +767,7 @@ class DailyTransportClient(EventHandler):
self._client.add_custom_audio_track(
track_name=track_name,
audio_track=audio_track,
+ ignore_audio_level=True,
completion=completion_callback(future),
)
diff --git a/src/pipecat/transports/services/tavus.py b/src/pipecat/transports/services/tavus.py
index 2c76b9b33..ff70416d2 100644
--- a/src/pipecat/transports/services/tavus.py
+++ b/src/pipecat/transports/services/tavus.py
@@ -1,6 +1,4 @@
-import asyncio
-import base64
-import time
+import os
from functools import partial
from typing import Any, Awaitable, Callable, Mapping, Optional
@@ -11,8 +9,6 @@ from pydantic import BaseModel
from pipecat.audio.utils import create_default_resampler
from pipecat.frames.frames import (
- BotStartedSpeakingFrame,
- BotStoppedSpeakingFrame,
CancelFrame,
EndFrame,
Frame,
@@ -40,6 +36,8 @@ class TavusApi:
"""
BASE_URL = "https://tavusapi.com/v2"
+ MOCK_CONVERSATION_ID = "dev-conversation"
+ MOCK_PERSONA_NAME = "TestTavusTransport"
def __init__(self, api_key: str, session: aiohttp.ClientSession):
"""
@@ -52,8 +50,16 @@ class TavusApi:
self._api_key = api_key
self._session = session
self._headers = {"Content-Type": "application/json", "x-api-key": self._api_key}
+ # Only for development
+ self._dev_room_url = os.getenv("TAVUS_SAMPLE_ROOM_URL")
async def create_conversation(self, replica_id: str, persona_id: str) -> dict:
+ if self._dev_room_url:
+ return {
+ "conversation_id": self.MOCK_CONVERSATION_ID,
+ "conversation_url": self._dev_room_url,
+ }
+
logger.debug(f"Creating Tavus conversation: replica={replica_id}, persona={persona_id}")
url = f"{self.BASE_URL}/conversations"
payload = {
@@ -67,7 +73,7 @@ class TavusApi:
return response
async def end_conversation(self, conversation_id: str):
- if conversation_id is None:
+ if conversation_id is None or conversation_id == self.MOCK_CONVERSATION_ID:
return
url = f"{self.BASE_URL}/conversations/{conversation_id}/end"
@@ -76,6 +82,9 @@ class TavusApi:
logger.debug(f"Ended Tavus conversation {conversation_id}")
async def get_persona_name(self, persona_id: str) -> str:
+ if self._dev_room_url is not None:
+ return self.MOCK_PERSONA_NAME
+
url = f"{self.BASE_URL}/personas/{persona_id}"
async with self._session.get(url, headers=self._headers) as r:
r.raise_for_status()
@@ -119,7 +128,7 @@ class TavusTransportClient:
callbacks (TavusCallbacks): Callback handlers for Tavus-related events.
api_key (str): API key for authenticating with Tavus API.
replica_id (str): ID of the replica to use in the Tavus conversation.
- persona_id (str): ID of the Tavus persona. Defaults to "pipecat0", which signals Tavus to use
+ persona_id (str): ID of the Tavus persona. Defaults to "pipecat-stream", which signals Tavus to use
the TTS voice of the Pipecat bot instead of a Tavus persona voice.
session (aiohttp.ClientSession): The aiohttp session for making async HTTP requests.
sample_rate: Audio sample rate to be used by the client.
@@ -133,7 +142,7 @@ class TavusTransportClient:
callbacks: TavusCallbacks,
api_key: str,
replica_id: str,
- persona_id: str = "pipecat0", # Use `pipecat0` so that your TTS voice is used in place of the Tavus persona
+ persona_id: str = "pipecat-stream",
session: aiohttp.ClientSession,
) -> None:
self._bot_name = bot_name
@@ -141,7 +150,6 @@ class TavusTransportClient:
self._replica_id = replica_id
self._persona_id = persona_id
self._conversation_id: Optional[str] = None
- self._other_participant_has_joined = False
self._client: Optional[DailyTransportClient] = None
self._callbacks = callbacks
self._params = params
@@ -153,6 +161,7 @@ class TavusTransportClient:
async def setup(self, setup: FrameProcessorSetup):
if self._conversation_id is not None:
+ logger.debug(f"Conversation ID already defined: {self._conversation_id}")
return
try:
room_url = await self._initialize()
@@ -194,12 +203,13 @@ class TavusTransportClient:
except Exception as e:
logger.error(f"Failed to setup TavusTransportClient: {e}")
await self._api.end_conversation(self._conversation_id)
+ self._conversation_id = None
async def cleanup(self):
- if self._client is None:
- return
- await self._client.cleanup()
- self._client = None
+ try:
+ await self._client.cleanup()
+ except Exception as e:
+ logger.exception(f"Exception during cleanup: {e}")
async def _on_joined(self, data):
logger.debug("TavusTransportClient joined!")
@@ -221,6 +231,7 @@ class TavusTransportClient:
async def stop(self):
await self._client.leave()
await self._api.end_conversation(self._conversation_id)
+ self._conversation_id = None
async def capture_participant_video(
self,
@@ -257,11 +268,6 @@ class TavusTransportClient:
def in_sample_rate(self) -> int:
return self._client.in_sample_rate
- async def encode_audio_and_send(self, audio: bytes, done: bool, inference_id: str):
- """Encodes audio to base64 and sends it to Tavus"""
- audio_base64 = base64.b64encode(audio).decode("utf-8")
- await self._send_audio_message(audio_base64, done=done, inference_id=inference_id)
-
async def send_interrupt_message(self) -> None:
transport_frame = TransportMessageUrgentFrame(
message={
@@ -272,23 +278,6 @@ class TavusTransportClient:
)
await self.send_message(transport_frame)
- async def _send_audio_message(self, audio_base64: str, done: bool, inference_id: str):
- transport_frame = TransportMessageUrgentFrame(
- message={
- "message_type": "conversation",
- "event_type": "conversation.echo",
- "conversation_id": self._conversation_id,
- "properties": {
- "modality": "audio",
- "inference_id": inference_id,
- "audio": audio_base64,
- "done": done,
- "sample_rate": self.out_sample_rate,
- },
- }
- )
- await self.send_message(transport_frame)
-
async def update_subscriptions(self, participant_settings=None, profile_settings=None):
if not self._client:
return
@@ -300,9 +289,14 @@ class TavusTransportClient:
async def write_audio_frame(self, frame: OutputAudioRawFrame):
if not self._client:
return
-
await self._client.write_audio_frame(frame)
+ async def register_audio_destination(self, destination: str):
+ if not self._client:
+ return
+
+ await self._client.register_audio_destination(destination)
+
class TavusInputTransport(BaseInputTransport):
def __init__(
@@ -379,12 +373,11 @@ class TavusOutputTransport(BaseOutputTransport):
super().__init__(params, **kwargs)
self._client = client
self._params = params
- self._samples_sent = 0
- self._start_time = None
- self._current_idx_str: Optional[str] = None
# Whether we have seen a StartFrame already.
self._initialized = False
+ # This is the custom track destination expected by Tavus
+ self._transport_destination: Optional[str] = "stream"
async def setup(self, setup: FrameProcessorSetup):
await super().setup(setup)
@@ -403,6 +396,10 @@ class TavusOutputTransport(BaseOutputTransport):
self._initialized = True
await self._client.start(frame)
+
+ if self._transport_destination:
+ await self._client.register_audio_destination(self._transport_destination)
+
await self.set_transport_ready(frame)
async def stop(self, frame: EndFrame):
@@ -417,23 +414,6 @@ class TavusOutputTransport(BaseOutputTransport):
logger.info(f"TavusOutputTransport sending message {frame}")
await self._client.send_message(frame)
- async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM):
- # The BotStartedSpeakingFrame and BotStoppedSpeakingFrame are created inside BaseOutputTransport
- # so TavusOutputTransport never receives these frames.
- # This is a workaround, so we can more reliably be aware when the bot has started or stopped speaking
- if direction == FrameDirection.DOWNSTREAM:
- if isinstance(frame, BotStartedSpeakingFrame):
- if self._current_idx_str is not None:
- logger.warning("TavusOutputTransport self._current_idx_str is already defined!")
- self._current_idx_str = str(frame.id)
- self._start_time = time.time()
- self._samples_sent = 0
- elif isinstance(frame, BotStoppedSpeakingFrame):
- silence = b"\x00" * self.audio_chunk_size
- await self._client.encode_audio_and_send(silence, True, self._current_idx_str)
- self._current_idx_str = None
- await super().push_frame(frame, direction)
-
async def process_frame(self, frame: Frame, direction: FrameDirection):
await super().process_frame(frame, direction)
if isinstance(frame, StartInterruptionFrame):
@@ -443,20 +423,12 @@ class TavusOutputTransport(BaseOutputTransport):
await self._client.send_interrupt_message()
async def write_audio_frame(self, frame: OutputAudioRawFrame):
- # Compute wait time for synchronization
- wait = self._start_time + (self._samples_sent / self.sample_rate) - time.time()
- if wait > 0:
- logger.trace(f"TavusOutputTransport write_audio_frame wait: {wait}")
- await asyncio.sleep(wait)
+ # This is the custom track destination expected by Tavus
+ frame.transport_destination = self._transport_destination
+ await self._client.write_audio_frame(frame)
- if self._current_idx_str is None:
- logger.warning("TavusOutputTransport self._current_idx_str not defined yet!")
- return
-
- await self._client.encode_audio_and_send(frame.audio, False, self._current_idx_str)
-
- # Update timestamp based on number of samples sent
- self._samples_sent += len(frame.audio) // 2 # 2 bytes per sample (16-bit)
+ async def register_audio_destination(self, destination: str):
+ await self._client.register_audio_destination(destination)
class TavusTransport(BaseTransport):
@@ -472,7 +444,7 @@ class TavusTransport(BaseTransport):
session (aiohttp.ClientSession): aiohttp session used for async HTTP requests.
api_key (str): Tavus API key for authentication.
replica_id (str): ID of the replica model used for voice generation.
- persona_id (str): ID of the Tavus persona. Defaults to "pipecat0" to use the Pipecat TTS voice.
+ persona_id (str): ID of the Tavus persona. Defaults to "pipecat-stream" to use the Pipecat TTS voice.
params (TavusParams): Optional Tavus-specific configuration parameters.
input_name (Optional[str]): Optional name for the input transport.
output_name (Optional[str]): Optional name for the output transport.
@@ -484,7 +456,7 @@ class TavusTransport(BaseTransport):
session: aiohttp.ClientSession,
api_key: str,
replica_id: str,
- persona_id: str = "pipecat0", # Use `pipecat0` so that your TTS voice is used in place of the Tavus persona
+ persona_id: str = "pipecat-stream",
params: TavusParams = TavusParams(),
input_name: Optional[str] = None,
output_name: Optional[str] = None,
@@ -492,11 +464,6 @@ class TavusTransport(BaseTransport):
super().__init__(input_name=input_name, output_name=output_name)
self._params = params
- # TODO: Filipi - We can remove this if we stop sending the audio through app messages
- # Limiting this so we don't go over 20 messages per second
- # each message is going to have 50ms of audio
- self._params.audio_out_10ms_chunks = 5
-
callbacks = TavusCallbacks(
on_participant_joined=self._on_participant_joined,
on_participant_left=self._on_participant_left,
@@ -527,6 +494,7 @@ class TavusTransport(BaseTransport):
async def _on_participant_joined(self, participant):
# get persona, look up persona_name, set this as the bot name to ignore
persona_name = await self._client.get_persona_name()
+
# Ignore the Tavus replica's microphone
if participant.get("info", {}).get("userName", "") == persona_name:
self._tavus_participant_id = participant["id"]
diff --git a/src/pipecat/utils/tracing/turn_trace_observer.py b/src/pipecat/utils/tracing/turn_trace_observer.py
index 26fc5b38a..f67ca3b28 100644
--- a/src/pipecat/utils/tracing/turn_trace_observer.py
+++ b/src/pipecat/utils/tracing/turn_trace_observer.py
@@ -35,7 +35,11 @@ class TurnTraceObserver(BaseObserver):
"""
def __init__(
- self, turn_tracker: TurnTrackingObserver, conversation_id: Optional[str] = None, **kwargs
+ self,
+ turn_tracker: TurnTrackingObserver,
+ conversation_id: Optional[str] = None,
+ additional_span_attributes: Optional[dict] = None,
+ **kwargs,
):
super().__init__(**kwargs)
self._turn_tracker = turn_tracker
@@ -47,6 +51,7 @@ class TurnTraceObserver(BaseObserver):
# Conversation tracking properties
self._conversation_span: Optional["Span"] = None
self._conversation_id = conversation_id
+ self._additional_span_attributes = additional_span_attributes or {}
if turn_tracker:
@@ -89,6 +94,9 @@ class TurnTraceObserver(BaseObserver):
# Set span attributes
self._conversation_span.set_attribute("conversation.id", conversation_id)
self._conversation_span.set_attribute("conversation.type", "voice")
+ # Set custom otel attributes if provided
+ for k, v in (self._additional_span_attributes or {}).items():
+ self._conversation_span.set_attribute(k, v)
# Update the conversation context provider
context_provider.set_current_conversation_context(
diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py
index 84485098d..1146681f9 100644
--- a/tests/test_pipeline.py
+++ b/tests/test_pipeline.py
@@ -8,7 +8,14 @@ import asyncio
import time
import unittest
-from pipecat.frames.frames import EndFrame, HeartbeatFrame, StartFrame, StopFrame, TextFrame
+from pipecat.frames.frames import (
+ EndFrame,
+ HeartbeatFrame,
+ InputAudioRawFrame,
+ StartFrame,
+ StopFrame,
+ TextFrame,
+)
from pipecat.observers.base_observer import BaseObserver, FramePushed
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
from pipecat.pipeline.pipeline import Pipeline
@@ -321,7 +328,7 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase):
await task.run()
assert True
- async def test_idle_task_event_handler(self):
+ async def test_idle_task_event_handler_no_frames(self):
identity = IdentityFilter()
pipeline = Pipeline([identity])
task = PipelineTask(pipeline, idle_timeout_secs=0.2, cancel_on_idle_timeout=False)
@@ -336,7 +343,38 @@ class TestPipelineTask(unittest.IsolatedAsyncioTestCase):
await task.cancel()
await task.run()
- assert True
+ assert idle_timeout
+
+ async def test_idle_task_event_handler_quiet_user(self):
+ identity = IdentityFilter()
+ pipeline = Pipeline([identity])
+ task = PipelineTask(pipeline, idle_timeout_secs=0.2, cancel_on_idle_timeout=False)
+ task.set_event_loop(asyncio.get_event_loop())
+
+ idle_timeout = 0
+
+ @task.event_handler("on_idle_timeout")
+ async def on_idle_timeout(task: PipelineTask):
+ nonlocal idle_timeout
+ idle_timeout += 1
+ # Stay a bit longer here while user audio frames are still being
+ # pushed. We do this to make sure this function is only called once.
+ await asyncio.sleep(0.1)
+ await task.queue_frame(EndFrame())
+
+ async def send_audio():
+ # We send audio during and after the 0.2 seconds of idle
+ # timeout. Inside `on_idle_timeout` we are waiting a little bit
+ # simulating the pipeline finishing (e.g. goodbye message from bot
+ # flushing).
+ for i in range(30):
+ await task.queue_frame(
+ InputAudioRawFrame(audio=b"\x00", sample_rate=16000, num_channels=1)
+ )
+ await asyncio.sleep(0.01)
+
+ await asyncio.gather(send_audio(), task.run())
+ assert idle_timeout == 1
async def test_idle_task_frames(self):
idle_timeout_secs = 0.2