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 +
+
+ + +
+
+ + + +
+

Debug Info

+
+
+
+ + + + + + 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