From 9cc264471918e4622a98b8be4b3103cd4e6947f1 Mon Sep 17 00:00:00 2001 From: Kinshuk Bairagi Date: Wed, 14 Jan 2026 21:46:58 +0530 Subject: [PATCH 001/159] Improve system message extraction in traced_llm Enhanced the logic for extracting the system message in the traced_llm decorator to support LLMContext via adapter and handle exceptions gracefully. This improves compatibility with different context types and ensures better tracing information. --- changelog/3449.fixed.md | 1 + .../utils/tracing/service_decorators.py | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 changelog/3449.fixed.md diff --git a/changelog/3449.fixed.md b/changelog/3449.fixed.md new file mode 100644 index 000000000..a8663e004 --- /dev/null +++ b/changelog/3449.fixed.md @@ -0,0 +1 @@ +- Fixed telemetry record correct system_instruction in span for LLM services diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index 304ecb5e8..6f772aa88 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -500,7 +500,24 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - # Handle system message for different services system_message = None - if hasattr(context, "system"): + # Handle system message for different services + if isinstance(context, LLMContext): + # Universal LLMContext - use adapter to convert and get system message + if hasattr(self, "get_llm_adapter"): + adapter = self.get_llm_adapter() + try: + # Get LLM invocation params which includes system_instruction + params = adapter.get_llm_invocation_params(context) + if ( + isinstance(params, dict) + and "system_instruction" in params + ): + system_message = params["system_instruction"] + except Exception as e: + logging.debug( + f"Could not extract system instruction from adapter: {e}" + ) + elif hasattr(context, "system"): system_message = context.system elif hasattr(context, "system_message"): system_message = context.system_message From 0df421de9cebb64beea59b02c94cd319ef10c41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 12:53:13 -0700 Subject: [PATCH 002/159] Move google/llm_vertex.py to google/vertex/llm.py --- src/pipecat/services/google/vertex/__init__.py | 5 +++++ src/pipecat/services/google/{llm_vertex.py => vertex/llm.py} | 0 2 files changed, 5 insertions(+) create mode 100644 src/pipecat/services/google/vertex/__init__.py rename src/pipecat/services/google/{llm_vertex.py => vertex/llm.py} (100%) diff --git a/src/pipecat/services/google/vertex/__init__.py b/src/pipecat/services/google/vertex/__init__.py new file mode 100644 index 000000000..c4d243b97 --- /dev/null +++ b/src/pipecat/services/google/vertex/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# diff --git a/src/pipecat/services/google/llm_vertex.py b/src/pipecat/services/google/vertex/llm.py similarity index 100% rename from src/pipecat/services/google/llm_vertex.py rename to src/pipecat/services/google/vertex/llm.py From b159d02b0cc3d690bc375961e7677bbea22e81e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 12:54:05 -0700 Subject: [PATCH 003/159] Add deprecation stub for google/llm_vertex.py --- src/pipecat/services/google/__init__.py | 4 ++-- src/pipecat/services/google/llm_vertex.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/pipecat/services/google/llm_vertex.py diff --git a/src/pipecat/services/google/__init__.py b/src/pipecat/services/google/__init__.py index 032cf0eb8..009f71f08 100644 --- a/src/pipecat/services/google/__init__.py +++ b/src/pipecat/services/google/__init__.py @@ -13,11 +13,11 @@ from .gemini_live import * from .image import * from .llm import * from .llm_openai import * -from .llm_vertex import * from .rtvi import * from .stt import * from .tts import * +from .vertex import * sys.modules[__name__] = DeprecatedModuleProxy( - globals(), "google", "google.[frames,image,llm,llm_openai,llm_vertex,rtvi,stt,tts]" + globals(), "google", "google.[frames,image,llm,openai,vertex,rtvi,stt,tts]" ) diff --git a/src/pipecat/services/google/llm_vertex.py b/src/pipecat/services/google/llm_vertex.py new file mode 100644 index 000000000..54d338ad7 --- /dev/null +++ b/src/pipecat/services/google/llm_vertex.py @@ -0,0 +1,18 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Deprecated: use ``pipecat.services.google.vertex.llm`` instead.""" + +import warnings + +warnings.warn( + "Module `pipecat.services.google.llm_vertex` is deprecated, " + "use `pipecat.services.google.vertex.llm` instead.", + DeprecationWarning, + stacklevel=2, +) + +from pipecat.services.google.vertex.llm import * # noqa: E402, F401, F403 From 8ea006739c2f1cd52b1614ac16354420a7d55e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 12:54:37 -0700 Subject: [PATCH 004/159] Move google/llm_openai.py to google/openai/llm.py --- src/pipecat/services/google/openai/__init__.py | 5 +++++ src/pipecat/services/google/{llm_openai.py => openai/llm.py} | 0 2 files changed, 5 insertions(+) create mode 100644 src/pipecat/services/google/openai/__init__.py rename src/pipecat/services/google/{llm_openai.py => openai/llm.py} (100%) diff --git a/src/pipecat/services/google/openai/__init__.py b/src/pipecat/services/google/openai/__init__.py new file mode 100644 index 000000000..c4d243b97 --- /dev/null +++ b/src/pipecat/services/google/openai/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# diff --git a/src/pipecat/services/google/llm_openai.py b/src/pipecat/services/google/openai/llm.py similarity index 100% rename from src/pipecat/services/google/llm_openai.py rename to src/pipecat/services/google/openai/llm.py From 4fa3890cec1706701da12651740d753d5f8a7af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 12:55:16 -0700 Subject: [PATCH 005/159] Add deprecation stub for google/llm_openai.py --- src/pipecat/services/google/__init__.py | 2 +- src/pipecat/services/google/llm_openai.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/pipecat/services/google/llm_openai.py diff --git a/src/pipecat/services/google/__init__.py b/src/pipecat/services/google/__init__.py index 009f71f08..32b12e367 100644 --- a/src/pipecat/services/google/__init__.py +++ b/src/pipecat/services/google/__init__.py @@ -12,7 +12,7 @@ from .frames import * from .gemini_live import * from .image import * from .llm import * -from .llm_openai import * +from .openai import * from .rtvi import * from .stt import * from .tts import * diff --git a/src/pipecat/services/google/llm_openai.py b/src/pipecat/services/google/llm_openai.py new file mode 100644 index 000000000..f9d182e78 --- /dev/null +++ b/src/pipecat/services/google/llm_openai.py @@ -0,0 +1,18 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Deprecated: use ``pipecat.services.google.openai.llm`` instead.""" + +import warnings + +warnings.warn( + "Module `pipecat.services.google.llm_openai` is deprecated, " + "use `pipecat.services.google.openai.llm` instead.", + DeprecationWarning, + stacklevel=2, +) + +from pipecat.services.google.openai.llm import * # noqa: E402, F401, F403 From b23652caa68c757adbd0a8ce2e35d62a6813424a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 12:58:04 -0700 Subject: [PATCH 006/159] Update imports to use new google.vertex and google.openai paths --- .../14o-function-calling-gemini-openai-format.py | 2 +- .../foundational/14p-function-calling-gemini-vertex-ai.py | 2 +- .../foundational/55zk-update-settings-google-vertex-llm.py | 2 +- src/pipecat/services/google/google.py | 6 +++--- tests/test_google_llm_openai.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/foundational/14o-function-calling-gemini-openai-format.py b/examples/foundational/14o-function-calling-gemini-openai-format.py index 416e602bf..8ab53d9cf 100644 --- a/examples/foundational/14o-function-calling-gemini-openai-format.py +++ b/examples/foundational/14o-function-calling-gemini-openai-format.py @@ -26,7 +26,7 @@ from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsTTSService -from pipecat.services.google.llm_openai import GoogleLLMOpenAIBetaService +from pipecat.services.google.openai.llm import GoogleLLMOpenAIBetaService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py index 1f33efacf..c6259b992 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -26,7 +26,7 @@ from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsTTSService -from pipecat.services.google.llm_vertex import GoogleVertexLLMService +from pipecat.services.google.vertex.llm import GoogleVertexLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/55zk-update-settings-google-vertex-llm.py b/examples/foundational/55zk-update-settings-google-vertex-llm.py index 6b2babb7f..7f7a34cba 100644 --- a/examples/foundational/55zk-update-settings-google-vertex-llm.py +++ b/examples/foundational/55zk-update-settings-google-vertex-llm.py @@ -24,7 +24,7 @@ from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService -from pipecat.services.google.llm_vertex import GoogleVertexLLMService +from pipecat.services.google.vertex.llm import GoogleVertexLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams diff --git a/src/pipecat/services/google/google.py b/src/pipecat/services/google/google.py index 3d1814cf0..b2fc88b23 100644 --- a/src/pipecat/services/google/google.py +++ b/src/pipecat/services/google/google.py @@ -13,12 +13,12 @@ from pipecat.services import DeprecatedModuleProxy from .frames import * from .image import * from .llm import * -from .llm_openai import * -from .llm_vertex import * +from .openai import * from .rtvi import * from .stt import * from .tts import * +from .vertex import * sys.modules[__name__] = DeprecatedModuleProxy( - globals(), "google", "google.[frames,image,llm,llm_openai,llm_vertex,rtvi,stt,tts]" + globals(), "google", "google.[frames,image,llm,openai,vertex,rtvi,stt,tts]" ) diff --git a/tests/test_google_llm_openai.py b/tests/test_google_llm_openai.py index 2940bf7d7..5e6cee6f8 100644 --- a/tests/test_google_llm_openai.py +++ b/tests/test_google_llm_openai.py @@ -15,7 +15,7 @@ import pytest from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext try: - from pipecat.services.google.llm_openai import GoogleLLMOpenAIBetaService + from pipecat.services.google.openai.llm import GoogleLLMOpenAIBetaService google_available = True except Exception: From d086b9f138e460523e1bddbb2c6b1ccbcc57b61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 12:59:36 -0700 Subject: [PATCH 007/159] Move google/gemini_live/llm_vertex.py to google/gemini_live/vertex/llm.py --- src/pipecat/services/google/gemini_live/vertex/__init__.py | 5 +++++ .../google/gemini_live/{llm_vertex.py => vertex/llm.py} | 0 2 files changed, 5 insertions(+) create mode 100644 src/pipecat/services/google/gemini_live/vertex/__init__.py rename src/pipecat/services/google/gemini_live/{llm_vertex.py => vertex/llm.py} (100%) diff --git a/src/pipecat/services/google/gemini_live/vertex/__init__.py b/src/pipecat/services/google/gemini_live/vertex/__init__.py new file mode 100644 index 000000000..c4d243b97 --- /dev/null +++ b/src/pipecat/services/google/gemini_live/vertex/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# diff --git a/src/pipecat/services/google/gemini_live/llm_vertex.py b/src/pipecat/services/google/gemini_live/vertex/llm.py similarity index 100% rename from src/pipecat/services/google/gemini_live/llm_vertex.py rename to src/pipecat/services/google/gemini_live/vertex/llm.py From ea09586db633a4b8c815d2f3ac9458a937562c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 13:00:02 -0700 Subject: [PATCH 008/159] Add deprecation stub for google/gemini_live/llm_vertex.py --- .../services/google/gemini_live/__init__.py | 2 +- .../services/google/gemini_live/llm_vertex.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/pipecat/services/google/gemini_live/llm_vertex.py diff --git a/src/pipecat/services/google/gemini_live/__init__.py b/src/pipecat/services/google/gemini_live/__init__.py index f4bfbb5c8..4afeb99ce 100644 --- a/src/pipecat/services/google/gemini_live/__init__.py +++ b/src/pipecat/services/google/gemini_live/__init__.py @@ -1,6 +1,6 @@ from .file_api import GeminiFileAPI from .llm import GeminiLiveLLMService -from .llm_vertex import GeminiLiveVertexLLMService +from .vertex.llm import GeminiLiveVertexLLMService __all__ = [ "GeminiFileAPI", diff --git a/src/pipecat/services/google/gemini_live/llm_vertex.py b/src/pipecat/services/google/gemini_live/llm_vertex.py new file mode 100644 index 000000000..038d72e57 --- /dev/null +++ b/src/pipecat/services/google/gemini_live/llm_vertex.py @@ -0,0 +1,18 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Deprecated: use ``pipecat.services.google.gemini_live.vertex.llm`` instead.""" + +import warnings + +warnings.warn( + "Module `pipecat.services.google.gemini_live.llm_vertex` is deprecated, " + "use `pipecat.services.google.gemini_live.vertex.llm` instead.", + DeprecationWarning, + stacklevel=2, +) + +from pipecat.services.google.gemini_live.vertex.llm import * # noqa: E402, F401, F403 From 7be2c43e1d56e5400637b4a7526ffc95d367c290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 13:00:31 -0700 Subject: [PATCH 009/159] Update imports to use new google.gemini_live.vertex path --- .../foundational/26h-gemini-live-vertex-function-calling.py | 2 +- .../foundational/55zm-update-settings-gemini-live-vertex.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/foundational/26h-gemini-live-vertex-function-calling.py b/examples/foundational/26h-gemini-live-vertex-function-calling.py index 18e417f22..7dd894fd1 100644 --- a/examples/foundational/26h-gemini-live-vertex-function-calling.py +++ b/examples/foundational/26h-gemini-live-vertex-function-calling.py @@ -26,7 +26,7 @@ from pipecat.processors.aggregators.llm_response_universal import ( ) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.llm_vertex import GeminiLiveVertexLLMService +from pipecat.services.google.gemini_live.vertex.llm import GeminiLiveVertexLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams diff --git a/examples/foundational/55zm-update-settings-gemini-live-vertex.py b/examples/foundational/55zm-update-settings-gemini-live-vertex.py index f69b94557..8ff18e1eb 100644 --- a/examples/foundational/55zm-update-settings-gemini-live-vertex.py +++ b/examples/foundational/55zm-update-settings-gemini-live-vertex.py @@ -19,7 +19,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.google.gemini_live.llm_vertex import GeminiLiveVertexLLMService +from pipecat.services.google.gemini_live.vertex.llm import GeminiLiveVertexLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams From 23218aaed707903f620d0329aacc69f7b582c167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 13:04:16 -0700 Subject: [PATCH 010/159] Add changelog for #3980 --- changelog/3980.deprecated.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3980.deprecated.md diff --git a/changelog/3980.deprecated.md b/changelog/3980.deprecated.md new file mode 100644 index 000000000..f69964b62 --- /dev/null +++ b/changelog/3980.deprecated.md @@ -0,0 +1 @@ +- Deprecated `pipecat.services.google.llm_vertex`, `pipecat.services.google.llm_openai`, and `pipecat.services.google.gemini_live.llm_vertex` modules. Use `pipecat.services.google.vertex.llm`, `pipecat.services.google.openai.llm`, and `pipecat.services.google.gemini_live.vertex.llm` instead. The old import paths still work but will emit a `DeprecationWarning`. From 4c19337d890f6462a0565927caa1f432e2e5eb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 10 Mar 2026 15:29:52 -0700 Subject: [PATCH 011/159] Fix examples: Groq model, Google settings class, Nvidia system instruction --- examples/foundational/07l-interruptible-groq.py | 2 +- examples/foundational/07n-interruptible-google-http.py | 2 +- examples/foundational/14j-function-calling-nvidia.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/foundational/07l-interruptible-groq.py b/examples/foundational/07l-interruptible-groq.py index 208d39c2d..a4bfee8dc 100644 --- a/examples/foundational/07l-interruptible-groq.py +++ b/examples/foundational/07l-interruptible-groq.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GroqLLMService( api_key=os.getenv("GROQ_API_KEY"), settings=GroqLLMService.Settings( - model="meta-llama/llama-4-maverick-17b-128e-instruct", + model="llama-3.1-8b-instant", system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", ), ) diff --git a/examples/foundational/07n-interruptible-google-http.py b/examples/foundational/07n-interruptible-google-http.py index 91e822ec0..35c80a972 100644 --- a/examples/foundational/07n-interruptible-google-http.py +++ b/examples/foundational/07n-interruptible-google-http.py @@ -73,7 +73,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), - settings=GoogleLLMService.GoogleLLMSettings( + settings=GoogleLLMService.Settings( model="gemini-2.5-flash", # force a certain amount of thinking if you want it # thinking=GoogleLLMService.ThinkingConfig(thinking_budget=4096) diff --git a/examples/foundational/14j-function-calling-nvidia.py b/examples/foundational/14j-function-calling-nvidia.py index 39b75b7ac..7dd772c03 100644 --- a/examples/foundational/14j-function-calling-nvidia.py +++ b/examples/foundational/14j-function-calling-nvidia.py @@ -75,7 +75,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): model="nvidia/llama-3.3-nemotron-super-49b-v1.5", # Recommended when turning thinking off temperature=0.0, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="/no_think You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", ), ) # You can also register a function_name of None to get all functions From 610dc25fb16c3c87f93c817dd9819d11d8af11bc Mon Sep 17 00:00:00 2001 From: aconchillo <951761+aconchillo@users.noreply.github.com> Date: Wed, 11 Mar 2026 00:50:01 +0000 Subject: [PATCH 012/159] Update changelog for version 0.0.105 --- CHANGELOG.md | 265 +++++++++++++++++++++++++++++++++++ changelog/3804.added.md | 1 - changelog/3804.changed.md | 1 - changelog/3804.deprecated.md | 2 - changelog/3804.removed.md | 1 - changelog/3831.added.md | 1 - changelog/3831.changed.md | 1 - changelog/3848.changed.md | 1 - changelog/3848.fixed.md | 1 - changelog/3861.added.md | 1 - changelog/3861.changed.md | 1 - changelog/3889.changed.md | 3 - changelog/3889.fixed.md | 1 - changelog/3914.changed.md | 1 - changelog/3915.added.md | 1 - changelog/3916.added.md | 1 - changelog/3918.added.md | 15 -- changelog/3918.other.md | 1 - changelog/3927.added.md | 1 - changelog/3927.changed.md | 1 - changelog/3929.other.md | 1 - changelog/3930.added.md | 1 - changelog/3931.other.md | 1 - changelog/3932.added.md | 1 - changelog/3936.fixed.md | 1 - changelog/3937.fixed.md | 1 - changelog/3946.added.md | 1 - changelog/3947.added.md | 1 - changelog/3953.added.md | 1 - changelog/3956.fixed.md | 1 - changelog/3957.fixed.md | 1 - changelog/3958.changed.md | 1 - changelog/3958.fixed.md | 1 - changelog/3959.fixed.md | 1 - changelog/3960.fixed.md | 1 - changelog/3961.changed.md | 1 - changelog/3962.fixed.md | 1 - changelog/3967.fixed.md | 1 - changelog/3968.added.md | 1 - changelog/3970.changed.md | 1 - changelog/3973.changed.md | 1 - changelog/3974.changed.md | 1 - changelog/3976.fixed.md | 1 - changelog/3980.deprecated.md | 1 - 44 files changed, 265 insertions(+), 60 deletions(-) delete mode 100644 changelog/3804.added.md delete mode 100644 changelog/3804.changed.md delete mode 100644 changelog/3804.deprecated.md delete mode 100644 changelog/3804.removed.md delete mode 100644 changelog/3831.added.md delete mode 100644 changelog/3831.changed.md delete mode 100644 changelog/3848.changed.md delete mode 100644 changelog/3848.fixed.md delete mode 100644 changelog/3861.added.md delete mode 100644 changelog/3861.changed.md delete mode 100644 changelog/3889.changed.md delete mode 100644 changelog/3889.fixed.md delete mode 100644 changelog/3914.changed.md delete mode 100644 changelog/3915.added.md delete mode 100644 changelog/3916.added.md delete mode 100644 changelog/3918.added.md delete mode 100644 changelog/3918.other.md delete mode 100644 changelog/3927.added.md delete mode 100644 changelog/3927.changed.md delete mode 100644 changelog/3929.other.md delete mode 100644 changelog/3930.added.md delete mode 100644 changelog/3931.other.md delete mode 100644 changelog/3932.added.md delete mode 100644 changelog/3936.fixed.md delete mode 100644 changelog/3937.fixed.md delete mode 100644 changelog/3946.added.md delete mode 100644 changelog/3947.added.md delete mode 100644 changelog/3953.added.md delete mode 100644 changelog/3956.fixed.md delete mode 100644 changelog/3957.fixed.md delete mode 100644 changelog/3958.changed.md delete mode 100644 changelog/3958.fixed.md delete mode 100644 changelog/3959.fixed.md delete mode 100644 changelog/3960.fixed.md delete mode 100644 changelog/3961.changed.md delete mode 100644 changelog/3962.fixed.md delete mode 100644 changelog/3967.fixed.md delete mode 100644 changelog/3968.added.md delete mode 100644 changelog/3970.changed.md delete mode 100644 changelog/3973.changed.md delete mode 100644 changelog/3974.changed.md delete mode 100644 changelog/3976.fixed.md delete mode 100644 changelog/3980.deprecated.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 39dd6c194..71938516a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,271 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [0.0.105] - 2026-03-10 + +### Added + +- Added concurrent audio context support: `CartesiaTTSService` can now + synthesize the next sentence while the previous one is still playing, by + setting `pause_frame_processing=False` and routing each sentence through its + own audio context queue. + (PR [#3804](https://github.com/pipecat-ai/pipecat/pull/3804)) + +- Added custom video track support to Daily transport. Use + `video_out_destinations` in `DailyParams` to publish multiple video tracks + simultaneously, mirroring the existing `audio_out_destinations` feature. + (PR [#3831](https://github.com/pipecat-ai/pipecat/pull/3831)) + +- Added `ServiceSwitcherStrategyFailover` that automatically switches to the + next service when the active service reports a non-fatal error. Recovery + policies can be implemented via the `on_service_switched` event handler. + (PR [#3861](https://github.com/pipecat-ai/pipecat/pull/3861)) + +- Added optional `timeout_secs` parameter to `register_function()` and + `register_direct_function()` for per-tool function call timeout control, + overriding the global `function_call_timeout_secs` default. + (PR [#3915](https://github.com/pipecat-ai/pipecat/pull/3915)) + +- Added `cloud-audio-only` recording option to Daily transport's + `enable_recording` property. + (PR [#3916](https://github.com/pipecat-ai/pipecat/pull/3916)) + +- Wired up `system_instruction` in `BaseOpenAILLMService`, + `AnthropicLLMService`, and `AWSBedrockLLMService` so it works as a default + system prompt, matching the behavior of the Google services. This enables + sharing a single `LLMContext` across multiple LLM services, where each + service provides its own system instruction independently. + + ```python + llm = OpenAILLMService( + api_key=os.getenv("OPENAI_API_KEY"), + system_instruction="You are a helpful assistant.", + ) + + context = LLMContext() + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + context.add_message({"role": "user", "content": "Please introduce yourself."}) + await task.queue_frames([LLMRunFrame()]) + ``` + (PR [#3918](https://github.com/pipecat-ai/pipecat/pull/3918)) + +- Added `vad_threshold` parameter to `AssemblyAIConnectionParams` for + configuring voice activity detection sensitivity in U3 Pro. Aligning this + with external VAD thresholds (e.g., Silero VAD) prevents the "dead zone" + where AssemblyAI transcribes speech that VAD hasn't detected yet. + (PR [#3927](https://github.com/pipecat-ai/pipecat/pull/3927)) + +- Added `push_empty_transcripts` parameter to `BaseWhisperSTTService` and + `OpenAISTTService` to allow empty transcripts to be pushed downstream as + `TranscriptionFrame` instead of discarding them (the default behavior). This + is intended for situations where VAD fires even though the user did not + speak. In these cases, it is useful to know that nothing was transcribed so + that the agent can resume speaking, instead of waiting longer for a + transcription. + (PR [#3930](https://github.com/pipecat-ai/pipecat/pull/3930)) + +- LLM services (`BaseOpenAILLMService`, `AnthropicLLMService`, + `AWSBedrockLLMService`) now log a warning when both `system_instruction` and + a system message in the context are set. The constructor's + `system_instruction` takes precedence. + (PR [#3932](https://github.com/pipecat-ai/pipecat/pull/3932)) + +- Runtime settings updates (via `STTUpdateSettingsFrame`) now work for AWS + Transcribe, Azure, Cartesia, Deepgram, ElevenLabs Realtime, Gradium, and + Soniox STT services. Previously, changing settings at runtime only stored the + new values without reconnecting. + (PR [#3946](https://github.com/pipecat-ai/pipecat/pull/3946)) + +- Exposed `on_summary_applied` event on `LLMAssistantAggregator`, allowing + users to listen for context summarization events without accessing private + members. + (PR [#3947](https://github.com/pipecat-ai/pipecat/pull/3947)) + +- Deepgram Flux STT settings (`keyterm`, `eot_threshold`, + `eager_eot_threshold`, `eot_timeout_ms`) can now be updated mid-stream via + `STTUpdateSettingsFrame` without triggering a reconnect. The new values are + sent to Deepgram as a Configure WebSocket message on the existing connection. + (PR [#3953](https://github.com/pipecat-ai/pipecat/pull/3953)) + +- Added `system_instruction` parameter to `run_inference` across all LLM + services, allowing callers to override the system prompt for one-shot + inference calls. Used by `_generate_summary` to pass the summarization prompt + cleanly. + (PR [#3968](https://github.com/pipecat-ai/pipecat/pull/3968)) + +### Changed + +- Audio context management (previously in `AudioContextTTSService`) is now + built into `TTSService`. All WebSocket providers (`cartesia`, `elevenlabs`, + `asyncai`, `inworld`, `rime`, `gradium`, `resembleai`) now inherit from + `WebsocketTTSService` directly. Word-timestamp baseline is set automatically + on the first audio chunk of each context instead of requiring each provider + to call `start_word_timestamps()` in their receive loop. + (PR [#3804](https://github.com/pipecat-ai/pipecat/pull/3804)) + +- Daily transport now uses `CustomVideoSource`/`CustomVideoTrack` instead of + `VirtualCameraDevice` for the default camera output, mirroring how audio + already works with `CustomAudioSource`/`CustomAudioTrack`. + (PR [#3831](https://github.com/pipecat-ai/pipecat/pull/3831)) + +- ⚠️ Updated `DeepgramSTTService` to use `deepgram-sdk` v6. The `LiveOptions` + class was removed from the SDK and is now provided by pipecat directly; + import it from `pipecat.services.deepgram.stt` instead of `deepgram`. + (PR [#3848](https://github.com/pipecat-ai/pipecat/pull/3848)) + +- `ServiceSwitcherStrategy` base class now provides a `handle_error()` hook for + subclasses to implement error-based switching. `ServiceSwitcher` defaults to + `ServiceSwitcherStrategyManual` and `strategy_type` is now optional. + (PR [#3861](https://github.com/pipecat-ai/pipecat/pull/3861)) + +- Support for Voice Focus 2.0 models. + - Updated `aic-sdk` to `~=2.1.0` to support Voice Focus 2.0 models. + - Cleaned unused `ParameterFixedError` exception handling in `AICFilter` + parameter setup. + (PR [#3889](https://github.com/pipecat-ai/pipecat/pull/3889)) + +- `max_context_tokens` and `max_unsummarized_messages` in + `LLMAutoContextSummarizationConfig` (and deprecated + `LLMContextSummarizationConfig`) can now be set to `None` independently to + disable that summarization threshold. At least one must remain set. + (PR [#3914](https://github.com/pipecat-ai/pipecat/pull/3914)) + +- ⚠️ Removed `formatted_finals` and `word_finalization_max_wait_time` from + `AssemblyAIConnectionParams` as these were v2 API parameters not supported in + v3. Clarified that `format_turns` only applies to Universal-Streaming models; + U3 Pro has automatic formatting built-in. + (PR [#3927](https://github.com/pipecat-ai/pipecat/pull/3927)) + +- Changed `DeepgramTTSService` to send a Clear message on interruption instead + of disconnecting and reconnecting the WebSocket, allowing the connection to + persist throughout the session. + (PR [#3958](https://github.com/pipecat-ai/pipecat/pull/3958)) + +- Re-added `enhancement_level` support to `AICFilter` with runtime + `FilterEnableFrame` control, applying `ProcessorParameter.Bypass` and + `ProcessorParameter.EnhancementLevel` together. + (PR [#3961](https://github.com/pipecat-ai/pipecat/pull/3961)) + +- Updated `daily-python` dependency from `~=0.23.0` to `~=0.24.0`. + (PR [#3970](https://github.com/pipecat-ai/pipecat/pull/3970)) + +- Updated `FishAudioTTSService` default model from `s1` to `s2-pro`, matching + Fish Audio's latest recommended model for improved quality and speed. + (PR [#3973](https://github.com/pipecat-ai/pipecat/pull/3973)) + +- `AzureSTTService` `region` parameter is now optional when `private_endpoint` + is provided. A `ValueError` is raised if neither is given, and a warning is + logged if both are provided (`private_endpoint` takes priority). + (PR [#3974](https://github.com/pipecat-ai/pipecat/pull/3974)) + +### Deprecated + +- Deprecated `AudioContextTTSService` and `AudioContextWordTTSService`. + Subclass `WebsocketTTSService` directly instead; audio context management is + now part of the base `TTSService`. + - Deprecated `WordTTSService`, `WebsocketWordTTSService`, and + `InterruptibleWordTTSService`. Word timestamp logic is now always active in + `TTSService` and no longer needs to be opted into via a subclass. + (PR [#3804](https://github.com/pipecat-ai/pipecat/pull/3804)) + +- Deprecated `pipecat.services.google.llm_vertex`, + `pipecat.services.google.llm_openai`, and + `pipecat.services.google.gemini_live.llm_vertex` modules. Use + `pipecat.services.google.vertex.llm`, `pipecat.services.google.openai.llm`, + and `pipecat.services.google.gemini_live.vertex.llm` instead. The old import + paths still work but will emit a `DeprecationWarning`. + (PR [#3980](https://github.com/pipecat-ai/pipecat/pull/3980)) + +### Removed + +- ⚠️ Removed `supports_word_timestamps` parameter from `TTSService.__init__()`. + Word timestamp logic is now always active. Remove this argument from any + custom subclass `super().__init__()` calls. + (PR [#3804](https://github.com/pipecat-ai/pipecat/pull/3804)) + +### Fixed + +- Fixed `DeepgramSTTService` keepalive ping timeout disconnections. The + deepgram-sdk v6 removed automatic keepalive; pipecat now sends explicit + `KeepAlive` messages every 5 seconds, within the recommended 3–5 second + interval before Deepgram's 10-second inactivity timeout. + (PR [#3848](https://github.com/pipecat-ai/pipecat/pull/3848)) + +- Fixed `BufferError: Existing exports of data: object cannot be re-sized` in + `AICFilter` caused by holding a `memoryview` on the mutable audio buffer + across async yield points. + (PR [#3889](https://github.com/pipecat-ai/pipecat/pull/3889)) + +- Fixed TTS context not being appended to the assistant message history when + using `TTSSpeakFrame` with `append_to_context=True` with some TTS providers. + (PR [#3936](https://github.com/pipecat-ai/pipecat/pull/3936)) + +- Fixed context summarization leaving orphaned tool responses in the kept + context when tool calls were moved to the summarized portion. + (PR [#3937](https://github.com/pipecat-ai/pipecat/pull/3937)) + +- Fixed turn completion state not resetting at end of LLM responses. + `LLMFullResponseEndFrame` is pushed (not received) by the LLM service, so the + mixin now handles it in `push_frame` instead of `process_frame`. + (PR [#3956](https://github.com/pipecat-ai/pipecat/pull/3956)) + +- Fixed turn completion instructions being injected as a context system message + instead of using `system_instruction`. This caused warning spam when + `system_instruction` was also set and didn't persist across full context + updates. + (PR [#3957](https://github.com/pipecat-ai/pipecat/pull/3957)) + +- Fixed `TTSService` audio context queue getting blocked when + `append_to_audio_context()` was called with a `None` context ID, which + prevented subsequent audio from being delivered. + (PR [#3958](https://github.com/pipecat-ai/pipecat/pull/3958)) + +- Fixed `on_call_state_updated` event handler in LiveKit transport receiving + incorrect number of arguments due to redundant `self` passed to + `_call_event_handler`. + (PR [#3959](https://github.com/pipecat-ai/pipecat/pull/3959)) + +- Fixed OpenAI Realtime, OpenAI Realtime Beta, and Grok realtime services + treating `conversation_already_has_active_response` as a fatal error. These + services now log it as a non-fatal debug event when a response is already in + progress. + (PR [#3960](https://github.com/pipecat-ai/pipecat/pull/3960)) + +- Fixed `SmallWebRTCConnection` silently discarding messages sent before the + data channel is open by queuing them and flushing once the channel is ready. + A bounded queue (`MAX_MESSAGE_QUEUE_SIZE = 50`) prevents unbounded memory + growth, and a 10-second timeout after connection clears the queue and falls + back to discard mode if the data channel never opens. + (PR [#3962](https://github.com/pipecat-ai/pipecat/pull/3962)) + +- Fixed `AzureSTTService` failing to initialize when `private_endpoint` is + provided. The Azure Speech SDK's `SpeechConfig` does not accept both `region` + and `endpoint` simultaneously, so they are now passed conditionally. + (PR [#3967](https://github.com/pipecat-ai/pipecat/pull/3967)) + +- Fixed `GoogleLLMService` ignoring the `system_instruction` set via + constructor or `GoogleLLMSettings` when a system message was also present in + the context. The settings value now correctly takes priority, and a warning + is logged when both are set. + (PR [#3976](https://github.com/pipecat-ai/pipecat/pull/3976)) + +### Other + +- Updated foundational examples to use `system_instruction` on LLM services + instead of adding system messages to `LLMContext`. + (PR [#3918](https://github.com/pipecat-ai/pipecat/pull/3918)) + +- Updated AssemblyAI turn detection example to use `keyterms_prompt` list + format instead of `prompt` string for improved clarity. + (PR [#3929](https://github.com/pipecat-ai/pipecat/pull/3929)) + +- Updated foundational examples and eval scripts to use `"user"` role instead + of `"system"` when adding messages to `LLMContext`, since system prompts + should be set via `system_instruction` on the LLM service. + (PR [#3931](https://github.com/pipecat-ai/pipecat/pull/3931)) + ## [0.0.104] - 2026-03-02 ### Added diff --git a/changelog/3804.added.md b/changelog/3804.added.md deleted file mode 100644 index 0ad7676c9..000000000 --- a/changelog/3804.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added concurrent audio context support: `CartesiaTTSService` can now synthesize the next sentence while the previous one is still playing, by setting `pause_frame_processing=False` and routing each sentence through its own audio context queue. diff --git a/changelog/3804.changed.md b/changelog/3804.changed.md deleted file mode 100644 index 9caae491f..000000000 --- a/changelog/3804.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Audio context management (previously in `AudioContextTTSService`) is now built into `TTSService`. All WebSocket providers (`cartesia`, `elevenlabs`, `asyncai`, `inworld`, `rime`, `gradium`, `resembleai`) now inherit from `WebsocketTTSService` directly. Word-timestamp baseline is set automatically on the first audio chunk of each context instead of requiring each provider to call `start_word_timestamps()` in their receive loop. diff --git a/changelog/3804.deprecated.md b/changelog/3804.deprecated.md deleted file mode 100644 index 0babfe7d4..000000000 --- a/changelog/3804.deprecated.md +++ /dev/null @@ -1,2 +0,0 @@ -- Deprecated `AudioContextTTSService` and `AudioContextWordTTSService`. Subclass `WebsocketTTSService` directly instead; audio context management is now part of the base `TTSService`. -- Deprecated `WordTTSService`, `WebsocketWordTTSService`, and `InterruptibleWordTTSService`. Word timestamp logic is now always active in `TTSService` and no longer needs to be opted into via a subclass. diff --git a/changelog/3804.removed.md b/changelog/3804.removed.md deleted file mode 100644 index 1813b5999..000000000 --- a/changelog/3804.removed.md +++ /dev/null @@ -1 +0,0 @@ -- ⚠️ Removed `supports_word_timestamps` parameter from `TTSService.__init__()`. Word timestamp logic is now always active. Remove this argument from any custom subclass `super().__init__()` calls. diff --git a/changelog/3831.added.md b/changelog/3831.added.md deleted file mode 100644 index 7c3f7f4df..000000000 --- a/changelog/3831.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added custom video track support to Daily transport. Use `video_out_destinations` in `DailyParams` to publish multiple video tracks simultaneously, mirroring the existing `audio_out_destinations` feature. diff --git a/changelog/3831.changed.md b/changelog/3831.changed.md deleted file mode 100644 index bb4d7df83..000000000 --- a/changelog/3831.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Daily transport now uses `CustomVideoSource`/`CustomVideoTrack` instead of `VirtualCameraDevice` for the default camera output, mirroring how audio already works with `CustomAudioSource`/`CustomAudioTrack`. diff --git a/changelog/3848.changed.md b/changelog/3848.changed.md deleted file mode 100644 index 1590e284a..000000000 --- a/changelog/3848.changed.md +++ /dev/null @@ -1 +0,0 @@ -- ⚠️ Updated `DeepgramSTTService` to use `deepgram-sdk` v6. The `LiveOptions` class was removed from the SDK and is now provided by pipecat directly; import it from `pipecat.services.deepgram.stt` instead of `deepgram`. diff --git a/changelog/3848.fixed.md b/changelog/3848.fixed.md deleted file mode 100644 index a6651b763..000000000 --- a/changelog/3848.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `DeepgramSTTService` keepalive ping timeout disconnections. The deepgram-sdk v6 removed automatic keepalive; pipecat now sends explicit `KeepAlive` messages every 5 seconds, within the recommended 3–5 second interval before Deepgram's 10-second inactivity timeout. diff --git a/changelog/3861.added.md b/changelog/3861.added.md deleted file mode 100644 index c0a6f6aee..000000000 --- a/changelog/3861.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `ServiceSwitcherStrategyFailover` that automatically switches to the next service when the active service reports a non-fatal error. Recovery policies can be implemented via the `on_service_switched` event handler. diff --git a/changelog/3861.changed.md b/changelog/3861.changed.md deleted file mode 100644 index 41c577dcd..000000000 --- a/changelog/3861.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `ServiceSwitcherStrategy` base class now provides a `handle_error()` hook for subclasses to implement error-based switching. `ServiceSwitcher` defaults to `ServiceSwitcherStrategyManual` and `strategy_type` is now optional. diff --git a/changelog/3889.changed.md b/changelog/3889.changed.md deleted file mode 100644 index c04aa0080..000000000 --- a/changelog/3889.changed.md +++ /dev/null @@ -1,3 +0,0 @@ -- Support for Voice Focus 2.0 models. - - Updated `aic-sdk` to `~=2.1.0` to support Voice Focus 2.0 models. - - Cleaned unused `ParameterFixedError` exception handling in `AICFilter` parameter setup. diff --git a/changelog/3889.fixed.md b/changelog/3889.fixed.md deleted file mode 100644 index babe3eb35..000000000 --- a/changelog/3889.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `BufferError: Existing exports of data: object cannot be re-sized` in `AICFilter` caused by holding a `memoryview` on the mutable audio buffer across async yield points. diff --git a/changelog/3914.changed.md b/changelog/3914.changed.md deleted file mode 100644 index 22d4ff94e..000000000 --- a/changelog/3914.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `max_context_tokens` and `max_unsummarized_messages` in `LLMAutoContextSummarizationConfig` (and deprecated `LLMContextSummarizationConfig`) can now be set to `None` independently to disable that summarization threshold. At least one must remain set. diff --git a/changelog/3915.added.md b/changelog/3915.added.md deleted file mode 100644 index 66d4fb383..000000000 --- a/changelog/3915.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added optional `timeout_secs` parameter to `register_function()` and `register_direct_function()` for per-tool function call timeout control, overriding the global `function_call_timeout_secs` default. diff --git a/changelog/3916.added.md b/changelog/3916.added.md deleted file mode 100644 index 05c3f124f..000000000 --- a/changelog/3916.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `cloud-audio-only` recording option to Daily transport's `enable_recording` property. diff --git a/changelog/3918.added.md b/changelog/3918.added.md deleted file mode 100644 index 6b4d4446f..000000000 --- a/changelog/3918.added.md +++ /dev/null @@ -1,15 +0,0 @@ -- Wired up `system_instruction` in `BaseOpenAILLMService`, `AnthropicLLMService`, and `AWSBedrockLLMService` so it works as a default system prompt, matching the behavior of the Google services. This enables sharing a single `LLMContext` across multiple LLM services, where each service provides its own system instruction independently. - - ```python - llm = OpenAILLMService( - api_key=os.getenv("OPENAI_API_KEY"), - system_instruction="You are a helpful assistant.", - ) - - context = LLMContext() - - @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - context.add_message({"role": "user", "content": "Please introduce yourself."}) - await task.queue_frames([LLMRunFrame()]) - ``` diff --git a/changelog/3918.other.md b/changelog/3918.other.md deleted file mode 100644 index 6caa1ba05..000000000 --- a/changelog/3918.other.md +++ /dev/null @@ -1 +0,0 @@ -- Updated foundational examples to use `system_instruction` on LLM services instead of adding system messages to `LLMContext`. diff --git a/changelog/3927.added.md b/changelog/3927.added.md deleted file mode 100644 index 378815fc3..000000000 --- a/changelog/3927.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `vad_threshold` parameter to `AssemblyAIConnectionParams` for configuring voice activity detection sensitivity in U3 Pro. Aligning this with external VAD thresholds (e.g., Silero VAD) prevents the "dead zone" where AssemblyAI transcribes speech that VAD hasn't detected yet. diff --git a/changelog/3927.changed.md b/changelog/3927.changed.md deleted file mode 100644 index b397e86a2..000000000 --- a/changelog/3927.changed.md +++ /dev/null @@ -1 +0,0 @@ -- ⚠️ Removed `formatted_finals` and `word_finalization_max_wait_time` from `AssemblyAIConnectionParams` as these were v2 API parameters not supported in v3. Clarified that `format_turns` only applies to Universal-Streaming models; U3 Pro has automatic formatting built-in. diff --git a/changelog/3929.other.md b/changelog/3929.other.md deleted file mode 100644 index c1d9f8dda..000000000 --- a/changelog/3929.other.md +++ /dev/null @@ -1 +0,0 @@ -- Updated AssemblyAI turn detection example to use `keyterms_prompt` list format instead of `prompt` string for improved clarity. diff --git a/changelog/3930.added.md b/changelog/3930.added.md deleted file mode 100644 index dd799f9ec..000000000 --- a/changelog/3930.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `push_empty_transcripts` parameter to `BaseWhisperSTTService` and `OpenAISTTService` to allow empty transcripts to be pushed downstream as `TranscriptionFrame` instead of discarding them (the default behavior). This is intended for situations where VAD fires even though the user did not speak. In these cases, it is useful to know that nothing was transcribed so that the agent can resume speaking, instead of waiting longer for a transcription. \ No newline at end of file diff --git a/changelog/3931.other.md b/changelog/3931.other.md deleted file mode 100644 index a1ebef414..000000000 --- a/changelog/3931.other.md +++ /dev/null @@ -1 +0,0 @@ -- Updated foundational examples and eval scripts to use `"user"` role instead of `"system"` when adding messages to `LLMContext`, since system prompts should be set via `system_instruction` on the LLM service. diff --git a/changelog/3932.added.md b/changelog/3932.added.md deleted file mode 100644 index e97690d44..000000000 --- a/changelog/3932.added.md +++ /dev/null @@ -1 +0,0 @@ -- LLM services (`BaseOpenAILLMService`, `AnthropicLLMService`, `AWSBedrockLLMService`) now log a warning when both `system_instruction` and a system message in the context are set. The constructor's `system_instruction` takes precedence. diff --git a/changelog/3936.fixed.md b/changelog/3936.fixed.md deleted file mode 100644 index 27e48815c..000000000 --- a/changelog/3936.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed TTS context not being appended to the assistant message history when using `TTSSpeakFrame` with `append_to_context=True` with some TTS providers. diff --git a/changelog/3937.fixed.md b/changelog/3937.fixed.md deleted file mode 100644 index eda9d27f6..000000000 --- a/changelog/3937.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed context summarization leaving orphaned tool responses in the kept context when tool calls were moved to the summarized portion. diff --git a/changelog/3946.added.md b/changelog/3946.added.md deleted file mode 100644 index 6aabfefb2..000000000 --- a/changelog/3946.added.md +++ /dev/null @@ -1 +0,0 @@ -- Runtime settings updates (via `STTUpdateSettingsFrame`) now work for AWS Transcribe, Azure, Cartesia, Deepgram, ElevenLabs Realtime, Gradium, and Soniox STT services. Previously, changing settings at runtime only stored the new values without reconnecting. diff --git a/changelog/3947.added.md b/changelog/3947.added.md deleted file mode 100644 index 175cb87bd..000000000 --- a/changelog/3947.added.md +++ /dev/null @@ -1 +0,0 @@ -- Exposed `on_summary_applied` event on `LLMAssistantAggregator`, allowing users to listen for context summarization events without accessing private members. diff --git a/changelog/3953.added.md b/changelog/3953.added.md deleted file mode 100644 index f30c31733..000000000 --- a/changelog/3953.added.md +++ /dev/null @@ -1 +0,0 @@ -- Deepgram Flux STT settings (`keyterm`, `eot_threshold`, `eager_eot_threshold`, `eot_timeout_ms`) can now be updated mid-stream via `STTUpdateSettingsFrame` without triggering a reconnect. The new values are sent to Deepgram as a Configure WebSocket message on the existing connection. diff --git a/changelog/3956.fixed.md b/changelog/3956.fixed.md deleted file mode 100644 index c8af8db91..000000000 --- a/changelog/3956.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed turn completion state not resetting at end of LLM responses. `LLMFullResponseEndFrame` is pushed (not received) by the LLM service, so the mixin now handles it in `push_frame` instead of `process_frame`. diff --git a/changelog/3957.fixed.md b/changelog/3957.fixed.md deleted file mode 100644 index a501aecb1..000000000 --- a/changelog/3957.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed turn completion instructions being injected as a context system message instead of using `system_instruction`. This caused warning spam when `system_instruction` was also set and didn't persist across full context updates. diff --git a/changelog/3958.changed.md b/changelog/3958.changed.md deleted file mode 100644 index dbeabd0f2..000000000 --- a/changelog/3958.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Changed `DeepgramTTSService` to send a Clear message on interruption instead of disconnecting and reconnecting the WebSocket, allowing the connection to persist throughout the session. diff --git a/changelog/3958.fixed.md b/changelog/3958.fixed.md deleted file mode 100644 index 8bc21dffe..000000000 --- a/changelog/3958.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `TTSService` audio context queue getting blocked when `append_to_audio_context()` was called with a `None` context ID, which prevented subsequent audio from being delivered. diff --git a/changelog/3959.fixed.md b/changelog/3959.fixed.md deleted file mode 100644 index a10521d92..000000000 --- a/changelog/3959.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `on_call_state_updated` event handler in LiveKit transport receiving incorrect number of arguments due to redundant `self` passed to `_call_event_handler`. diff --git a/changelog/3960.fixed.md b/changelog/3960.fixed.md deleted file mode 100644 index 404569234..000000000 --- a/changelog/3960.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed OpenAI Realtime, OpenAI Realtime Beta, and Grok realtime services treating `conversation_already_has_active_response` as a fatal error. These services now log it as a non-fatal debug event when a response is already in progress. diff --git a/changelog/3961.changed.md b/changelog/3961.changed.md deleted file mode 100644 index 19c2780e3..000000000 --- a/changelog/3961.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Re-added `enhancement_level` support to `AICFilter` with runtime `FilterEnableFrame` control, applying `ProcessorParameter.Bypass` and `ProcessorParameter.EnhancementLevel` together. diff --git a/changelog/3962.fixed.md b/changelog/3962.fixed.md deleted file mode 100644 index 1d326cd05..000000000 --- a/changelog/3962.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `SmallWebRTCConnection` silently discarding messages sent before the data channel is open by queuing them and flushing once the channel is ready. A bounded queue (`MAX_MESSAGE_QUEUE_SIZE = 50`) prevents unbounded memory growth, and a 10-second timeout after connection clears the queue and falls back to discard mode if the data channel never opens. diff --git a/changelog/3967.fixed.md b/changelog/3967.fixed.md deleted file mode 100644 index dff880c2c..000000000 --- a/changelog/3967.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `AzureSTTService` failing to initialize when `private_endpoint` is provided. The Azure Speech SDK's `SpeechConfig` does not accept both `region` and `endpoint` simultaneously, so they are now passed conditionally. diff --git a/changelog/3968.added.md b/changelog/3968.added.md deleted file mode 100644 index cd9be5996..000000000 --- a/changelog/3968.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `system_instruction` parameter to `run_inference` across all LLM services, allowing callers to override the system prompt for one-shot inference calls. Used by `_generate_summary` to pass the summarization prompt cleanly. diff --git a/changelog/3970.changed.md b/changelog/3970.changed.md deleted file mode 100644 index f420a7e73..000000000 --- a/changelog/3970.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Updated `daily-python` dependency from `~=0.23.0` to `~=0.24.0`. diff --git a/changelog/3973.changed.md b/changelog/3973.changed.md deleted file mode 100644 index d6834ce8e..000000000 --- a/changelog/3973.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Updated `FishAudioTTSService` default model from `s1` to `s2-pro`, matching Fish Audio's latest recommended model for improved quality and speed. diff --git a/changelog/3974.changed.md b/changelog/3974.changed.md deleted file mode 100644 index 7c4c360d5..000000000 --- a/changelog/3974.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `AzureSTTService` `region` parameter is now optional when `private_endpoint` is provided. A `ValueError` is raised if neither is given, and a warning is logged if both are provided (`private_endpoint` takes priority). diff --git a/changelog/3976.fixed.md b/changelog/3976.fixed.md deleted file mode 100644 index 3153cdcbe..000000000 --- a/changelog/3976.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `GoogleLLMService` ignoring the `system_instruction` set via constructor or `GoogleLLMSettings` when a system message was also present in the context. The settings value now correctly takes priority, and a warning is logged when both are set. diff --git a/changelog/3980.deprecated.md b/changelog/3980.deprecated.md deleted file mode 100644 index f69964b62..000000000 --- a/changelog/3980.deprecated.md +++ /dev/null @@ -1 +0,0 @@ -- Deprecated `pipecat.services.google.llm_vertex`, `pipecat.services.google.llm_openai`, and `pipecat.services.google.gemini_live.llm_vertex` modules. Use `pipecat.services.google.vertex.llm`, `pipecat.services.google.openai.llm`, and `pipecat.services.google.gemini_live.vertex.llm` instead. The old import paths still work but will emit a `DeprecationWarning`. From 087abc9bb901687403c2f4f0bbdd9bfedca648fc Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 09:56:32 -0400 Subject: [PATCH 013/159] Fix outdated CambTTSService docstring example --- src/pipecat/services/camb/tts.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/camb/tts.py b/src/pipecat/services/camb/tts.py index 9918b2320..f458fa2e4 100644 --- a/src/pipecat/services/camb/tts.py +++ b/src/pipecat/services/camb/tts.py @@ -158,13 +158,20 @@ class CambTTSService(TTSService): Example:: # Basic usage with mars-flash (fast) - tts = CambTTSService(api_key="your-api-key", model="mars-flash") + tts = CambTTSService( + api_key="your-api-key", + settings=CambTTSService.Settings( + model="mars-flash" + ) + ) # High quality with mars-pro tts = CambTTSService( api_key="your-api-key", - voice_id=12345, - model="mars-pro", + settings=CambTTSService.Settings( + voice=12345, + model="mars-pro", + ) ) """ From 916936d3ee33c0df5d92fe2dda0d96f8516b27ab Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 10:07:07 -0400 Subject: [PATCH 014/159] Fix outdated Sarvam TTS docstring examples --- src/pipecat/services/sarvam/tts.py | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index 639a860ac..0d6a872eb 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -326,28 +326,28 @@ class SarvamHttpTTSService(TTSService): # Using bulbul:v2 (default) tts = SarvamHttpTTSService( api_key="your-api-key", - voice_id="anushka", - model="bulbul:v2", aiohttp_session=session, - params=SarvamHttpTTSService.InputParams( + settings=SarvamHttpTTSService.Settings( + voice="anushka", + model="bulbul:v2", language=Language.HI, pitch=0.1, pace=1.2, - loudness=1.5 - ) + loudness=1.5, + ), ) # Using bulbul:v3-beta with temperature control tts_v3 = SarvamHttpTTSService( api_key="your-api-key", - voice_id="aditya", # Use v3 speaker - model="bulbul:v3-beta", aiohttp_session=session, - params=SarvamHttpTTSService.InputParams( + settings=SarvamHttpTTSService.Settings( + voice="aditya", # Use v3 speaker + model="bulbul:v3-beta", language=Language.HI, pace=1.2, # Range: 0.5-2.0 for v3 - temperature=0.8 - ) + temperature=0.8, + ), ) """ @@ -693,26 +693,26 @@ class SarvamTTSService(InterruptibleTTSService): # Using bulbul:v2 (default) tts = SarvamTTSService( api_key="your-api-key", - voice_id="anushka", - model="bulbul:v2", - params=SarvamTTSService.InputParams( + settings=SarvamTTSService.Settings( + voice="anushka", + model="bulbul:v2", language=Language.HI, pitch=0.1, pace=1.2, - loudness=1.5 - ) + loudness=1.5, + ), ) # Using bulbul:v3-beta with temperature control tts_v3 = SarvamTTSService( api_key="your-api-key", - voice_id="aditya", # Use v3 speaker - model="bulbul:v3-beta", - params=SarvamTTSService.InputParams( + settings=SarvamTTSService.Settings( + voice="aditya", # Use v3 speaker + model="bulbul:v3-beta", language=Language.HI, pace=1.2, # Range: 0.5-2.0 for v3 - temperature=0.8 - ) + temperature=0.8, + ), ) See https://docs.sarvam.ai/api-reference-docs/text-to-speech/stream for API details. From 264ce681f7f017c4dc735ba0a51cd34d64f15920 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 10:10:15 -0400 Subject: [PATCH 015/159] Fix outdated DeepgramSageMakerSTTService docstring example --- src/pipecat/services/deepgram/sagemaker/stt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/deepgram/sagemaker/stt.py b/src/pipecat/services/deepgram/sagemaker/stt.py index 812a2701e..f6093a630 100644 --- a/src/pipecat/services/deepgram/sagemaker/stt.py +++ b/src/pipecat/services/deepgram/sagemaker/stt.py @@ -69,7 +69,7 @@ class DeepgramSageMakerSTTService(STTService): stt = DeepgramSageMakerSTTService( endpoint_name="my-deepgram-endpoint", region="us-east-2", - settings=DeepgramSageMakerSTTSettings( + settings=DeepgramSageMakerSTTService.Settings( model="nova-3", language="en", interim_results=True, From 0ebcb555829c73103f15810b90f133accdc60c43 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 10:11:26 -0400 Subject: [PATCH 016/159] Fix outdated DeepgramSageMakerTTSService docstring example --- src/pipecat/services/deepgram/sagemaker/tts.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/deepgram/sagemaker/tts.py b/src/pipecat/services/deepgram/sagemaker/tts.py index a40f56713..d315b9e01 100644 --- a/src/pipecat/services/deepgram/sagemaker/tts.py +++ b/src/pipecat/services/deepgram/sagemaker/tts.py @@ -63,7 +63,9 @@ class DeepgramSageMakerTTSService(TTSService): tts = DeepgramSageMakerTTSService( endpoint_name="my-deepgram-tts-endpoint", region="us-east-2", - voice="aura-2-helena-en", + settings=DeepgramSageMakerTTSService.Settings( + voice="aura-2-helena-en", + ) ) """ From df82df8e39ac5f3a4353734ce7ff0abf8646f7ba Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 10:14:18 -0400 Subject: [PATCH 017/159] Fix outdated Google + Gemini TTS service docstring examples --- src/pipecat/services/google/tts.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pipecat/services/google/tts.py b/src/pipecat/services/google/tts.py index a9394028b..9d92c91ea 100644 --- a/src/pipecat/services/google/tts.py +++ b/src/pipecat/services/google/tts.py @@ -1014,8 +1014,8 @@ class GoogleTTSService(GoogleBaseTTSService): tts = GoogleTTSService( credentials_path="/path/to/service-account.json", - voice_id="en-US-Chirp3-HD-Charon", - params=GoogleTTSService.InputParams( + settings=GoogleTTSService.Settings( + voice="en-US-Chirp3-HD-Charon", language=Language.EN_US, ) ) @@ -1191,9 +1191,9 @@ class GeminiTTSService(GoogleBaseTTSService): tts = GeminiTTSService( credentials_path="/path/to/service-account.json", - model="gemini-2.5-flash-tts", - voice_id="Kore", - params=GeminiTTSService.InputParams( + settings=GeminiTTSService.Settings( + model="gemini-2.5-flash-tts", + voice="Kore", language=Language.EN_US, prompt="Say this in a friendly and helpful tone" ) From 42262d10bbe0908cfa1cf52d8237c84e28676757 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 10:17:24 -0400 Subject: [PATCH 018/159] Move OpenAIRealtimeSTTService's noise_reduction into its Settings object, as it might be useful to update it at runtime, and fix outdated OpenAIRealtimeSTTService docstring example --- src/pipecat/services/openai/stt.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/pipecat/services/openai/stt.py b/src/pipecat/services/openai/stt.py index e2f9ec0ee..473b23637 100644 --- a/src/pipecat/services/openai/stt.py +++ b/src/pipecat/services/openai/stt.py @@ -187,9 +187,15 @@ class OpenAIRealtimeSTTSettings(STTSettings): Parameters: prompt: Optional prompt text to guide transcription style. + noise_reduction: Noise reduction mode. ``"near_field"`` for close + microphones, ``"far_field"`` for distant microphones, or ``None`` + to disable. """ prompt: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + noise_reduction: Literal["near_field", "far_field"] | None | _NotGiven = field( + default_factory=lambda: NOT_GIVEN + ) class OpenAIRealtimeSTTService(WebsocketSTTService): @@ -220,8 +226,10 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): stt = OpenAIRealtimeSTTService( api_key="sk-...", - model="gpt-4o-transcribe", - noise_reduction="near_field", + settings=OpenAIRealtimeSTTService.Settings( + model="gpt-4o-transcribe", + noise_reduction="near_field", + ), ) """ @@ -274,6 +282,9 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): noise_reduction: Noise reduction mode. ``"near_field"`` for close microphones, ``"far_field"`` for distant microphones, or ``None`` to disable. + + .. deprecated:: 0.0.106 + Use ``settings=OpenAIRealtimeSTTSettings(noise_reduction=...)`` instead. should_interrupt: Whether to interrupt bot output when speech is detected by server-side VAD. Only applies when turn detection is enabled. Defaults to True. @@ -295,6 +306,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): model="gpt-4o-transcribe", language=Language.EN, prompt=None, + noise_reduction=None, ) # --- 2. Deprecated direct-arg overrides --- @@ -307,6 +319,9 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): if prompt is not None: _warn_deprecated_param("prompt", OpenAIRealtimeSTTSettings, "prompt") default_settings.prompt = prompt + if noise_reduction is not None: + _warn_deprecated_param("noise_reduction", OpenAIRealtimeSTTSettings, "noise_reduction") + default_settings.noise_reduction = noise_reduction # --- 3. (no params object for this service) --- @@ -324,7 +339,6 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): self._base_url = base_url self._turn_detection = turn_detection - self._noise_reduction = noise_reduction self._should_interrupt = should_interrupt self._receive_task = None @@ -544,9 +558,9 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): input_audio["turn_detection"] = self._turn_detection # Noise reduction - if self._noise_reduction: + if self._settings.noise_reduction: input_audio["noise_reduction"] = { - "type": self._noise_reduction, + "type": self._settings.noise_reduction, } await self._ws_send( From 3cbd27d20235dc90b6b60d6b37a24f70cb1ac4fe Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 10:34:00 -0400 Subject: [PATCH 019/159] Add changelog for PR #3991 --- changelog/3991.changed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3991.changed.md diff --git a/changelog/3991.changed.md b/changelog/3991.changed.md new file mode 100644 index 000000000..5767dc8aa --- /dev/null +++ b/changelog/3991.changed.md @@ -0,0 +1 @@ +- `OpenAIRealtimeSTTService`'s `noise_reduction` parameter is now part of `OpenAIRealtimeSTTSettings`, making it runtime-updatable via `STTUpdateSettingsFrame`. The direct `noise_reduction` init argument is deprecated as of 0.0.106. From 6b168d6bbbbc67444e9a23e9aeb1c992cc4eddc5 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 12:15:00 -0400 Subject: [PATCH 020/159] Prefer Service.Settings over raw settings class names across all services Replace direct references to settings class names (e.g. `FooSettings`) with the nested `Settings` alias form throughout all 87 service files: - Type annotations: `Settings` - Runtime code: `self.Settings` - Docstrings: `ServiceClass.Settings` - Cross-file inheritance: `ParentService.Settings` This makes the `Settings` alias the canonical way to reference a service's settings, keeping only the class definition and alias assignment as the remaining hits for each raw settings class name. --- src/pipecat/services/anthropic/llm.py | 16 ++--- src/pipecat/services/assemblyai/models.py | 2 +- src/pipecat/services/assemblyai/stt.py | 18 +++--- src/pipecat/services/asyncai/tts.py | 40 ++++++------- src/pipecat/services/aws/llm.py | 20 +++---- src/pipecat/services/aws/nova_sonic/llm.py | 28 ++++----- src/pipecat/services/aws/stt.py | 10 ++-- src/pipecat/services/aws/tts.py | 16 ++--- src/pipecat/services/azure/image.py | 14 ++--- src/pipecat/services/azure/llm.py | 12 ++-- src/pipecat/services/azure/realtime/llm.py | 6 +- src/pipecat/services/azure/stt.py | 10 ++-- src/pipecat/services/azure/tts.py | 29 ++++----- src/pipecat/services/camb/tts.py | 20 +++---- src/pipecat/services/cartesia/stt.py | 14 ++--- src/pipecat/services/cartesia/tts.py | 36 +++++------ src/pipecat/services/cerebras/llm.py | 14 ++--- src/pipecat/services/deepgram/flux/stt.py | 20 +++---- .../services/deepgram/sagemaker/stt.py | 20 +++---- .../services/deepgram/sagemaker/tts.py | 10 ++-- src/pipecat/services/deepgram/stt.py | 16 ++--- src/pipecat/services/deepgram/tts.py | 22 +++---- src/pipecat/services/deepseek/llm.py | 14 ++--- src/pipecat/services/elevenlabs/stt.py | 34 +++++------ src/pipecat/services/elevenlabs/tts.py | 56 ++++++++--------- src/pipecat/services/fal/image.py | 18 +++--- src/pipecat/services/fal/stt.py | 12 ++-- src/pipecat/services/fireworks/llm.py | 14 ++--- src/pipecat/services/fish/tts.py | 22 +++---- src/pipecat/services/gladia/config.py | 2 +- src/pipecat/services/gladia/stt.py | 16 ++--- .../services/google/gemini_live/llm.py | 20 +++---- .../services/google/gemini_live/vertex/llm.py | 21 ++++--- src/pipecat/services/google/image.py | 12 ++-- src/pipecat/services/google/llm.py | 20 +++---- src/pipecat/services/google/openai/llm.py | 14 ++--- src/pipecat/services/google/stt.py | 30 +++++----- src/pipecat/services/google/tts.py | 58 +++++++++--------- src/pipecat/services/google/vertex/llm.py | 24 ++++---- src/pipecat/services/gradium/stt.py | 14 ++--- src/pipecat/services/gradium/tts.py | 22 +++---- src/pipecat/services/grok/llm.py | 14 ++--- src/pipecat/services/grok/realtime/llm.py | 20 +++---- src/pipecat/services/groq/llm.py | 14 ++--- src/pipecat/services/groq/stt.py | 25 ++++---- src/pipecat/services/groq/tts.py | 20 +++---- src/pipecat/services/heygen/video.py | 4 +- src/pipecat/services/hume/tts.py | 22 +++---- src/pipecat/services/inworld/tts.py | 40 ++++++------- src/pipecat/services/kokoro/tts.py | 16 ++--- src/pipecat/services/lmnt/tts.py | 16 ++--- src/pipecat/services/minimax/tts.py | 20 +++---- src/pipecat/services/mistral/llm.py | 14 ++--- src/pipecat/services/moondream/vision.py | 10 ++-- src/pipecat/services/neuphonic/tts.py | 32 +++++----- src/pipecat/services/nvidia/llm.py | 14 ++--- src/pipecat/services/nvidia/stt.py | 26 ++++---- src/pipecat/services/nvidia/tts.py | 18 +++--- src/pipecat/services/ollama/llm.py | 14 ++--- src/pipecat/services/openai/base_llm.py | 12 ++-- src/pipecat/services/openai/image.py | 14 ++--- src/pipecat/services/openai/llm.py | 16 ++--- src/pipecat/services/openai/realtime/llm.py | 24 ++++---- src/pipecat/services/openai/stt.py | 47 +++++++-------- src/pipecat/services/openai/tts.py | 28 ++++----- .../services/openai_realtime_beta/azure.py | 6 +- .../services/openai_realtime_beta/openai.py | 10 ++-- src/pipecat/services/openpipe/llm.py | 14 ++--- src/pipecat/services/openrouter/llm.py | 14 ++--- src/pipecat/services/perplexity/llm.py | 14 ++--- src/pipecat/services/piper/tts.py | 22 +++---- src/pipecat/services/qwen/llm.py | 14 ++--- src/pipecat/services/resembleai/tts.py | 10 ++-- src/pipecat/services/rime/tts.py | 60 +++++++++---------- src/pipecat/services/sambanova/llm.py | 14 ++--- src/pipecat/services/sambanova/stt.py | 23 ++++--- src/pipecat/services/sarvam/stt.py | 28 ++++----- src/pipecat/services/sarvam/tts.py | 42 ++++++------- src/pipecat/services/soniox/stt.py | 18 +++--- src/pipecat/services/speechmatics/stt.py | 28 ++++----- src/pipecat/services/speechmatics/tts.py | 16 ++--- src/pipecat/services/tavus/video.py | 4 +- src/pipecat/services/together/llm.py | 14 ++--- src/pipecat/services/ultravox/llm.py | 8 +-- src/pipecat/services/whisper/base_stt.py | 22 +++---- src/pipecat/services/whisper/stt.py | 40 ++++++------- src/pipecat/services/xtts/tts.py | 10 ++-- 87 files changed, 859 insertions(+), 868 deletions(-) diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index 6bf7bf180..0c6d436e1 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -160,7 +160,7 @@ class AnthropicLLMService(LLMService): """ Settings = AnthropicLLMSettings - _settings: AnthropicLLMSettings + _settings: Settings # Overriding the default adapter to use the Anthropic one. adapter_class = AnthropicLLMAdapter @@ -172,7 +172,7 @@ class AnthropicLLMService(LLMService): """Input parameters for Anthropic model inference. .. deprecated:: 0.0.105 - Use ``AnthropicLLMSettings`` instead. Pass settings directly via the + Use ``AnthropicLLMService.Settings`` instead. Pass settings directly via the ``settings`` parameter of :class:`AnthropicLLMService`. Parameters: @@ -220,7 +220,7 @@ class AnthropicLLMService(LLMService): api_key: str, model: Optional[str] = None, params: Optional[InputParams] = None, - settings: Optional[AnthropicLLMSettings] = None, + settings: Optional[Settings] = None, client=None, retry_timeout_secs: Optional[float] = 5.0, retry_on_timeout: Optional[bool] = False, @@ -233,12 +233,12 @@ class AnthropicLLMService(LLMService): model: Model name to use. .. deprecated:: 0.0.105 - Use ``settings=AnthropicLLMSettings(model=...)`` instead. + Use ``settings=AnthropicLLMService.Settings(model=...)`` instead. params: Optional model parameters for inference. .. deprecated:: 0.0.105 - Use ``settings=AnthropicLLMSettings(...)`` instead. + Use ``settings=AnthropicLLMService.Settings(...)`` instead. settings: Runtime-updatable settings for this service. When both deprecated parameters and *settings* are provided, *settings* @@ -249,7 +249,7 @@ class AnthropicLLMService(LLMService): **kwargs: Additional arguments passed to parent LLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AnthropicLLMSettings( + default_settings = self.Settings( model="claude-sonnet-4-6", system_instruction=None, max_tokens=4096, @@ -268,12 +268,12 @@ class AnthropicLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", AnthropicLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", AnthropicLLMSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/assemblyai/models.py b/src/pipecat/services/assemblyai/models.py index efc13d482..cffebcf06 100644 --- a/src/pipecat/services/assemblyai/models.py +++ b/src/pipecat/services/assemblyai/models.py @@ -125,7 +125,7 @@ class AssemblyAIConnectionParams(BaseModel): """Configuration parameters for AssemblyAI WebSocket connection. .. deprecated:: 0.0.105 - Use ``settings=AssemblyAISTTSettings(foo=...)`` instead. + Use ``settings=AssemblyAISTTService.Settings(foo=...)`` instead. Parameters: sample_rate: Audio sample rate in Hz. Defaults to 16000. diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index ddfcb4a47..e7a2854b0 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -129,7 +129,7 @@ class AssemblyAISTTService(WebsocketSTTService): """ Settings = AssemblyAISTTSettings - _settings: AssemblyAISTTSettings + _settings: Settings def __init__( self, @@ -143,7 +143,7 @@ class AssemblyAISTTService(WebsocketSTTService): vad_force_turn_endpoint: bool = True, should_interrupt: bool = True, speaker_format: Optional[str] = None, - settings: Optional[AssemblyAISTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = ASSEMBLYAI_TTFS_P99, **kwargs, ): @@ -154,7 +154,7 @@ class AssemblyAISTTService(WebsocketSTTService): language: Language code for transcription. Defaults to English (Language.EN). .. deprecated:: 0.0.105 - Use ``settings=AssemblyAISTTSettings(language=...)`` instead. + Use ``settings=AssemblyAISTTService.Settings(language=...)`` instead. api_endpoint_base_url: WebSocket endpoint URL. Defaults to AssemblyAI's streaming endpoint. sample_rate: Audio sample rate in Hz. Defaults to 16000. @@ -162,7 +162,7 @@ class AssemblyAISTTService(WebsocketSTTService): connection_params: Connection configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=AssemblyAISTTSettings(...)`` instead. + Use ``settings=AssemblyAISTTService.Settings(...)`` instead. vad_force_turn_endpoint: Controls turn detection mode. When True (Pipecat mode, default): Forces AssemblyAI to return finals ASAP @@ -190,7 +190,7 @@ class AssemblyAISTTService(WebsocketSTTService): **kwargs: Additional arguments passed to parent STTService class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AssemblyAISTTSettings( + default_settings = self.Settings( model="u3-rt-pro", language=Language.EN, formatted_finals=True, @@ -208,12 +208,12 @@ class AssemblyAISTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if language is not None: - _warn_deprecated_param("language", AssemblyAISTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = language # 3. Apply connection_params overrides (deprecated) — only if settings not provided if connection_params is not None: - _warn_deprecated_param("connection_params", AssemblyAISTTSettings) + _warn_deprecated_param("connection_params", self.Settings) if not settings: sample_rate = connection_params.sample_rate encoding = connection_params.encoding @@ -299,7 +299,7 @@ class AssemblyAISTTService(WebsocketSTTService): self._user_speaking = False - def _configure_pipecat_turn_mode(self, settings: AssemblyAISTTSettings, is_u3_pro: bool): + def _configure_pipecat_turn_mode(self, settings: Settings, is_u3_pro: bool): """Configure settings for Pipecat turn detection mode. When vad_force_turn_endpoint is enabled, force AssemblyAI to return @@ -353,7 +353,7 @@ class AssemblyAISTTService(WebsocketSTTService): """ return True - async def _update_settings(self, delta: AssemblyAISTTSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply a settings delta and reconnect to apply changes. Args: diff --git a/src/pipecat/services/asyncai/tts.py b/src/pipecat/services/asyncai/tts.py index 67f79dbfd..2cecb2d5b 100644 --- a/src/pipecat/services/asyncai/tts.py +++ b/src/pipecat/services/asyncai/tts.py @@ -86,13 +86,13 @@ class AsyncAITTSService(WebsocketTTSService): """ Settings = AsyncAITTSSettings - _settings: AsyncAITTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Async TTS configuration. .. deprecated:: 0.0.105 - Use ``AsyncAITTSSettings`` directly via the ``settings`` parameter instead. + Use ``AsyncAITTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language to use for synthesis. @@ -112,7 +112,7 @@ class AsyncAITTSService(WebsocketTTSService): encoding: str = "pcm_s16le", container: str = "raw", params: Optional[InputParams] = None, - settings: Optional[AsyncAITTSSettings] = None, + settings: Optional[Settings] = None, aggregate_sentences: Optional[bool] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, **kwargs, @@ -125,14 +125,14 @@ class AsyncAITTSService(WebsocketTTSService): https://docs.async.com/list-voices-16699698e0 .. deprecated:: 0.0.105 - Use ``settings=AsyncAITTSSettings(voice=...)`` instead. + Use ``settings=AsyncAITTSService.Settings(voice=...)`` instead. version: Async API version. url: WebSocket URL for Async TTS API. model: TTS model to use (e.g., "async_flash_v1.0"). .. deprecated:: 0.0.105 - Use ``settings=AsyncAITTSSettings(model=...)`` instead. + Use ``settings=AsyncAITTSService.Settings(model=...)`` instead. sample_rate: Audio sample rate. encoding: Audio encoding format. @@ -140,7 +140,7 @@ class AsyncAITTSService(WebsocketTTSService): params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=AsyncAITTSSettings(...)`` instead. + Use ``settings=AsyncAITTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -153,7 +153,7 @@ class AsyncAITTSService(WebsocketTTSService): **kwargs: Additional arguments passed to the parent service. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AsyncAITTSSettings( + default_settings = self.Settings( model="async_flash_v1.0", voice=None, language=None, @@ -161,15 +161,15 @@ class AsyncAITTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", AsyncAITTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", AsyncAITTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", AsyncAITTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None @@ -487,13 +487,13 @@ class AsyncAIHttpTTSService(TTSService): """ Settings = AsyncAITTSSettings - _settings: AsyncAITTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Async API. .. deprecated:: 0.0.105 - Use ``AsyncAITTSSettings`` directly via the ``settings`` parameter instead. + Use ``AsyncAIHttpTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language to use for synthesis. @@ -514,7 +514,7 @@ class AsyncAIHttpTTSService(TTSService): encoding: str = "pcm_s16le", container: str = "raw", params: Optional[InputParams] = None, - settings: Optional[AsyncAITTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Async TTS service. @@ -524,13 +524,13 @@ class AsyncAIHttpTTSService(TTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=AsyncAITTSSettings(voice=...)`` instead. + Use ``settings=AsyncAIHttpTTSService.Settings(voice=...)`` instead. aiohttp_session: An aiohttp session for making HTTP requests. model: TTS model to use (e.g., "async_flash_v1.0"). .. deprecated:: 0.0.105 - Use ``settings=AsyncAITTSSettings(model=...)`` instead. + Use ``settings=AsyncAIHttpTTSService.Settings(model=...)`` instead. url: Base URL for Async API. version: API version string for Async API. @@ -540,14 +540,14 @@ class AsyncAIHttpTTSService(TTSService): params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=AsyncAITTSSettings(...)`` instead. + Use ``settings=AsyncAIHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to the parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AsyncAITTSSettings( + default_settings = self.Settings( model="async_flash_v1.0", voice=None, language=None, @@ -555,15 +555,15 @@ class AsyncAIHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", AsyncAITTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", AsyncAITTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", AsyncAITTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index c77321ead..9a2ebfdcf 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -747,7 +747,7 @@ class AWSBedrockLLMService(LLMService): """ Settings = AWSBedrockLLMSettings - _settings: AWSBedrockLLMSettings + _settings: Settings # Overriding the default adapter to use the Anthropic one. adapter_class = AWSBedrockLLMAdapter @@ -756,7 +756,7 @@ class AWSBedrockLLMService(LLMService): """Input parameters for AWS Bedrock LLM service. .. deprecated:: 0.0.105 - Use ``AWSBedrockLLMSettings`` instead. Pass settings directly via the + Use ``AWSBedrockLLMService.Settings`` instead. Pass settings directly via the ``settings`` parameter of :class:`AWSBedrockLLMService`. Parameters: @@ -784,7 +784,7 @@ class AWSBedrockLLMService(LLMService): aws_session_token: Optional[str] = None, aws_region: Optional[str] = None, params: Optional[InputParams] = None, - settings: Optional[AWSBedrockLLMSettings] = None, + settings: Optional[Settings] = None, stop_sequences: Optional[List[str]] = None, client_config: Optional[Config] = None, retry_timeout_secs: Optional[float] = 5.0, @@ -797,7 +797,7 @@ class AWSBedrockLLMService(LLMService): model: The AWS Bedrock model identifier to use. .. deprecated:: 0.0.105 - Use ``settings=AWSBedrockLLMSettings(model=...)`` instead. + Use ``settings=AWSBedrockLLMService.Settings(model=...)`` instead. aws_access_key: AWS access key ID. If None, uses default credentials. aws_secret_key: AWS secret access key. If None, uses default credentials. @@ -806,7 +806,7 @@ class AWSBedrockLLMService(LLMService): params: Model parameters and configuration. .. deprecated:: 0.0.105 - Use ``settings=AWSBedrockLLMSettings(...)`` instead. + Use ``settings=AWSBedrockLLMService.Settings(...)`` instead. settings: Runtime-updatable settings for this service. When both deprecated parameters and *settings* are provided, *settings* @@ -814,7 +814,7 @@ class AWSBedrockLLMService(LLMService): stop_sequences: List of strings that stop generation. .. deprecated:: 0.0.105 - Use ``settings=AWSBedrockLLMSettings(stop_sequences=...)`` instead. + Use ``settings=AWSBedrockLLMService.Settings(stop_sequences=...)`` instead. client_config: Custom boto3 client configuration. retry_timeout_secs: Request timeout in seconds for retry logic. @@ -822,7 +822,7 @@ class AWSBedrockLLMService(LLMService): **kwargs: Additional arguments passed to parent LLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AWSBedrockLLMSettings( + default_settings = self.Settings( model="us.amazon.nova-lite-v1:0", system_instruction=None, max_tokens=None, @@ -841,15 +841,15 @@ class AWSBedrockLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", AWSBedrockLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if stop_sequences is not None: - _warn_deprecated_param("stop_sequences", AWSBedrockLLMSettings, "stop_sequences") + _warn_deprecated_param("stop_sequences", self.Settings, "stop_sequences") default_settings.stop_sequences = stop_sequences # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", AWSBedrockLLMSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 8e4633544..0309c0fad 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -150,7 +150,7 @@ class Params(BaseModel): """Configuration parameters for AWS Nova Sonic. .. deprecated:: 0.0.105 - Use ``settings=AWSNovaSonicLLMSettings(...)`` for inference settings + Use ``settings=AWSNovaSonicLLMService.Settings(...)`` for inference settings and ``audio_config=AudioConfig(...)`` for audio configuration. Parameters: @@ -247,7 +247,7 @@ class AWSNovaSonicLLMService(LLMService): """ Settings = AWSNovaSonicLLMSettings - _settings: AWSNovaSonicLLMSettings + _settings: Settings # Override the default adapter to use the AWSNovaSonicLLMAdapter one adapter_class = AWSNovaSonicLLMAdapter @@ -263,7 +263,7 @@ class AWSNovaSonicLLMService(LLMService): voice_id: str = "matthew", params: Optional[Params] = None, audio_config: Optional[AudioConfig] = None, - settings: Optional[AWSNovaSonicLLMSettings] = None, + settings: Optional[Settings] = None, system_instruction: Optional[str] = None, tools: Optional[ToolsSchema] = None, send_transcription_frames: bool = True, @@ -282,7 +282,7 @@ class AWSNovaSonicLLMService(LLMService): model: Model identifier. Defaults to "amazon.nova-2-sonic-v1:0". .. deprecated:: 0.0.105 - Use ``settings=AWSNovaSonicLLMSettings(model=...)`` instead. + Use ``settings=AWSNovaSonicLLMService.Settings(model=...)`` instead. voice_id: Voice ID for speech synthesis. Note that some voices are designed for use with a specific language. @@ -291,12 +291,12 @@ class AWSNovaSonicLLMService(LLMService): - Nova Sonic (the older model): see https://docs.aws.amazon.com/nova/latest/userguide/available-voices.html. .. deprecated:: 0.0.105 - Use ``settings=AWSNovaSonicLLMSettings(voice=...)`` instead. + Use ``settings=AWSNovaSonicLLMService.Settings(voice=...)`` instead. params: Model parameters for audio configuration and inference. .. deprecated:: 0.0.105 - Use ``settings=AWSNovaSonicLLMSettings(...)`` for inference + Use ``settings=AWSNovaSonicLLMService.Settings(...)`` for inference settings and ``audio_config=AudioConfig(...)`` for audio configuration. @@ -308,7 +308,7 @@ class AWSNovaSonicLLMService(LLMService): system_instruction: System-level instruction for the model. .. deprecated:: 0.0.105 - Use ``settings=AWSNovaSonicLLMSettings(system_instruction=...)`` instead. + Use ``settings=AWSNovaSonicLLMService.Settings(system_instruction=...)`` instead. tools: Available tools/functions for the model to use. send_transcription_frames: Whether to emit transcription frames. @@ -319,7 +319,7 @@ class AWSNovaSonicLLMService(LLMService): **kwargs: Additional arguments passed to the parent LLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AWSNovaSonicLLMSettings( + default_settings = self.Settings( model="amazon.nova-2-sonic-v1:0", system_instruction=None, voice="matthew", @@ -337,15 +337,13 @@ class AWSNovaSonicLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model != "amazon.nova-2-sonic-v1:0": - _warn_deprecated_param("model", AWSNovaSonicLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id != "matthew": - _warn_deprecated_param("voice_id", AWSNovaSonicLLMSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if system_instruction is not None: - _warn_deprecated_param( - "system_instruction", AWSNovaSonicLLMSettings, "system_instruction" - ) + _warn_deprecated_param("system_instruction", self.Settings, "system_instruction") default_settings.system_instruction = system_instruction # 3. Apply params overrides — only if settings not provided @@ -356,7 +354,7 @@ class AWSNovaSonicLLMService(LLMService): warnings.simplefilter("always") warnings.warn( "The `params` parameter is deprecated. " - "Use `settings=AWSNovaSonicLLMSettings(...)` for inference settings " + "Use `settings=self.Settings(...)` for inference settings " "(temperature, max_tokens, top_p, endpointing_sensitivity) " "and `audio_config=AudioConfig(...)` for audio configuration " "(sample rates, sample sizes, channel counts).", @@ -447,7 +445,7 @@ class AWSNovaSonicLLMService(LLMService): # settings # - async def _update_settings(self, delta: AWSNovaSonicLLMSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply a settings delta. Settings are stored but not applied to the active connection. diff --git a/src/pipecat/services/aws/stt.py b/src/pipecat/services/aws/stt.py index 8b28c006a..0a648759c 100644 --- a/src/pipecat/services/aws/stt.py +++ b/src/pipecat/services/aws/stt.py @@ -61,7 +61,7 @@ class AWSTranscribeSTTService(WebsocketSTTService): """ Settings = AWSTranscribeSTTSettings - _settings: AWSTranscribeSTTSettings + _settings: Settings def __init__( self, @@ -72,7 +72,7 @@ class AWSTranscribeSTTService(WebsocketSTTService): region: Optional[str] = None, sample_rate: Optional[int] = None, language: Optional[Language] = None, - settings: Optional[AWSTranscribeSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = AWS_TRANSCRIBE_TTFS_P99, **kwargs, ): @@ -89,7 +89,7 @@ class AWSTranscribeSTTService(WebsocketSTTService): language: Language for transcription. .. deprecated:: 0.0.105 - Use ``settings=AWSTranscribeSTTSettings(language=...)`` instead. + Use ``settings=AWSTranscribeSTTService.Settings(language=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -98,14 +98,14 @@ class AWSTranscribeSTTService(WebsocketSTTService): **kwargs: Additional arguments passed to parent STTService class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AWSTranscribeSTTSettings( + default_settings = self.Settings( model=None, language=self.language_to_service_language(Language.EN), ) # 2. Apply direct init arg overrides (deprecated) if language is not None: - _warn_deprecated_param("language", AWSTranscribeSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = self.language_to_service_language(language) # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/aws/tts.py b/src/pipecat/services/aws/tts.py index 47b12429a..1e16958fe 100644 --- a/src/pipecat/services/aws/tts.py +++ b/src/pipecat/services/aws/tts.py @@ -149,13 +149,13 @@ class AWSPollyTTSService(TTSService): """ Settings = AWSPollyTTSSettings - _settings: AWSPollyTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for AWS Polly TTS configuration. .. deprecated:: 0.0.105 - Use ``AWSPollyTTSSettings`` directly via the ``settings`` parameter instead. + Use ``AWSPollyTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: engine: TTS engine to use ('standard', 'neural', etc.). @@ -183,7 +183,7 @@ class AWSPollyTTSService(TTSService): voice_id: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[AWSPollyTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initializes the AWS Polly TTS service. @@ -196,20 +196,20 @@ class AWSPollyTTSService(TTSService): voice_id: Voice ID to use for synthesis. Defaults to 'Joanna'. .. deprecated:: 0.0.105 - Use ``settings=AWSPollyTTSSettings(voice=...)`` instead. + Use ``settings=AWSPollyTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate. If None, uses service default. params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=AWSPollyTTSSettings(...)`` instead. + Use ``settings=AWSPollyTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AWSPollyTTSSettings( + default_settings = self.Settings( model=None, voice="Joanna", language="en-US", @@ -222,12 +222,12 @@ class AWSPollyTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", AWSPollyTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", AWSPollyTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.engine = params.engine default_settings.language = ( diff --git a/src/pipecat/services/azure/image.py b/src/pipecat/services/azure/image.py index 1b62fbfe4..a80496599 100644 --- a/src/pipecat/services/azure/image.py +++ b/src/pipecat/services/azure/image.py @@ -44,7 +44,7 @@ class AzureImageGenServiceREST(ImageGenService): """ Settings = AzureImageGenSettings - _settings: AzureImageGenSettings + _settings: Settings def __init__( self, @@ -55,7 +55,7 @@ class AzureImageGenServiceREST(ImageGenService): model: Optional[str] = None, aiohttp_session: aiohttp.ClientSession, api_version="2023-06-01-preview", - settings: Optional[AzureImageGenSettings] = None, + settings: Optional[Settings] = None, ): """Initialize the AzureImageGenServiceREST. @@ -63,14 +63,14 @@ class AzureImageGenServiceREST(ImageGenService): image_size: Size specification for generated images (e.g., "1024x1024"). .. deprecated:: 0.0.105 - Use ``settings=AzureImageGenSettings(image_size=...)`` instead. + Use ``settings=AzureImageGenServiceREST.Settings(image_size=...)`` instead. api_key: Azure OpenAI API key for authentication. endpoint: Azure OpenAI endpoint URL. model: The image generation model to use. .. deprecated:: 0.0.105 - Use ``settings=AzureImageGenSettings(model=...)`` instead. + Use ``settings=AzureImageGenServiceREST.Settings(model=...)`` instead. aiohttp_session: Shared aiohttp session for HTTP requests. api_version: Azure API version string. Defaults to "2023-06-01-preview". @@ -78,18 +78,18 @@ class AzureImageGenServiceREST(ImageGenService): parameters, ``settings`` values take precedence. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AzureImageGenSettings( + default_settings = self.Settings( model=None, image_size=None, ) # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", AzureImageGenSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if image_size is not None: - _warn_deprecated_param("image_size", AzureImageGenSettings, "image_size") + _warn_deprecated_param("image_size", self.Settings, "image_size") default_settings.image_size = image_size # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/azure/llm.py b/src/pipecat/services/azure/llm.py index 322ff0b3e..b4f2de5dc 100644 --- a/src/pipecat/services/azure/llm.py +++ b/src/pipecat/services/azure/llm.py @@ -12,13 +12,13 @@ from typing import Optional from loguru import logger from openai import AsyncAzureOpenAI -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class AzureLLMSettings(OpenAILLMSettings): +class AzureLLMSettings(BaseOpenAILLMService.Settings): """Settings for AzureLLMService.""" pass @@ -40,7 +40,7 @@ class AzureLLMService(OpenAILLMService): endpoint: str, model: Optional[str] = None, api_version: str = "2024-09-01-preview", - settings: Optional[AzureLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Azure LLM service. @@ -51,7 +51,7 @@ class AzureLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "gpt-4o". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=AzureLLMService.Settings(model=...)`` instead. api_version: Azure API version. Defaults to "2024-09-01-preview". settings: Runtime-updatable settings. When provided alongside deprecated @@ -59,11 +59,11 @@ class AzureLLMService(OpenAILLMService): **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AzureLLMSettings(model="gpt-4o") + default_settings = self.Settings(model="gpt-4o") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", AzureLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/azure/realtime/llm.py b/src/pipecat/services/azure/realtime/llm.py index c791af94d..e6bc05478 100644 --- a/src/pipecat/services/azure/realtime/llm.py +++ b/src/pipecat/services/azure/realtime/llm.py @@ -10,7 +10,7 @@ from dataclasses import dataclass from loguru import logger -from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService, OpenAIRealtimeLLMSettings +from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService try: from websockets.asyncio.client import connect as websocket_connect @@ -21,7 +21,7 @@ except ModuleNotFoundError as e: @dataclass -class AzureRealtimeLLMSettings(OpenAIRealtimeLLMSettings): +class AzureRealtimeLLMSettings(OpenAIRealtimeLLMService.Settings): """Settings for AzureRealtimeLLMService.""" pass @@ -36,7 +36,7 @@ class AzureRealtimeLLMService(OpenAIRealtimeLLMService): """ Settings = AzureRealtimeLLMSettings - _settings: AzureRealtimeLLMSettings + _settings: Settings def __init__( self, diff --git a/src/pipecat/services/azure/stt.py b/src/pipecat/services/azure/stt.py index 6abe52db1..7309a4a4e 100644 --- a/src/pipecat/services/azure/stt.py +++ b/src/pipecat/services/azure/stt.py @@ -67,7 +67,7 @@ class AzureSTTService(STTService): """ Settings = AzureSTTSettings - _settings: AzureSTTSettings + _settings: Settings def __init__( self, @@ -78,7 +78,7 @@ class AzureSTTService(STTService): sample_rate: Optional[int] = None, private_endpoint: Optional[str] = None, endpoint_id: Optional[str] = None, - settings: Optional[AzureSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = AZURE_TTFS_P99, **kwargs, ): @@ -91,7 +91,7 @@ class AzureSTTService(STTService): language: Language for speech recognition. Defaults to English (US). .. deprecated:: 0.0.105 - Use ``settings=AzureSTTSettings(language=...)`` instead. + Use ``settings=AzureSTTService.Settings(language=...)`` instead. sample_rate: Audio sample rate in Hz. If None, uses service default. private_endpoint: Private endpoint for STT behind firewall. @@ -104,14 +104,14 @@ class AzureSTTService(STTService): **kwargs: Additional arguments passed to parent STTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AzureSTTSettings( + default_settings = self.Settings( model=None, language=language_to_azure_language(Language.EN_US), ) # 2. Apply direct init arg overrides (deprecated) if language is not None and language != Language.EN_US: - _warn_deprecated_param("language", AzureSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = language_to_azure_language(language) # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/azure/tts.py b/src/pipecat/services/azure/tts.py index 1c74c7655..d33937d52 100644 --- a/src/pipecat/services/azure/tts.py +++ b/src/pipecat/services/azure/tts.py @@ -97,7 +97,8 @@ class AzureBaseTTSService: This is a mixin class and should be used alongside TTSService or its subclasses. """ - _settings: AzureTTSSettings + Settings = AzureTTSSettings + _settings: Settings # Define SSML escape mappings based on SSML reserved characters # See - https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-synthesis-markup-structure @@ -113,7 +114,7 @@ class AzureBaseTTSService: """Input parameters for Azure TTS voice configuration. .. deprecated:: 0.0.105 - Use ``settings=AzureTTSSettings(...)`` instead. + Use ``settings=AzureBaseTTSService.Settings(...)`` instead. Parameters: emphasis: Emphasis level for speech ("strong", "moderate", "reduced"). @@ -256,7 +257,7 @@ class AzureTTSService(TTSService, AzureBaseTTSService): voice: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[AzureBaseTTSService.InputParams] = None, - settings: Optional[AzureTTSSettings] = None, + settings: Optional[Settings] = None, aggregate_sentences: Optional[bool] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, **kwargs, @@ -269,13 +270,13 @@ class AzureTTSService(TTSService, AzureBaseTTSService): voice: Voice name to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=AzureTTSSettings(voice=...)`` instead. + Use ``settings=AzureTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate in Hz. If None, uses service default. params: Voice and synthesis parameters configuration. .. deprecated:: 0.0.105 - Use ``settings=AzureTTSSettings(...)`` instead. + Use ``settings=AzureTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -288,7 +289,7 @@ class AzureTTSService(TTSService, AzureBaseTTSService): **kwargs: Additional arguments passed to parent WordTTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AzureTTSSettings( + default_settings = self.Settings( model=None, voice="en-US-SaraNeural", language="en-US", @@ -303,12 +304,12 @@ class AzureTTSService(TTSService, AzureBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", AzureTTSSettings, "voice") + _warn_deprecated_param("voice", self.Settings, "voice") default_settings.voice = voice # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", AzureTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.emphasis = params.emphasis default_settings.language = ( @@ -761,7 +762,7 @@ class AzureHttpTTSService(TTSService, AzureBaseTTSService): voice: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[AzureBaseTTSService.InputParams] = None, - settings: Optional[AzureTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Azure HTTP TTS service. @@ -772,20 +773,20 @@ class AzureHttpTTSService(TTSService, AzureBaseTTSService): voice: Voice name to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=AzureTTSSettings(voice=...)`` instead. + Use ``settings=AzureHttpTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate in Hz. If None, uses service default. params: Voice and synthesis parameters configuration. .. deprecated:: 0.0.105 - Use ``settings=AzureTTSSettings(...)`` instead. + Use ``settings=AzureHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = AzureTTSSettings( + default_settings = self.Settings( model=None, voice="en-US-SaraNeural", language="en-US", @@ -800,12 +801,12 @@ class AzureHttpTTSService(TTSService, AzureBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", AzureTTSSettings, "voice") + _warn_deprecated_param("voice", self.Settings, "voice") default_settings.voice = voice # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", AzureTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.emphasis = params.emphasis default_settings.language = ( diff --git a/src/pipecat/services/camb/tts.py b/src/pipecat/services/camb/tts.py index f458fa2e4..7586644ce 100644 --- a/src/pipecat/services/camb/tts.py +++ b/src/pipecat/services/camb/tts.py @@ -176,13 +176,13 @@ class CambTTSService(TTSService): """ Settings = CambTTSSettings - _settings: CambTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Camb.ai TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=CambTTSSettings(...)`` instead. + Use ``settings=CambTTSService.Settings(...)`` instead. Parameters: language: Language for synthesis (BCP-47 format). Defaults to English. @@ -207,7 +207,7 @@ class CambTTSService(TTSService): timeout: float = 60.0, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[CambTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Camb.ai TTS service. @@ -217,12 +217,12 @@ class CambTTSService(TTSService): voice_id: Voice ID to use. .. deprecated:: 0.0.105 - Use ``settings=CambTTSSettings(voice=...)`` instead. + Use ``settings=CambTTSService.Settings(voice=...)`` instead. model: TTS model to use. Options: "mars-flash" (fast), "mars-pro" (high quality). .. deprecated:: 0.0.105 - Use ``settings=CambTTSSettings(model=...)`` instead. + Use ``settings=CambTTSService.Settings(model=...)`` instead. timeout: Request timeout in seconds. Defaults to 60.0 (minimum recommended by Camb.ai). @@ -230,14 +230,14 @@ class CambTTSService(TTSService): params: Additional voice parameters. If None, uses defaults. .. deprecated:: 0.0.105 - Use ``settings=CambTTSSettings(...)`` instead. + Use ``settings=CambTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = CambTTSSettings( + default_settings = self.Settings( model="mars-flash", voice=147320, language="en-us", @@ -246,15 +246,15 @@ class CambTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", CambTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", CambTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", CambTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = ( diff --git a/src/pipecat/services/cartesia/stt.py b/src/pipecat/services/cartesia/stt.py index 9c0924827..e094ef044 100644 --- a/src/pipecat/services/cartesia/stt.py +++ b/src/pipecat/services/cartesia/stt.py @@ -55,7 +55,7 @@ class CartesiaLiveOptions: """Configuration options for Cartesia Live STT service. .. deprecated:: 0.0.105 - Use ``settings=CartesiaSTTSettings(...)`` for model/language and + Use ``settings=CartesiaSTTService.Settings(...)`` for model/language and direct ``__init__`` parameters for encoding/sample_rate instead. """ @@ -147,7 +147,7 @@ class CartesiaSTTService(WebsocketSTTService): """ Settings = CartesiaSTTSettings - _settings: CartesiaSTTSettings + _settings: Settings def __init__( self, @@ -157,7 +157,7 @@ class CartesiaSTTService(WebsocketSTTService): encoding: str = "pcm_s16le", sample_rate: Optional[int] = None, live_options: Optional[CartesiaLiveOptions] = None, - settings: Optional[CartesiaSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = CARTESIA_TTFS_P99, **kwargs, ): @@ -172,7 +172,7 @@ class CartesiaSTTService(WebsocketSTTService): live_options: Configuration options for transcription service. .. deprecated:: 0.0.105 - Use ``settings=CartesiaSTTSettings(...)`` for model/language + Use ``settings=CartesiaSTTService.Settings(...)`` for model/language and direct init parameters for encoding/sample_rate instead. settings: Runtime-updatable settings. When provided alongside deprecated @@ -182,14 +182,14 @@ class CartesiaSTTService(WebsocketSTTService): **kwargs: Additional arguments passed to parent STTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = CartesiaSTTSettings( + default_settings = self.Settings( model="ink-whisper", language=Language.EN.value, ) # 2. Apply live_options overrides — only if settings not provided if live_options is not None: - _warn_deprecated_param("live_options", CartesiaSTTSettings) + _warn_deprecated_param("live_options", self.Settings) if not settings: if live_options.sample_rate and sample_rate is None: sample_rate = live_options.sample_rate @@ -313,7 +313,7 @@ class CartesiaSTTService(WebsocketSTTService): """Apply a settings delta. Args: - delta: A :class:`STTSettings` (or ``CartesiaSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``CartesiaSTTService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index d41f341ca..90497eb1d 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -211,7 +211,7 @@ class CartesiaTTSService(WebsocketTTSService): """ Settings = CartesiaTTSSettings - _settings: CartesiaTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Cartesia TTS configuration. @@ -239,7 +239,7 @@ class CartesiaTTSService(WebsocketTTSService): encoding: str = "pcm_s16le", container: str = "raw", params: Optional[InputParams] = None, - settings: Optional[CartesiaTTSSettings] = None, + settings: Optional[Settings] = None, text_aggregator: Optional[BaseTextAggregator] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, aggregate_sentences: Optional[bool] = None, @@ -252,14 +252,14 @@ class CartesiaTTSService(WebsocketTTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=CartesiaTTSSettings(voice=...)`` instead. + Use ``settings=CartesiaTTSService.Settings(voice=...)`` instead. cartesia_version: API version string for Cartesia service. url: WebSocket URL for Cartesia TTS API. model: TTS model to use (e.g., "sonic-3"). .. deprecated:: 0.0.105 - Use ``settings=CartesiaTTSSettings(model=...)`` instead. + Use ``settings=CartesiaTTSService.Settings(model=...)`` instead. sample_rate: Audio sample rate. If None, uses default. encoding: Audio encoding format. @@ -267,7 +267,7 @@ class CartesiaTTSService(WebsocketTTSService): params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=CartesiaTTSSettings(...)`` instead. + Use ``settings=CartesiaTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -299,7 +299,7 @@ class CartesiaTTSService(WebsocketTTSService): # playout timing of the audio! # 1. Initialize default_settings with hardcoded defaults - default_settings = CartesiaTTSSettings( + default_settings = self.Settings( model="sonic-3", voice=None, language=language_to_cartesia_language(Language.EN), @@ -309,15 +309,15 @@ class CartesiaTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", CartesiaTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", CartesiaTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", CartesiaTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -683,7 +683,7 @@ class CartesiaHttpTTSService(TTSService): """ Settings = CartesiaTTSSettings - _settings: CartesiaTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Cartesia HTTP TTS configuration. @@ -712,7 +712,7 @@ class CartesiaHttpTTSService(TTSService): encoding: str = "pcm_s16le", container: str = "raw", params: Optional[InputParams] = None, - settings: Optional[CartesiaTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Cartesia HTTP TTS service. @@ -722,12 +722,12 @@ class CartesiaHttpTTSService(TTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=CartesiaTTSSettings(voice=...)`` instead. + Use ``settings=CartesiaHttpTTSService.Settings(voice=...)`` instead. model: TTS model to use (e.g., "sonic-3"). .. deprecated:: 0.0.105 - Use ``settings=CartesiaTTSSettings(model=...)`` instead. + Use ``settings=CartesiaHttpTTSService.Settings(model=...)`` instead. base_url: Base URL for Cartesia HTTP API. cartesia_version: API version string for Cartesia service. @@ -739,14 +739,14 @@ class CartesiaHttpTTSService(TTSService): params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=CartesiaTTSSettings(...)`` instead. + Use ``settings=CartesiaHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to the parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = CartesiaTTSSettings( + default_settings = self.Settings( model="sonic-3", voice=None, language=language_to_cartesia_language(Language.EN), @@ -756,15 +756,15 @@ class CartesiaHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", CartesiaTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", CartesiaTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", CartesiaTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) diff --git a/src/pipecat/services/cerebras/llm.py b/src/pipecat/services/cerebras/llm.py index 5d4bc2d14..e2a15f4e5 100644 --- a/src/pipecat/services/cerebras/llm.py +++ b/src/pipecat/services/cerebras/llm.py @@ -12,13 +12,13 @@ from typing import Optional from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class CerebrasLLMSettings(OpenAILLMSettings): +class CerebrasLLMSettings(BaseOpenAILLMService.Settings): """Settings for CerebrasLLMService.""" pass @@ -32,7 +32,7 @@ class CerebrasLLMService(OpenAILLMService): """ Settings = CerebrasLLMSettings - _settings: CerebrasLLMSettings + _settings: Settings def __init__( self, @@ -40,7 +40,7 @@ class CerebrasLLMService(OpenAILLMService): api_key: str, base_url: str = "https://api.cerebras.ai/v1", model: Optional[str] = None, - settings: Optional[CerebrasLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Cerebras LLM service. @@ -51,18 +51,18 @@ class CerebrasLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "gpt-oss-120b". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=CerebrasLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = CerebrasLLMSettings(model="gpt-oss-120b") + default_settings = self.Settings(model="gpt-oss-120b") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", CerebrasLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/deepgram/flux/stt.py b/src/pipecat/services/deepgram/flux/stt.py index 3227bd0a6..085d1ed2c 100644 --- a/src/pipecat/services/deepgram/flux/stt.py +++ b/src/pipecat/services/deepgram/flux/stt.py @@ -116,14 +116,14 @@ class DeepgramFluxSTTService(WebsocketSTTService): """ Settings = DeepgramFluxSTTSettings - _settings: DeepgramFluxSTTSettings + _settings: Settings _CONFIGURE_FIELDS = {"keyterm", "eot_threshold", "eager_eot_threshold", "eot_timeout_ms"} class InputParams(BaseModel): """Configuration parameters for Deepgram Flux API. .. deprecated:: 0.0.105 - Use ``settings=DeepgramFluxSTTSettings(...)`` instead. + Use ``settings=DeepgramFluxSTTService.Settings(...)`` instead. Parameters: eager_eot_threshold: Optional. EagerEndOfTurn/TurnResumed are off by default. @@ -162,7 +162,7 @@ class DeepgramFluxSTTService(WebsocketSTTService): tag: Optional[list] = None, params: Optional[InputParams] = None, should_interrupt: bool = True, - settings: Optional[DeepgramFluxSTTSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Deepgram Flux STT service. @@ -176,7 +176,7 @@ class DeepgramFluxSTTService(WebsocketSTTService): model: Deepgram Flux model to use for transcription. .. deprecated:: 0.0.105 - Use ``settings=DeepgramFluxSTTSettings(model=...)`` instead. + Use ``settings=DeepgramFluxSTTService.Settings(model=...)`` instead. flux_encoding: Audio encoding format required by Flux API. Must be "linear16". Raw signed little-endian 16-bit PCM encoding. @@ -184,7 +184,7 @@ class DeepgramFluxSTTService(WebsocketSTTService): params: InputParams instance containing detailed API configuration options. .. deprecated:: 0.0.105 - Use ``settings=DeepgramFluxSTTSettings(...)`` instead. + Use ``settings=DeepgramFluxSTTService.Settings(...)`` instead. should_interrupt: Determine whether the bot should be interrupted when Flux detects that the user is speaking. settings: Runtime-updatable settings. When provided alongside deprecated @@ -200,7 +200,7 @@ class DeepgramFluxSTTService(WebsocketSTTService): stt = DeepgramFluxSTTService( api_key="your-api-key", - settings=DeepgramFluxSTTSettings( + settings=DeepgramFluxSTTService.Settings( model="flux-general-en", eager_eot_threshold=0.5, eot_threshold=0.8, @@ -221,7 +221,7 @@ class DeepgramFluxSTTService(WebsocketSTTService): # already try to reconnect if needed. # 1. Initialize default_settings with hardcoded defaults - default_settings = DeepgramFluxSTTSettings( + default_settings = self.Settings( model="flux-general-en", language=Language.EN, eager_eot_threshold=None, @@ -233,12 +233,12 @@ class DeepgramFluxSTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", DeepgramFluxSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", DeepgramFluxSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.eager_eot_threshold = params.eager_eot_threshold default_settings.eot_threshold = params.eot_threshold @@ -448,7 +448,7 @@ class DeepgramFluxSTTService(WebsocketSTTService): """ return True - async def _update_settings(self, delta: DeepgramFluxSTTSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply a settings delta. Configure-able fields (keyterm, eot_threshold, eager_eot_threshold, diff --git a/src/pipecat/services/deepgram/sagemaker/stt.py b/src/pipecat/services/deepgram/sagemaker/stt.py index f6093a630..98b3556cc 100644 --- a/src/pipecat/services/deepgram/sagemaker/stt.py +++ b/src/pipecat/services/deepgram/sagemaker/stt.py @@ -32,7 +32,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.aws.sagemaker.bidi_client import SageMakerBidiClient -from pipecat.services.deepgram.stt import DeepgramSTTSettings, LiveOptions +from pipecat.services.deepgram.stt import DeepgramSTTService, LiveOptions from pipecat.services.settings import STTSettings, _warn_deprecated_param, is_given from pipecat.services.stt_latency import DEEPGRAM_SAGEMAKER_TTFS_P99 from pipecat.services.stt_service import STTService @@ -42,10 +42,10 @@ from pipecat.utils.tracing.service_decorators import traced_stt @dataclass -class DeepgramSageMakerSTTSettings(DeepgramSTTSettings): +class DeepgramSageMakerSTTSettings(DeepgramSTTService.Settings): """Settings for the Deepgram SageMaker STT service. - Inherits all fields from :class:`DeepgramSTTSettings`. + Inherits all fields from :class:`DeepgramSTTService.Settings`. """ pass @@ -79,7 +79,7 @@ class DeepgramSageMakerSTTService(STTService): """ Settings = DeepgramSageMakerSTTSettings - _settings: DeepgramSageMakerSTTSettings + _settings: Settings def __init__( self, @@ -92,7 +92,7 @@ class DeepgramSageMakerSTTService(STTService): sample_rate: Optional[int] = None, mip_opt_out: Optional[bool] = None, live_options: Optional[LiveOptions] = None, - settings: Optional[DeepgramSageMakerSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = DEEPGRAM_SAGEMAKER_TTFS_P99, **kwargs, ): @@ -112,7 +112,7 @@ class DeepgramSageMakerSTTService(STTService): live_options: Legacy configuration options. .. deprecated:: 0.0.105 - Use ``settings=DeepgramSageMakerSTTSettings(...)`` for + Use ``settings=DeepgramSageMakerSTTService.Settings(...)`` for runtime-updatable fields and direct init parameters for connection-level config. @@ -124,7 +124,7 @@ class DeepgramSageMakerSTTService(STTService): **kwargs: Additional arguments passed to the parent STTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = DeepgramSageMakerSTTSettings( + default_settings = self.Settings( model="nova-3", language=Language.EN, detect_entities=False, @@ -147,7 +147,7 @@ class DeepgramSageMakerSTTService(STTService): # 2. Apply live_options overrides — only if settings not provided if live_options is not None: - _warn_deprecated_param("live_options", DeepgramSageMakerSTTSettings) + _warn_deprecated_param("live_options", self.Settings) if not settings: # Extract init-only fields from live_options if live_options.sample_rate is not None and sample_rate is None: @@ -170,7 +170,7 @@ class DeepgramSageMakerSTTService(STTService): "mip_opt_out", } lo_dict = {k: v for k, v in live_options.to_dict().items() if k not in init_only} - delta = DeepgramSageMakerSTTSettings.from_mapping(lo_dict) + delta = self.Settings.from_mapping(lo_dict) default_settings.apply_update(delta) # 3. Apply settings delta (canonical API, always wins) @@ -216,7 +216,7 @@ class DeepgramSageMakerSTTService(STTService): return changed # Sync extra to fields after the update so self._settings stays unambiguous - if isinstance(self._settings, DeepgramSTTSettings): + if isinstance(self._settings, self.Settings): self._settings._sync_extra_to_fields() # TODO: someday we could reconnect here to apply updated settings. diff --git a/src/pipecat/services/deepgram/sagemaker/tts.py b/src/pipecat/services/deepgram/sagemaker/tts.py index d315b9e01..f2c55c882 100644 --- a/src/pipecat/services/deepgram/sagemaker/tts.py +++ b/src/pipecat/services/deepgram/sagemaker/tts.py @@ -70,7 +70,7 @@ class DeepgramSageMakerTTSService(TTSService): """ Settings = DeepgramSageMakerTTSSettings - _settings: DeepgramSageMakerTTSSettings + _settings: Settings def __init__( self, @@ -80,7 +80,7 @@ class DeepgramSageMakerTTSService(TTSService): voice: Optional[str] = None, sample_rate: Optional[int] = None, encoding: str = "linear16", - settings: Optional[DeepgramSageMakerTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Deepgram SageMaker TTS service. @@ -92,7 +92,7 @@ class DeepgramSageMakerTTSService(TTSService): voice: Voice model to use for synthesis. Defaults to "aura-2-helena-en". .. deprecated:: 0.0.105 - Use ``settings=DeepgramSageMakerTTSSettings(voice=...)`` instead. + Use ``settings=DeepgramSageMakerTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate in Hz. If None, uses the value from StartFrame. encoding: Audio encoding format. Defaults to "linear16". @@ -101,11 +101,11 @@ class DeepgramSageMakerTTSService(TTSService): **kwargs: Additional arguments passed to the parent TTSService. """ if voice is not None: - _warn_deprecated_param("voice", DeepgramSageMakerTTSSettings, "voice") + _warn_deprecated_param("voice", self.Settings, "voice") voice = voice or "aura-2-helena-en" - default_settings = DeepgramSageMakerTTSSettings( + default_settings = self.Settings( model=None, voice=voice, language=None, diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index d8f782f5d..e84964dce 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -59,7 +59,7 @@ class LiveOptions: deepgram-sdk v6. .. deprecated:: 0.0.105 - Use ``settings=DeepgramSTTSettings(...)`` for runtime-updatable fields + Use ``settings=DeepgramSTTService.Settings(...)`` for runtime-updatable fields and direct ``__init__`` parameters for connection-level config instead. """ @@ -267,7 +267,7 @@ class DeepgramSTTService(STTService): """ Settings = DeepgramSTTSettings - _settings: DeepgramSTTSettings + _settings: Settings def __init__( self, @@ -286,7 +286,7 @@ class DeepgramSTTService(STTService): live_options: Optional[LiveOptions] = None, addons: Optional[dict] = None, should_interrupt: bool = True, - settings: Optional[DeepgramSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = DEEPGRAM_TTFS_P99, **kwargs, ): @@ -313,7 +313,7 @@ class DeepgramSTTService(STTService): live_options: Legacy configuration options. .. deprecated:: 0.0.105 - Use ``settings=DeepgramSTTSettings(...)`` for runtime-updatable + Use ``settings=DeepgramSTTService.Settings(...)`` for runtime-updatable fields and direct init parameters for connection-level config. addons: Additional Deepgram features to enable. @@ -345,7 +345,7 @@ class DeepgramSTTService(STTService): base_url = url # 1. Initialize default_settings with hardcoded defaults - default_settings = DeepgramSTTSettings( + default_settings = self.Settings( model="nova-3-general", language=Language.EN, detect_entities=False, @@ -370,7 +370,7 @@ class DeepgramSTTService(STTService): # 3. Apply live_options overrides — only if settings not provided if live_options is not None: - _warn_deprecated_param("live_options", DeepgramSTTSettings) + _warn_deprecated_param("live_options", self.Settings) if not settings: # Extract init-only fields from live_options if live_options.sample_rate is not None and sample_rate is None: @@ -402,7 +402,7 @@ class DeepgramSTTService(STTService): "mip_opt_out", } lo_dict = {k: v for k, v in live_options.to_dict().items() if k not in init_only} - delta = DeepgramSTTSettings.from_mapping(lo_dict) + delta = self.Settings.from_mapping(lo_dict) default_settings.apply_update(delta) # 4. Apply settings delta (canonical API, always wins) @@ -494,7 +494,7 @@ class DeepgramSTTService(STTService): return changed # Sync extra to fields after the update so self._settings stays unambiguous - if isinstance(self._settings, DeepgramSTTSettings): + if isinstance(self._settings, self.Settings): self._settings._sync_extra_to_fields() if self._connection: diff --git a/src/pipecat/services/deepgram/tts.py b/src/pipecat/services/deepgram/tts.py index 791462a91..5fdbeb700 100644 --- a/src/pipecat/services/deepgram/tts.py +++ b/src/pipecat/services/deepgram/tts.py @@ -57,7 +57,7 @@ class DeepgramTTSService(WebsocketTTSService): """ Settings = DeepgramTTSSettings - _settings: DeepgramTTSSettings + _settings: Settings SUPPORTED_ENCODINGS = ("linear16", "mulaw", "alaw") @@ -69,7 +69,7 @@ class DeepgramTTSService(WebsocketTTSService): base_url: str = "wss://api.deepgram.com", sample_rate: Optional[int] = None, encoding: str = "linear16", - settings: Optional[DeepgramTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Deepgram WebSocket TTS service. @@ -79,7 +79,7 @@ class DeepgramTTSService(WebsocketTTSService): voice: Voice model to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=DeepgramTTSSettings(voice=...)`` instead. + Use ``settings=DeepgramTTSService.Settings(voice=...)`` instead. base_url: WebSocket base URL for Deepgram API. Defaults to "wss://api.deepgram.com". sample_rate: Audio sample rate in Hz. If None, uses service default. @@ -97,7 +97,7 @@ class DeepgramTTSService(WebsocketTTSService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = DeepgramTTSSettings( + default_settings = self.Settings( model=None, voice="aura-2-helena-en", language=None, @@ -105,7 +105,7 @@ class DeepgramTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", DeepgramTTSSettings, "voice") + _warn_deprecated_param("voice", self.Settings, "voice") default_settings.model = voice default_settings.voice = voice @@ -189,7 +189,7 @@ class DeepgramTTSService(WebsocketTTSService): """Apply a settings delta. Args: - delta: A :class:`TTSSettings` (or ``DeepgramTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``DeepgramTTSService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. @@ -367,7 +367,7 @@ class DeepgramHttpTTSService(TTSService): """ Settings = DeepgramTTSSettings - _settings: DeepgramTTSSettings + _settings: Settings def __init__( self, @@ -378,7 +378,7 @@ class DeepgramHttpTTSService(TTSService): base_url: str = "https://api.deepgram.com", sample_rate: Optional[int] = None, encoding: str = "linear16", - settings: Optional[DeepgramTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Deepgram TTS service. @@ -388,7 +388,7 @@ class DeepgramHttpTTSService(TTSService): voice: Voice model to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=DeepgramTTSSettings(voice=...)`` instead. + Use ``settings=DeepgramHttpTTSService.Settings(voice=...)`` instead. aiohttp_session: Shared aiohttp session for HTTP requests with connection pooling. base_url: Custom base URL for Deepgram API. Defaults to "https://api.deepgram.com". @@ -399,7 +399,7 @@ class DeepgramHttpTTSService(TTSService): **kwargs: Additional arguments passed to parent TTSService class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = DeepgramTTSSettings( + default_settings = self.Settings( model=None, voice="aura-2-helena-en", language=None, @@ -407,7 +407,7 @@ class DeepgramHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", DeepgramTTSSettings, "voice") + _warn_deprecated_param("voice", self.Settings, "voice") default_settings.model = voice default_settings.voice = voice diff --git a/src/pipecat/services/deepseek/llm.py b/src/pipecat/services/deepseek/llm.py index 8fc4b5db8..abb0d56bb 100644 --- a/src/pipecat/services/deepseek/llm.py +++ b/src/pipecat/services/deepseek/llm.py @@ -12,13 +12,13 @@ from typing import Optional from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class DeepSeekLLMSettings(OpenAILLMSettings): +class DeepSeekLLMSettings(BaseOpenAILLMService.Settings): """Settings for DeepSeekLLMService.""" pass @@ -32,7 +32,7 @@ class DeepSeekLLMService(OpenAILLMService): """ Settings = DeepSeekLLMSettings - _settings: DeepSeekLLMSettings + _settings: Settings def __init__( self, @@ -40,7 +40,7 @@ class DeepSeekLLMService(OpenAILLMService): api_key: str, base_url: str = "https://api.deepseek.com/v1", model: Optional[str] = None, - settings: Optional[DeepSeekLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the DeepSeek LLM service. @@ -51,18 +51,18 @@ class DeepSeekLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "deepseek-chat". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=DeepSeekLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = DeepSeekLLMSettings(model="deepseek-chat") + default_settings = self.Settings(model="deepseek-chat") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", DeepSeekLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/elevenlabs/stt.py b/src/pipecat/services/elevenlabs/stt.py index 76e854ade..7247241d3 100644 --- a/src/pipecat/services/elevenlabs/stt.py +++ b/src/pipecat/services/elevenlabs/stt.py @@ -217,13 +217,13 @@ class ElevenLabsSTTService(SegmentedSTTService): """ Settings = ElevenLabsSTTSettings - _settings: ElevenLabsSTTSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for ElevenLabs STT API. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsSTTSettings(...)`` instead. + Use ``settings=ElevenLabsSTTService.Settings(...)`` instead. Parameters: language: Target language for transcription. @@ -242,7 +242,7 @@ class ElevenLabsSTTService(SegmentedSTTService): model: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[ElevenLabsSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = ELEVENLABS_TTFS_P99, **kwargs, ): @@ -255,13 +255,13 @@ class ElevenLabsSTTService(SegmentedSTTService): model: Model ID for transcription. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsSTTSettings(model=...)`` instead. + Use ``settings=ElevenLabsSTTService.Settings(model=...)`` instead. sample_rate: Audio sample rate in Hz. If not provided, uses the pipeline's rate. params: Configuration parameters for the STT service. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsSTTSettings(...)`` instead. + Use ``settings=ElevenLabsSTTService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -270,7 +270,7 @@ class ElevenLabsSTTService(SegmentedSTTService): **kwargs: Additional arguments passed to SegmentedSTTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = ElevenLabsSTTSettings( + default_settings = self.Settings( model="scribe_v2", language=language_to_elevenlabs_language(Language.EN), tag_audio_events=None, @@ -278,12 +278,12 @@ class ElevenLabsSTTService(SegmentedSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", ElevenLabsSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", ElevenLabsSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = language_to_elevenlabs_language(params.language) @@ -450,13 +450,13 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): """ Settings = ElevenLabsRealtimeSTTSettings - _settings: ElevenLabsRealtimeSTTSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for ElevenLabs Realtime STT API. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsRealtimeSTTSettings(...)`` instead. + Use ``settings=ElevenLabsRealtimeSTTService.Settings(...)`` instead. Parameters: language_code: ISO-639-1 or ISO-639-3 language code. Leave None for auto-detection. @@ -496,7 +496,7 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): enable_logging: bool = False, include_language_detection: bool = False, params: Optional[InputParams] = None, - settings: Optional[ElevenLabsRealtimeSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = ELEVENLABS_REALTIME_TTFS_P99, **kwargs, ): @@ -511,7 +511,7 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): model: Model ID for transcription. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsRealtimeSTTSettings(model=...)`` instead. + Use ``settings=ElevenLabsRealtimeSTTService.Settings(model=...)`` instead. sample_rate: Audio sample rate in Hz. If not provided, uses the pipeline's rate. include_timestamps: Whether to include word-level timestamps in transcripts. @@ -520,7 +520,7 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): params: Configuration parameters for the STT service. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsRealtimeSTTSettings(...)`` instead. + Use ``settings=ElevenLabsRealtimeSTTService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -529,7 +529,7 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): **kwargs: Additional arguments passed to WebsocketSTTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = ElevenLabsRealtimeSTTSettings( + default_settings = self.Settings( model="scribe_v2_realtime", language=None, vad_silence_threshold_secs=None, @@ -540,12 +540,12 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", ElevenLabsRealtimeSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", ElevenLabsRealtimeSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = params.language_code if params.commit_strategy != CommitStrategy.MANUAL: @@ -597,7 +597,7 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): """Apply a settings delta and reconnect if anything changed. Args: - delta: A :class:`STTSettings` (or ``ElevenLabsRealtimeSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``ElevenLabsRealtimeSTTService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 01f3cd516..46acc2f1c 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -317,13 +317,13 @@ class ElevenLabsTTSService(WebsocketTTSService): """ Settings = ElevenLabsTTSSettings - _settings: ElevenLabsTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for ElevenLabs TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsTTSSettings(...)`` instead. + Use ``settings=ElevenLabsTTSService.Settings(...)`` instead. Parameters: language: Language to use for synthesis. @@ -364,7 +364,7 @@ class ElevenLabsTTSService(WebsocketTTSService): enable_logging: Optional[bool] = None, pronunciation_dictionary_locators: Optional[List[PronunciationDictionaryLocator]] = None, params: Optional[InputParams] = None, - settings: Optional[ElevenLabsTTSSettings] = None, + settings: Optional[Settings] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, aggregate_sentences: Optional[bool] = None, **kwargs, @@ -376,12 +376,12 @@ class ElevenLabsTTSService(WebsocketTTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsTTSSettings(voice=...)`` instead. + Use ``settings=ElevenLabsTTSService.Settings(voice=...)`` instead. model: TTS model to use (e.g., "eleven_turbo_v2_5"). .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsTTSSettings(model=...)`` instead. + Use ``settings=ElevenLabsTTSService.Settings(model=...)`` instead. url: WebSocket URL for ElevenLabs TTS API. sample_rate: Audio sample rate. If None, uses default. @@ -393,7 +393,7 @@ class ElevenLabsTTSService(WebsocketTTSService): params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsTTSSettings(...)`` instead. + Use ``settings=ElevenLabsTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -423,7 +423,7 @@ class ElevenLabsTTSService(WebsocketTTSService): # after a short period not receiving any audio. # 1. Initialize default_settings with hardcoded defaults - default_settings = ElevenLabsTTSSettings( + default_settings = self.Settings( model="eleven_turbo_v2_5", voice=None, language=None, @@ -437,16 +437,16 @@ class ElevenLabsTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", ElevenLabsTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", ElevenLabsTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided _pronunciation_dictionary_locators = pronunciation_dictionary_locators if params is not None: - _warn_deprecated_param("params", ElevenLabsTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -533,11 +533,11 @@ class ElevenLabsTTSService(WebsocketTTSService): """Apply a settings delta, reconnecting as needed. Uses the declarative ``URL_FIELDS`` and ``VOICE_SETTINGS_FIELDS`` - sets on :class:`ElevenLabsTTSSettings` to decide whether to + sets on :class:`ElevenLabsTTSService.Settings` to decide whether to reconnect the WebSocket or close the current audio context. Args: - delta: A :class:`TTSSettings` (or ``ElevenLabsTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``ElevenLabsTTSService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. @@ -550,19 +550,19 @@ class ElevenLabsTTSService(WebsocketTTSService): # Rebuild voice settings for next context self._voice_settings = self._set_voice_settings() - url_changed = bool(changed.keys() & ElevenLabsTTSSettings.URL_FIELDS) - voice_settings_changed = bool(changed.keys() & ElevenLabsTTSSettings.VOICE_SETTINGS_FIELDS) + url_changed = bool(changed.keys() & self.Settings.URL_FIELDS) + voice_settings_changed = bool(changed.keys() & self.Settings.VOICE_SETTINGS_FIELDS) if url_changed: logger.debug( - f"URL-level setting changed ({changed.keys() & ElevenLabsTTSSettings.URL_FIELDS}), " + f"URL-level setting changed ({changed.keys() & self.Settings.URL_FIELDS}), " f"reconnecting WebSocket" ) await self._disconnect() await self._connect() elif voice_settings_changed: logger.debug( - f"Voice settings changed ({changed.keys() & ElevenLabsTTSSettings.VOICE_SETTINGS_FIELDS}), " + f"Voice settings changed ({changed.keys() & self.Settings.VOICE_SETTINGS_FIELDS}), " f"closing current context to apply changes" ) audio_contexts = self.get_audio_contexts() @@ -573,7 +573,7 @@ class ElevenLabsTTSService(WebsocketTTSService): if not url_changed: # Reconnect applies all settings; only warn about fields not handled # by voice settings or URL changes. - handled = ElevenLabsTTSSettings.URL_FIELDS | ElevenLabsTTSSettings.VOICE_SETTINGS_FIELDS + handled = self.Settings.URL_FIELDS | self.Settings.VOICE_SETTINGS_FIELDS self._warn_unhandled_updated_settings(changed.keys() - handled) return changed @@ -906,13 +906,13 @@ class ElevenLabsHttpTTSService(TTSService): """ Settings = ElevenLabsHttpTTSSettings - _settings: ElevenLabsHttpTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for ElevenLabs HTTP TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsHttpTTSSettings(...)`` instead. + Use ``settings=ElevenLabsHttpTTSService.Settings(...)`` instead. Parameters: language: Language to use for synthesis. @@ -947,7 +947,7 @@ class ElevenLabsHttpTTSService(TTSService): sample_rate: Optional[int] = None, pronunciation_dictionary_locators: Optional[List[PronunciationDictionaryLocator]] = None, params: Optional[InputParams] = None, - settings: Optional[ElevenLabsHttpTTSSettings] = None, + settings: Optional[Settings] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, aggregate_sentences: Optional[bool] = None, **kwargs, @@ -959,13 +959,13 @@ class ElevenLabsHttpTTSService(TTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsHttpTTSSettings(voice=...)`` instead. + Use ``settings=ElevenLabsHttpTTSService.Settings(voice=...)`` instead. aiohttp_session: aiohttp ClientSession for HTTP requests. model: TTS model to use (e.g., "eleven_turbo_v2_5"). .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsHttpTTSSettings(model=...)`` instead. + Use ``settings=ElevenLabsHttpTTSService.Settings(model=...)`` instead. base_url: Base URL for ElevenLabs HTTP API. sample_rate: Audio sample rate. If None, uses default. @@ -974,7 +974,7 @@ class ElevenLabsHttpTTSService(TTSService): params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=ElevenLabsHttpTTSSettings(...)`` instead. + Use ``settings=ElevenLabsHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -987,7 +987,7 @@ class ElevenLabsHttpTTSService(TTSService): **kwargs: Additional arguments passed to the parent service. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = ElevenLabsHttpTTSSettings( + default_settings = self.Settings( model="eleven_turbo_v2_5", voice=None, language=None, @@ -1002,16 +1002,16 @@ class ElevenLabsHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", ElevenLabsHttpTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", ElevenLabsHttpTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided _pronunciation_dictionary_locators = pronunciation_dictionary_locators if params is not None: - _warn_deprecated_param("params", ElevenLabsHttpTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -1091,7 +1091,7 @@ class ElevenLabsHttpTTSService(TTSService): """Apply a settings delta and rebuild voice settings. Args: - delta: A :class:`TTSSettings` (or ``ElevenLabsHttpTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``ElevenLabsHttpTTSService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/fal/image.py b/src/pipecat/services/fal/image.py index cb4d646ba..3c15a918a 100644 --- a/src/pipecat/services/fal/image.py +++ b/src/pipecat/services/fal/image.py @@ -71,13 +71,13 @@ class FalImageGenService(ImageGenService): """ Settings = FalImageGenSettings - _settings: FalImageGenSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Fal.ai image generation. .. deprecated:: 0.0.105 - Use ``settings=FalImageGenSettings(...)`` instead. + Use ``settings=FalImageGenService.Settings(...)`` instead. Parameters: seed: Random seed for reproducible generation. If None, uses random seed. @@ -97,7 +97,7 @@ class FalImageGenService(ImageGenService): enable_safety_checker: bool = True format: str = "png" - _settings: FalImageGenSettings + _settings: Settings def __init__( self, @@ -106,7 +106,7 @@ class FalImageGenService(ImageGenService): aiohttp_session: aiohttp.ClientSession, model: Optional[str] = None, key: Optional[str] = None, - settings: Optional[FalImageGenSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the FalImageGenService. @@ -115,13 +115,13 @@ class FalImageGenService(ImageGenService): params: Input parameters for image generation configuration. .. deprecated:: 0.0.105 - Use ``settings=FalImageGenSettings(...)`` instead. + Use ``settings=FalImageGenService.Settings(...)`` instead. aiohttp_session: HTTP client session for downloading generated images. model: The Fal.ai model to use for generation. Defaults to "fal-ai/fast-sdxl". .. deprecated:: 0.0.105 - Use ``settings=FalImageGenSettings(model=...)`` instead. + Use ``settings=FalImageGenService.Settings(model=...)`` instead. key: Optional API key for Fal.ai. If provided, sets FAL_KEY environment variable. settings: Runtime-updatable settings. When provided alongside deprecated @@ -129,7 +129,7 @@ class FalImageGenService(ImageGenService): **kwargs: Additional arguments passed to parent ImageGenService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = FalImageGenSettings( + default_settings = self.Settings( model="fal-ai/fast-sdxl", seed=None, num_inference_steps=8, @@ -142,11 +142,11 @@ class FalImageGenService(ImageGenService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", FalImageGenSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if params is not None: - _warn_deprecated_param("params", FalImageGenSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.seed = params.seed default_settings.num_inference_steps = params.num_inference_steps diff --git a/src/pipecat/services/fal/stt.py b/src/pipecat/services/fal/stt.py index 692878bf6..c1aae24b7 100644 --- a/src/pipecat/services/fal/stt.py +++ b/src/pipecat/services/fal/stt.py @@ -156,13 +156,13 @@ class FalSTTService(SegmentedSTTService): """ Settings = FalSTTSettings - _settings: FalSTTSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Fal's Wizper API. .. deprecated:: 0.0.105 - Use ``settings=FalSTTSettings(...)`` instead. + Use ``settings=FalSTTService.Settings(...)`` instead. Parameters: language: Language of the audio input. Defaults to English. @@ -186,7 +186,7 @@ class FalSTTService(SegmentedSTTService): version: str = "3", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[FalSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = FAL_TTFS_P99, **kwargs, ): @@ -204,7 +204,7 @@ class FalSTTService(SegmentedSTTService): params: Configuration parameters for the Wizper API. .. deprecated:: 0.0.105 - Use ``settings=FalSTTSettings(...)`` for model/language and + Use ``settings=FalSTTService.Settings(...)`` for model/language and direct init parameters for task/chunk_level/version instead. settings: Runtime-updatable settings. When provided alongside deprecated @@ -214,7 +214,7 @@ class FalSTTService(SegmentedSTTService): **kwargs: Additional arguments passed to SegmentedSTTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = FalSTTSettings( + default_settings = self.Settings( model=None, language=language_to_fal_language(Language.EN), ) @@ -223,7 +223,7 @@ class FalSTTService(SegmentedSTTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", FalSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = language_to_fal_language(params.language) diff --git a/src/pipecat/services/fireworks/llm.py b/src/pipecat/services/fireworks/llm.py index 118090be9..fdd688149 100644 --- a/src/pipecat/services/fireworks/llm.py +++ b/src/pipecat/services/fireworks/llm.py @@ -12,13 +12,13 @@ from typing import Optional from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class FireworksLLMSettings(OpenAILLMSettings): +class FireworksLLMSettings(BaseOpenAILLMService.Settings): """Settings for FireworksLLMService.""" pass @@ -32,7 +32,7 @@ class FireworksLLMService(OpenAILLMService): """ Settings = FireworksLLMSettings - _settings: FireworksLLMSettings + _settings: Settings def __init__( self, @@ -40,7 +40,7 @@ class FireworksLLMService(OpenAILLMService): api_key: str, model: Optional[str] = None, base_url: str = "https://api.fireworks.ai/inference/v1", - settings: Optional[FireworksLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Fireworks LLM service. @@ -50,7 +50,7 @@ class FireworksLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "accounts/fireworks/models/firefunction-v2". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=FireworksLLMService.Settings(model=...)`` instead. base_url: The base URL for Fireworks API. Defaults to "https://api.fireworks.ai/inference/v1". settings: Runtime-updatable settings. When provided alongside deprecated @@ -58,11 +58,11 @@ class FireworksLLMService(OpenAILLMService): **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = FireworksLLMSettings(model="accounts/fireworks/models/firefunction-v2") + default_settings = self.Settings(model="accounts/fireworks/models/firefunction-v2") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", FireworksLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/fish/tts.py b/src/pipecat/services/fish/tts.py index f21b42b97..39669574a 100644 --- a/src/pipecat/services/fish/tts.py +++ b/src/pipecat/services/fish/tts.py @@ -85,13 +85,13 @@ class FishAudioTTSService(InterruptibleTTSService): """ Settings = FishAudioTTSSettings - _settings: FishAudioTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Fish Audio TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=FishAudioTTSSettings(...)`` instead. + Use ``settings=FishAudioTTSService.Settings(...)`` instead. Parameters: language: Language for synthesis. Defaults to English. @@ -117,7 +117,7 @@ class FishAudioTTSService(InterruptibleTTSService): output_format: FishAudioOutputFormat = "pcm", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[FishAudioTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Fish Audio TTS service. @@ -127,7 +127,7 @@ class FishAudioTTSService(InterruptibleTTSService): reference_id: Reference ID of the voice model to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=FishAudioTTSSettings(voice=...)`` instead. + Use ``settings=FishAudioTTSService.Settings(voice=...)`` instead. model: Deprecated. Reference ID of the voice model to use for synthesis. @@ -138,14 +138,14 @@ class FishAudioTTSService(InterruptibleTTSService): model_id: Specify which Fish Audio TTS model to use (e.g. "s1"). .. deprecated:: 0.0.105 - Use ``settings=FishAudioTTSSettings(model=...)`` instead. + Use ``settings=FishAudioTTSService.Settings(model=...)`` instead. output_format: Audio output format. Defaults to "pcm". sample_rate: Audio sample rate. If None, uses default. params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=FishAudioTTSSettings(...)`` instead. + Use ``settings=FishAudioTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -171,7 +171,7 @@ class FishAudioTTSService(InterruptibleTTSService): reference_id = model # 1. Initialize default_settings with hardcoded defaults - default_settings = FishAudioTTSSettings( + default_settings = self.Settings( model="s2-pro", voice=None, language=None, @@ -185,15 +185,15 @@ class FishAudioTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if reference_id is not None: - _warn_deprecated_param("reference_id", FishAudioTTSSettings, "voice") + _warn_deprecated_param("reference_id", self.Settings, "voice") default_settings.voice = reference_id if model_id is not None: - _warn_deprecated_param("model_id", FishAudioTTSSettings, "model") + _warn_deprecated_param("model_id", self.Settings, "model") default_settings.model = model_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", FishAudioTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.latency is not None: default_settings.latency = params.latency @@ -240,7 +240,7 @@ class FishAudioTTSService(InterruptibleTTSService): Any change to voice or model triggers a WebSocket reconnect. Args: - delta: A :class:`TTSSettings` (or ``FishAudioTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``FishAudioTTSService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/gladia/config.py b/src/pipecat/services/gladia/config.py index ec8997d7c..c492a04a1 100644 --- a/src/pipecat/services/gladia/config.py +++ b/src/pipecat/services/gladia/config.py @@ -153,7 +153,7 @@ class GladiaInputParams(BaseModel): """Configuration parameters for the Gladia STT service. .. deprecated:: 0.0.105 - Use ``settings=GladiaSTTSettings(...)`` for runtime-updatable + Use ``settings=GladiaSTTService.Settings(...)`` for runtime-updatable fields and direct init parameters for encoding/bit_depth/channels. Parameters: diff --git a/src/pipecat/services/gladia/stt.py b/src/pipecat/services/gladia/stt.py index fc09d94fc..a68bf78b3 100644 --- a/src/pipecat/services/gladia/stt.py +++ b/src/pipecat/services/gladia/stt.py @@ -231,7 +231,7 @@ class GladiaSTTService(WebsocketSTTService): """ Settings = GladiaSTTSettings - _settings: GladiaSTTSettings + _settings: Settings # Maintain backward compatibility InputParams = _InputParamsDescriptor() @@ -251,7 +251,7 @@ class GladiaSTTService(WebsocketSTTService): params: Optional[GladiaInputParams] = None, max_buffer_size: int = 1024 * 1024 * 20, # 20MB default buffer should_interrupt: bool = True, - settings: Optional[GladiaSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = GLADIA_TTFS_P99, **kwargs, ): @@ -274,12 +274,12 @@ class GladiaSTTService(WebsocketSTTService): model: Model to use for transcription. .. deprecated:: 0.0.105 - Use ``settings=GladiaSTTSettings(model=...)`` instead. + Use ``settings=GladiaSTTService.Settings(model=...)`` instead. params: Additional configuration parameters for Gladia service. .. deprecated:: 0.0.105 - Use ``settings=GladiaSTTSettings(...)`` for runtime-updatable + Use ``settings=GladiaSTTService.Settings(...)`` for runtime-updatable fields and direct init parameters for encoding/bit_depth/channels. max_buffer_size: Maximum size of audio buffer in bytes. Defaults to 20MB. @@ -302,7 +302,7 @@ class GladiaSTTService(WebsocketSTTService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = GladiaSTTSettings( + default_settings = self.Settings( model="solaria-1", language=None, language_config=None, @@ -317,12 +317,12 @@ class GladiaSTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GladiaSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GladiaSTTSettings) + _warn_deprecated_param("params", self.Settings) if params.language is not None: with warnings.catch_warnings(): warnings.simplefilter("always") @@ -469,7 +469,7 @@ class GladiaSTTService(WebsocketSTTService): await super().start(frame) await self._connect() - async def _update_settings(self, delta: GladiaSTTSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply settings delta. Settings are stored but not applied to the active session. diff --git a/src/pipecat/services/google/gemini_live/llm.py b/src/pipecat/services/google/gemini_live/llm.py index 07f185f99..167abb938 100644 --- a/src/pipecat/services/google/gemini_live/llm.py +++ b/src/pipecat/services/google/gemini_live/llm.py @@ -553,7 +553,7 @@ class InputParams(BaseModel): """Input parameters for Gemini Live generation. .. deprecated:: 0.0.105 - Use ``GeminiLiveLLMSettings`` instead. + Use ``GeminiLiveLLMService.Settings`` instead. Parameters: frequency_penalty: Frequency penalty for generation (0.0-2.0). Defaults to None. @@ -643,7 +643,7 @@ class GeminiLiveLLMService(LLMService): """ Settings = GeminiLiveLLMSettings - _settings: GeminiLiveLLMSettings + _settings: Settings # Overriding the default adapter to use the Gemini one. adapter_class = GeminiLLMAdapter @@ -660,7 +660,7 @@ class GeminiLiveLLMService(LLMService): system_instruction: Optional[str] = None, tools: Optional[Union[List[dict], ToolsSchema]] = None, params: Optional[InputParams] = None, - settings: Optional[GeminiLiveLLMSettings] = None, + settings: Optional[Settings] = None, inference_on_context_initialization: bool = True, file_api_base_url: str = "https://generativelanguage.googleapis.com/v1beta/files", http_options: Optional[HttpOptions] = None, @@ -680,12 +680,12 @@ class GeminiLiveLLMService(LLMService): model: Model identifier to use. .. deprecated:: 0.0.105 - Use ``settings=GeminiLiveLLMSettings(model=...)`` instead. + Use ``settings=GeminiLiveLLMService.Settings(model=...)`` instead. voice_id: TTS voice identifier. Defaults to "Charon". .. deprecated:: 0.0.105 - Use ``settings=GeminiLiveLLMSettings(voice=...)`` instead. + Use ``settings=GeminiLiveLLMService.Settings(voice=...)`` instead. start_audio_paused: Whether to start with audio input paused. Defaults to False. start_video_paused: Whether to start with video input paused. Defaults to False. system_instruction: System prompt for the model. Defaults to None. @@ -693,7 +693,7 @@ class GeminiLiveLLMService(LLMService): params: Configuration parameters for the model. .. deprecated:: 0.0.105 - Use ``settings=GeminiLiveLLMSettings(...)`` instead. + Use ``settings=GeminiLiveLLMService.Settings(...)`` instead. settings: Gemini Live LLM settings. If provided together with deprecated top-level parameters, the ``settings`` values take precedence. @@ -716,7 +716,7 @@ class GeminiLiveLLMService(LLMService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = GeminiLiveLLMSettings( + default_settings = self.Settings( model="models/gemini-2.5-flash-native-audio-preview-12-2025", system_instruction=system_instruction, voice="Charon", @@ -742,15 +742,15 @@ class GeminiLiveLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GeminiLiveLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id != "Charon": - _warn_deprecated_param("voice_id", GeminiLiveLLMSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GeminiLiveLLMSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.frequency_penalty = params.frequency_penalty default_settings.max_tokens = params.max_tokens diff --git a/src/pipecat/services/google/gemini_live/vertex/llm.py b/src/pipecat/services/google/gemini_live/vertex/llm.py index cdfde1660..c610b4cdb 100644 --- a/src/pipecat/services/google/gemini_live/vertex/llm.py +++ b/src/pipecat/services/google/gemini_live/vertex/llm.py @@ -20,7 +20,6 @@ from loguru import logger from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.services.google.gemini_live.llm import ( GeminiLiveLLMService, - GeminiLiveLLMSettings, GeminiMediaResolution, GeminiModalities, HttpOptions, @@ -43,7 +42,7 @@ except ModuleNotFoundError as e: @dataclass -class GeminiLiveVertexLLMSettings(GeminiLiveLLMSettings): +class GeminiLiveVertexLLMSettings(GeminiLiveLLMService.Settings): """Settings for GeminiLiveVertexLLMService.""" pass @@ -58,7 +57,7 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): """ Settings = GeminiLiveVertexLLMSettings - _settings: GeminiLiveVertexLLMSettings + _settings: Settings def __init__( self, @@ -74,7 +73,7 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): system_instruction: Optional[str] = None, tools: Optional[Union[List[dict], ToolsSchema]] = None, params: Optional[InputParams] = None, - settings: Optional[GeminiLiveVertexLLMSettings] = None, + settings: Optional[Settings] = None, inference_on_context_initialization: bool = True, file_api_base_url: str = "https://generativelanguage.googleapis.com/v1beta/files", http_options: Optional[HttpOptions] = None, @@ -90,12 +89,12 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): model: Model identifier to use. .. deprecated:: 0.0.105 - Use ``settings=GeminiLiveLLMSettings(model=...)`` instead. + Use ``settings=GeminiLiveVertexLLMService.Settings(model=...)`` instead. voice_id: TTS voice identifier. Defaults to "Charon". .. deprecated:: 0.0.105 - Use ``settings=GeminiLiveVertexLLMSettings(voice=...)`` instead. + Use ``settings=GeminiLiveVertexLLMService.Settings(voice=...)`` instead. start_audio_paused: Whether to start with audio input paused. Defaults to False. start_video_paused: Whether to start with video input paused. Defaults to False. system_instruction: System prompt for the model. Defaults to None. @@ -104,7 +103,7 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): location and project ID. .. deprecated:: 0.0.105 - Use ``settings=GeminiLiveLLMSettings(...)`` instead. + Use ``settings=GeminiLiveVertexLLMService.Settings(...)`` instead. settings: Gemini Live LLM settings. If provided together with deprecated top-level parameters, the ``settings`` values take precedence. @@ -136,7 +135,7 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): # double deprecation warnings from the parent. # 1. Initialize default_settings with hardcoded defaults - default_settings = GeminiLiveVertexLLMSettings( + default_settings = self.Settings( model="google/gemini-live-2.5-flash-native-audio", voice="Charon", frequency_penalty=None, @@ -161,15 +160,15 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GeminiLiveVertexLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id != "Charon": - _warn_deprecated_param("voice_id", GeminiLiveVertexLLMSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GeminiLiveVertexLLMSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.frequency_penalty = params.frequency_penalty default_settings.max_tokens = params.max_tokens diff --git a/src/pipecat/services/google/image.py b/src/pipecat/services/google/image.py index 5cf37aa4e..40706ad50 100644 --- a/src/pipecat/services/google/image.py +++ b/src/pipecat/services/google/image.py @@ -60,13 +60,13 @@ class GoogleImageGenService(ImageGenService): """ Settings = GoogleImageGenSettings - _settings: GoogleImageGenSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Google image generation. .. deprecated:: 0.0.105 - Use ``settings=GoogleImageGenSettings(...)`` instead. + Use ``settings=GoogleImageGenService.Settings(...)`` instead. Parameters: number_of_images: Number of images to generate (1-8). Defaults to 1. @@ -84,7 +84,7 @@ class GoogleImageGenService(ImageGenService): api_key: str, params: Optional[InputParams] = None, http_options: Optional[Any] = None, - settings: Optional[GoogleImageGenSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the GoogleImageGenService with API key and parameters. @@ -94,7 +94,7 @@ class GoogleImageGenService(ImageGenService): params: Configuration parameters for image generation. .. deprecated:: 0.0.105 - Use ``settings=GoogleImageGenSettings(...)`` instead. + Use ``settings=GoogleImageGenService.Settings(...)`` instead. http_options: HTTP options for the client. settings: Runtime-updatable settings. When provided alongside deprecated @@ -102,7 +102,7 @@ class GoogleImageGenService(ImageGenService): **kwargs: Additional arguments passed to the parent ImageGenService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GoogleImageGenSettings( + default_settings = self.Settings( model="imagen-3.0-generate-002", number_of_images=1, negative_prompt=None, @@ -110,7 +110,7 @@ class GoogleImageGenService(ImageGenService): # 2. Apply params overrides (deprecated) if params is not None: - _warn_deprecated_param("params", GoogleImageGenSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.model = params.model default_settings.number_of_images = params.number_of_images diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index cf9e91b53..2ade23906 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -743,7 +743,7 @@ class GoogleLLMService(LLMService): """ Settings = GoogleLLMSettings - _settings: GoogleLLMSettings + _settings: Settings # Overriding the default adapter to use the Gemini one. adapter_class = GeminiLLMAdapter @@ -755,7 +755,7 @@ class GoogleLLMService(LLMService): """Input parameters for Google AI models. .. deprecated:: 0.0.105 - Use ``settings=GoogleLLMSettings(...)`` instead. + Use ``settings=GoogleLLMService.Settings(...)`` instead. Parameters: max_tokens: Maximum number of tokens to generate. @@ -784,7 +784,7 @@ class GoogleLLMService(LLMService): api_key: str, model: Optional[str] = None, params: Optional[InputParams] = None, - settings: Optional[GoogleLLMSettings] = None, + settings: Optional[Settings] = None, system_instruction: Optional[str] = None, tools: Optional[List[Dict[str, Any]]] = None, tool_config: Optional[Dict[str, Any]] = None, @@ -798,12 +798,12 @@ class GoogleLLMService(LLMService): model: Model name to use. .. deprecated:: 0.0.105 - Use ``settings=GoogleLLMSettings(model=...)`` instead. + Use ``settings=GoogleLLMService.Settings(model=...)`` instead. params: Optional model parameters for inference. .. deprecated:: 0.0.105 - Use ``settings=GoogleLLMSettings(...)`` instead. + Use ``settings=GoogleLLMService.Settings(...)`` instead. settings: Runtime-updatable settings for this service. When both deprecated parameters and *settings* are provided, *settings* @@ -811,14 +811,14 @@ class GoogleLLMService(LLMService): system_instruction: System instruction/prompt for the model. .. deprecated:: 0.0.105 - Use ``settings=GoogleLLMSettings(system_instruction=...)`` instead. + Use ``settings=GoogleLLMService.Settings(system_instruction=...)`` instead. tools: List of available tools/functions. tool_config: Configuration for tool usage. http_options: HTTP options for the client. **kwargs: Additional arguments passed to parent class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GoogleLLMSettings( + default_settings = self.Settings( model="gemini-2.5-flash", system_instruction=None, max_tokens=4096, @@ -836,15 +836,15 @@ class GoogleLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GoogleLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if system_instruction is not None: - _warn_deprecated_param("system_instruction", GoogleLLMSettings, "system_instruction") + _warn_deprecated_param("system_instruction", self.Settings, "system_instruction") default_settings.system_instruction = system_instruction # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GoogleLLMSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/google/openai/llm.py b/src/pipecat/services/google/openai/llm.py index cd5e0f060..717c1e379 100644 --- a/src/pipecat/services/google/openai/llm.py +++ b/src/pipecat/services/google/openai/llm.py @@ -28,13 +28,13 @@ from loguru import logger from pipecat.frames.frames import LLMTextFrame from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class GoogleOpenAILLMSettings(OpenAILLMSettings): +class GoogleOpenAILLMSettings(BaseOpenAILLMService.Settings): """Settings for GoogleLLMOpenAIBetaService.""" pass @@ -59,7 +59,7 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService): """ Settings = GoogleOpenAILLMSettings - _settings: GoogleOpenAILLMSettings + _settings: Settings def __init__( self, @@ -67,7 +67,7 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService): api_key: str, base_url: str = "https://generativelanguage.googleapis.com/v1beta/openai/", model: Optional[str] = None, - settings: Optional[GoogleOpenAILLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Google LLM service. @@ -78,7 +78,7 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService): model: Google model name to use (e.g., "gemini-2.0-flash"). .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=GoogleLLMOpenAIBetaService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -96,11 +96,11 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = GoogleOpenAILLMSettings(model="gemini-2.0-flash") + default_settings = self.Settings(model="gemini-2.0-flash") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GoogleOpenAILLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/google/stt.py b/src/pipecat/services/google/stt.py index 70e51ac3c..cab176668 100644 --- a/src/pipecat/services/google/stt.py +++ b/src/pipecat/services/google/stt.py @@ -415,7 +415,7 @@ class GoogleSTTService(STTService): """ Settings = GoogleSTTSettings - _settings: GoogleSTTSettings + _settings: Settings # Google Cloud's STT service has a connection time limit of 5 minutes per stream. # They've shared an "endless streaming" example that guided this implementation: @@ -427,7 +427,7 @@ class GoogleSTTService(STTService): """Configuration parameters for Google Speech-to-Text. .. deprecated:: 0.0.105 - Use ``settings=GoogleSTTSettings(...)`` instead. + Use ``settings=GoogleSTTService.Settings(...)`` instead. Parameters: languages: Single language or list of recognition languages. First language is primary. @@ -488,7 +488,7 @@ class GoogleSTTService(STTService): location: str = "global", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[GoogleSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = GOOGLE_TTFS_P99, **kwargs, ): @@ -502,7 +502,7 @@ class GoogleSTTService(STTService): params: Configuration parameters for the service. .. deprecated:: 0.0.105 - Use ``settings=GoogleSTTSettings(...)`` instead. + Use ``settings=GoogleSTTService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated ``params``, ``settings`` values take precedence. @@ -511,7 +511,7 @@ class GoogleSTTService(STTService): **kwargs: Additional arguments passed to STTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GoogleSTTSettings( + default_settings = self.Settings( language=None, languages=[Language.EN_US], language_codes=None, @@ -531,7 +531,7 @@ class GoogleSTTService(STTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GoogleSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.languages = list(params.language_list) default_settings.model = params.model @@ -655,7 +655,7 @@ class GoogleSTTService(STTService): """Update the service's recognition languages. .. deprecated:: 0.0.104 - Use ``STTUpdateSettingsFrame`` with ``GoogleSTTSettings(languages=...)`` + Use ``STTUpdateSettingsFrame`` with ``GoogleSTTService.Settings(languages=...)`` instead. Args: @@ -665,13 +665,13 @@ class GoogleSTTService(STTService): warnings.simplefilter("always") warnings.warn( "set_languages() is deprecated. Use STTUpdateSettingsFrame with " - "GoogleSTTSettings(languages=...) instead.", + "self.Settings(languages=...) instead.", DeprecationWarning, ) logger.debug(f"Switching STT languages to: {languages}") - await self._update_settings(GoogleSTTSettings(languages=list(languages))) + await self._update_settings(self.Settings(languages=list(languages))) - async def _update_settings(self, delta: GoogleSTTSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply settings delta and reconnect if anything changed. Handles ``language`` from base ``set_language`` by converting it to @@ -698,8 +698,8 @@ class GoogleSTTService(STTService): with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( - "GoogleSTTSettings.language_codes is deprecated. " - "Use GoogleSTTSettings.languages (List[Language]) instead.", + "self.Settings.language_codes is deprecated. " + "Use self.Settings.languages (List[Language]) instead.", DeprecationWarning, stacklevel=2, ) @@ -756,7 +756,7 @@ class GoogleSTTService(STTService): """Update service options dynamically. .. deprecated:: - Use ``STTUpdateSettingsFrame`` with ``GoogleSTTSettings(...)`` + Use ``STTUpdateSettingsFrame`` with ``GoogleSTTService.Settings(...)`` instead. Args: @@ -780,11 +780,11 @@ class GoogleSTTService(STTService): warnings.simplefilter("always") warnings.warn( "update_options() is deprecated. Use STTUpdateSettingsFrame with " - "GoogleSTTSettings(...) instead.", + "self.Settings(...) instead.", DeprecationWarning, ) # Build a settings delta from the provided options - delta = GoogleSTTSettings() + delta = self.Settings() if languages is not None: delta.languages = list(languages) diff --git a/src/pipecat/services/google/tts.py b/src/pipecat/services/google/tts.py index 9d92c91ea..c2e32ad3c 100644 --- a/src/pipecat/services/google/tts.py +++ b/src/pipecat/services/google/tts.py @@ -523,7 +523,7 @@ class GoogleTTSSettings(TTSSettings): #: .. deprecated:: 0.0.105 -#: Use ``GoogleTTSSettings`` instead. +#: Use ``GoogleTTSService.Settings`` instead. GoogleStreamTTSSettings = GoogleTTSSettings @@ -559,13 +559,13 @@ class GoogleHttpTTSService(TTSService): """ Settings = GoogleHttpTTSSettings - _settings: GoogleHttpTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Google HTTP TTS voice customization. .. deprecated:: 0.0.105 - Use ``GoogleHttpTTSSettings`` directly via the ``settings`` parameter instead. + Use ``GoogleHttpTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: pitch: Voice pitch adjustment (e.g., "+2st", "-50%"). @@ -596,7 +596,7 @@ class GoogleHttpTTSService(TTSService): voice_id: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[GoogleHttpTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initializes the Google HTTP TTS service. @@ -608,20 +608,20 @@ class GoogleHttpTTSService(TTSService): voice_id: Google TTS voice identifier (e.g., "en-US-Standard-A"). .. deprecated:: 0.0.105 - Use ``settings=GoogleHttpTTSSettings(voice=...)`` instead. + Use ``settings=GoogleHttpTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate in Hz. If None, uses default. params: Voice customization parameters including pitch, rate, volume, etc. .. deprecated:: 0.0.105 - Use ``settings=GoogleHttpTTSSettings(...)`` instead. + Use ``settings=GoogleHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GoogleHttpTTSSettings( + default_settings = self.Settings( model=None, voice="en-US-Chirp3-HD-Charon", language="en-US", @@ -636,12 +636,12 @@ class GoogleHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", GoogleHttpTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GoogleHttpTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.pitch is not None: default_settings.pitch = params.pitch @@ -747,7 +747,7 @@ class GoogleHttpTTSService(TTSService): Args: delta: Settings delta. Can include 'speaking_rate' (float). """ - if isinstance(delta, GoogleHttpTTSSettings) and is_given(delta.speaking_rate): + if isinstance(delta, self.Settings) and is_given(delta.speaking_rate): rate_value = float(delta.speaking_rate) if not (0.25 <= rate_value <= 2.0): logger.warning( @@ -1022,13 +1022,13 @@ class GoogleTTSService(GoogleBaseTTSService): """ Settings = GoogleTTSSettings - _settings: GoogleTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Google streaming TTS configuration. .. deprecated:: 0.0.105 - Use ``GoogleTTSSettings`` directly via the ``settings`` parameter instead. + Use ``GoogleTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language for synthesis. Defaults to English. @@ -1048,7 +1048,7 @@ class GoogleTTSService(GoogleBaseTTSService): voice_cloning_key: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[GoogleTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initializes the Google streaming TTS service. @@ -1060,21 +1060,21 @@ class GoogleTTSService(GoogleBaseTTSService): voice_id: Google TTS voice identifier (e.g., "en-US-Chirp3-HD-Charon"). .. deprecated:: 0.0.105 - Use ``settings=GoogleTTSSettings(voice=...)`` instead. + Use ``settings=GoogleTTSService.Settings(voice=...)`` instead. voice_cloning_key: The voice cloning key for Chirp 3 custom voices. sample_rate: Audio sample rate in Hz. If None, uses default. params: Language configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=GoogleTTSSettings(...)`` instead. + Use ``settings=GoogleTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GoogleTTSSettings( + default_settings = self.Settings( model=None, voice="en-US-Chirp3-HD-Charon", language="en-US", @@ -1083,12 +1083,12 @@ class GoogleTTSService(GoogleBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", GoogleTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GoogleTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -1119,7 +1119,7 @@ class GoogleTTSService(GoogleBaseTTSService): Args: delta: Settings delta. Can include 'speaking_rate' (float). """ - if isinstance(delta, GoogleTTSSettings) and is_given(delta.speaking_rate): + if isinstance(delta, self.Settings) and is_given(delta.speaking_rate): rate_value = float(delta.speaking_rate) if not (0.25 <= rate_value <= 2.0): logger.warning( @@ -1201,7 +1201,7 @@ class GeminiTTSService(GoogleBaseTTSService): """ Settings = GeminiTTSSettings - _settings: GeminiTTSSettings + _settings: Settings GOOGLE_SAMPLE_RATE = 24000 # Google TTS always outputs at 24kHz @@ -1243,7 +1243,7 @@ class GeminiTTSService(GoogleBaseTTSService): """Input parameters for Gemini TTS configuration. .. deprecated:: 0.0.105 - Use ``GeminiTTSSettings`` directly via the ``settings`` parameter instead. + Use ``GeminiTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language for synthesis. Defaults to English. @@ -1268,7 +1268,7 @@ class GeminiTTSService(GoogleBaseTTSService): voice_id: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[GeminiTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initializes the Gemini TTS service. @@ -1284,7 +1284,7 @@ class GeminiTTSService(GoogleBaseTTSService): "gemini-2.5-flash-tts" or "gemini-2.5-pro-tts". .. deprecated:: 0.0.105 - Use ``settings=GeminiTTSSettings(model=...)`` instead. + Use ``settings=GeminiTTSService.Settings(model=...)`` instead. credentials: JSON string containing Google Cloud service account credentials. credentials_path: Path to Google Cloud service account JSON file. @@ -1292,13 +1292,13 @@ class GeminiTTSService(GoogleBaseTTSService): voice_id: Voice name from the available Gemini voices. .. deprecated:: 0.0.105 - Use ``settings=GeminiTTSSettings(voice=...)`` instead. + Use ``settings=GeminiTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate in Hz. If None, uses Google's default 24kHz. params: TTS configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=GeminiTTSSettings(...)`` instead. + Use ``settings=GeminiTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -1320,7 +1320,7 @@ class GeminiTTSService(GoogleBaseTTSService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = GeminiTTSSettings( + default_settings = self.Settings( model="gemini-2.5-flash-tts", voice="Kore", language="en-US", @@ -1331,10 +1331,10 @@ class GeminiTTSService(GoogleBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GeminiTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", GeminiTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if default_settings.voice not in self.AVAILABLE_VOICES: @@ -1344,7 +1344,7 @@ class GeminiTTSService(GoogleBaseTTSService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GeminiTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) diff --git a/src/pipecat/services/google/vertex/llm.py b/src/pipecat/services/google/vertex/llm.py index 3901b5d50..014dea405 100644 --- a/src/pipecat/services/google/vertex/llm.py +++ b/src/pipecat/services/google/vertex/llm.py @@ -21,7 +21,7 @@ from typing import Optional from loguru import logger -from pipecat.services.google.llm import GoogleLLMService, GoogleLLMSettings +from pipecat.services.google.llm import GoogleLLMService from pipecat.services.settings import _warn_deprecated_param try: @@ -41,7 +41,7 @@ except ModuleNotFoundError as e: @dataclass -class GoogleVertexLLMSettings(GoogleLLMSettings): +class GoogleVertexLLMSettings(GoogleLLMService.Settings): """Settings for GoogleVertexLLMService.""" pass @@ -60,7 +60,7 @@ class GoogleVertexLLMService(GoogleLLMService): """ Settings = GoogleVertexLLMSettings - _settings: GoogleVertexLLMSettings + _settings: Settings class InputParams(GoogleLLMService.InputParams): """Input parameters specific to Vertex AI. @@ -115,7 +115,7 @@ class GoogleVertexLLMService(GoogleLLMService): location: Optional[str] = None, project_id: Optional[str] = None, params: Optional[GoogleLLMService.InputParams] = None, - settings: Optional[GoogleVertexLLMSettings] = None, + settings: Optional[Settings] = None, system_instruction: Optional[str] = None, tools: Optional[list] = None, tool_config: Optional[dict] = None, @@ -130,14 +130,14 @@ class GoogleVertexLLMService(GoogleLLMService): model: Model identifier (e.g., "gemini-2.5-flash"). .. deprecated:: 0.0.105 - Use ``settings=GoogleLLMSettings(model=...)`` instead. + Use ``settings=GoogleVertexLLMService.Settings(model=...)`` instead. location: GCP region for Vertex AI endpoint (e.g., "us-east4"). project_id: Google Cloud project ID. params: Input parameters for the model. .. deprecated:: 0.0.105 - Use ``settings=GoogleLLMSettings(...)`` instead. + Use ``settings=GoogleVertexLLMService.Settings(...)`` instead. settings: Runtime-updatable settings for this service. When both deprecated parameters and *settings* are provided, *settings* @@ -145,7 +145,7 @@ class GoogleVertexLLMService(GoogleLLMService): system_instruction: System instruction/prompt for the model. .. deprecated:: 0.0.105 - Use ``settings=GoogleVertexLLMSettings(system_instruction=...)`` instead. + Use ``settings=GoogleVertexLLMService.Settings(system_instruction=...)`` instead. tools: List of available tools/functions. tool_config: Configuration for tool usage. http_options: HTTP options for the client. @@ -197,7 +197,7 @@ class GoogleVertexLLMService(GoogleLLMService): self._location = location # 1. Initialize default_settings with hardcoded defaults - default_settings = GoogleVertexLLMSettings( + default_settings = self.Settings( model="gemini-2.5-flash", system_instruction=None, max_tokens=4096, @@ -215,17 +215,15 @@ class GoogleVertexLLMService(GoogleLLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GoogleVertexLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if system_instruction is not None: - _warn_deprecated_param( - "system_instruction", GoogleVertexLLMSettings, "system_instruction" - ) + _warn_deprecated_param("system_instruction", self.Settings, "system_instruction") default_settings.system_instruction = system_instruction # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GoogleVertexLLMSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/gradium/stt.py b/src/pipecat/services/gradium/stt.py index 3fddf0470..90d1e800f 100644 --- a/src/pipecat/services/gradium/stt.py +++ b/src/pipecat/services/gradium/stt.py @@ -89,13 +89,13 @@ class GradiumSTTService(WebsocketSTTService): """ Settings = GradiumSTTSettings - _settings: GradiumSTTSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Gradium STT API. .. deprecated:: 0.0.105 - Use ``settings=GradiumSTTSettings(...)`` instead. + Use ``settings=GradiumSTTService.Settings(...)`` instead. Parameters: language: Expected language of the audio (e.g., "en", "es", "fr"). @@ -117,7 +117,7 @@ class GradiumSTTService(WebsocketSTTService): api_endpoint_base_url: str = "wss://eu.api.gradium.ai/api/speech/asr", params: Optional[InputParams] = None, json_config: Optional[str] = None, - settings: Optional[GradiumSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = GRADIUM_TTFS_P99, **kwargs, ): @@ -129,7 +129,7 @@ class GradiumSTTService(WebsocketSTTService): params: Configuration parameters for language and delay settings. .. deprecated:: 0.0.105 - Use ``settings=GradiumSTTSettings(...)`` instead. + Use ``settings=GradiumSTTService.Settings(...)`` instead. json_config: Optional JSON configuration string for additional model settings. @@ -152,7 +152,7 @@ class GradiumSTTService(WebsocketSTTService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = GradiumSTTSettings( + default_settings = self.Settings( model=None, language=None, delay_in_frames=None, @@ -162,7 +162,7 @@ class GradiumSTTService(WebsocketSTTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GradiumSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = params.language if params.delay_in_frames is not None: @@ -207,7 +207,7 @@ class GradiumSTTService(WebsocketSTTService): """Apply a settings delta, sync params, and reconnect. Args: - delta: A :class:`STTSettings` (or ``GradiumSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``GradiumSTTService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/gradium/tts.py b/src/pipecat/services/gradium/tts.py index 03b4d7008..43114b193 100644 --- a/src/pipecat/services/gradium/tts.py +++ b/src/pipecat/services/gradium/tts.py @@ -48,13 +48,13 @@ class GradiumTTSService(WebsocketTTSService): """Text-to-Speech service using Gradium's websocket API.""" Settings = GradiumTTSSettings - _settings: GradiumTTSSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Gradium TTS service. .. deprecated:: 0.0.105 - Use ``GradiumTTSSettings`` directly via the ``settings`` parameter instead. + Use ``GradiumTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: temp: Temperature to be used for generation, defaults to 0.6. @@ -71,7 +71,7 @@ class GradiumTTSService(WebsocketTTSService): model: Optional[str] = None, json_config: Optional[str] = None, params: Optional[InputParams] = None, - settings: Optional[GradiumTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Gradium TTS service. @@ -81,26 +81,26 @@ class GradiumTTSService(WebsocketTTSService): voice_id: the voice identifier. .. deprecated:: 0.0.105 - Use ``settings=GradiumTTSSettings(voice=...)`` instead. + Use ``settings=GradiumTTSService.Settings(voice=...)`` instead. url: Gradium websocket API endpoint. model: Model ID to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=GradiumTTSSettings(model=...)`` instead. + Use ``settings=GradiumTTSService.Settings(model=...)`` instead. json_config: Optional JSON configuration string for additional model settings. params: Additional configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=GradiumTTSSettings(...)`` instead. + Use ``settings=GradiumTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GradiumTTSSettings( + default_settings = self.Settings( model="default", voice="YTpq7expH9539ERJ", language=None, @@ -108,15 +108,15 @@ class GradiumTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GradiumTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", GradiumTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GradiumTTSSettings) + _warn_deprecated_param("params", self.Settings) # Note: params.temp has no corresponding settings field # 4. Apply settings delta (canonical API, always wins) @@ -153,7 +153,7 @@ class GradiumTTSService(WebsocketTTSService): """Apply a settings delta and reconnect if voice changed. Args: - delta: A :class:`TTSSettings` (or ``GradiumTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``GradiumTTSService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/grok/llm.py b/src/pipecat/services/grok/llm.py index 255ee2cf5..3eaf0646a 100644 --- a/src/pipecat/services/grok/llm.py +++ b/src/pipecat/services/grok/llm.py @@ -23,7 +23,7 @@ from pipecat.processors.aggregators.llm_response import ( LLMUserAggregatorParams, ) from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import ( OpenAIAssistantContextAggregator, OpenAILLMService, @@ -71,7 +71,7 @@ class GrokContextAggregatorPair: @dataclass -class GrokLLMSettings(OpenAILLMSettings): +class GrokLLMSettings(BaseOpenAILLMService.Settings): """Settings for GrokLLMService.""" pass @@ -87,7 +87,7 @@ class GrokLLMService(OpenAILLMService): """ Settings = GrokLLMSettings - _settings: GrokLLMSettings + _settings: Settings def __init__( self, @@ -95,7 +95,7 @@ class GrokLLMService(OpenAILLMService): api_key: str, base_url: str = "https://api.x.ai/v1", model: Optional[str] = None, - settings: Optional[GrokLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the GrokLLMService with API key and model. @@ -106,18 +106,18 @@ class GrokLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "grok-3-beta". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=GrokLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GrokLLMSettings(model="grok-3-beta") + default_settings = self.Settings(model="grok-3-beta") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GrokLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/grok/realtime/llm.py b/src/pipecat/services/grok/realtime/llm.py index 9063ac22c..dcd7ac59e 100644 --- a/src/pipecat/services/grok/realtime/llm.py +++ b/src/pipecat/services/grok/realtime/llm.py @@ -109,7 +109,7 @@ class GrokRealtimeLLMSettings(LLMSettings): # -- Bidirectional sync helpers ------------------------------------------ @staticmethod - def _sync_top_level_to_sp(settings: "GrokRealtimeLLMSettings"): + def _sync_top_level_to_sp(settings: "GrokRealtimeLLMService.Settings"): """Push top-level ``system_instruction`` into ``session_properties``.""" if not is_given(settings.session_properties): return @@ -119,7 +119,7 @@ class GrokRealtimeLLMSettings(LLMSettings): # -- apply_update override ----------------------------------------------- - def apply_update(self, delta: "GrokRealtimeLLMSettings") -> Dict[str, Any]: + def apply_update(self, delta: "GrokRealtimeLLMService.Settings") -> Dict[str, Any]: """Merge a delta, keeping ``system_instruction`` in sync with SP. When the delta contains ``session_properties``, it **replaces** the @@ -151,8 +151,8 @@ class GrokRealtimeLLMSettings(LLMSettings): @classmethod def from_mapping( - cls: Type["GrokRealtimeLLMSettings"], settings: Mapping[str, Any] - ) -> "GrokRealtimeLLMSettings": + cls: Type["GrokRealtimeLLMService.Settings"], settings: Mapping[str, Any] + ) -> "GrokRealtimeLLMService.Settings": """Build a delta from a plain dict, routing SP keys into ``session_properties``. Keys that correspond to ``SessionProperties`` fields are collected into @@ -203,7 +203,7 @@ class GrokRealtimeLLMService(LLMService): """ Settings = GrokRealtimeLLMSettings - _settings: GrokRealtimeLLMSettings + _settings: Settings # Use the Grok-specific adapter adapter_class = GrokRealtimeLLMAdapter @@ -214,7 +214,7 @@ class GrokRealtimeLLMService(LLMService): api_key: str, base_url: str = "wss://api.x.ai/v1/realtime", session_properties: Optional[events.SessionProperties] = None, - settings: Optional[GrokRealtimeLLMSettings] = None, + settings: Optional[Settings] = None, start_audio_paused: bool = False, **kwargs, ): @@ -228,7 +228,7 @@ class GrokRealtimeLLMService(LLMService): If None, uses default SessionProperties with voice "Ara". .. deprecated:: 0.0.105 - Use ``settings=GrokRealtimeLLMSettings(session_properties=...)`` + Use ``settings=GrokRealtimeLLMService.Settings(session_properties=...)`` instead. To set a different voice, configure it in session_properties: @@ -241,7 +241,7 @@ class GrokRealtimeLLMService(LLMService): **kwargs: Additional arguments passed to parent LLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GrokRealtimeLLMSettings( + default_settings = self.Settings( model=None, system_instruction=None, temperature=None, @@ -260,7 +260,7 @@ class GrokRealtimeLLMService(LLMService): if session_properties is not None: _warn_deprecated_param( "session_properties", - GrokRealtimeLLMSettings, + self.Settings, "session_properties", ) default_settings.session_properties = session_properties @@ -269,7 +269,7 @@ class GrokRealtimeLLMService(LLMService): default_settings.system_instruction = session_properties.instructions # Sync top-level system_instruction back into session_properties - GrokRealtimeLLMSettings._sync_top_level_to_sp(default_settings) + self.Settings._sync_top_level_to_sp(default_settings) # 3. Apply settings delta (canonical API, always wins) if settings is not None: diff --git a/src/pipecat/services/groq/llm.py b/src/pipecat/services/groq/llm.py index 6669c385a..8b6bdc3bc 100644 --- a/src/pipecat/services/groq/llm.py +++ b/src/pipecat/services/groq/llm.py @@ -11,13 +11,13 @@ from typing import Optional from loguru import logger -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class GroqLLMSettings(OpenAILLMSettings): +class GroqLLMSettings(BaseOpenAILLMService.Settings): """Settings for GroqLLMService.""" pass @@ -31,7 +31,7 @@ class GroqLLMService(OpenAILLMService): """ Settings = GroqLLMSettings - _settings: GroqLLMSettings + _settings: Settings def __init__( self, @@ -39,7 +39,7 @@ class GroqLLMService(OpenAILLMService): api_key: str, base_url: str = "https://api.groq.com/openai/v1", model: Optional[str] = None, - settings: Optional[GroqLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize Groq LLM service. @@ -50,18 +50,18 @@ class GroqLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "llama-3.3-70b-versatile". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=GroqLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = GroqLLMSettings(model="llama-3.3-70b-versatile") + default_settings = self.Settings(model="llama-3.3-70b-versatile") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", GroqLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/groq/stt.py b/src/pipecat/services/groq/stt.py index b5d59181a..6a234cc27 100644 --- a/src/pipecat/services/groq/stt.py +++ b/src/pipecat/services/groq/stt.py @@ -13,14 +13,13 @@ from pipecat.services.settings import _warn_deprecated_param from pipecat.services.stt_latency import GROQ_TTFS_P99 from pipecat.services.whisper.base_stt import ( BaseWhisperSTTService, - BaseWhisperSTTSettings, Transcription, ) from pipecat.transcriptions.language import Language @dataclass -class GroqSTTSettings(BaseWhisperSTTSettings): +class GroqSTTSettings(BaseWhisperSTTService.Settings): """Settings for the Groq STT service. Parameters: @@ -38,7 +37,7 @@ class GroqSTTService(BaseWhisperSTTService): """ Settings = GroqSTTSettings - _settings: GroqSTTSettings + _settings: Settings def __init__( self, @@ -49,7 +48,7 @@ class GroqSTTService(BaseWhisperSTTService): language: Optional[Language] = None, prompt: Optional[str] = None, temperature: Optional[float] = None, - settings: Optional[GroqSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = GROQ_TTFS_P99, **kwargs, ): @@ -59,24 +58,24 @@ class GroqSTTService(BaseWhisperSTTService): model: Whisper model to use. .. deprecated:: 0.0.105 - Use ``settings=GroqSTTSettings(model=...)`` instead. + Use ``settings=GroqSTTService.Settings(model=...)`` instead. api_key: Groq API key. Defaults to None. base_url: API base URL. Defaults to "https://api.groq.com/openai/v1". language: Language of the audio input. .. deprecated:: 0.0.105 - Use ``settings=GroqSTTSettings(language=...)`` instead. + Use ``settings=GroqSTTService.Settings(language=...)`` instead. prompt: Optional text to guide the model's style or continue a previous segment. .. deprecated:: 0.0.105 - Use ``settings=GroqSTTSettings(prompt=...)`` instead. + Use ``settings=GroqSTTService.Settings(prompt=...)`` instead. temperature: Optional sampling temperature between 0 and 1. .. deprecated:: 0.0.105 - Use ``settings=GroqSTTSettings(temperature=...)`` instead. + Use ``settings=GroqSTTService.Settings(temperature=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -85,7 +84,7 @@ class GroqSTTService(BaseWhisperSTTService): **kwargs: Additional arguments passed to BaseWhisperSTTService. """ # --- 1. Hardcoded defaults --- - default_settings = GroqSTTSettings( + default_settings = self.Settings( model="whisper-large-v3-turbo", language=self.language_to_service_language(Language.EN), prompt=None, @@ -94,16 +93,16 @@ class GroqSTTService(BaseWhisperSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", GroqSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if language is not None: - _warn_deprecated_param("language", GroqSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = self.language_to_service_language(language) if prompt is not None: - _warn_deprecated_param("prompt", GroqSTTSettings, "prompt") + _warn_deprecated_param("prompt", self.Settings, "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", GroqSTTSettings, "temperature") + _warn_deprecated_param("temperature", self.Settings, "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/groq/tts.py b/src/pipecat/services/groq/tts.py index bb39a9067..714ecdeb1 100644 --- a/src/pipecat/services/groq/tts.py +++ b/src/pipecat/services/groq/tts.py @@ -52,13 +52,13 @@ class GroqTTSService(TTSService): """ Settings = GroqTTSSettings - _settings: GroqTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Groq TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=GroqTTSSettings(...)`` instead. + Use ``settings=GroqTTSService.Settings(...)`` instead. Parameters: language: Language for speech synthesis. Defaults to English. @@ -79,7 +79,7 @@ class GroqTTSService(TTSService): model_name: Optional[str] = None, voice_id: Optional[str] = None, sample_rate: Optional[int] = GROQ_SAMPLE_RATE, - settings: Optional[GroqTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize Groq TTS service. @@ -90,17 +90,17 @@ class GroqTTSService(TTSService): params: Additional input parameters for voice customization. .. deprecated:: 0.0.105 - Use ``settings=GroqTTSSettings(...)`` instead. + Use ``settings=GroqTTSService.Settings(...)`` instead. model_name: TTS model to use. .. deprecated:: 0.0.105 - Use ``settings=GroqTTSSettings(model=...)`` instead. + Use ``settings=GroqTTSService.Settings(model=...)`` instead. voice_id: Voice identifier to use. .. deprecated:: 0.0.105 - Use ``settings=GroqTTSSettings(voice=...)`` instead. + Use ``settings=GroqTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate. Must be 48000 Hz for Groq TTS. settings: Runtime-updatable settings. When provided alongside deprecated @@ -111,7 +111,7 @@ class GroqTTSService(TTSService): logger.warning(f"Groq TTS only supports {self.GROQ_SAMPLE_RATE}Hz sample rate. ") # 1. Initialize default_settings with hardcoded defaults - default_settings = GroqTTSSettings( + default_settings = self.Settings( model="canopylabs/orpheus-v1-english", voice="autumn", language="en", @@ -120,15 +120,15 @@ class GroqTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model_name is not None: - _warn_deprecated_param("model_name", GroqTTSSettings, "model") + _warn_deprecated_param("model_name", self.Settings, "model") default_settings.model = model_name if voice_id is not None: - _warn_deprecated_param("voice_id", GroqTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", GroqTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = str(params.language) if params.language else "en" default_settings.speed = params.speed diff --git a/src/pipecat/services/heygen/video.py b/src/pipecat/services/heygen/video.py index 2c42dfc6b..9a20f35ef 100644 --- a/src/pipecat/services/heygen/video.py +++ b/src/pipecat/services/heygen/video.py @@ -83,7 +83,7 @@ class HeyGenVideoService(AIService): """ Settings = HeyGenVideoSettings - _settings: HeyGenVideoSettings + _settings: Settings def __init__( self, @@ -92,7 +92,7 @@ class HeyGenVideoService(AIService): session: aiohttp.ClientSession, session_request: Optional[Union[LiveAvatarNewSessionRequest, NewSessionRequest]] = None, service_type: Optional[ServiceType] = None, - settings: Optional[HeyGenVideoSettings] = None, + settings: Optional[Settings] = None, **kwargs, ) -> None: """Initialize the HeyGen video service. diff --git a/src/pipecat/services/hume/tts.py b/src/pipecat/services/hume/tts.py index 5eb2db646..9fa1cd46c 100644 --- a/src/pipecat/services/hume/tts.py +++ b/src/pipecat/services/hume/tts.py @@ -79,13 +79,13 @@ class HumeTTSService(TTSService): """ Settings = HumeTTSSettings - _settings: HumeTTSSettings + _settings: Settings class InputParams(BaseModel): """Optional synthesis parameters for Hume TTS. .. deprecated:: 0.0.105 - Use ``settings=HumeTTSSettings(...)`` instead. + Use ``settings=HumeTTSService.Settings(...)`` instead. Parameters: description: Natural-language acting directions (up to 100 characters). @@ -104,7 +104,7 @@ class HumeTTSService(TTSService): voice_id: Optional[str] = None, params: Optional[InputParams] = None, sample_rate: Optional[int] = HUME_SAMPLE_RATE, - settings: Optional[HumeTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ) -> None: """Initialize the HumeTTSService. @@ -114,12 +114,12 @@ class HumeTTSService(TTSService): voice_id: ID of the voice to use. Only voice IDs are supported; voice names are not. .. deprecated:: 0.0.105 - Use ``settings=HumeTTSSettings(voice=...)`` instead. + Use ``settings=HumeTTSService.Settings(voice=...)`` instead. params: Optional synthesis controls (acting instructions, speed, trailing silence). .. deprecated:: 0.0.105 - Use ``settings=HumeTTSSettings(...)`` instead. + Use ``settings=HumeTTSService.Settings(...)`` instead. sample_rate: Output sample rate for emitted PCM frames. Defaults to 48_000 (Hume). settings: Runtime-updatable settings. When provided alongside deprecated @@ -136,7 +136,7 @@ class HumeTTSService(TTSService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = HumeTTSSettings( + default_settings = self.Settings( model=None, voice=None, language=None, # Not applicable here @@ -147,12 +147,12 @@ class HumeTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", HumeTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", HumeTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.description = params.description default_settings.speed = params.speed @@ -242,7 +242,7 @@ class HumeTTSService(TTSService): """Runtime updates via key/value pair. .. deprecated:: 0.0.104 - Use ``TTSUpdateSettingsFrame(delta=HumeTTSSettings(...))`` instead. + Use ``TTSUpdateSettingsFrame(delta=HumeTTSService.Settings(...))`` instead. Args: key: The name of the setting to update. Recognized keys are: @@ -256,7 +256,7 @@ class HumeTTSService(TTSService): warnings.simplefilter("always") warnings.warn( "'update_setting' is deprecated, use " - "'TTSUpdateSettingsFrame(delta=HumeTTSSettings(...))' instead.", + "'TTSUpdateSettingsFrame(delta=self.Settings(...))' instead.", DeprecationWarning, stacklevel=2, ) @@ -274,7 +274,7 @@ class HumeTTSService(TTSService): kwargs["speed"] = None if value is None else float(value) elif key_l == "trailing_silence": kwargs["trailing_silence"] = None if value is None else float(value) - await self._update_settings(HumeTTSSettings(**kwargs)) + await self._update_settings(self.Settings(**kwargs)) @traced_tts async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: diff --git a/src/pipecat/services/inworld/tts.py b/src/pipecat/services/inworld/tts.py index dc09c0481..f24e9dcf6 100644 --- a/src/pipecat/services/inworld/tts.py +++ b/src/pipecat/services/inworld/tts.py @@ -101,13 +101,13 @@ class InworldHttpTTSService(TTSService): """ Settings = InworldTTSSettings - _settings: InworldTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Inworld TTS configuration. .. deprecated:: 0.0.105 - Use ``InworldTTSSettings`` directly via the ``settings`` parameter instead. + Use ``InworldHttpTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: temperature: Temperature for speech synthesis. @@ -131,7 +131,7 @@ class InworldHttpTTSService(TTSService): encoding: str = "LINEAR16", timestamp_transport_strategy: Optional[Literal["ASYNC", "SYNC"]] = "ASYNC", params: Optional[InputParams] = None, - settings: Optional[InworldTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Inworld TTS service. @@ -142,12 +142,12 @@ class InworldHttpTTSService(TTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=InworldTTSSettings(voice=...)`` instead. + Use ``settings=InworldHttpTTSService.Settings(voice=...)`` instead. model: ID of the model to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=InworldTTSSettings(model=...)`` instead. + Use ``settings=InworldHttpTTSService.Settings(model=...)`` instead. streaming: Whether to use streaming mode. sample_rate: Audio sample rate in Hz. @@ -157,14 +157,14 @@ class InworldHttpTTSService(TTSService): params: Input parameters for Inworld TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=InworldTTSSettings(...)`` instead. + Use ``settings=InworldHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to the parent class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = InworldTTSSettings( + default_settings = self.Settings( model="inworld-tts-1.5-max", voice="Ashley", language=None, @@ -174,15 +174,15 @@ class InworldHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", InworldTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", InworldTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", InworldTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.speaking_rate is not None: default_settings.speaking_rate = params.speaking_rate @@ -489,13 +489,13 @@ class InworldTTSService(WebsocketTTSService): """ Settings = InworldTTSSettings - _settings: InworldTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Inworld WebSocket TTS configuration. .. deprecated:: 0.0.105 - Use ``InworldTTSSettings`` directly via the ``settings`` parameter instead. + Use ``InworldTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: temperature: Temperature for speech synthesis. @@ -532,7 +532,7 @@ class InworldTTSService(WebsocketTTSService): apply_text_normalization: Optional[str] = None, timestamp_transport_strategy: Optional[Literal["ASYNC", "SYNC"]] = "ASYNC", params: Optional[InputParams] = None, - settings: Optional[InworldTTSSettings] = None, + settings: Optional[Settings] = None, aggregate_sentences: Optional[bool] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, append_trailing_space: bool = True, @@ -545,12 +545,12 @@ class InworldTTSService(WebsocketTTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=InworldTTSSettings(voice=...)`` instead. + Use ``settings=InworldTTSService.Settings(voice=...)`` instead. model: ID of the model to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=InworldTTSSettings(model=...)`` instead. + Use ``settings=InworldTTSService.Settings(model=...)`` instead. url: URL of the Inworld WebSocket API. sample_rate: Audio sample rate in Hz. @@ -564,7 +564,7 @@ class InworldTTSService(WebsocketTTSService): params: Input parameters for Inworld WebSocket TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=InworldTTSSettings(...)`` instead. + Use ``settings=InworldTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -582,7 +582,7 @@ class InworldTTSService(WebsocketTTSService): auto_mode = True if aggregate_sentences is None else aggregate_sentences # 1. Initialize default_settings with hardcoded defaults - default_settings = InworldTTSSettings( + default_settings = self.Settings( model="inworld-tts-1.5-max", voice="Ashley", language=None, @@ -592,17 +592,17 @@ class InworldTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", InworldTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", InworldTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided _buffer_max_delay_ms = None _buffer_char_threshold = None if params is not None: - _warn_deprecated_param("params", InworldTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.speaking_rate is not None: default_settings.speaking_rate = params.speaking_rate diff --git a/src/pipecat/services/kokoro/tts.py b/src/pipecat/services/kokoro/tts.py index 98f181c6f..fd7f15c27 100644 --- a/src/pipecat/services/kokoro/tts.py +++ b/src/pipecat/services/kokoro/tts.py @@ -102,13 +102,13 @@ class KokoroTTSService(TTSService): """ Settings = KokoroTTSSettings - _settings: KokoroTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Kokoro TTS configuration. .. deprecated:: 0.0.105 - Use ``KokoroTTSSettings`` directly via the ``settings`` parameter instead. + Use ``KokoroTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language to use for synthesis. @@ -123,7 +123,7 @@ class KokoroTTSService(TTSService): model_path: Optional[str] = None, voices_path: Optional[str] = None, params: Optional[InputParams] = None, - settings: Optional[KokoroTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Kokoro TTS service. @@ -132,14 +132,14 @@ class KokoroTTSService(TTSService): voice_id: Voice identifier to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=KokoroTTSSettings(voice=...)`` instead. + Use ``settings=KokoroTTSService.Settings(voice=...)`` instead. model_path: Path to the kokoro ONNX model file. Defaults to auto-downloaded file. voices_path: Path to the voices binary file. Defaults to auto-downloaded file. params: Configuration parameters for synthesis. .. deprecated:: 0.0.105 - Use ``settings=KokoroTTSSettings(...)`` instead. + Use ``settings=KokoroTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -147,7 +147,7 @@ class KokoroTTSService(TTSService): """ # 1. Initialize default_settings with hardcoded defaults - default_settings = KokoroTTSSettings( + default_settings = self.Settings( model=None, voice=None, language=language_to_kokoro_language(Language.EN), @@ -155,12 +155,12 @@ class KokoroTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", KokoroTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", KokoroTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = language_to_kokoro_language(params.language) diff --git a/src/pipecat/services/lmnt/tts.py b/src/pipecat/services/lmnt/tts.py index 46e5e4697..ea5986ffc 100644 --- a/src/pipecat/services/lmnt/tts.py +++ b/src/pipecat/services/lmnt/tts.py @@ -89,7 +89,7 @@ class LmntTTSService(InterruptibleTTSService): """ Settings = LmntTTSSettings - _settings: LmntTTSSettings + _settings: Settings def __init__( self, @@ -100,7 +100,7 @@ class LmntTTSService(InterruptibleTTSService): language: Language = Language.EN, output_format: str = "pcm_s16le", model: Optional[str] = None, - settings: Optional[LmntTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the LMNT TTS service. @@ -110,7 +110,7 @@ class LmntTTSService(InterruptibleTTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=LmntTTSSettings(voice=...)`` instead. + Use ``settings=LmntTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate. If None, uses default. language: Language for synthesis. Defaults to English. @@ -119,14 +119,14 @@ class LmntTTSService(InterruptibleTTSService): model: TTS model to use. .. deprecated:: 0.0.105 - Use ``settings=LmntTTSSettings(model=...)`` instead. + Use ``settings=LmntTTSService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent InterruptibleTTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = LmntTTSSettings( + default_settings = self.Settings( model="aurora", voice=None, language=self.language_to_service_language(language), @@ -134,10 +134,10 @@ class LmntTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", LmntTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", LmntTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) @@ -237,7 +237,7 @@ class LmntTTSService(InterruptibleTTSService): """Apply a settings delta. Args: - delta: A :class:`TTSSettings` (or ``LmntTTSSettings``) delta. + delta: A :class:`TTSSettings` (or ``LmntTTSService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/minimax/tts.py b/src/pipecat/services/minimax/tts.py index f62e8d46b..173c1ea05 100644 --- a/src/pipecat/services/minimax/tts.py +++ b/src/pipecat/services/minimax/tts.py @@ -141,13 +141,13 @@ class MiniMaxHttpTTSService(TTSService): """ Settings = MiniMaxTTSSettings - _settings: MiniMaxTTSSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for MiniMax TTS. .. deprecated:: 0.0.105 - Use ``MiniMaxTTSSettings`` directly via the ``settings`` parameter instead. + Use ``MiniMaxHttpTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language for TTS generation. Supports 40 languages. @@ -190,7 +190,7 @@ class MiniMaxHttpTTSService(TTSService): sample_rate: Optional[int] = None, stream: bool = True, params: Optional[InputParams] = None, - settings: Optional[MiniMaxTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the MiniMax TTS service. @@ -208,12 +208,12 @@ class MiniMaxHttpTTSService(TTSService): "speech-01-hd", "speech-01-turbo". .. deprecated:: 0.0.105 - Use ``settings=MiniMaxTTSSettings(model=...)`` instead. + Use ``settings=MiniMaxHttpTTSService.Settings(model=...)`` instead. voice_id: Voice identifier. Defaults to "Calm_Woman". .. deprecated:: 0.0.105 - Use ``settings=MiniMaxTTSSettings(voice=...)`` instead. + Use ``settings=MiniMaxHttpTTSService.Settings(voice=...)`` instead. aiohttp_session: aiohttp.ClientSession for API communication. sample_rate: Output audio sample rate in Hz. If None, uses pipeline default. @@ -221,14 +221,14 @@ class MiniMaxHttpTTSService(TTSService): params: Additional configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=MiniMaxTTSSettings(...)`` instead. + Use ``settings=MiniMaxHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = MiniMaxTTSSettings( + default_settings = self.Settings( model="speech-02-turbo", voice="Calm_Woman", language=None, @@ -243,15 +243,15 @@ class MiniMaxHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", MiniMaxTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", MiniMaxTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", MiniMaxTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.speed = params.speed default_settings.volume = params.volume diff --git a/src/pipecat/services/mistral/llm.py b/src/pipecat/services/mistral/llm.py index 9e01a76b5..4b74695ef 100644 --- a/src/pipecat/services/mistral/llm.py +++ b/src/pipecat/services/mistral/llm.py @@ -14,13 +14,13 @@ from openai.types.chat import ChatCompletionMessageParam from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.frames.frames import FunctionCallFromLLM -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class MistralLLMSettings(OpenAILLMSettings): +class MistralLLMSettings(BaseOpenAILLMService.Settings): """Settings for MistralLLMService.""" pass @@ -34,7 +34,7 @@ class MistralLLMService(OpenAILLMService): """ Settings = MistralLLMSettings - _settings: MistralLLMSettings + _settings: Settings def __init__( self, @@ -42,7 +42,7 @@ class MistralLLMService(OpenAILLMService): api_key: str, base_url: str = "https://api.mistral.ai/v1", model: Optional[str] = None, - settings: Optional[MistralLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Mistral LLM service. @@ -53,18 +53,18 @@ class MistralLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "mistral-small-latest". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=MistralLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = MistralLLMSettings(model="mistral-small-latest") + default_settings = self.Settings(model="mistral-small-latest") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", MistralLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/moondream/vision.py b/src/pipecat/services/moondream/vision.py index abc344fc5..b8ab5a270 100644 --- a/src/pipecat/services/moondream/vision.py +++ b/src/pipecat/services/moondream/vision.py @@ -80,7 +80,7 @@ class MoondreamService(VisionService): """ Settings = MoondreamSettings - _settings: MoondreamSettings + _settings: Settings def __init__( self, @@ -88,7 +88,7 @@ class MoondreamService(VisionService): model: Optional[str] = None, revision="2025-01-09", use_cpu=False, - settings: Optional[MoondreamSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Moondream service. @@ -97,7 +97,7 @@ class MoondreamService(VisionService): model: Hugging Face model identifier for the Moondream model. .. deprecated:: 0.0.105 - Use ``settings=MoondreamSettings(model=...)`` instead. + Use ``settings=MoondreamService.Settings(model=...)`` instead. revision: Specific model revision to use. use_cpu: Whether to force CPU usage instead of hardware acceleration. @@ -106,11 +106,11 @@ class MoondreamService(VisionService): **kwargs: Additional arguments passed to the parent VisionService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = MoondreamSettings(model="vikhyatk/moondream2") + default_settings = self.Settings(model="vikhyatk/moondream2") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", MoondreamSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/neuphonic/tts.py b/src/pipecat/services/neuphonic/tts.py index abc33c37e..5c0605293 100644 --- a/src/pipecat/services/neuphonic/tts.py +++ b/src/pipecat/services/neuphonic/tts.py @@ -92,13 +92,13 @@ class NeuphonicTTSService(InterruptibleTTSService): """ Settings = NeuphonicTTSSettings - _settings: NeuphonicTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Neuphonic TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=NeuphonicTTSSettings(...)`` instead. + Use ``settings=NeuphonicTTSService.Settings(...)`` instead. Parameters: language: Language for synthesis. Defaults to English. @@ -117,7 +117,7 @@ class NeuphonicTTSService(InterruptibleTTSService): sample_rate: Optional[int] = 22050, encoding: str = "pcm_linear", params: Optional[InputParams] = None, - settings: Optional[NeuphonicTTSSettings] = None, + settings: Optional[Settings] = None, aggregate_sentences: Optional[bool] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, **kwargs, @@ -129,7 +129,7 @@ class NeuphonicTTSService(InterruptibleTTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=NeuphonicTTSSettings(voice=...)`` instead. + Use ``settings=NeuphonicTTSService.Settings(voice=...)`` instead. url: WebSocket URL for the Neuphonic API. sample_rate: Audio sample rate in Hz. Defaults to 22050. @@ -137,7 +137,7 @@ class NeuphonicTTSService(InterruptibleTTSService): params: Additional input parameters for TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=NeuphonicTTSSettings(...)`` instead. + Use ``settings=NeuphonicTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -150,7 +150,7 @@ class NeuphonicTTSService(InterruptibleTTSService): **kwargs: Additional arguments passed to parent InterruptibleTTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = NeuphonicTTSSettings( + default_settings = self.Settings( model=None, voice=None, language=self.language_to_service_language(Language.EN), @@ -159,12 +159,12 @@ class NeuphonicTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", NeuphonicTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", NeuphonicTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -432,13 +432,13 @@ class NeuphonicHttpTTSService(TTSService): """ Settings = NeuphonicTTSSettings - _settings: NeuphonicTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Neuphonic HTTP TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=NeuphonicTTSSettings(...)`` instead. + Use ``settings=NeuphonicHttpTTSService.Settings(...)`` instead. Parameters: language: Language for synthesis. Defaults to English. @@ -458,7 +458,7 @@ class NeuphonicHttpTTSService(TTSService): sample_rate: Optional[int] = 22050, encoding: Optional[str] = "pcm_linear", params: Optional[InputParams] = None, - settings: Optional[NeuphonicTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Neuphonic HTTP TTS service. @@ -468,7 +468,7 @@ class NeuphonicHttpTTSService(TTSService): voice_id: ID of the voice to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=NeuphonicTTSSettings(voice=...)`` instead. + Use ``settings=NeuphonicHttpTTSService.Settings(voice=...)`` instead. aiohttp_session: Shared aiohttp session for HTTP requests. url: Base URL for the Neuphonic HTTP API. @@ -477,14 +477,14 @@ class NeuphonicHttpTTSService(TTSService): params: Additional input parameters for TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=NeuphonicTTSSettings(...)`` instead. + Use ``settings=NeuphonicHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = NeuphonicTTSSettings( + default_settings = self.Settings( model=None, voice=None, language=self.language_to_service_language(Language.EN), @@ -493,12 +493,12 @@ class NeuphonicHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", NeuphonicTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", NeuphonicTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) diff --git a/src/pipecat/services/nvidia/llm.py b/src/pipecat/services/nvidia/llm.py index 17490f513..b40190bec 100644 --- a/src/pipecat/services/nvidia/llm.py +++ b/src/pipecat/services/nvidia/llm.py @@ -16,13 +16,13 @@ from typing import Optional from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class NvidiaLLMSettings(OpenAILLMSettings): +class NvidiaLLMSettings(BaseOpenAILLMService.Settings): """Settings for NvidiaLLMService.""" pass @@ -37,7 +37,7 @@ class NvidiaLLMService(OpenAILLMService): """ Settings = NvidiaLLMSettings - _settings: NvidiaLLMSettings + _settings: Settings def __init__( self, @@ -45,7 +45,7 @@ class NvidiaLLMService(OpenAILLMService): api_key: str, base_url: str = "https://integrate.api.nvidia.com/v1", model: Optional[str] = None, - settings: Optional[NvidiaLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the NvidiaLLMService. @@ -57,18 +57,18 @@ class NvidiaLLMService(OpenAILLMService): "nvidia/llama-3.1-nemotron-70b-instruct". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=NvidiaLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = NvidiaLLMSettings(model="nvidia/llama-3.1-nemotron-70b-instruct") + default_settings = self.Settings(model="nvidia/llama-3.1-nemotron-70b-instruct") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", NvidiaLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/nvidia/stt.py b/src/pipecat/services/nvidia/stt.py index 5935e7846..3c8004baa 100644 --- a/src/pipecat/services/nvidia/stt.py +++ b/src/pipecat/services/nvidia/stt.py @@ -126,13 +126,13 @@ class NvidiaSTTService(STTService): """ Settings = NvidiaSTTSettings - _settings: NvidiaSTTSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for NVIDIA Riva STT service. .. deprecated:: 0.0.105 - Use ``settings=NvidiaSTTSettings(...)`` instead. + Use ``settings=NvidiaSTTService.Settings(...)`` instead. Parameters: language: Target language for transcription. Defaults to EN_US. @@ -152,7 +152,7 @@ class NvidiaSTTService(STTService): sample_rate: Optional[int] = None, params: Optional[InputParams] = None, use_ssl: bool = True, - settings: Optional[NvidiaSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = NVIDIA_TTFS_P99, **kwargs, ): @@ -166,7 +166,7 @@ class NvidiaSTTService(STTService): params: Additional configuration parameters for NVIDIA Riva. .. deprecated:: 0.0.105 - Use ``settings=NvidiaSTTSettings(...)`` instead. + Use ``settings=NvidiaSTTService.Settings(...)`` instead. use_ssl: Whether to use SSL for the NVIDIA Riva server. Defaults to True. settings: Runtime-updatable settings. When provided alongside deprecated @@ -176,7 +176,7 @@ class NvidiaSTTService(STTService): **kwargs: Additional arguments passed to STTService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = NvidiaSTTSettings( + default_settings = self.Settings( model=model_function_map.get("model_name"), language=Language.EN_US, ) @@ -185,7 +185,7 @@ class NvidiaSTTService(STTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", NvidiaSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = params.language @@ -441,13 +441,13 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): """ Settings = NvidiaSegmentedSTTSettings - _settings: NvidiaSegmentedSTTSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for NVIDIA Riva segmented STT service. .. deprecated:: 0.0.105 - Use ``settings=NvidiaSegmentedSTTSettings(...)`` instead. + Use ``settings=NvidiaSegmentedSTTService.Settings(...)`` instead. Parameters: language: Target language for transcription. Defaults to EN_US. @@ -477,7 +477,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): sample_rate: Optional[int] = None, params: Optional[InputParams] = None, use_ssl: bool = True, - settings: Optional[NvidiaSegmentedSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = NVIDIA_TTFS_P99, **kwargs, ): @@ -491,7 +491,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): params: Additional configuration parameters for NVIDIA Riva .. deprecated:: 0.0.105 - Use ``settings=NvidiaSegmentedSTTSettings(...)`` instead. + Use ``settings=NvidiaSegmentedSTTService.Settings(...)`` instead. use_ssl: Whether to use SSL for the NVIDIA Riva server. Defaults to True. settings: Runtime-updatable settings. When provided alongside deprecated @@ -501,7 +501,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): **kwargs: Additional arguments passed to SegmentedSTTService """ # 1. Initialize default_settings with hardcoded defaults - default_settings = NvidiaSegmentedSTTSettings( + default_settings = self.Settings( model=model_function_map.get("model_name"), language=language_to_nvidia_riva_language(Language.EN_US) or "en-US", profanity_filter=False, @@ -515,7 +515,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", NvidiaSegmentedSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = ( language_to_nvidia_riva_language(params.language or Language.EN_US) or "en-US" @@ -641,7 +641,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): """Apply a settings delta and sync internal state. Args: - delta: A :class:`STTSettings` (or ``NvidiaSegmentedSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``NvidiaSegmentedSTTService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/nvidia/tts.py b/src/pipecat/services/nvidia/tts.py index ad39d505b..b41de8b47 100644 --- a/src/pipecat/services/nvidia/tts.py +++ b/src/pipecat/services/nvidia/tts.py @@ -62,13 +62,13 @@ class NvidiaTTSService(TTSService): """ Settings = NvidiaTTSSettings - _settings: NvidiaTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Riva TTS configuration. .. deprecated:: 0.0.105 - Use ``NvidiaTTSSettings`` directly via the ``settings`` parameter instead. + Use ``NvidiaTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language code for synthesis. Defaults to US English. @@ -90,7 +90,7 @@ class NvidiaTTSService(TTSService): "model_name": "magpie-tts-multilingual", }, params: Optional[InputParams] = None, - settings: Optional[NvidiaTTSSettings] = None, + settings: Optional[Settings] = None, use_ssl: bool = True, **kwargs, ): @@ -102,14 +102,14 @@ class NvidiaTTSService(TTSService): voice_id: Voice model identifier. Defaults to multilingual Aria voice. .. deprecated:: 0.0.105 - Use ``settings=NvidiaTTSSettings(voice=...)`` instead. + Use ``settings=NvidiaTTSService.Settings(voice=...)`` instead. sample_rate: Audio sample rate. If None, uses service default. model_function_map: Dictionary containing function_id and model_name for the TTS model. params: Additional configuration parameters for TTS synthesis. .. deprecated:: 0.0.105 - Use ``settings=NvidiaTTSSettings(...)`` instead. + Use ``settings=NvidiaTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -117,7 +117,7 @@ class NvidiaTTSService(TTSService): **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = NvidiaTTSSettings( + default_settings = self.Settings( model=model_function_map.get("model_name"), voice="Magpie-Multilingual.EN-US.Aria", language=Language.EN_US, @@ -126,12 +126,12 @@ class NvidiaTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", NvidiaTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", NvidiaTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = params.language @@ -186,7 +186,7 @@ class NvidiaTTSService(TTSService): stacklevel=2, ) - async def _update_settings(self, delta: NvidiaTTSSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply a settings delta. Settings are stored but not applied to the active connection. diff --git a/src/pipecat/services/ollama/llm.py b/src/pipecat/services/ollama/llm.py index f4d138d78..3b15049a4 100644 --- a/src/pipecat/services/ollama/llm.py +++ b/src/pipecat/services/ollama/llm.py @@ -11,13 +11,13 @@ from typing import Optional from loguru import logger -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class OllamaLLMSettings(OpenAILLMSettings): +class OllamaLLMSettings(BaseOpenAILLMService.Settings): """Settings for OLLamaLLMService.""" pass @@ -31,14 +31,14 @@ class OLLamaLLMService(OpenAILLMService): """ Settings = OllamaLLMSettings - _settings: OllamaLLMSettings + _settings: Settings def __init__( self, *, model: Optional[str] = None, base_url: str = "http://localhost:11434/v1", - settings: Optional[OllamaLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize OLLama LLM service. @@ -47,7 +47,7 @@ class OLLamaLLMService(OpenAILLMService): model: The OLLama model to use. Defaults to "llama2". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=OLLamaLLMService.Settings(model=...)`` instead. base_url: The base URL for the OLLama API endpoint. Defaults to "http://localhost:11434/v1". @@ -56,11 +56,11 @@ class OLLamaLLMService(OpenAILLMService): **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = OllamaLLMSettings(model="llama2") + default_settings = self.Settings(model="llama2") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", OllamaLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 9e3b6a282..d1d695ef6 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -69,13 +69,13 @@ class BaseOpenAILLMService(LLMService): """ Settings = OpenAILLMSettings - _settings: OpenAILLMSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for OpenAI model configuration. .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(...)`` instead of + Use ``settings=BaseOpenAILLMService.Settings(...)`` instead of ``params=InputParams(...)``. Parameters: @@ -119,7 +119,7 @@ class BaseOpenAILLMService(LLMService): default_headers: Optional[Mapping[str, str]] = None, service_tier: Optional[str] = None, params: Optional[InputParams] = None, - settings: Optional[OpenAILLMSettings] = None, + settings: Optional[Settings] = None, retry_timeout_secs: Optional[float] = 5.0, retry_on_timeout: Optional[bool] = False, **kwargs, @@ -130,7 +130,7 @@ class BaseOpenAILLMService(LLMService): model: The OpenAI model name to use (e.g., "gpt-4.1", "gpt-4o"). .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=BaseOpenAILLMService.Settings(model=...)`` instead. api_key: OpenAI API key. If None, uses environment variable. base_url: Custom base URL for OpenAI API. If None, uses default. @@ -141,7 +141,7 @@ class BaseOpenAILLMService(LLMService): params: Input parameters for model configuration and behavior. .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(...)`` instead. + Use ``settings=BaseOpenAILLMService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -150,7 +150,7 @@ class BaseOpenAILLMService(LLMService): **kwargs: Additional arguments passed to the parent LLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenAILLMSettings( + default_settings = self.Settings( model="gpt-4o", system_instruction=None, frequency_penalty=NOT_GIVEN, diff --git a/src/pipecat/services/openai/image.py b/src/pipecat/services/openai/image.py index 6091863f3..31f9949b8 100644 --- a/src/pipecat/services/openai/image.py +++ b/src/pipecat/services/openai/image.py @@ -49,7 +49,7 @@ class OpenAIImageGenService(ImageGenService): """ Settings = OpenAIImageGenSettings - _settings: OpenAIImageGenSettings + _settings: Settings def __init__( self, @@ -61,7 +61,7 @@ class OpenAIImageGenService(ImageGenService): Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"] ] = None, model: Optional[str] = None, - settings: Optional[OpenAIImageGenSettings] = None, + settings: Optional[Settings] = None, ): """Initialize the OpenAI image generation service. @@ -72,29 +72,29 @@ class OpenAIImageGenService(ImageGenService): image_size: Target size for generated images. Defaults to "1024x1024". .. deprecated:: 0.0.105 - Use ``settings=OpenAIImageGenSettings(image_size=...)`` instead. + Use ``settings=OpenAIImageGenService.Settings(image_size=...)`` instead. model: DALL-E model to use for generation. Defaults to "dall-e-3". .. deprecated:: 0.0.105 - Use ``settings=OpenAIImageGenSettings(model=...)`` instead. + Use ``settings=OpenAIImageGenService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenAIImageGenSettings( + default_settings = self.Settings( model="dall-e-3", image_size=None, ) # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", OpenAIImageGenSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if image_size is not None: - _warn_deprecated_param("image_size", OpenAIImageGenSettings, "image_size") + _warn_deprecated_param("image_size", self.Settings, "image_size") default_settings.image_size = image_size # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/openai/llm.py b/src/pipecat/services/openai/llm.py index ab1384a4c..032d4dfa4 100644 --- a/src/pipecat/services/openai/llm.py +++ b/src/pipecat/services/openai/llm.py @@ -25,7 +25,7 @@ from pipecat.processors.aggregators.llm_response import ( LLMUserContextAggregator, ) from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.openai.base_llm import BaseOpenAILLMService, OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.settings import _warn_deprecated_param @@ -72,13 +72,15 @@ class OpenAILLMService(BaseOpenAILLMService): context aggregator creation. """ + Settings = BaseOpenAILLMService.Settings + def __init__( self, *, model: Optional[str] = None, service_tier: Optional[str] = None, params: Optional[BaseOpenAILLMService.InputParams] = None, - settings: Optional[OpenAILLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize OpenAI LLM service. @@ -87,20 +89,20 @@ class OpenAILLMService(BaseOpenAILLMService): model: The OpenAI model name to use. Defaults to "gpt-4.1". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=OpenAILLMService.Settings(model=...)`` instead. service_tier: Service tier to use (e.g., "auto", "flex", "priority"). params: Input parameters for model configuration. .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(...)`` instead. + Use ``settings=OpenAILLMService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to the parent BaseOpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenAILLMSettings( + default_settings = self.Settings( model="gpt-4.1", system_instruction=None, frequency_penalty=NOT_GIVEN, @@ -118,7 +120,7 @@ class OpenAILLMService(BaseOpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", OpenAILLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # Handle service_tier from deprecated params @@ -127,7 +129,7 @@ class OpenAILLMService(BaseOpenAILLMService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", OpenAILLMSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.frequency_penalty = params.frequency_penalty default_settings.presence_penalty = params.presence_penalty diff --git a/src/pipecat/services/openai/realtime/llm.py b/src/pipecat/services/openai/realtime/llm.py index 23d044c5f..300792cbd 100644 --- a/src/pipecat/services/openai/realtime/llm.py +++ b/src/pipecat/services/openai/realtime/llm.py @@ -115,7 +115,7 @@ class OpenAIRealtimeLLMSettings(LLMSettings): # -- Bidirectional sync helpers ------------------------------------------ @staticmethod - def _sync_top_level_to_sp(settings: "OpenAIRealtimeLLMSettings"): + def _sync_top_level_to_sp(settings: "OpenAIRealtimeLLMService.Settings"): """Push top-level ``model``/``system_instruction`` into ``session_properties``.""" if not is_given(settings.session_properties): return @@ -127,7 +127,7 @@ class OpenAIRealtimeLLMSettings(LLMSettings): # -- apply_update override ----------------------------------------------- - def apply_update(self, delta: "OpenAIRealtimeLLMSettings") -> Dict[str, Any]: + def apply_update(self, delta: "OpenAIRealtimeLLMService.Settings") -> Dict[str, Any]: """Merge a delta, keeping ``model``/``system_instruction`` in sync with SP. When the delta contains ``session_properties``, it **replaces** the @@ -165,8 +165,8 @@ class OpenAIRealtimeLLMSettings(LLMSettings): @classmethod def from_mapping( - cls: Type["OpenAIRealtimeLLMSettings"], settings: Mapping[str, Any] - ) -> "OpenAIRealtimeLLMSettings": + cls: Type["OpenAIRealtimeLLMService.Settings"], settings: Mapping[str, Any] + ) -> "OpenAIRealtimeLLMService.Settings": """Build a delta from a plain dict, routing SP keys into ``session_properties``. Keys that correspond to ``SessionProperties`` fields (except ``model``) @@ -211,7 +211,7 @@ class OpenAIRealtimeLLMService(LLMService): """ Settings = OpenAIRealtimeLLMSettings - _settings: OpenAIRealtimeLLMSettings + _settings: Settings # Overriding the default adapter to use the OpenAIRealtimeLLMAdapter one. adapter_class = OpenAIRealtimeLLMAdapter @@ -223,7 +223,7 @@ class OpenAIRealtimeLLMService(LLMService): model: Optional[str] = None, base_url: str = "wss://api.openai.com/v1/realtime", session_properties: Optional[events.SessionProperties] = None, - settings: Optional[OpenAIRealtimeLLMSettings] = None, + settings: Optional[Settings] = None, start_audio_paused: bool = False, start_video_paused: bool = False, video_frame_detail: str = "auto", @@ -237,7 +237,7 @@ class OpenAIRealtimeLLMService(LLMService): model: OpenAI model name. .. deprecated:: 0.0.105 - Use ``settings=OpenAIRealtimeLLMSettings(model=...)`` instead. + Use ``settings=OpenAIRealtimeLLMService.Settings(model=...)`` instead. This is a connection-level parameter set via the WebSocket URL query parameter and cannot be changed during the session. @@ -247,7 +247,7 @@ class OpenAIRealtimeLLMService(LLMService): If None, uses default SessionProperties. .. deprecated:: 0.0.105 - Use ``settings=OpenAIRealtimeLLMSettings(session_properties=...)`` + Use ``settings=OpenAIRealtimeLLMService.Settings(session_properties=...)`` instead. settings: Runtime-updatable settings for this service. start_audio_paused: Whether to start with audio input paused. Defaults to False. @@ -277,7 +277,7 @@ class OpenAIRealtimeLLMService(LLMService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenAIRealtimeLLMSettings( + default_settings = self.Settings( model="gpt-realtime-1.5", system_instruction=None, temperature=None, @@ -294,13 +294,13 @@ class OpenAIRealtimeLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", OpenAIRealtimeLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if session_properties is not None: _warn_deprecated_param( "session_properties", - OpenAIRealtimeLLMSettings, + self.Settings, "session_properties", ) default_settings.session_properties = session_properties @@ -312,7 +312,7 @@ class OpenAIRealtimeLLMService(LLMService): default_settings.system_instruction = session_properties.instructions # Sync top-level model back into session_properties - OpenAIRealtimeLLMSettings._sync_top_level_to_sp(default_settings) + self.Settings._sync_top_level_to_sp(default_settings) # 3. Apply settings delta (canonical API, always wins) if settings is not None: diff --git a/src/pipecat/services/openai/stt.py b/src/pipecat/services/openai/stt.py index 473b23637..943be17de 100644 --- a/src/pipecat/services/openai/stt.py +++ b/src/pipecat/services/openai/stt.py @@ -40,7 +40,6 @@ from pipecat.services.stt_latency import OPENAI_REALTIME_TTFS_P99, OPENAI_TTFS_P from pipecat.services.stt_service import WebsocketSTTService from pipecat.services.whisper.base_stt import ( BaseWhisperSTTService, - BaseWhisperSTTSettings, Transcription, ) from pipecat.transcriptions.language import Language @@ -56,7 +55,7 @@ except ModuleNotFoundError: @dataclass -class OpenAISTTSettings(BaseWhisperSTTSettings): +class OpenAISTTSettings(BaseWhisperSTTService.Settings): """Settings for the OpenAI STT service.""" pass @@ -70,7 +69,7 @@ class OpenAISTTService(BaseWhisperSTTService): """ Settings = OpenAISTTSettings - _settings: OpenAISTTSettings + _settings: Settings def __init__( self, @@ -81,7 +80,7 @@ class OpenAISTTService(BaseWhisperSTTService): language: Optional[Language] = Language.EN, prompt: Optional[str] = None, temperature: Optional[float] = None, - settings: Optional[OpenAISTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = OPENAI_TTFS_P99, **kwargs, ): @@ -91,24 +90,24 @@ class OpenAISTTService(BaseWhisperSTTService): model: Model to use — either gpt-4o or Whisper. .. deprecated:: 0.0.105 - Use ``settings=OpenAISTTSettings(model=...)`` instead. + Use ``settings=OpenAISTTService.Settings(model=...)`` instead. api_key: OpenAI API key. Defaults to None. base_url: API base URL. Defaults to None. language: Language of the audio input. Defaults to English. .. deprecated:: 0.0.105 - Use ``settings=OpenAISTTSettings(language=...)`` instead. + Use ``settings=OpenAISTTService.Settings(language=...)`` instead. prompt: Optional text to guide the model's style or continue a previous segment. .. deprecated:: 0.0.105 - Use ``settings=OpenAISTTSettings(prompt=...)`` instead. + Use ``settings=OpenAISTTService.Settings(prompt=...)`` instead. temperature: Optional sampling temperature between 0 and 1. Defaults to 0.0. .. deprecated:: 0.0.105 - Use ``settings=OpenAISTTSettings(temperature=...)`` instead. + Use ``settings=OpenAISTTService.Settings(temperature=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -118,7 +117,7 @@ class OpenAISTTService(BaseWhisperSTTService): """ # --- 1. Hardcoded defaults --- _language = language or Language.EN - default_settings = OpenAISTTSettings( + default_settings = self.Settings( model="gpt-4o-transcribe", language=self.language_to_service_language(_language), prompt=None, @@ -127,13 +126,13 @@ class OpenAISTTService(BaseWhisperSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", OpenAISTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if prompt is not None: - _warn_deprecated_param("prompt", OpenAISTTSettings, "prompt") + _warn_deprecated_param("prompt", self.Settings, "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", OpenAISTTSettings, "temperature") + _warn_deprecated_param("temperature", self.Settings, "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- @@ -234,7 +233,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): """ Settings = OpenAIRealtimeSTTSettings - _settings: OpenAIRealtimeSTTSettings + _settings: Settings def __init__( self, @@ -247,7 +246,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): turn_detection: Optional[Union[dict, Literal[False]]] = False, noise_reduction: Optional[Literal["near_field", "far_field"]] = None, should_interrupt: bool = True, - settings: Optional[OpenAIRealtimeSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = OPENAI_REALTIME_TTFS_P99, **kwargs, ): @@ -259,20 +258,20 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): ``"gpt-4o-transcribe"`` and ``"gpt-4o-mini-transcribe"``. .. deprecated:: 0.0.105 - Use ``settings=OpenAIRealtimeSTTSettings(model=...)`` instead. + Use ``settings=OpenAIRealtimeSTTService.Settings(model=...)`` instead. base_url: WebSocket base URL for the Realtime API. Defaults to ``"wss://api.openai.com/v1/realtime"``. language: Language of the audio input. Defaults to English. .. deprecated:: 0.0.105 - Use ``settings=OpenAIRealtimeSTTSettings(language=...)`` instead. + Use ``settings=OpenAIRealtimeSTTService.Settings(language=...)`` instead. prompt: Optional prompt text to guide transcription style or provide keyword hints. .. deprecated:: 0.0.105 - Use ``settings=OpenAIRealtimeSTTSettings(prompt=...)`` instead. + Use ``settings=OpenAIRealtimeSTTService.Settings(prompt=...)`` instead. turn_detection: Server-side VAD configuration. Defaults to ``False`` (disabled), which relies on a local VAD @@ -284,7 +283,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): microphones, or ``None`` to disable. .. deprecated:: 0.0.106 - Use ``settings=OpenAIRealtimeSTTSettings(noise_reduction=...)`` instead. + Use ``settings=OpenAIRealtimeSTTService.Settings(noise_reduction=...)`` instead. should_interrupt: Whether to interrupt bot output when speech is detected by server-side VAD. Only applies when turn detection is enabled. Defaults to True. @@ -302,7 +301,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): ) # --- 1. Hardcoded defaults --- - default_settings = OpenAIRealtimeSTTSettings( + default_settings = self.Settings( model="gpt-4o-transcribe", language=Language.EN, prompt=None, @@ -311,16 +310,16 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", OpenAIRealtimeSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if language is not None and language != Language.EN: - _warn_deprecated_param("language", OpenAIRealtimeSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = language if prompt is not None: - _warn_deprecated_param("prompt", OpenAIRealtimeSTTSettings, "prompt") + _warn_deprecated_param("prompt", self.Settings, "prompt") default_settings.prompt = prompt if noise_reduction is not None: - _warn_deprecated_param("noise_reduction", OpenAIRealtimeSTTSettings, "noise_reduction") + _warn_deprecated_param("noise_reduction", self.Settings, "noise_reduction") default_settings.noise_reduction = noise_reduction # --- 3. (no params object for this service) --- @@ -376,7 +375,7 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): Sends a ``session.update`` to the server when the session is active. Args: - delta: A :class:`STTSettings` (or ``OpenAIRealtimeSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``OpenAIRealtimeSTTService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. diff --git a/src/pipecat/services/openai/tts.py b/src/pipecat/services/openai/tts.py index 264475113..7eb80cd1e 100644 --- a/src/pipecat/services/openai/tts.py +++ b/src/pipecat/services/openai/tts.py @@ -82,7 +82,7 @@ class OpenAITTSService(TTSService): """ Settings = OpenAITTSSettings - _settings: OpenAITTSSettings + _settings: Settings OPENAI_SAMPLE_RATE = 24000 # OpenAI TTS always outputs at 24kHz @@ -90,7 +90,7 @@ class OpenAITTSService(TTSService): """Input parameters for OpenAI TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=OpenAITTSSettings(...)`` instead. + Use ``settings=OpenAITTSService.Settings(...)`` instead. Parameters: instructions: Instructions to guide voice synthesis behavior. @@ -111,7 +111,7 @@ class OpenAITTSService(TTSService): instructions: Optional[str] = None, speed: Optional[float] = None, params: Optional[InputParams] = None, - settings: Optional[OpenAITTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize OpenAI TTS service. @@ -122,28 +122,28 @@ class OpenAITTSService(TTSService): voice: Voice ID to use for synthesis. Defaults to "alloy". .. deprecated:: 0.0.105 - Use ``settings=OpenAITTSSettings(voice=...)`` instead. + Use ``settings=OpenAITTSService.Settings(voice=...)`` instead. model: TTS model to use. Defaults to "gpt-4o-mini-tts". .. deprecated:: 0.0.105 - Use ``settings=OpenAITTSSettings(model=...)`` instead. + Use ``settings=OpenAITTSService.Settings(model=...)`` instead. sample_rate: Output audio sample rate in Hz. If None, uses OpenAI's default 24kHz. instructions: Optional instructions to guide voice synthesis behavior. .. deprecated:: 0.0.105 - Use ``settings=OpenAITTSSettings(instructions=...)`` instead. + Use ``settings=OpenAITTSService.Settings(instructions=...)`` instead. speed: Voice speed control (0.25 to 4.0, default 1.0). .. deprecated:: 0.0.105 - Use ``settings=OpenAITTSSettings(speed=...)`` instead. + Use ``settings=OpenAITTSService.Settings(speed=...)`` instead. params: Optional synthesis controls (acting instructions, speed, ...). .. deprecated:: 0.0.105 - Use ``settings=OpenAITTSSettings(...)`` instead. + Use ``settings=OpenAITTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -156,7 +156,7 @@ class OpenAITTSService(TTSService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenAITTSSettings( + default_settings = self.Settings( model="gpt-4o-mini-tts", voice="alloy", language=None, @@ -166,21 +166,21 @@ class OpenAITTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", OpenAITTSSettings, "voice") + _warn_deprecated_param("voice", self.Settings, "voice") default_settings.voice = voice if model is not None: - _warn_deprecated_param("model", OpenAITTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if instructions is not None: - _warn_deprecated_param("instructions", OpenAITTSSettings, "instructions") + _warn_deprecated_param("instructions", self.Settings, "instructions") default_settings.instructions = instructions if speed is not None: - _warn_deprecated_param("speed", OpenAITTSSettings, "speed") + _warn_deprecated_param("speed", self.Settings, "speed") default_settings.speed = speed # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", OpenAITTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.instructions is not None: default_settings.instructions = params.instructions diff --git a/src/pipecat/services/openai_realtime_beta/azure.py b/src/pipecat/services/openai_realtime_beta/azure.py index 6497e1266..b590f2c29 100644 --- a/src/pipecat/services/openai_realtime_beta/azure.py +++ b/src/pipecat/services/openai_realtime_beta/azure.py @@ -11,7 +11,7 @@ from dataclasses import dataclass from loguru import logger -from .openai import OpenAIRealtimeBetaLLMService, OpenAIRealtimeBetaLLMSettings +from .openai import OpenAIRealtimeBetaLLMService try: from websockets.asyncio.client import connect as websocket_connect @@ -24,7 +24,7 @@ except ModuleNotFoundError as e: @dataclass -class AzureRealtimeBetaLLMSettings(OpenAIRealtimeBetaLLMSettings): +class AzureRealtimeBetaLLMSettings(OpenAIRealtimeBetaLLMService.Settings): """Settings for AzureRealtimeBetaLLMService.""" pass @@ -43,7 +43,7 @@ class AzureRealtimeBetaLLMService(OpenAIRealtimeBetaLLMService): """ Settings = AzureRealtimeBetaLLMSettings - _settings: AzureRealtimeBetaLLMSettings + _settings: Settings def __init__( self, diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index d8f817ffc..209ff3122 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -112,7 +112,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): """ Settings = OpenAIRealtimeBetaLLMSettings - _settings: OpenAIRealtimeBetaLLMSettings + _settings: Settings # Overriding the default adapter to use the OpenAIRealtimeLLMAdapter one. adapter_class = OpenAIRealtimeLLMAdapter @@ -124,7 +124,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): model: Optional[str] = None, base_url: str = "wss://api.openai.com/v1/realtime", session_properties: Optional[events.SessionProperties] = None, - settings: Optional[OpenAIRealtimeBetaLLMSettings] = None, + settings: Optional[Settings] = None, start_audio_paused: bool = False, send_transcription_frames: bool = True, **kwargs, @@ -136,7 +136,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): model: OpenAI model name. .. deprecated:: 0.0.105 - Use ``settings=OpenAIRealtimeBetaLLMSettings(model=...)`` instead. + Use ``settings=OpenAIRealtimeBetaLLMService.Settings(model=...)`` instead. base_url: WebSocket base URL for the realtime API. Defaults to "wss://api.openai.com/v1/realtime". @@ -157,7 +157,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenAIRealtimeBetaLLMSettings( + default_settings = self.Settings( model="gpt-4o-realtime-preview-2025-06-03", system_instruction=None, temperature=None, @@ -173,7 +173,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", OpenAIRealtimeBetaLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply settings delta (canonical API, always wins) if settings is not None: diff --git a/src/pipecat/services/openpipe/llm.py b/src/pipecat/services/openpipe/llm.py index 3fef1e3af..a2198eb74 100644 --- a/src/pipecat/services/openpipe/llm.py +++ b/src/pipecat/services/openpipe/llm.py @@ -16,7 +16,7 @@ from typing import Dict, Optional from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @@ -29,7 +29,7 @@ except ModuleNotFoundError as e: @dataclass -class OpenPipeLLMSettings(OpenAILLMSettings): +class OpenPipeLLMSettings(BaseOpenAILLMService.Settings): """Settings for OpenPipeLLMService.""" pass @@ -44,7 +44,7 @@ class OpenPipeLLMService(OpenAILLMService): """ Settings = OpenPipeLLMSettings - _settings: OpenPipeLLMSettings + _settings: Settings def __init__( self, @@ -55,7 +55,7 @@ class OpenPipeLLMService(OpenAILLMService): openpipe_api_key: Optional[str] = None, openpipe_base_url: str = "https://app.openpipe.ai/api/v1", tags: Optional[Dict[str, str]] = None, - settings: Optional[OpenPipeLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize OpenPipe LLM service. @@ -64,7 +64,7 @@ class OpenPipeLLMService(OpenAILLMService): model: The model name to use. Defaults to "gpt-4.1". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=OpenPipeLLMService.Settings(model=...)`` instead. api_key: OpenAI API key for authentication. If None, reads from environment. base_url: Custom OpenAI API endpoint URL. Uses default if None. @@ -76,11 +76,11 @@ class OpenPipeLLMService(OpenAILLMService): **kwargs: Additional arguments passed to parent OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenPipeLLMSettings(model="gpt-4.1") + default_settings = self.Settings(model="gpt-4.1") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", OpenPipeLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/openrouter/llm.py b/src/pipecat/services/openrouter/llm.py index d57c5cf24..84647890f 100644 --- a/src/pipecat/services/openrouter/llm.py +++ b/src/pipecat/services/openrouter/llm.py @@ -15,13 +15,13 @@ from typing import Any, Dict, Optional from loguru import logger -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class OpenRouterLLMSettings(OpenAILLMSettings): +class OpenRouterLLMSettings(BaseOpenAILLMService.Settings): """Settings for OpenRouterLLMService.""" pass @@ -35,7 +35,7 @@ class OpenRouterLLMService(OpenAILLMService): """ Settings = OpenRouterLLMSettings - _settings: OpenRouterLLMSettings + _settings: Settings def __init__( self, @@ -43,7 +43,7 @@ class OpenRouterLLMService(OpenAILLMService): api_key: Optional[str] = None, model: Optional[str] = None, base_url: str = "https://openrouter.ai/api/v1", - settings: Optional[OpenRouterLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the OpenRouter LLM service. @@ -54,7 +54,7 @@ class OpenRouterLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "openai/gpt-4o-2024-11-20". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=OpenRouterLLMService.Settings(model=...)`` instead. base_url: The base URL for OpenRouter API. Defaults to "https://openrouter.ai/api/v1". settings: Runtime-updatable settings. When provided alongside deprecated @@ -62,11 +62,11 @@ class OpenRouterLLMService(OpenAILLMService): **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = OpenRouterLLMSettings(model="openai/gpt-4o-2024-11-20") + default_settings = self.Settings(model="openai/gpt-4o-2024-11-20") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", OpenRouterLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/perplexity/llm.py b/src/pipecat/services/perplexity/llm.py index b13fb20f0..c93a77c16 100644 --- a/src/pipecat/services/perplexity/llm.py +++ b/src/pipecat/services/perplexity/llm.py @@ -18,13 +18,13 @@ from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class PerplexityLLMSettings(OpenAILLMSettings): +class PerplexityLLMSettings(BaseOpenAILLMService.Settings): """Settings for PerplexityLLMService.""" pass @@ -39,7 +39,7 @@ class PerplexityLLMService(OpenAILLMService): """ Settings = PerplexityLLMSettings - _settings: PerplexityLLMSettings + _settings: Settings def __init__( self, @@ -47,7 +47,7 @@ class PerplexityLLMService(OpenAILLMService): api_key: str, base_url: str = "https://api.perplexity.ai", model: Optional[str] = None, - settings: Optional[PerplexityLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Perplexity LLM service. @@ -58,18 +58,18 @@ class PerplexityLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "sonar". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=PerplexityLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = PerplexityLLMSettings(model="sonar") + default_settings = self.Settings(model="sonar") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", PerplexityLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/piper/tts.py b/src/pipecat/services/piper/tts.py index 3cafd2dcd..0d56d377e 100644 --- a/src/pipecat/services/piper/tts.py +++ b/src/pipecat/services/piper/tts.py @@ -48,7 +48,7 @@ class PiperTTSService(TTSService): """ Settings = PiperTTSSettings - _settings: PiperTTSSettings + _settings: Settings def __init__( self, @@ -57,7 +57,7 @@ class PiperTTSService(TTSService): download_dir: Optional[Path] = None, force_redownload: bool = False, use_cuda: bool = False, - settings: Optional[PiperTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Piper TTS service. @@ -66,7 +66,7 @@ class PiperTTSService(TTSService): voice_id: Piper voice model identifier (e.g. `en_US-ryan-high`). .. deprecated:: 0.0.105 - Use ``settings=PiperTTSSettings(voice=...)`` instead. + Use ``settings=PiperTTSService.Settings(voice=...)`` instead. download_dir: Directory for storing voice model files. Defaults to the current working directory. @@ -77,11 +77,11 @@ class PiperTTSService(TTSService): **kwargs: Additional arguments passed to the parent `TTSService`. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = PiperTTSSettings(model=None, voice=None, language=None) + default_settings = self.Settings(model=None, voice=None, language=None) # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", PiperTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) @@ -121,7 +121,7 @@ class PiperTTSService(TTSService): """ return True - async def _update_settings(self, delta: PiperTTSSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply a settings delta. Settings are stored but not applied to the active connection. @@ -202,7 +202,7 @@ class PiperHttpTTSService(TTSService): """ Settings = PiperHttpTTSSettings - _settings: PiperHttpTTSSettings + _settings: Settings def __init__( self, @@ -210,7 +210,7 @@ class PiperHttpTTSService(TTSService): base_url: str, aiohttp_session: aiohttp.ClientSession, voice_id: Optional[str] = None, - settings: Optional[PiperHttpTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Piper TTS service. @@ -221,18 +221,18 @@ class PiperHttpTTSService(TTSService): voice_id: Piper voice model identifier (e.g. `en_US-ryan-high`). .. deprecated:: 0.0.105 - Use ``settings=PiperHttpTTSSettings(voice=...)`` instead. + Use ``settings=PiperHttpTTSService.Settings(voice=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to the parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = PiperHttpTTSSettings(model=None, voice=None, language=None) + default_settings = self.Settings(model=None, voice=None, language=None) # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", PiperHttpTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/qwen/llm.py b/src/pipecat/services/qwen/llm.py index d145f8339..4c3d7015c 100644 --- a/src/pipecat/services/qwen/llm.py +++ b/src/pipecat/services/qwen/llm.py @@ -11,13 +11,13 @@ from typing import Optional from loguru import logger -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class QwenLLMSettings(OpenAILLMSettings): +class QwenLLMSettings(BaseOpenAILLMService.Settings): """Settings for QwenLLMService.""" pass @@ -31,7 +31,7 @@ class QwenLLMService(OpenAILLMService): """ Settings = QwenLLMSettings - _settings: QwenLLMSettings + _settings: Settings def __init__( self, @@ -39,7 +39,7 @@ class QwenLLMService(OpenAILLMService): api_key: str, base_url: str = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", model: Optional[str] = None, - settings: Optional[QwenLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Qwen LLM service. @@ -50,18 +50,18 @@ class QwenLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "qwen-plus". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=QwenLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = QwenLLMSettings(model="qwen-plus") + default_settings = self.Settings(model="qwen-plus") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", QwenLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/resembleai/tts.py b/src/pipecat/services/resembleai/tts.py index 31977ca0b..7375b9a1b 100644 --- a/src/pipecat/services/resembleai/tts.py +++ b/src/pipecat/services/resembleai/tts.py @@ -52,7 +52,7 @@ class ResembleAITTSService(WebsocketTTSService): """ Settings = ResembleAITTSSettings - _settings: ResembleAITTSSettings + _settings: Settings def __init__( self, @@ -63,7 +63,7 @@ class ResembleAITTSService(WebsocketTTSService): precision: Optional[str] = "PCM_16", output_format: Optional[str] = "wav", sample_rate: Optional[int] = 22050, - settings: Optional[ResembleAITTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Resemble AI TTS service. @@ -73,7 +73,7 @@ class ResembleAITTSService(WebsocketTTSService): voice_id: Voice UUID to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=ResembleAITTSSettings(voice=...)`` instead. + Use ``settings=ResembleAITTSService.Settings(voice=...)`` instead. url: WebSocket URL for Resemble AI TTS API. precision: PCM bit depth (PCM_32, PCM_24, PCM_16, or MULAW). @@ -84,7 +84,7 @@ class ResembleAITTSService(WebsocketTTSService): **kwargs: Additional arguments passed to the parent service. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = ResembleAITTSSettings( + default_settings = self.Settings( model=None, voice=None, language=None, @@ -92,7 +92,7 @@ class ResembleAITTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", ResembleAITTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index 15cc8b88a..df1b269cd 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -132,13 +132,13 @@ class RimeTTSService(WebsocketTTSService): """ Settings = RimeTTSSettings - _settings: RimeTTSSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Rime TTS service. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(...)`` instead. + Use ``settings=RimeTTSService.Settings(...)`` instead. Parameters: language: Language for synthesis. Defaults to English. @@ -177,7 +177,7 @@ class RimeTTSService(WebsocketTTSService): model: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[RimeTTSSettings] = None, + settings: Optional[Settings] = None, text_aggregator: Optional[BaseTextAggregator] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, aggregate_sentences: Optional[bool] = None, @@ -190,19 +190,19 @@ class RimeTTSService(WebsocketTTSService): voice_id: ID of the voice to use. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(voice=...)`` instead. + Use ``settings=RimeTTSService.Settings(voice=...)`` instead. url: Rime websocket API endpoint. model: Model ID to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(model=...)`` instead. + Use ``settings=RimeTTSService.Settings(model=...)`` instead. sample_rate: Audio sample rate in Hz. params: Additional configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(...)`` instead. + Use ``settings=RimeTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -220,7 +220,7 @@ class RimeTTSService(WebsocketTTSService): **kwargs: Additional arguments passed to parent class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = RimeTTSSettings( + default_settings = self.Settings( model="arcana", voice=None, language=None, @@ -241,15 +241,15 @@ class RimeTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", RimeTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", RimeTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", RimeTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None @@ -663,13 +663,13 @@ class RimeHttpTTSService(TTSService): """ Settings = RimeTTSSettings - _settings: RimeTTSSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Rime HTTP TTS service. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(...)`` instead. + Use ``settings=RimeHttpTTSService.Settings(...)`` instead. Parameters: language: Language for synthesis. Defaults to English. @@ -696,7 +696,7 @@ class RimeHttpTTSService(TTSService): model: Optional[str] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[RimeTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize Rime HTTP TTS service. @@ -706,26 +706,26 @@ class RimeHttpTTSService(TTSService): voice_id: ID of the voice to use. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(voice=...)`` instead. + Use ``settings=RimeHttpTTSService.Settings(voice=...)`` instead. aiohttp_session: Shared aiohttp session for HTTP requests. model: Model ID to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(model=...)`` instead. + Use ``settings=RimeHttpTTSService.Settings(model=...)`` instead. sample_rate: Audio sample rate in Hz. params: Additional configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=RimeTTSSettings(...)`` instead. + Use ``settings=RimeHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = RimeTTSSettings( + default_settings = self.Settings( model="mistv2", voice=None, language="eng", @@ -744,15 +744,15 @@ class RimeHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", RimeTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", RimeTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", RimeTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else "eng" @@ -888,13 +888,13 @@ class RimeNonJsonTTSService(InterruptibleTTSService): """ Settings = RimeNonJsonTTSSettings - _settings: RimeNonJsonTTSSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Rime Non-JSON WebSocket TTS service. .. deprecated:: 0.0.105 - Use ``settings=RimeNonJsonTTSSettings(...)`` instead. + Use ``settings=RimeNonJsonTTSService.Settings(...)`` instead. Args: language: Language for synthesis. Defaults to English. @@ -922,7 +922,7 @@ class RimeNonJsonTTSService(InterruptibleTTSService): audio_format: str = "pcm", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[RimeNonJsonTTSSettings] = None, + settings: Optional[Settings] = None, aggregate_sentences: Optional[bool] = None, text_aggregation_mode: Optional[TextAggregationMode] = None, **kwargs, @@ -934,20 +934,20 @@ class RimeNonJsonTTSService(InterruptibleTTSService): voice_id: ID of the voice to use. .. deprecated:: 0.0.105 - Use ``settings=RimeNonJsonTTSSettings(voice=...)`` instead. + Use ``settings=RimeNonJsonTTSService.Settings(voice=...)`` instead. url: Rime websocket API endpoint. model: Model ID to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=RimeNonJsonTTSSettings(model=...)`` instead. + Use ``settings=RimeNonJsonTTSService.Settings(model=...)`` instead. audio_format: Audio format to use. sample_rate: Audio sample rate in Hz. params: Additional configuration parameters. .. deprecated:: 0.0.105 - Use ``settings=RimeNonJsonTTSSettings(...)`` instead. + Use ``settings=RimeNonJsonTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -962,7 +962,7 @@ class RimeNonJsonTTSService(InterruptibleTTSService): **kwargs: Additional arguments passed to parent class. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = RimeNonJsonTTSSettings( + default_settings = self.Settings( voice=None, model="arcana", language=None, @@ -974,15 +974,15 @@ class RimeNonJsonTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", RimeNonJsonTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", RimeNonJsonTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", RimeNonJsonTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None diff --git a/src/pipecat/services/sambanova/llm.py b/src/pipecat/services/sambanova/llm.py index 629c3d4c5..a77252ff8 100644 --- a/src/pipecat/services/sambanova/llm.py +++ b/src/pipecat/services/sambanova/llm.py @@ -22,14 +22,14 @@ from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.llm_service import FunctionCallFromLLM -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param from pipecat.utils.tracing.service_decorators import traced_llm @dataclass -class SambaNovaLLMSettings(OpenAILLMSettings): +class SambaNovaLLMSettings(BaseOpenAILLMService.Settings): """Settings for SambaNovaLLMService.""" pass @@ -43,7 +43,7 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore """ Settings = SambaNovaLLMSettings - _settings: SambaNovaLLMSettings + _settings: Settings def __init__( self, @@ -51,7 +51,7 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore api_key: str, model: Optional[str] = None, base_url: str = "https://api.sambanova.ai/v1", - settings: Optional[SambaNovaLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs: Dict[Any, Any], ) -> None: """Initialize SambaNova LLM service. @@ -61,7 +61,7 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore model: The model identifier to use. Defaults to "Llama-4-Maverick-17B-128E-Instruct". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=SambaNovaLLMService.Settings(model=...)`` instead. base_url: The base URL for SambaNova API. Defaults to "https://api.sambanova.ai/v1". settings: Runtime-updatable settings. When provided alongside deprecated @@ -69,11 +69,11 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = SambaNovaLLMSettings(model="Llama-4-Maverick-17B-128E-Instruct") + default_settings = self.Settings(model="Llama-4-Maverick-17B-128E-Instruct") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", SambaNovaLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/sambanova/stt.py b/src/pipecat/services/sambanova/stt.py index c273b67eb..c42a491e1 100644 --- a/src/pipecat/services/sambanova/stt.py +++ b/src/pipecat/services/sambanova/stt.py @@ -15,14 +15,13 @@ from pipecat.services.settings import _warn_deprecated_param from pipecat.services.stt_latency import SAMBANOVA_TTFS_P99 from pipecat.services.whisper.base_stt import ( BaseWhisperSTTService, - BaseWhisperSTTSettings, Transcription, ) from pipecat.transcriptions.language import Language @dataclass -class SambaNovaSTTSettings(BaseWhisperSTTSettings): +class SambaNovaSTTSettings(BaseWhisperSTTService.Settings): """Settings for the SambaNova STT service.""" pass @@ -46,7 +45,7 @@ class SambaNovaSTTService(BaseWhisperSTTService): # type: ignore language: Optional[Language] = None, prompt: Optional[str] = None, temperature: Optional[float] = None, - settings: Optional[SambaNovaSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = SAMBANOVA_TTFS_P99, **kwargs: Any, ) -> None: @@ -56,24 +55,24 @@ class SambaNovaSTTService(BaseWhisperSTTService): # type: ignore model: Whisper model to use. .. deprecated:: 0.0.105 - Use ``settings=SambaNovaSTTSettings(model=...)`` instead. + Use ``settings=SambaNovaSTTService.Settings(model=...)`` instead. api_key: SambaNova API key. Defaults to None. base_url: API base URL. Defaults to "https://api.sambanova.ai/v1". language: Language of the audio input. .. deprecated:: 0.0.105 - Use ``settings=SambaNovaSTTSettings(language=...)`` instead. + Use ``settings=SambaNovaSTTService.Settings(language=...)`` instead. prompt: Optional text to guide the model's style or continue a previous segment. .. deprecated:: 0.0.105 - Use ``settings=SambaNovaSTTSettings(prompt=...)`` instead. + Use ``settings=SambaNovaSTTService.Settings(prompt=...)`` instead. temperature: Optional sampling temperature between 0 and 1. .. deprecated:: 0.0.105 - Use ``settings=SambaNovaSTTSettings(temperature=...)`` instead. + Use ``settings=SambaNovaSTTService.Settings(temperature=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -82,7 +81,7 @@ class SambaNovaSTTService(BaseWhisperSTTService): # type: ignore **kwargs: Additional arguments passed to `pipecat.services.whisper.base_stt.BaseWhisperSTTService`. """ # --- 1. Hardcoded defaults --- - default_settings = SambaNovaSTTSettings( + default_settings = self.Settings( model="Whisper-Large-v3", language=self.language_to_service_language(Language.EN), prompt=None, @@ -91,16 +90,16 @@ class SambaNovaSTTService(BaseWhisperSTTService): # type: ignore # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", SambaNovaSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if language is not None: - _warn_deprecated_param("language", SambaNovaSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = self.language_to_service_language(language) if prompt is not None: - _warn_deprecated_param("prompt", SambaNovaSTTSettings, "prompt") + _warn_deprecated_param("prompt", self.Settings, "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", SambaNovaSTTSettings, "temperature") + _warn_deprecated_param("temperature", self.Settings, "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/sarvam/stt.py b/src/pipecat/services/sarvam/stt.py index 429973838..b2c02f7cd 100644 --- a/src/pipecat/services/sarvam/stt.py +++ b/src/pipecat/services/sarvam/stt.py @@ -172,13 +172,13 @@ class SarvamSTTService(STTService): """ Settings = SarvamSTTSettings - _settings: SarvamSTTSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Sarvam STT service. .. deprecated:: 0.0.105 - Use ``settings=SarvamSTTSettings(...)`` instead. + Use ``settings=SarvamSTTService.Settings(...)`` instead. Parameters: language: Target language for transcription. @@ -210,7 +210,7 @@ class SarvamSTTService(STTService): sample_rate: Optional[int] = None, input_audio_codec: str = "wav", params: Optional[InputParams] = None, - settings: Optional[SarvamSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = SARVAM_TTFS_P99, keepalive_timeout: Optional[float] = None, keepalive_interval: float = 5.0, @@ -223,7 +223,7 @@ class SarvamSTTService(STTService): model: Sarvam model to use for transcription. .. deprecated:: 0.0.105 - Use ``settings=SarvamSTTSettings(model=...)`` instead. + Use ``settings=SarvamSTTService.Settings(model=...)`` instead. mode: Mode of operation. Options: transcribe, translate, verbatim, translit, codemix. Only applicable to models that support it @@ -233,7 +233,7 @@ class SarvamSTTService(STTService): params: Configuration parameters for Sarvam STT service. .. deprecated:: 0.0.105 - Use ``settings=SarvamSTTSettings(...)`` instead. + Use ``settings=SarvamSTTService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -245,7 +245,7 @@ class SarvamSTTService(STTService): **kwargs: Additional arguments passed to the parent STTService. """ # --- 1. Hardcoded defaults --- - default_settings = SarvamSTTSettings( + default_settings = self.Settings( model="saarika:v2.5", language=None, prompt=None, @@ -255,12 +255,12 @@ class SarvamSTTService(STTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", SarvamSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # --- 3. Deprecated params overrides --- if params is not None: - _warn_deprecated_param("params", SarvamSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = params.language default_settings.prompt = params.prompt @@ -374,7 +374,7 @@ class SarvamSTTService(STTService): """Apply a settings delta, validate, sync state, and reconnect. Args: - delta: A :class:`STTSettings` (or ``SarvamSTTSettings``) delta. + delta: A :class:`STTSettings` (or ``SarvamSTTService.Settings``) delta. Returns: Dict mapping changed field names to their previous values. @@ -389,11 +389,7 @@ class SarvamSTTService(STTService): f"Model '{self._settings.model}' does not support language parameter " "(auto-detects language)." ) - if ( - isinstance(delta, SarvamSTTSettings) - and is_given(delta.prompt) - and delta.prompt is not None - ): + if isinstance(delta, self.Settings) and is_given(delta.prompt) and delta.prompt is not None: if not self._config.supports_prompt: raise ValueError( f"Model '{self._settings.model}' does not support prompt parameter." @@ -417,7 +413,7 @@ class SarvamSTTService(STTService): """Set the transcription/translation prompt and reconnect. .. deprecated:: 0.0.104 - Use ``STTUpdateSettingsFrame(SarvamSTTSettings(prompt=...))`` instead. + Use ``STTUpdateSettingsFrame(SarvamSTTService.Settings(prompt=...))`` instead. Args: prompt: Prompt text to guide transcription/translation style/context. @@ -430,7 +426,7 @@ class SarvamSTTService(STTService): warnings.simplefilter("always") warnings.warn( f"{self.__class__.__name__}.set_prompt() is deprecated. " - "Use STTUpdateSettingsFrame(SarvamSTTSettings(prompt=...)) instead.", + "Use STTUpdateSettingsFrame(self.Settings(prompt=...)) instead.", DeprecationWarning, stacklevel=2, ) diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index 0d6a872eb..a469dbf33 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -284,7 +284,7 @@ class SarvamHttpTTSSettings(TTSSettings): class SarvamTTSSettings(SarvamHttpTTSSettings): """Settings for SarvamTTSService. - Extends :class:`SarvamHttpTTSSettings` with WebSocket-specific buffering parameters. + Extends :class:`SarvamHttpTTSService.Settings` with WebSocket-specific buffering parameters. Parameters: min_buffer_size: Minimum characters to buffer before generating audio. @@ -352,13 +352,13 @@ class SarvamHttpTTSService(TTSService): """ Settings = SarvamHttpTTSSettings - _settings: SarvamHttpTTSSettings + _settings: Settings class InputParams(BaseModel): """Input parameters for Sarvam TTS configuration. .. deprecated:: 0.0.105 - Use ``SarvamHttpTTSSettings`` directly via the ``settings`` parameter instead. + Use ``SarvamHttpTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: language: Language for synthesis. Defaults to English (India). @@ -416,7 +416,7 @@ class SarvamHttpTTSService(TTSService): base_url: str = "https://api.sarvam.ai", sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[SarvamHttpTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Sarvam TTS service. @@ -427,14 +427,14 @@ class SarvamHttpTTSService(TTSService): voice_id: Speaker voice ID. If None, uses model-appropriate default. .. deprecated:: 0.0.105 - Use ``settings=SarvamHttpTTSSettings(voice=...)`` instead. + Use ``settings=SarvamHttpTTSService.Settings(voice=...)`` instead. model: TTS model to use. Options: - "bulbul:v2" (default): Standard model with pitch/loudness support - "bulbul:v3-beta": Advanced model with temperature control .. deprecated:: 0.0.105 - Use ``settings=SarvamHttpTTSSettings(model=...)`` instead. + Use ``settings=SarvamHttpTTSService.Settings(model=...)`` instead. base_url: Sarvam AI API base URL. Defaults to "https://api.sarvam.ai". sample_rate: Audio sample rate in Hz (8000, 16000, 22050, 24000). @@ -442,14 +442,14 @@ class SarvamHttpTTSService(TTSService): params: Additional voice and preprocessing parameters. If None, uses defaults. .. deprecated:: 0.0.105 - Use ``settings=SarvamHttpTTSSettings(...)`` instead. + Use ``settings=SarvamHttpTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = SarvamHttpTTSSettings( + default_settings = self.Settings( model="bulbul:v2", voice="anushka", language="en-IN", @@ -462,15 +462,15 @@ class SarvamHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", SarvamHttpTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", SarvamHttpTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", SarvamHttpTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = ( @@ -719,13 +719,13 @@ class SarvamTTSService(InterruptibleTTSService): """ Settings = SarvamTTSSettings - _settings: SarvamTTSSettings + _settings: Settings class InputParams(BaseModel): """Configuration parameters for Sarvam TTS WebSocket service. .. deprecated:: 0.0.105 - Use ``SarvamTTSSettings`` directly via the ``settings`` parameter instead. + Use ``SarvamTTSService.Settings`` directly via the ``settings`` parameter instead. Parameters: pitch: Voice pitch adjustment (-0.75 to 0.75). Defaults to 0.0. @@ -819,7 +819,7 @@ class SarvamTTSService(InterruptibleTTSService): text_aggregation_mode: Optional[TextAggregationMode] = None, sample_rate: Optional[int] = None, params: Optional[InputParams] = None, - settings: Optional[SarvamTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Sarvam TTS service with voice and transport configuration. @@ -831,12 +831,12 @@ class SarvamTTSService(InterruptibleTTSService): - "bulbul:v3-beta": Advanced model with temperature control .. deprecated:: 0.0.105 - Use ``settings=SarvamTTSSettings(model=...)`` instead. + Use ``settings=SarvamTTSService.Settings(model=...)`` instead. voice_id: Speaker voice ID. If None, uses model-appropriate default. .. deprecated:: 0.0.105 - Use ``settings=SarvamTTSSettings(voice=...)`` instead. + Use ``settings=SarvamTTSService.Settings(voice=...)`` instead. url: WebSocket URL for the TTS backend (default production URL). aggregate_sentences: Deprecated. Use text_aggregation_mode instead. @@ -850,7 +850,7 @@ class SarvamTTSService(InterruptibleTTSService): params: Optional input parameters to override defaults. .. deprecated:: 0.0.105 - Use ``settings=SarvamTTSSettings(...)`` instead. + Use ``settings=SarvamTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -859,7 +859,7 @@ class SarvamTTSService(InterruptibleTTSService): See https://docs.sarvam.ai/api-reference-docs/text-to-speech/stream """ # 1. Initialize default_settings with hardcoded defaults - default_settings = SarvamTTSSettings( + default_settings = self.Settings( model="bulbul:v2", voice="anushka", language="en-IN", @@ -874,10 +874,10 @@ class SarvamTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", SarvamTTSSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", SarvamTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # Init-only audio format fields (not runtime-updatable) @@ -886,7 +886,7 @@ class SarvamTTSService(InterruptibleTTSService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", SarvamTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: if params.language is not None: default_settings.language = ( diff --git a/src/pipecat/services/soniox/stt.py b/src/pipecat/services/soniox/stt.py index 6e5a8f62c..abf42dcdc 100644 --- a/src/pipecat/services/soniox/stt.py +++ b/src/pipecat/services/soniox/stt.py @@ -80,7 +80,7 @@ class SonioxInputParams(BaseModel): """Real-time transcription settings. .. deprecated:: 0.0.105 - Use ``settings=SonioxSTTSettings(...)`` instead. + Use ``settings=SonioxSTTService.Settings(...)`` instead. See Soniox WebSocket API documentation for more details: https://soniox.com/docs/speech-to-text/api-reference/websocket-api#configuration-parameters @@ -175,7 +175,7 @@ class SonioxSTTService(WebsocketSTTService): """ Settings = SonioxSTTSettings - _settings: SonioxSTTSettings + _settings: Settings def __init__( self, @@ -188,7 +188,7 @@ class SonioxSTTService(WebsocketSTTService): num_channels: int = 1, params: Optional[SonioxInputParams] = None, vad_force_turn_endpoint: bool = True, - settings: Optional[SonioxSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = SONIOX_TTFS_P99, **kwargs, ): @@ -201,7 +201,7 @@ class SonioxSTTService(WebsocketSTTService): model: Soniox model to use for transcription. .. deprecated:: 0.0.105 - Use ``settings=SonioxSTTSettings(model=...)`` instead. + Use ``settings=SonioxSTTService.Settings(model=...)`` instead. audio_format: Audio format for transcription. Defaults to ``"pcm_s16le"``. num_channels: Number of audio channels. Defaults to 1. @@ -209,7 +209,7 @@ class SonioxSTTService(WebsocketSTTService): speaker diarization. .. deprecated:: 0.0.105 - Use ``settings=SonioxSTTSettings(...)`` instead. + Use ``settings=SonioxSTTService.Settings(...)`` instead. vad_force_turn_endpoint: Listen to `VADUserStoppedSpeakingFrame` to send finalize message to Soniox. If disabled, Soniox will detect the end of the speech. Defaults to True. @@ -220,7 +220,7 @@ class SonioxSTTService(WebsocketSTTService): **kwargs: Additional arguments passed to the STTService. """ # --- 1. Hardcoded defaults --- - default_settings = SonioxSTTSettings( + default_settings = self.Settings( model="stt-rt-v4", language=None, language_hints=None, @@ -233,12 +233,12 @@ class SonioxSTTService(WebsocketSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", SonioxSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # --- 3. Deprecated params overrides --- if params is not None: - _warn_deprecated_param("params", SonioxSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.model = params.model if params.audio_format is not None: @@ -297,7 +297,7 @@ class SonioxSTTService(WebsocketSTTService): await super().start(frame) await self._connect() - async def _update_settings(self, delta: SonioxSTTSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply settings delta and reconnect if anything changed. Args: diff --git a/src/pipecat/services/speechmatics/stt.py b/src/pipecat/services/speechmatics/stt.py index aaff90900..2a54b2b08 100644 --- a/src/pipecat/services/speechmatics/stt.py +++ b/src/pipecat/services/speechmatics/stt.py @@ -176,7 +176,7 @@ class SpeechmaticsSTTService(STTService): """ Settings = SpeechmaticsSTTSettings - _settings: SpeechmaticsSTTSettings + _settings: Settings # Export related classes as class attributes TurnDetectionMode = TurnDetectionMode @@ -343,7 +343,7 @@ class SpeechmaticsSTTService(STTService): """Update parameters for Speechmatics STT service. .. deprecated:: 0.0.104 - Use ``SpeechmaticsSTTSettings`` with ``STTUpdateSettingsFrame`` instead. + Use ``SpeechmaticsSTTService.Settings`` with ``STTUpdateSettingsFrame`` instead. Parameters: focus_speakers: List of speaker IDs to focus on. When enabled, only these speakers are @@ -380,7 +380,7 @@ class SpeechmaticsSTTService(STTService): encoding: AudioEncoding = AudioEncoding.PCM_S16LE, params: InputParams | None = None, should_interrupt: bool = True, - settings: SpeechmaticsSTTSettings | None = None, + settings: Settings | None = None, ttfs_p99_latency: float | None = SPEECHMATICS_TTFS_P99, **kwargs, ): @@ -396,7 +396,7 @@ class SpeechmaticsSTTService(STTService): params: Input parameters for the service. .. deprecated:: 0.0.105 - Use ``settings=SpeechmaticsSTTSettings(...)`` instead. + Use ``settings=SpeechmaticsSTTService.Settings(...)`` instead. should_interrupt: Determine whether the bot should be interrupted when Speechmatics turn_detection_mode is configured to detect user speech. settings: Runtime-updatable settings. When provided alongside deprecated @@ -424,7 +424,7 @@ class SpeechmaticsSTTService(STTService): self._check_deprecated_args(kwargs, _params) # --- 1. Hardcoded defaults --- - default_settings = SpeechmaticsSTTSettings( + default_settings = self.Settings( model=None, # Will be resolved from operating_point after config is built language=Language.EN, domain=None, @@ -454,7 +454,7 @@ class SpeechmaticsSTTService(STTService): # --- 3. Deprecated params overrides --- if params is not None: - _warn_deprecated_param("params", SpeechmaticsSTTSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.language = _params.language default_settings.domain = _params.domain @@ -537,11 +537,11 @@ class SpeechmaticsSTTService(STTService): await super().start(frame) await self._connect() - async def _update_settings(self, delta: SpeechmaticsSTTSettings) -> dict[str, Any]: + async def _update_settings(self, delta: Settings) -> dict[str, Any]: """Apply settings delta, reconnecting only when necessary. Fields are classified into three categories (see - ``SpeechmaticsSTTSettings``): + ``SpeechmaticsSTTService.Settings``): * **HOT_FIELDS** – diarization speaker settings that can be pushed to a live Speechmatics connection without reconnecting. @@ -561,7 +561,7 @@ class SpeechmaticsSTTService(STTService): if not changed: return changed - no_reconnect = SpeechmaticsSTTSettings.HOT_FIELDS | SpeechmaticsSTTSettings.LOCAL_FIELDS + no_reconnect = self.Settings.HOT_FIELDS | self.Settings.LOCAL_FIELDS needs_reconnect = bool(changed.keys() - no_reconnect) if needs_reconnect: @@ -571,7 +571,7 @@ class SpeechmaticsSTTService(STTService): self._config = self._build_config(self._settings) await self._disconnect() await self._connect() - elif changed.keys() & SpeechmaticsSTTSettings.HOT_FIELDS: + elif changed.keys() & self.Settings.HOT_FIELDS: logger.debug(f"{self} applying hot settings update: {changed.keys()}") if self._config.enable_diarization: # Only hot-updatable fields changed — push to the live session. @@ -586,7 +586,7 @@ class SpeechmaticsSTTService(STTService): ) # Diarization not enabled — the new settings will take effect # if/when diarization is enabled, which does require a reconnect. - elif changed.keys() & SpeechmaticsSTTSettings.LOCAL_FIELDS: + elif changed.keys() & self.Settings.LOCAL_FIELDS: logger.debug( f"{self} local settings update, no special action required: {changed.keys()}" ) @@ -705,7 +705,7 @@ class SpeechmaticsSTTService(STTService): # CONFIGURATION # ============================================================================ - def _build_config(self, settings: SpeechmaticsSTTSettings) -> VoiceAgentConfig: + def _build_config(self, settings: Settings) -> VoiceAgentConfig: """Build a ``VoiceAgentConfig`` from the given settings. Used both at init time (with explicit settings, before @@ -778,7 +778,7 @@ class SpeechmaticsSTTService(STTService): .. deprecated:: 0.0.104 Use ``STTUpdateSettingsFrame`` with - ``SpeechmaticsSTTSettings(...)`` instead. + ``SpeechmaticsSTTService.Settings(...)`` instead. This can update the speakers to listen to or ignore during an in-flight transcription. Only available if diarization is enabled. @@ -790,7 +790,7 @@ class SpeechmaticsSTTService(STTService): warnings.simplefilter("always") warnings.warn( "update_params() is deprecated. Use STTUpdateSettingsFrame with " - "SpeechmaticsSTTSettings(...) instead.", + "self.Settings(...) instead.", DeprecationWarning, ) # Check possible diff --git a/src/pipecat/services/speechmatics/tts.py b/src/pipecat/services/speechmatics/tts.py index 20a3212ff..243437cb3 100644 --- a/src/pipecat/services/speechmatics/tts.py +++ b/src/pipecat/services/speechmatics/tts.py @@ -54,7 +54,7 @@ class SpeechmaticsTTSService(TTSService): """ Settings = SpeechmaticsTTSSettings - _settings: SpeechmaticsTTSSettings + _settings: Settings SPEECHMATICS_SAMPLE_RATE = 16000 @@ -62,7 +62,7 @@ class SpeechmaticsTTSService(TTSService): """Optional input parameters for Speechmatics TTS configuration. .. deprecated:: 0.0.105 - Use ``settings=SpeechmaticsTTSSettings(...)`` instead. + Use ``settings=SpeechmaticsTTSService.Settings(...)`` instead. Parameters: max_retries: Maximum number of retries for TTS requests. Defaults to 5. @@ -79,7 +79,7 @@ class SpeechmaticsTTSService(TTSService): aiohttp_session: aiohttp.ClientSession, sample_rate: Optional[int] = SPEECHMATICS_SAMPLE_RATE, params: Optional[InputParams] = None, - settings: Optional[SpeechmaticsTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Speechmatics TTS service. @@ -90,14 +90,14 @@ class SpeechmaticsTTSService(TTSService): voice_id: Voice model to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=SpeechmaticsTTSSettings(voice=...)`` instead. + Use ``settings=SpeechmaticsTTSService.Settings(voice=...)`` instead. aiohttp_session: Shared aiohttp session for HTTP requests. sample_rate: Audio sample rate in Hz. params: Input parameters for the service. .. deprecated:: 0.0.105 - Use ``settings=SpeechmaticsTTSSettings(...)`` instead. + Use ``settings=SpeechmaticsTTSService.Settings(...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -110,7 +110,7 @@ class SpeechmaticsTTSService(TTSService): ) # 1. Initialize default_settings with hardcoded defaults - default_settings = SpeechmaticsTTSSettings( + default_settings = self.Settings( model=None, voice="sarah", language=None, @@ -119,12 +119,12 @@ class SpeechmaticsTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", SpeechmaticsTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", SpeechmaticsTTSSettings) + _warn_deprecated_param("params", self.Settings) if not settings: default_settings.max_retries = params.max_retries diff --git a/src/pipecat/services/tavus/video.py b/src/pipecat/services/tavus/video.py index 3d5946e54..9043710c8 100644 --- a/src/pipecat/services/tavus/video.py +++ b/src/pipecat/services/tavus/video.py @@ -60,7 +60,7 @@ class TavusVideoService(AIService): """ Settings = TavusVideoSettings - _settings: TavusVideoSettings + _settings: Settings def __init__( self, @@ -69,7 +69,7 @@ class TavusVideoService(AIService): replica_id: str, persona_id: str = "pipecat-stream", session: aiohttp.ClientSession, - settings: Optional[TavusVideoSettings] = None, + settings: Optional[Settings] = None, **kwargs, ) -> None: """Initialize the Tavus video service. diff --git a/src/pipecat/services/together/llm.py b/src/pipecat/services/together/llm.py index 21663206e..b1eb83d26 100644 --- a/src/pipecat/services/together/llm.py +++ b/src/pipecat/services/together/llm.py @@ -11,13 +11,13 @@ from typing import Optional from loguru import logger -from pipecat.services.openai.base_llm import OpenAILLMSettings +from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.settings import _warn_deprecated_param @dataclass -class TogetherLLMSettings(OpenAILLMSettings): +class TogetherLLMSettings(BaseOpenAILLMService.Settings): """Settings for TogetherLLMService.""" pass @@ -31,7 +31,7 @@ class TogetherLLMService(OpenAILLMService): """ Settings = TogetherLLMSettings - _settings: TogetherLLMSettings + _settings: Settings def __init__( self, @@ -39,7 +39,7 @@ class TogetherLLMService(OpenAILLMService): api_key: str, base_url: str = "https://api.together.xyz/v1", model: Optional[str] = None, - settings: Optional[TogetherLLMSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize Together.ai LLM service. @@ -50,18 +50,18 @@ class TogetherLLMService(OpenAILLMService): model: The model identifier to use. Defaults to "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo". .. deprecated:: 0.0.105 - Use ``settings=OpenAILLMSettings(model=...)`` instead. + Use ``settings=TogetherLLMService.Settings(model=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = TogetherLLMSettings(model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo") + default_settings = self.Settings(model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo") # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", TogetherLLMSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/ultravox/llm.py b/src/pipecat/services/ultravox/llm.py index bfd34ceae..fe8a97549 100644 --- a/src/pipecat/services/ultravox/llm.py +++ b/src/pipecat/services/ultravox/llm.py @@ -167,13 +167,13 @@ class UltravoxRealtimeLLMService(LLMService): """ Settings = UltravoxRealtimeLLMSettings - _settings: UltravoxRealtimeLLMSettings + _settings: Settings def __init__( self, *, params: Union[AgentInputParams, OneShotInputParams, JoinUrlInputParams], - settings: Optional[UltravoxRealtimeLLMSettings] = None, + settings: Optional[Settings] = None, one_shot_selected_tools: Optional[ToolsSchema] = None, **kwargs, ): @@ -188,7 +188,7 @@ class UltravoxRealtimeLLMService(LLMService): **kwargs: Additional arguments passed to parent LLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = UltravoxRealtimeLLMSettings( + default_settings = self.Settings( model=None, system_instruction=None, temperature=None, @@ -383,7 +383,7 @@ class UltravoxRealtimeLLMService(LLMService): await self.cancel_task(self._receive_task, timeout=1.0) self._receive_task = None - async def _update_settings(self, delta: UltravoxRealtimeLLMSettings): + async def _update_settings(self, delta: Settings): changed = await super()._update_settings(delta) if "output_medium" in changed: await self._update_output_medium(self._settings.output_medium) diff --git a/src/pipecat/services/whisper/base_stt.py b/src/pipecat/services/whisper/base_stt.py index 869f92a55..d045eafc7 100644 --- a/src/pipecat/services/whisper/base_stt.py +++ b/src/pipecat/services/whisper/base_stt.py @@ -123,7 +123,7 @@ class BaseWhisperSTTService(SegmentedSTTService): """ Settings = BaseWhisperSTTSettings - _settings: BaseWhisperSTTSettings + _settings: Settings def __init__( self, @@ -136,7 +136,7 @@ class BaseWhisperSTTService(SegmentedSTTService): temperature: Optional[float] = None, include_prob_metrics: bool = False, push_empty_transcripts: bool = False, - settings: Optional[BaseWhisperSTTSettings] = None, + settings: Optional[Settings] = None, ttfs_p99_latency: Optional[float] = WHISPER_TTFS_P99, **kwargs, ): @@ -146,24 +146,24 @@ class BaseWhisperSTTService(SegmentedSTTService): model: Name of the Whisper model to use. .. deprecated:: 0.0.105 - Use ``settings=BaseWhisperSTTSettings(model=...)`` instead. + Use ``settings=BaseWhisperSTTService.Settings(model=...)`` instead. api_key: Service API key. Defaults to None. base_url: Service API base URL. Defaults to None. language: Language of the audio input. .. deprecated:: 0.0.105 - Use ``settings=BaseWhisperSTTSettings(language=...)`` instead. + Use ``settings=BaseWhisperSTTService.Settings(language=...)`` instead. prompt: Optional text to guide the model's style or continue a previous segment. .. deprecated:: 0.0.105 - Use ``settings=BaseWhisperSTTSettings(prompt=...)`` instead. + Use ``settings=BaseWhisperSTTService.Settings(prompt=...)`` instead. temperature: Sampling temperature between 0 and 1. .. deprecated:: 0.0.105 - Use ``settings=BaseWhisperSTTSettings(temperature=...)`` instead. + Use ``settings=BaseWhisperSTTService.Settings(temperature=...)`` instead. include_prob_metrics: If True, enables probability metrics in API response. Each service implements this differently (see child classes). @@ -181,7 +181,7 @@ class BaseWhisperSTTService(SegmentedSTTService): **kwargs: Additional arguments passed to SegmentedSTTService. """ # --- 1. Hardcoded defaults --- - default_settings = BaseWhisperSTTSettings( + default_settings = self.Settings( model=None, language=None, prompt=None, @@ -190,16 +190,16 @@ class BaseWhisperSTTService(SegmentedSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", BaseWhisperSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if language is not None: - _warn_deprecated_param("language", BaseWhisperSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = self.language_to_service_language(language) if prompt is not None: - _warn_deprecated_param("prompt", BaseWhisperSTTSettings, "prompt") + _warn_deprecated_param("prompt", self.Settings, "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", BaseWhisperSTTSettings, "temperature") + _warn_deprecated_param("temperature", self.Settings, "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/whisper/stt.py b/src/pipecat/services/whisper/stt.py index b5971ce9a..29574007a 100644 --- a/src/pipecat/services/whisper/stt.py +++ b/src/pipecat/services/whisper/stt.py @@ -208,7 +208,7 @@ class WhisperSTTService(SegmentedSTTService): """ Settings = WhisperSTTSettings - _settings: WhisperSTTSettings + _settings: Settings def __init__( self, @@ -218,7 +218,7 @@ class WhisperSTTService(SegmentedSTTService): compute_type: str = "default", no_speech_prob: Optional[float] = None, language: Optional[Language] = None, - settings: Optional[WhisperSTTSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Whisper STT service. @@ -227,7 +227,7 @@ class WhisperSTTService(SegmentedSTTService): model: The Whisper model to use for transcription. Can be a Model enum or string. .. deprecated:: 0.0.105 - Use ``settings=WhisperSTTSettings(model=...)`` instead. + Use ``settings=WhisperSTTService.Settings(model=...)`` instead. device: The device to run inference on ('cpu', 'cuda', or 'auto'). Defaults to ``"auto"``. @@ -236,19 +236,19 @@ class WhisperSTTService(SegmentedSTTService): no_speech_prob: Probability threshold for filtering out non-speech segments. .. deprecated:: 0.0.105 - Use ``settings=WhisperSTTSettings(no_speech_prob=...)`` instead. + Use ``settings=WhisperSTTService.Settings(no_speech_prob=...)`` instead. language: The default language for transcription. .. deprecated:: 0.0.105 - Use ``settings=WhisperSTTSettings(language=...)`` instead. + Use ``settings=WhisperSTTService.Settings(language=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to SegmentedSTTService. """ # --- 1. Hardcoded defaults --- - default_settings = WhisperSTTSettings( + default_settings = self.Settings( model=Model.DISTIL_MEDIUM_EN.value, language=Language.EN, no_speech_prob=0.4, @@ -256,13 +256,13 @@ class WhisperSTTService(SegmentedSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", WhisperSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if isinstance(model, str) else model.value if no_speech_prob is not None: - _warn_deprecated_param("no_speech_prob", WhisperSTTSettings, "no_speech_prob") + _warn_deprecated_param("no_speech_prob", self.Settings, "no_speech_prob") default_settings.no_speech_prob = no_speech_prob if language is not None: - _warn_deprecated_param("language", WhisperSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = language # --- 3. (no params object for this service) --- @@ -382,7 +382,7 @@ class WhisperSTTServiceMLX(WhisperSTTService): """ Settings = WhisperMLXSTTSettings - _settings: WhisperMLXSTTSettings + _settings: Settings def __init__( self, @@ -391,7 +391,7 @@ class WhisperSTTServiceMLX(WhisperSTTService): no_speech_prob: Optional[float] = None, language: Optional[Language] = None, temperature: Optional[float] = None, - settings: Optional[WhisperMLXSTTSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the MLX Whisper STT service. @@ -400,29 +400,29 @@ class WhisperSTTServiceMLX(WhisperSTTService): model: The MLX Whisper model to use for transcription. Can be an MLXModel enum or string. .. deprecated:: 0.0.105 - Use ``settings=WhisperMLXSTTSettings(model=...)`` instead. + Use ``settings=WhisperSTTServiceMLX.Settings(model=...)`` instead. no_speech_prob: Probability threshold for filtering out non-speech segments. .. deprecated:: 0.0.105 - Use ``settings=WhisperMLXSTTSettings(no_speech_prob=...)`` instead. + Use ``settings=WhisperSTTServiceMLX.Settings(no_speech_prob=...)`` instead. language: The default language for transcription. .. deprecated:: 0.0.105 - Use ``settings=WhisperMLXSTTSettings(language=...)`` instead. + Use ``settings=WhisperSTTServiceMLX.Settings(language=...)`` instead. temperature: Temperature for sampling. Can be a float or tuple of floats. .. deprecated:: 0.0.105 - Use ``settings=WhisperMLXSTTSettings(temperature=...)`` instead. + Use ``settings=WhisperSTTServiceMLX.Settings(temperature=...)`` instead. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to SegmentedSTTService. """ # --- 1. Hardcoded defaults --- - default_settings = WhisperMLXSTTSettings( + default_settings = self.Settings( model=MLXModel.TINY.value, language=Language.EN, no_speech_prob=0.6, @@ -432,16 +432,16 @@ class WhisperSTTServiceMLX(WhisperSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", WhisperMLXSTTSettings, "model") + _warn_deprecated_param("model", self.Settings, "model") default_settings.model = model if isinstance(model, str) else model.value if no_speech_prob is not None: - _warn_deprecated_param("no_speech_prob", WhisperMLXSTTSettings, "no_speech_prob") + _warn_deprecated_param("no_speech_prob", self.Settings, "no_speech_prob") default_settings.no_speech_prob = no_speech_prob if language is not None: - _warn_deprecated_param("language", WhisperMLXSTTSettings, "language") + _warn_deprecated_param("language", self.Settings, "language") default_settings.language = language if temperature is not None: - _warn_deprecated_param("temperature", WhisperMLXSTTSettings, "temperature") + _warn_deprecated_param("temperature", self.Settings, "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/xtts/tts.py b/src/pipecat/services/xtts/tts.py index 32c2781f2..b769742f3 100644 --- a/src/pipecat/services/xtts/tts.py +++ b/src/pipecat/services/xtts/tts.py @@ -84,7 +84,7 @@ class XTTSService(TTSService): """ Settings = XTTSTTSSettings - _settings: XTTSTTSSettings + _settings: Settings def __init__( self, @@ -94,7 +94,7 @@ class XTTSService(TTSService): aiohttp_session: aiohttp.ClientSession, language: Language = Language.EN, sample_rate: Optional[int] = None, - settings: Optional[XTTSTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the XTTS service. @@ -103,7 +103,7 @@ class XTTSService(TTSService): voice_id: ID of the voice/speaker to use for synthesis. .. deprecated:: 0.0.105 - Use ``settings=XTTSTTSSettings(voice=...)`` instead. + Use ``settings=XTTSService.Settings(voice=...)`` instead. base_url: Base URL of the XTTS streaming server. aiohttp_session: HTTP session for making requests to the server. @@ -114,7 +114,7 @@ class XTTSService(TTSService): **kwargs: Additional arguments passed to parent TTSService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = XTTSTTSSettings( + default_settings = self.Settings( model=None, voice=None, language=self.language_to_service_language(language), @@ -122,7 +122,7 @@ class XTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", XTTSTTSSettings, "voice") + _warn_deprecated_param("voice_id", self.Settings, "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) From 51a8a28a991be8e3ae8d651f03abc0c97537730c Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 12:34:10 -0400 Subject: [PATCH 021/159] Prefer Service.ThinkingConfig over raw ThinkingConfig class names in Anthropic and Google services and examples --- examples/foundational/49a-thinking-anthropic.py | 4 ++-- examples/foundational/49b-thinking-google.py | 4 ++-- .../foundational/49c-thinking-functions-anthropic.py | 4 ++-- .../foundational/49d-thinking-functions-google.py | 4 ++-- src/pipecat/services/anthropic/llm.py | 12 ++++++++---- src/pipecat/services/google/llm.py | 12 +++++++----- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/examples/foundational/49a-thinking-anthropic.py b/examples/foundational/49a-thinking-anthropic.py index 8825ce7c5..727e8e0a7 100644 --- a/examples/foundational/49a-thinking-anthropic.py +++ b/examples/foundational/49a-thinking-anthropic.py @@ -22,7 +22,7 @@ from pipecat.processors.aggregators.llm_response_universal import ( ) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.anthropic.llm import AnthropicLLMService, AnthropicThinkingConfig +from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.transports.base_transport import BaseTransport, TransportParams @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AnthropicLLMService( api_key=os.getenv("ANTHROPIC_API_KEY"), settings=AnthropicLLMService.Settings( - thinking=AnthropicThinkingConfig( + thinking=AnthropicLLMService.ThinkingConfig( type="enabled", budget_tokens=2048, ), diff --git a/examples/foundational/49b-thinking-google.py b/examples/foundational/49b-thinking-google.py index ed7a393b0..b2b44d59a 100644 --- a/examples/foundational/49b-thinking-google.py +++ b/examples/foundational/49b-thinking-google.py @@ -24,7 +24,7 @@ from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService -from pipecat.services.google.llm import GoogleLLMService, GoogleThinkingConfig +from pipecat.services.google.llm import GoogleLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("GOOGLE_API_KEY"), # model="gemini-3-pro-preview", # A more powerful reasoning model, but slower settings=GoogleLLMService.Settings( - thinking=GoogleThinkingConfig( + thinking=GoogleLLMService.ThinkingConfig( thinking_budget=-1, # Dynamic thinking include_thoughts=True, ), diff --git a/examples/foundational/49c-thinking-functions-anthropic.py b/examples/foundational/49c-thinking-functions-anthropic.py index bc9f334c8..d6b2749e1 100644 --- a/examples/foundational/49c-thinking-functions-anthropic.py +++ b/examples/foundational/49c-thinking-functions-anthropic.py @@ -23,7 +23,7 @@ from pipecat.processors.aggregators.llm_response_universal import ( ) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport -from pipecat.services.anthropic.llm import AnthropicLLMService, AnthropicThinkingConfig +from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams @@ -85,7 +85,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AnthropicLLMService( api_key=os.getenv("ANTHROPIC_API_KEY"), settings=AnthropicLLMService.Settings( - thinking=AnthropicThinkingConfig( + thinking=AnthropicLLMService.ThinkingConfig( type="enabled", budget_tokens=2048, ), diff --git a/examples/foundational/49d-thinking-functions-google.py b/examples/foundational/49d-thinking-functions-google.py index e8ecefcdd..f5b9a1d20 100644 --- a/examples/foundational/49d-thinking-functions-google.py +++ b/examples/foundational/49d-thinking-functions-google.py @@ -25,7 +25,7 @@ from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService -from pipecat.services.google.llm import GoogleLLMService, GoogleThinkingConfig +from pipecat.services.google.llm import GoogleLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -86,7 +86,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("GOOGLE_API_KEY"), # model="gemini-3-pro-preview", # A more powerful reasoning model, but slower settings=GoogleLLMService.Settings( - thinking=GoogleThinkingConfig( + thinking=GoogleLLMService.ThinkingConfig( thinking_budget=-1, # Dynamic thinking include_thoughts=True, ), diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index 0c6d436e1..2f77e1665 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -98,18 +98,20 @@ class AnthropicLLMSettings(LLMSettings): """ enable_prompt_caching: bool | _NotGiven = field(default_factory=lambda: _NOT_GIVEN) - thinking: AnthropicThinkingConfig | _NotGiven = field(default_factory=lambda: _NOT_GIVEN) + thinking: Union["AnthropicLLMService.ThinkingConfig", _NotGiven] = field( + default_factory=lambda: _NOT_GIVEN + ) @classmethod def from_mapping(cls, settings): """Convert a plain dict to settings, coercing thinking dicts. For backward compatibility, a ``thinking`` value that is a plain dict - is converted to a :class:`AnthropicThinkingConfig`. + is converted to a :class:`AnthropicLLMService.ThinkingConfig`. """ instance = super().from_mapping(settings) if is_given(instance.thinking) and isinstance(instance.thinking, dict): - instance.thinking = AnthropicThinkingConfig(**instance.thinking) + instance.thinking = AnthropicLLMService.ThinkingConfig(**instance.thinking) return instance @@ -199,7 +201,9 @@ class AnthropicLLMService(LLMService): temperature: Optional[float] = Field(default_factory=lambda: NOT_GIVEN, ge=0.0, le=1.0) top_k: Optional[int] = Field(default_factory=lambda: NOT_GIVEN, ge=0) top_p: Optional[float] = Field(default_factory=lambda: NOT_GIVEN, ge=0.0, le=1.0) - thinking: Optional[AnthropicThinkingConfig] = Field(default_factory=lambda: NOT_GIVEN) + thinking: Optional["AnthropicLLMService.ThinkingConfig"] = Field( + default_factory=lambda: NOT_GIVEN + ) extra: Optional[Dict[str, Any]] = Field(default_factory=dict) def model_post_init(self, __context): diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index 2ade23906..02b5a5c19 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -16,7 +16,7 @@ import json import os import uuid from dataclasses import dataclass, field -from typing import Any, AsyncIterator, Dict, List, Literal, Optional +from typing import Any, AsyncIterator, Dict, List, Literal, Optional, Union from loguru import logger from PIL import Image @@ -719,18 +719,20 @@ class GoogleLLMSettings(LLMSettings): thinking: Thinking configuration. """ - thinking: GoogleThinkingConfig | _NotGiven = field(default_factory=lambda: NOT_GIVEN) + thinking: Union["GoogleLLMService.ThinkingConfig", _NotGiven] = field( + default_factory=lambda: NOT_GIVEN + ) @classmethod def from_mapping(cls, settings): """Convert a plain dict to settings, coercing thinking dicts. For backward compatibility, a ``thinking`` value that is a plain dict - is converted to a :class:`GoogleThinkingConfig`. + is converted to a :class:`GoogleLLMService.ThinkingConfig`. """ instance = super().from_mapping(settings) if is_given(instance.thinking) and isinstance(instance.thinking, dict): - instance.thinking = GoogleThinkingConfig(**instance.thinking) + instance.thinking = GoogleLLMService.ThinkingConfig(**instance.thinking) return instance @@ -775,7 +777,7 @@ class GoogleLLMService(LLMService): temperature: Optional[float] = Field(default=None, ge=0.0, le=2.0) top_k: Optional[int] = Field(default=None, ge=0) top_p: Optional[float] = Field(default=None, ge=0.0, le=1.0) - thinking: Optional[GoogleThinkingConfig] = Field(default=None) + thinking: Optional["GoogleLLMService.ThinkingConfig"] = Field(default=None) extra: Optional[Dict[str, Any]] = Field(default_factory=dict) def __init__( From eb9212f152a8e08575103ec2ee33cd1cecc8deb4 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 12:37:43 -0400 Subject: [PATCH 022/159] Update COMMUNITY_INTEGRATIONS.md code sample to prefer Settings alias over raw settings class name --- COMMUNITY_INTEGRATIONS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/COMMUNITY_INTEGRATIONS.md b/COMMUNITY_INTEGRATIONS.md index f6f92a5d3..c7e4c8e90 100644 --- a/COMMUNITY_INTEGRATIONS.md +++ b/COMMUNITY_INTEGRATIONS.md @@ -280,17 +280,17 @@ from typing import Optional class MyTTSService(TTSService): Settings = MyTTSSettings - _settings: MyTTSSettings + _settings: Settings def __init__( self, *, api_key: str, - settings: Optional[MyTTSSettings] = None, + settings: Optional[Settings] = None, **kwargs, ): # 1. Defaults — every field has a real value (store mode). - default_settings = MyTTSSettings( + default_settings = self.Settings( model="my-model-v1", voice="default-voice", language="en", From e5b60ba0957b060abb962322111d0e44fd662217 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 13:04:15 -0400 Subject: [PATCH 023/159] Make deprecated-init-param warnings recommend the preferred Service.Settings(...) pattern Move the warning helper into AIService as _warn_init_param_moved_to_settings. It now uses type(self).__name__ to produce messages like "Use settings=AnthropicLLMService.Settings(model=...)" instead of the raw settings class name "AnthropicLLMSettings(model=...)". Callers no longer need to pass the settings class explicitly. --- src/pipecat/services/ai_service.py | 38 ++++++++++++++++++ src/pipecat/services/anthropic/llm.py | 6 +-- src/pipecat/services/assemblyai/stt.py | 6 +-- src/pipecat/services/asyncai/tts.py | 14 +++---- src/pipecat/services/aws/llm.py | 8 ++-- src/pipecat/services/aws/nova_sonic/llm.py | 8 ++-- src/pipecat/services/aws/stt.py | 4 +- src/pipecat/services/aws/tts.py | 6 +-- src/pipecat/services/azure/image.py | 6 +-- src/pipecat/services/azure/llm.py | 3 +- src/pipecat/services/azure/stt.py | 4 +- src/pipecat/services/azure/tts.py | 10 ++--- src/pipecat/services/camb/tts.py | 8 ++-- src/pipecat/services/cartesia/stt.py | 4 +- src/pipecat/services/cartesia/tts.py | 14 +++---- src/pipecat/services/cerebras/llm.py | 3 +- src/pipecat/services/deepgram/flux/stt.py | 6 +-- .../services/deepgram/sagemaker/stt.py | 4 +- .../services/deepgram/sagemaker/tts.py | 4 +- src/pipecat/services/deepgram/stt.py | 3 +- src/pipecat/services/deepgram/tts.py | 6 +-- src/pipecat/services/deepseek/llm.py | 3 +- src/pipecat/services/elevenlabs/stt.py | 10 ++--- src/pipecat/services/elevenlabs/tts.py | 14 +++---- src/pipecat/services/fal/image.py | 6 +-- src/pipecat/services/fal/stt.py | 4 +- src/pipecat/services/fireworks/llm.py | 3 +- src/pipecat/services/fish/tts.py | 8 ++-- src/pipecat/services/gladia/stt.py | 6 +-- .../services/google/gemini_live/llm.py | 8 ++-- .../services/google/gemini_live/vertex/llm.py | 7 ++-- src/pipecat/services/google/image.py | 4 +- src/pipecat/services/google/llm.py | 7 ++-- src/pipecat/services/google/openai/llm.py | 3 +- src/pipecat/services/google/stt.py | 4 +- src/pipecat/services/google/tts.py | 15 ++++--- src/pipecat/services/google/vertex/llm.py | 7 ++-- src/pipecat/services/gradium/stt.py | 4 +- src/pipecat/services/gradium/tts.py | 8 ++-- src/pipecat/services/grok/llm.py | 3 +- src/pipecat/services/grok/realtime/llm.py | 1 - src/pipecat/services/groq/llm.py | 3 +- src/pipecat/services/groq/stt.py | 9 ++--- src/pipecat/services/groq/tts.py | 8 ++-- src/pipecat/services/hume/tts.py | 6 +-- src/pipecat/services/inworld/tts.py | 14 +++---- src/pipecat/services/kokoro/tts.py | 6 +-- src/pipecat/services/lmnt/tts.py | 6 +-- src/pipecat/services/minimax/tts.py | 8 ++-- src/pipecat/services/mistral/llm.py | 3 +- src/pipecat/services/moondream/vision.py | 4 +- src/pipecat/services/neuphonic/tts.py | 10 ++--- src/pipecat/services/nvidia/llm.py | 3 +- src/pipecat/services/nvidia/stt.py | 6 +-- src/pipecat/services/nvidia/tts.py | 6 +-- src/pipecat/services/ollama/llm.py | 3 +- src/pipecat/services/openai/image.py | 6 +-- src/pipecat/services/openai/llm.py | 5 +-- src/pipecat/services/openai/realtime/llm.py | 3 +- src/pipecat/services/openai/stt.py | 16 ++++---- src/pipecat/services/openai/tts.py | 12 +++--- .../services/openai_realtime_beta/openai.py | 4 +- src/pipecat/services/openpipe/llm.py | 3 +- src/pipecat/services/openrouter/llm.py | 3 +- src/pipecat/services/perplexity/llm.py | 3 +- src/pipecat/services/piper/tts.py | 6 +-- src/pipecat/services/qwen/llm.py | 3 +- src/pipecat/services/resembleai/tts.py | 4 +- src/pipecat/services/rime/tts.py | 20 +++++----- src/pipecat/services/sambanova/llm.py | 3 +- src/pipecat/services/sambanova/stt.py | 9 ++--- src/pipecat/services/sarvam/stt.py | 5 +-- src/pipecat/services/sarvam/tts.py | 14 +++---- src/pipecat/services/settings.py | 40 ------------------- src/pipecat/services/soniox/stt.py | 6 +-- src/pipecat/services/speechmatics/stt.py | 4 +- src/pipecat/services/speechmatics/tts.py | 6 +-- src/pipecat/services/together/llm.py | 3 +- src/pipecat/services/whisper/base_stt.py | 10 ++--- src/pipecat/services/whisper/stt.py | 16 ++++---- src/pipecat/services/xtts/tts.py | 4 +- 81 files changed, 282 insertions(+), 311 deletions(-) diff --git a/src/pipecat/services/ai_service.py b/src/pipecat/services/ai_service.py index c4e45a417..dd9ef1dba 100644 --- a/src/pipecat/services/ai_service.py +++ b/src/pipecat/services/ai_service.py @@ -10,6 +10,7 @@ Provides the foundation for all AI services in the Pipecat framework, including model management, settings handling, and frame processing lifecycle methods. """ +import warnings from typing import Any, AsyncGenerator, Dict from loguru import logger @@ -130,6 +131,43 @@ class AIService(FrameProcessor): return changed + def _warn_init_param_moved_to_settings( + self, + param_name: str, + settings_field: str | None = None, + stacklevel: int = 3, + ): + """Warn that an ``__init__`` param has moved to ``Settings``. + + Emits a ``DeprecationWarning`` directing users to the canonical + ``settings=ServiceClass.Settings(field=...)`` API. + + Args: + param_name: Name of the deprecated ``__init__`` parameter. + settings_field: The corresponding field on the ``Settings`` + dataclass, if different from *param_name*. When ``None`` + the message omits the field hint. + stacklevel: Stack depth for the warning. Default ``3`` targets + the caller's caller (i.e. user code that instantiated the + service). + """ + label = f"{type(self).__name__}.Settings" + if settings_field: + msg = ( + f"The `{param_name}` parameter is deprecated. " + f"Use `settings={label}({settings_field}=...)` instead. " + f"If both are provided, `settings` takes precedence." + ) + else: + msg = ( + f"The `{param_name}` parameter is deprecated. " + f"Use `settings={label}(...)` instead. " + f"If both are provided, `settings` takes precedence." + ) + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) + def _warn_unhandled_updated_settings(self, unhandled): """Log a warning for settings changes that won't take effect at runtime. diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index 2f77e1665..fcaed1dfe 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -58,7 +58,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( from pipecat.processors.frame_processor import FrameDirection from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.settings import NOT_GIVEN as _NOT_GIVEN -from pipecat.services.settings import LLMSettings, _NotGiven, _warn_deprecated_param, is_given +from pipecat.services.settings import LLMSettings, _NotGiven, is_given from pipecat.utils.tracing.service_decorators import traced_llm try: @@ -272,12 +272,12 @@ class AnthropicLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index e7a2854b0..f52c1d935 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -32,7 +32,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import ASSEMBLYAI_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language @@ -208,12 +208,12 @@ class AssemblyAISTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if language is not None: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = language # 3. Apply connection_params overrides (deprecated) — only if settings not provided if connection_params is not None: - _warn_deprecated_param("connection_params", self.Settings) + self._warn_init_param_moved_to_settings("connection_params") if not settings: sample_rate = connection_params.sample_rate encoding = connection_params.encoding diff --git a/src/pipecat/services/asyncai/tts.py b/src/pipecat/services/asyncai/tts.py index 2cecb2d5b..42c0a09a4 100644 --- a/src/pipecat/services/asyncai/tts.py +++ b/src/pipecat/services/asyncai/tts.py @@ -26,7 +26,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import TextAggregationMode, TTSService, WebsocketTTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -161,15 +161,15 @@ class AsyncAITTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None @@ -555,15 +555,15 @@ class AsyncAIHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index 9a2ebfdcf..59859abf7 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -55,7 +55,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.llm_service import LLMService -from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven from pipecat.utils.tracing.service_decorators import traced_llm try: @@ -841,15 +841,15 @@ class AWSBedrockLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if stop_sequences is not None: - _warn_deprecated_param("stop_sequences", self.Settings, "stop_sequences") + self._warn_init_param_moved_to_settings("stop_sequences", "stop_sequences") default_settings.stop_sequences = stop_sequences # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 0309c0fad..b1f56dbbd 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -60,7 +60,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.llm_service import LLMService -from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven from pipecat.utils.time import time_now_iso8601 try: @@ -337,13 +337,13 @@ class AWSNovaSonicLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model != "amazon.nova-2-sonic-v1:0": - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id != "matthew": - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if system_instruction is not None: - _warn_deprecated_param("system_instruction", self.Settings, "system_instruction") + self._warn_init_param_moved_to_settings("system_instruction", "system_instruction") default_settings.system_instruction = system_instruction # 3. Apply params overrides — only if settings not provided diff --git a/src/pipecat/services/aws/stt.py b/src/pipecat/services/aws/stt.py index 0a648759c..eed1a321d 100644 --- a/src/pipecat/services/aws/stt.py +++ b/src/pipecat/services/aws/stt.py @@ -29,7 +29,7 @@ from pipecat.frames.frames import ( TranscriptionFrame, ) from pipecat.services.aws.utils import build_event_message, decode_event, get_presigned_url -from pipecat.services.settings import STTSettings, _warn_deprecated_param +from pipecat.services.settings import STTSettings from pipecat.services.stt_latency import AWS_TRANSCRIBE_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language, resolve_language @@ -105,7 +105,7 @@ class AWSTranscribeSTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if language is not None: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = self.language_to_service_language(language) # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/aws/tts.py b/src/pipecat/services/aws/tts.py index 1e16958fe..9648c45c8 100644 --- a/src/pipecat/services/aws/tts.py +++ b/src/pipecat/services/aws/tts.py @@ -23,7 +23,7 @@ from pipecat.frames.frames import ( Frame, TTSAudioRawFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -222,12 +222,12 @@ class AWSPollyTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.engine = params.engine default_settings.language = ( diff --git a/src/pipecat/services/azure/image.py b/src/pipecat/services/azure/image.py index a80496599..fc50d710a 100644 --- a/src/pipecat/services/azure/image.py +++ b/src/pipecat/services/azure/image.py @@ -20,7 +20,7 @@ from PIL import Image from pipecat.frames.frames import ErrorFrame, Frame, URLImageRawFrame from pipecat.services.image_service import ImageGenService -from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven @dataclass @@ -85,11 +85,11 @@ class AzureImageGenServiceREST(ImageGenService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if image_size is not None: - _warn_deprecated_param("image_size", self.Settings, "image_size") + self._warn_init_param_moved_to_settings("image_size", "image_size") default_settings.image_size = image_size # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/azure/llm.py b/src/pipecat/services/azure/llm.py index b4f2de5dc..ea705eec6 100644 --- a/src/pipecat/services/azure/llm.py +++ b/src/pipecat/services/azure/llm.py @@ -14,7 +14,6 @@ from openai import AsyncAzureOpenAI from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -63,7 +62,7 @@ class AzureLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/azure/stt.py b/src/pipecat/services/azure/stt.py index 7309a4a4e..857b166cb 100644 --- a/src/pipecat/services/azure/stt.py +++ b/src/pipecat/services/azure/stt.py @@ -26,7 +26,7 @@ from pipecat.frames.frames import ( TranscriptionFrame, ) from pipecat.services.azure.common import language_to_azure_language -from pipecat.services.settings import STTSettings, _warn_deprecated_param +from pipecat.services.settings import STTSettings from pipecat.services.stt_latency import AZURE_TTFS_P99 from pipecat.services.stt_service import STTService from pipecat.transcriptions.language import Language @@ -111,7 +111,7 @@ class AzureSTTService(STTService): # 2. Apply direct init arg overrides (deprecated) if language is not None and language != Language.EN_US: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = language_to_azure_language(language) # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/azure/tts.py b/src/pipecat/services/azure/tts.py index d33937d52..0130ef5cb 100644 --- a/src/pipecat/services/azure/tts.py +++ b/src/pipecat/services/azure/tts.py @@ -25,7 +25,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.azure.common import language_to_azure_language -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TextAggregationMode, TTSService from pipecat.transcriptions.language import Language from pipecat.utils.tracing.service_decorators import traced_tts @@ -304,12 +304,12 @@ class AzureTTSService(TTSService, AzureBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice", "voice") default_settings.voice = voice # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.emphasis = params.emphasis default_settings.language = ( @@ -801,12 +801,12 @@ class AzureHttpTTSService(TTSService, AzureBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice", "voice") default_settings.voice = voice # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.emphasis = params.emphasis default_settings.language = ( diff --git a/src/pipecat/services/camb/tts.py b/src/pipecat/services/camb/tts.py index 7586644ce..b0c237165 100644 --- a/src/pipecat/services/camb/tts.py +++ b/src/pipecat/services/camb/tts.py @@ -30,7 +30,7 @@ from pipecat.frames.frames import ( StartFrame, TTSAudioRawFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -246,15 +246,15 @@ class CambTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = ( diff --git a/src/pipecat/services/cartesia/stt.py b/src/pipecat/services/cartesia/stt.py index e094ef044..85d43bcd3 100644 --- a/src/pipecat/services/cartesia/stt.py +++ b/src/pipecat/services/cartesia/stt.py @@ -28,7 +28,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import STTSettings, _warn_deprecated_param +from pipecat.services.settings import STTSettings from pipecat.services.stt_latency import CARTESIA_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language @@ -189,7 +189,7 @@ class CartesiaSTTService(WebsocketSTTService): # 2. Apply live_options overrides — only if settings not provided if live_options is not None: - _warn_deprecated_param("live_options", self.Settings) + self._warn_init_param_moved_to_settings("live_options") if not settings: if live_options.sample_rate and sample_rate is None: sample_rate = live_options.sample_rate diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index 90497eb1d..aca5c46c6 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -25,7 +25,7 @@ from pipecat.frames.frames import ( TTSAudioRawFrame, TTSStoppedFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TextAggregationMode, TTSService, WebsocketTTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.text.base_text_aggregator import BaseTextAggregator @@ -309,15 +309,15 @@ class CartesiaTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -756,15 +756,15 @@ class CartesiaHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) diff --git a/src/pipecat/services/cerebras/llm.py b/src/pipecat/services/cerebras/llm.py index e2a15f4e5..7c31a6857 100644 --- a/src/pipecat/services/cerebras/llm.py +++ b/src/pipecat/services/cerebras/llm.py @@ -14,7 +14,6 @@ from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -62,7 +61,7 @@ class CerebrasLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/deepgram/flux/stt.py b/src/pipecat/services/deepgram/flux/stt.py index 085d1ed2c..64d4e87b9 100644 --- a/src/pipecat/services/deepgram/flux/stt.py +++ b/src/pipecat/services/deepgram/flux/stt.py @@ -28,7 +28,7 @@ from pipecat.frames.frames import ( UserStartedSpeakingFrame, UserStoppedSpeakingFrame, ) -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language from pipecat.utils.time import time_now_iso8601 @@ -233,12 +233,12 @@ class DeepgramFluxSTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.eager_eot_threshold = params.eager_eot_threshold default_settings.eot_threshold = params.eot_threshold diff --git a/src/pipecat/services/deepgram/sagemaker/stt.py b/src/pipecat/services/deepgram/sagemaker/stt.py index 98b3556cc..1087b124f 100644 --- a/src/pipecat/services/deepgram/sagemaker/stt.py +++ b/src/pipecat/services/deepgram/sagemaker/stt.py @@ -33,7 +33,7 @@ from pipecat.frames.frames import ( from pipecat.processors.frame_processor import FrameDirection from pipecat.services.aws.sagemaker.bidi_client import SageMakerBidiClient from pipecat.services.deepgram.stt import DeepgramSTTService, LiveOptions -from pipecat.services.settings import STTSettings, _warn_deprecated_param, is_given +from pipecat.services.settings import STTSettings, is_given from pipecat.services.stt_latency import DEEPGRAM_SAGEMAKER_TTFS_P99 from pipecat.services.stt_service import STTService from pipecat.transcriptions.language import Language @@ -147,7 +147,7 @@ class DeepgramSageMakerSTTService(STTService): # 2. Apply live_options overrides — only if settings not provided if live_options is not None: - _warn_deprecated_param("live_options", self.Settings) + self._warn_init_param_moved_to_settings("live_options") if not settings: # Extract init-only fields from live_options if live_options.sample_rate is not None and sample_rate is None: diff --git a/src/pipecat/services/deepgram/sagemaker/tts.py b/src/pipecat/services/deepgram/sagemaker/tts.py index f2c55c882..70be4a00e 100644 --- a/src/pipecat/services/deepgram/sagemaker/tts.py +++ b/src/pipecat/services/deepgram/sagemaker/tts.py @@ -33,7 +33,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.aws.sagemaker.bidi_client import SageMakerBidiClient -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import TTSService from pipecat.utils.tracing.service_decorators import traced_tts @@ -101,7 +101,7 @@ class DeepgramSageMakerTTSService(TTSService): **kwargs: Additional arguments passed to the parent TTSService. """ if voice is not None: - _warn_deprecated_param("voice", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice", "voice") voice = voice or "aura-2-helena-en" diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index e84964dce..7d849a160 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -29,7 +29,6 @@ from pipecat.services.settings import ( NOT_GIVEN, STTSettings, _NotGiven, - _warn_deprecated_param, is_given, ) from pipecat.services.stt_latency import DEEPGRAM_TTFS_P99 @@ -370,7 +369,7 @@ class DeepgramSTTService(STTService): # 3. Apply live_options overrides — only if settings not provided if live_options is not None: - _warn_deprecated_param("live_options", self.Settings) + self._warn_init_param_moved_to_settings("live_options") if not settings: # Extract init-only fields from live_options if live_options.sample_rate is not None and sample_rate is None: diff --git a/src/pipecat/services/deepgram/tts.py b/src/pipecat/services/deepgram/tts.py index 5fdbeb700..9f2dc3976 100644 --- a/src/pipecat/services/deepgram/tts.py +++ b/src/pipecat/services/deepgram/tts.py @@ -26,7 +26,7 @@ from pipecat.frames.frames import ( TTSAudioRawFrame, TTSStoppedFrame, ) -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import TTSService, WebsocketTTSService from pipecat.utils.tracing.service_decorators import traced_tts @@ -105,7 +105,7 @@ class DeepgramTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice", "voice") default_settings.model = voice default_settings.voice = voice @@ -407,7 +407,7 @@ class DeepgramHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice", "voice") default_settings.model = voice default_settings.voice = voice diff --git a/src/pipecat/services/deepseek/llm.py b/src/pipecat/services/deepseek/llm.py index abb0d56bb..cfb69cb9a 100644 --- a/src/pipecat/services/deepseek/llm.py +++ b/src/pipecat/services/deepseek/llm.py @@ -14,7 +14,6 @@ from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -62,7 +61,7 @@ class DeepSeekLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/elevenlabs/stt.py b/src/pipecat/services/elevenlabs/stt.py index 7247241d3..daca9be3d 100644 --- a/src/pipecat/services/elevenlabs/stt.py +++ b/src/pipecat/services/elevenlabs/stt.py @@ -35,7 +35,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import ELEVENLABS_REALTIME_TTFS_P99, ELEVENLABS_TTFS_P99 from pipecat.services.stt_service import SegmentedSTTService, WebsocketSTTService from pipecat.transcriptions.language import Language, resolve_language @@ -278,12 +278,12 @@ class ElevenLabsSTTService(SegmentedSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = language_to_elevenlabs_language(params.language) @@ -540,12 +540,12 @@ class ElevenLabsRealtimeSTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = params.language_code if params.commit_strategy != CommitStrategy.MANUAL: diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 46acc2f1c..8cfd1abe2 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -44,7 +44,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import ( TextAggregationMode, TTSService, @@ -437,16 +437,16 @@ class ElevenLabsTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided _pronunciation_dictionary_locators = pronunciation_dictionary_locators if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -1002,16 +1002,16 @@ class ElevenLabsHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided _pronunciation_dictionary_locators = pronunciation_dictionary_locators if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) diff --git a/src/pipecat/services/fal/image.py b/src/pipecat/services/fal/image.py index 3c15a918a..31af55440 100644 --- a/src/pipecat/services/fal/image.py +++ b/src/pipecat/services/fal/image.py @@ -23,7 +23,7 @@ from pydantic import BaseModel from pipecat.frames.frames import ErrorFrame, Frame, URLImageRawFrame from pipecat.services.image_service import ImageGenService -from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven @dataclass @@ -142,11 +142,11 @@ class FalImageGenService(ImageGenService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.seed = params.seed default_settings.num_inference_steps = params.num_inference_steps diff --git a/src/pipecat/services/fal/stt.py b/src/pipecat/services/fal/stt.py index c1aae24b7..7bfcfbcfd 100644 --- a/src/pipecat/services/fal/stt.py +++ b/src/pipecat/services/fal/stt.py @@ -20,7 +20,7 @@ from loguru import logger from pydantic import BaseModel from pipecat.frames.frames import ErrorFrame, Frame, TranscriptionFrame -from pipecat.services.settings import STTSettings, _warn_deprecated_param +from pipecat.services.settings import STTSettings from pipecat.services.stt_latency import FAL_TTFS_P99 from pipecat.services.stt_service import SegmentedSTTService from pipecat.transcriptions.language import Language, resolve_language @@ -223,7 +223,7 @@ class FalSTTService(SegmentedSTTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = language_to_fal_language(params.language) diff --git a/src/pipecat/services/fireworks/llm.py b/src/pipecat/services/fireworks/llm.py index fdd688149..bf141fac1 100644 --- a/src/pipecat/services/fireworks/llm.py +++ b/src/pipecat/services/fireworks/llm.py @@ -14,7 +14,6 @@ from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -62,7 +61,7 @@ class FireworksLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/fish/tts.py b/src/pipecat/services/fish/tts.py index 39669574a..92cb54701 100644 --- a/src/pipecat/services/fish/tts.py +++ b/src/pipecat/services/fish/tts.py @@ -27,7 +27,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import InterruptibleTTSService from pipecat.transcriptions.language import Language from pipecat.utils.tracing.service_decorators import traced_tts @@ -185,15 +185,15 @@ class FishAudioTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if reference_id is not None: - _warn_deprecated_param("reference_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("reference_id", "voice") default_settings.voice = reference_id if model_id is not None: - _warn_deprecated_param("model_id", self.Settings, "model") + self._warn_init_param_moved_to_settings("model_id", "model") default_settings.model = model_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.latency is not None: default_settings.latency = params.latency diff --git a/src/pipecat/services/gladia/stt.py b/src/pipecat/services/gladia/stt.py index a68bf78b3..2ce2a15b5 100644 --- a/src/pipecat/services/gladia/stt.py +++ b/src/pipecat/services/gladia/stt.py @@ -39,7 +39,7 @@ from pipecat.services.gladia.config import ( PreProcessingConfig, RealtimeProcessingConfig, ) -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import GLADIA_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language, resolve_language @@ -317,12 +317,12 @@ class GladiaSTTService(WebsocketSTTService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if params.language is not None: with warnings.catch_warnings(): warnings.simplefilter("always") diff --git a/src/pipecat/services/google/gemini_live/llm.py b/src/pipecat/services/google/gemini_live/llm.py index 167abb938..5c9e5f8b3 100644 --- a/src/pipecat/services/google/gemini_live/llm.py +++ b/src/pipecat/services/google/gemini_live/llm.py @@ -76,7 +76,7 @@ from pipecat.services.openai.llm import ( OpenAIAssistantContextAggregator, OpenAIUserContextAggregator, ) -from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.string import match_endofsentence from pipecat.utils.time import time_now_iso8601 @@ -742,15 +742,15 @@ class GeminiLiveLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id != "Charon": - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.frequency_penalty = params.frequency_penalty default_settings.max_tokens = params.max_tokens diff --git a/src/pipecat/services/google/gemini_live/vertex/llm.py b/src/pipecat/services/google/gemini_live/vertex/llm.py index c610b4cdb..cb9d74c62 100644 --- a/src/pipecat/services/google/gemini_live/vertex/llm.py +++ b/src/pipecat/services/google/gemini_live/vertex/llm.py @@ -26,7 +26,6 @@ from pipecat.services.google.gemini_live.llm import ( InputParams, language_to_gemini_language, ) -from pipecat.services.settings import _warn_deprecated_param try: from google.auth import default @@ -160,15 +159,15 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id != "Charon": - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.frequency_penalty = params.frequency_penalty default_settings.max_tokens = params.max_tokens diff --git a/src/pipecat/services/google/image.py b/src/pipecat/services/google/image.py index 40706ad50..6a2919986 100644 --- a/src/pipecat/services/google/image.py +++ b/src/pipecat/services/google/image.py @@ -26,7 +26,7 @@ from pydantic import BaseModel, Field from pipecat.frames.frames import ErrorFrame, Frame, URLImageRawFrame from pipecat.services.google.utils import update_google_client_http_options from pipecat.services.image_service import ImageGenService -from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven try: from google import genai @@ -110,7 +110,7 @@ class GoogleImageGenService(ImageGenService): # 2. Apply params overrides (deprecated) if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.model = params.model default_settings.number_of_images = params.number_of_images diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index 02b5a5c19..698b9f97f 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -61,7 +61,6 @@ from pipecat.services.settings import ( NOT_GIVEN, LLMSettings, _NotGiven, - _warn_deprecated_param, is_given, ) from pipecat.utils.tracing.service_decorators import traced_llm @@ -838,15 +837,15 @@ class GoogleLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if system_instruction is not None: - _warn_deprecated_param("system_instruction", self.Settings, "system_instruction") + self._warn_init_param_moved_to_settings("system_instruction", "system_instruction") default_settings.system_instruction = system_instruction # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/google/openai/llm.py b/src/pipecat/services/google/openai/llm.py index 717c1e379..da5d1be7a 100644 --- a/src/pipecat/services/google/openai/llm.py +++ b/src/pipecat/services/google/openai/llm.py @@ -30,7 +30,6 @@ from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -100,7 +99,7 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/google/stt.py b/src/pipecat/services/google/stt.py index cab176668..173b201fe 100644 --- a/src/pipecat/services/google/stt.py +++ b/src/pipecat/services/google/stt.py @@ -36,7 +36,7 @@ from pipecat.frames.frames import ( StartFrame, TranscriptionFrame, ) -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import GOOGLE_TTFS_P99 from pipecat.services.stt_service import STTService from pipecat.transcriptions.language import Language, resolve_language @@ -531,7 +531,7 @@ class GoogleSTTService(STTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.languages = list(params.language_list) default_settings.model = params.model diff --git a/src/pipecat/services/google/tts.py b/src/pipecat/services/google/tts.py index c2e32ad3c..3455a8125 100644 --- a/src/pipecat/services/google/tts.py +++ b/src/pipecat/services/google/tts.py @@ -39,7 +39,6 @@ from pipecat.services.settings import ( NOT_GIVEN, TTSSettings, _NotGiven, - _warn_deprecated_param, is_given, ) from pipecat.services.tts_service import TTSService @@ -636,12 +635,12 @@ class GoogleHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.pitch is not None: default_settings.pitch = params.pitch @@ -1083,12 +1082,12 @@ class GoogleTTSService(GoogleBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -1331,10 +1330,10 @@ class GeminiTTSService(GoogleBaseTTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if default_settings.voice not in self.AVAILABLE_VOICES: @@ -1344,7 +1343,7 @@ class GeminiTTSService(GoogleBaseTTSService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) diff --git a/src/pipecat/services/google/vertex/llm.py b/src/pipecat/services/google/vertex/llm.py index 014dea405..946a8f1bb 100644 --- a/src/pipecat/services/google/vertex/llm.py +++ b/src/pipecat/services/google/vertex/llm.py @@ -22,7 +22,6 @@ from typing import Optional from loguru import logger from pipecat.services.google.llm import GoogleLLMService -from pipecat.services.settings import _warn_deprecated_param try: from google.auth import default @@ -215,15 +214,15 @@ class GoogleVertexLLMService(GoogleLLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if system_instruction is not None: - _warn_deprecated_param("system_instruction", self.Settings, "system_instruction") + self._warn_init_param_moved_to_settings("system_instruction", "system_instruction") default_settings.system_instruction = system_instruction # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.max_tokens = params.max_tokens default_settings.temperature = params.temperature diff --git a/src/pipecat/services/gradium/stt.py b/src/pipecat/services/gradium/stt.py index 90d1e800f..c328dceab 100644 --- a/src/pipecat/services/gradium/stt.py +++ b/src/pipecat/services/gradium/stt.py @@ -28,7 +28,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import GRADIUM_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language, resolve_language @@ -162,7 +162,7 @@ class GradiumSTTService(WebsocketSTTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = params.language if params.delay_in_frames is not None: diff --git a/src/pipecat/services/gradium/tts.py b/src/pipecat/services/gradium/tts.py index 43114b193..f63f931ad 100644 --- a/src/pipecat/services/gradium/tts.py +++ b/src/pipecat/services/gradium/tts.py @@ -21,7 +21,7 @@ from pipecat.frames.frames import ( TTSAudioRawFrame, TTSStoppedFrame, ) -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import WebsocketTTSService from pipecat.utils.tracing.service_decorators import traced_tts @@ -108,15 +108,15 @@ class GradiumTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") # Note: params.temp has no corresponding settings field # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/grok/llm.py b/src/pipecat/services/grok/llm.py index 3eaf0646a..160ad3331 100644 --- a/src/pipecat/services/grok/llm.py +++ b/src/pipecat/services/grok/llm.py @@ -29,7 +29,6 @@ from pipecat.services.openai.llm import ( OpenAILLMService, OpenAIUserContextAggregator, ) -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -117,7 +116,7 @@ class GrokLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/grok/realtime/llm.py b/src/pipecat/services/grok/realtime/llm.py index dcd7ac59e..bfa192aad 100644 --- a/src/pipecat/services/grok/realtime/llm.py +++ b/src/pipecat/services/grok/realtime/llm.py @@ -60,7 +60,6 @@ from pipecat.services.settings import ( NOT_GIVEN, LLMSettings, _NotGiven, - _warn_deprecated_param, is_given, ) from pipecat.utils.time import time_now_iso8601 diff --git a/src/pipecat/services/groq/llm.py b/src/pipecat/services/groq/llm.py index 8b6bdc3bc..d36b52ab8 100644 --- a/src/pipecat/services/groq/llm.py +++ b/src/pipecat/services/groq/llm.py @@ -13,7 +13,6 @@ from loguru import logger from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -61,7 +60,7 @@ class GroqLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/groq/stt.py b/src/pipecat/services/groq/stt.py index 6a234cc27..6ff4075ae 100644 --- a/src/pipecat/services/groq/stt.py +++ b/src/pipecat/services/groq/stt.py @@ -9,7 +9,6 @@ from dataclasses import dataclass from typing import Optional -from pipecat.services.settings import _warn_deprecated_param from pipecat.services.stt_latency import GROQ_TTFS_P99 from pipecat.services.whisper.base_stt import ( BaseWhisperSTTService, @@ -93,16 +92,16 @@ class GroqSTTService(BaseWhisperSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if language is not None: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = self.language_to_service_language(language) if prompt is not None: - _warn_deprecated_param("prompt", self.Settings, "prompt") + self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", self.Settings, "temperature") + self._warn_init_param_moved_to_settings("temperature", "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/groq/tts.py b/src/pipecat/services/groq/tts.py index 714ecdeb1..00ff3ef84 100644 --- a/src/pipecat/services/groq/tts.py +++ b/src/pipecat/services/groq/tts.py @@ -19,7 +19,7 @@ from pipecat.frames.frames import ( Frame, TTSAudioRawFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language from pipecat.utils.tracing.service_decorators import traced_tts @@ -120,15 +120,15 @@ class GroqTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model_name is not None: - _warn_deprecated_param("model_name", self.Settings, "model") + self._warn_init_param_moved_to_settings("model_name", "model") default_settings.model = model_name if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = str(params.language) if params.language else "en" default_settings.speed = params.speed diff --git a/src/pipecat/services/hume/tts.py b/src/pipecat/services/hume/tts.py index 9fa1cd46c..e591ebec2 100644 --- a/src/pipecat/services/hume/tts.py +++ b/src/pipecat/services/hume/tts.py @@ -25,7 +25,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.utils.tracing.service_decorators import traced_tts @@ -147,12 +147,12 @@ class HumeTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.description = params.description default_settings.speed = params.speed diff --git a/src/pipecat/services/inworld/tts.py b/src/pipecat/services/inworld/tts.py index f24e9dcf6..334c3c617 100644 --- a/src/pipecat/services/inworld/tts.py +++ b/src/pipecat/services/inworld/tts.py @@ -40,7 +40,7 @@ from pipecat import version as pipecat_version USER_AGENT = f"pipecat/{pipecat_version()}" from pydantic import BaseModel -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven try: from websockets.asyncio.client import connect as websocket_connect @@ -174,15 +174,15 @@ class InworldHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.speaking_rate is not None: default_settings.speaking_rate = params.speaking_rate @@ -592,17 +592,17 @@ class InworldTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided _buffer_max_delay_ms = None _buffer_char_threshold = None if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.speaking_rate is not None: default_settings.speaking_rate = params.speaking_rate diff --git a/src/pipecat/services/kokoro/tts.py b/src/pipecat/services/kokoro/tts.py index fd7f15c27..34e703dab 100644 --- a/src/pipecat/services/kokoro/tts.py +++ b/src/pipecat/services/kokoro/tts.py @@ -21,7 +21,7 @@ from pipecat.frames.frames import ( Frame, TTSAudioRawFrame, ) -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -155,12 +155,12 @@ class KokoroTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = language_to_kokoro_language(params.language) diff --git a/src/pipecat/services/lmnt/tts.py b/src/pipecat/services/lmnt/tts.py index ea5986ffc..0ca91a107 100644 --- a/src/pipecat/services/lmnt/tts.py +++ b/src/pipecat/services/lmnt/tts.py @@ -22,7 +22,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import InterruptibleTTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -134,10 +134,10 @@ class LmntTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/minimax/tts.py b/src/pipecat/services/minimax/tts.py index 173c1ea05..d41fb6255 100644 --- a/src/pipecat/services/minimax/tts.py +++ b/src/pipecat/services/minimax/tts.py @@ -24,7 +24,7 @@ from pipecat.frames.frames import ( StartFrame, TTSAudioRawFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -243,15 +243,15 @@ class MiniMaxHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.speed = params.speed default_settings.volume = params.volume diff --git a/src/pipecat/services/mistral/llm.py b/src/pipecat/services/mistral/llm.py index 4b74695ef..3ee1b2623 100644 --- a/src/pipecat/services/mistral/llm.py +++ b/src/pipecat/services/mistral/llm.py @@ -16,7 +16,6 @@ from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.frames.frames import FunctionCallFromLLM from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -64,7 +63,7 @@ class MistralLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/moondream/vision.py b/src/pipecat/services/moondream/vision.py index b8ab5a270..6eeff19cd 100644 --- a/src/pipecat/services/moondream/vision.py +++ b/src/pipecat/services/moondream/vision.py @@ -25,7 +25,7 @@ from pipecat.frames.frames import ( VisionFullResponseStartFrame, VisionTextFrame, ) -from pipecat.services.settings import VisionSettings, _warn_deprecated_param +from pipecat.services.settings import VisionSettings from pipecat.services.vision_service import VisionService try: @@ -110,7 +110,7 @@ class MoondreamService(VisionService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/neuphonic/tts.py b/src/pipecat/services/neuphonic/tts.py index 5c0605293..b345a0ff4 100644 --- a/src/pipecat/services/neuphonic/tts.py +++ b/src/pipecat/services/neuphonic/tts.py @@ -33,7 +33,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import InterruptibleTTSService, TextAggregationMode, TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -159,12 +159,12 @@ class NeuphonicTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) @@ -493,12 +493,12 @@ class NeuphonicHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = self.language_to_service_language(params.language) diff --git a/src/pipecat/services/nvidia/llm.py b/src/pipecat/services/nvidia/llm.py index b40190bec..66bbd4402 100644 --- a/src/pipecat/services/nvidia/llm.py +++ b/src/pipecat/services/nvidia/llm.py @@ -18,7 +18,6 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -68,7 +67,7 @@ class NvidiaLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/nvidia/stt.py b/src/pipecat/services/nvidia/stt.py index 3c8004baa..fcca14741 100644 --- a/src/pipecat/services/nvidia/stt.py +++ b/src/pipecat/services/nvidia/stt.py @@ -23,7 +23,7 @@ from pipecat.frames.frames import ( StartFrame, TranscriptionFrame, ) -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import NVIDIA_TTFS_P99 from pipecat.services.stt_service import SegmentedSTTService, STTService from pipecat.transcriptions.language import Language, resolve_language @@ -185,7 +185,7 @@ class NvidiaSTTService(STTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = params.language @@ -515,7 +515,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = ( language_to_nvidia_riva_language(params.language or Language.EN_US) or "en-US" diff --git a/src/pipecat/services/nvidia/tts.py b/src/pipecat/services/nvidia/tts.py index b41de8b47..5e7a20a3c 100644 --- a/src/pipecat/services/nvidia/tts.py +++ b/src/pipecat/services/nvidia/tts.py @@ -29,7 +29,7 @@ from pipecat.frames.frames import ( StartFrame, TTSAudioRawFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language @@ -126,12 +126,12 @@ class NvidiaTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = params.language diff --git a/src/pipecat/services/ollama/llm.py b/src/pipecat/services/ollama/llm.py index 3b15049a4..a24ebfcaf 100644 --- a/src/pipecat/services/ollama/llm.py +++ b/src/pipecat/services/ollama/llm.py @@ -13,7 +13,6 @@ from loguru import logger from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -60,7 +59,7 @@ class OLLamaLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/openai/image.py b/src/pipecat/services/openai/image.py index 31f9949b8..de010247b 100644 --- a/src/pipecat/services/openai/image.py +++ b/src/pipecat/services/openai/image.py @@ -25,7 +25,7 @@ from pipecat.frames.frames import ( URLImageRawFrame, ) from pipecat.services.image_service import ImageGenService -from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, ImageGenSettings, _NotGiven @dataclass @@ -90,11 +90,11 @@ class OpenAIImageGenService(ImageGenService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if image_size is not None: - _warn_deprecated_param("image_size", self.Settings, "image_size") + self._warn_init_param_moved_to_settings("image_size", "image_size") default_settings.image_size = image_size # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/openai/llm.py b/src/pipecat/services/openai/llm.py index 032d4dfa4..abb3e7eec 100644 --- a/src/pipecat/services/openai/llm.py +++ b/src/pipecat/services/openai/llm.py @@ -26,7 +26,6 @@ from pipecat.processors.aggregators.llm_response import ( ) from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.openai.base_llm import BaseOpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -120,7 +119,7 @@ class OpenAILLMService(BaseOpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # Handle service_tier from deprecated params @@ -129,7 +128,7 @@ class OpenAILLMService(BaseOpenAILLMService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.frequency_penalty = params.frequency_penalty default_settings.presence_penalty = params.presence_penalty diff --git a/src/pipecat/services/openai/realtime/llm.py b/src/pipecat/services/openai/realtime/llm.py index 300792cbd..faaa19884 100644 --- a/src/pipecat/services/openai/realtime/llm.py +++ b/src/pipecat/services/openai/realtime/llm.py @@ -63,7 +63,6 @@ from pipecat.services.settings import ( NOT_GIVEN, LLMSettings, _NotGiven, - _warn_deprecated_param, is_given, ) from pipecat.transcriptions.language import Language @@ -294,7 +293,7 @@ class OpenAIRealtimeLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if session_properties is not None: diff --git a/src/pipecat/services/openai/stt.py b/src/pipecat/services/openai/stt.py index 943be17de..39ca60b25 100644 --- a/src/pipecat/services/openai/stt.py +++ b/src/pipecat/services/openai/stt.py @@ -35,7 +35,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import OPENAI_REALTIME_TTFS_P99, OPENAI_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.services.whisper.base_stt import ( @@ -126,13 +126,13 @@ class OpenAISTTService(BaseWhisperSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if prompt is not None: - _warn_deprecated_param("prompt", self.Settings, "prompt") + self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", self.Settings, "temperature") + self._warn_init_param_moved_to_settings("temperature", "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- @@ -310,16 +310,16 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if language is not None and language != Language.EN: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = language if prompt is not None: - _warn_deprecated_param("prompt", self.Settings, "prompt") + self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt if noise_reduction is not None: - _warn_deprecated_param("noise_reduction", self.Settings, "noise_reduction") + self._warn_init_param_moved_to_settings("noise_reduction", "noise_reduction") default_settings.noise_reduction = noise_reduction # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/openai/tts.py b/src/pipecat/services/openai/tts.py index 7eb80cd1e..074792b33 100644 --- a/src/pipecat/services/openai/tts.py +++ b/src/pipecat/services/openai/tts.py @@ -23,7 +23,7 @@ from pipecat.frames.frames import ( StartFrame, TTSAudioRawFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.utils.tracing.service_decorators import traced_tts @@ -166,21 +166,21 @@ class OpenAITTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice is not None: - _warn_deprecated_param("voice", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice", "voice") default_settings.voice = voice if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if instructions is not None: - _warn_deprecated_param("instructions", self.Settings, "instructions") + self._warn_init_param_moved_to_settings("instructions", "instructions") default_settings.instructions = instructions if speed is not None: - _warn_deprecated_param("speed", self.Settings, "speed") + self._warn_init_param_moved_to_settings("speed", "speed") default_settings.speed = speed # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.instructions is not None: default_settings.instructions = params.instructions diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index 209ff3122..0a6315986 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -54,7 +54,7 @@ from pipecat.processors.aggregators.openai_llm_context import ( from pipecat.processors.frame_processor import FrameDirection from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.openai.llm import OpenAIContextAggregatorPair -from pipecat.services.settings import LLMSettings, _warn_deprecated_param +from pipecat.services.settings import LLMSettings from pipecat.transcriptions.language import Language from pipecat.utils.time import time_now_iso8601 from pipecat.utils.tracing.service_decorators import traced_openai_realtime, traced_stt @@ -173,7 +173,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply settings delta (canonical API, always wins) if settings is not None: diff --git a/src/pipecat/services/openpipe/llm.py b/src/pipecat/services/openpipe/llm.py index a2198eb74..0a8fd9044 100644 --- a/src/pipecat/services/openpipe/llm.py +++ b/src/pipecat/services/openpipe/llm.py @@ -18,7 +18,6 @@ from loguru import logger from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param try: from openpipe import AsyncOpenAI as OpenPipeAI @@ -80,7 +79,7 @@ class OpenPipeLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/openrouter/llm.py b/src/pipecat/services/openrouter/llm.py index 84647890f..f92fb5e3b 100644 --- a/src/pipecat/services/openrouter/llm.py +++ b/src/pipecat/services/openrouter/llm.py @@ -17,7 +17,6 @@ from loguru import logger from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -66,7 +65,7 @@ class OpenRouterLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/perplexity/llm.py b/src/pipecat/services/perplexity/llm.py index c93a77c16..6c2ceba35 100644 --- a/src/pipecat/services/perplexity/llm.py +++ b/src/pipecat/services/perplexity/llm.py @@ -20,7 +20,6 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -69,7 +68,7 @@ class PerplexityLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/piper/tts.py b/src/pipecat/services/piper/tts.py index 0d56d377e..1b0037abb 100644 --- a/src/pipecat/services/piper/tts.py +++ b/src/pipecat/services/piper/tts.py @@ -19,7 +19,7 @@ from pipecat.frames.frames import ( Frame, TTSStoppedFrame, ) -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import TTSService from pipecat.utils.tracing.service_decorators import traced_tts @@ -81,7 +81,7 @@ class PiperTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) @@ -232,7 +232,7 @@ class PiperHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/qwen/llm.py b/src/pipecat/services/qwen/llm.py index 4c3d7015c..857c89bea 100644 --- a/src/pipecat/services/qwen/llm.py +++ b/src/pipecat/services/qwen/llm.py @@ -13,7 +13,6 @@ from loguru import logger from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -61,7 +60,7 @@ class QwenLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/resembleai/tts.py b/src/pipecat/services/resembleai/tts.py index 7375b9a1b..fc70d814b 100644 --- a/src/pipecat/services/resembleai/tts.py +++ b/src/pipecat/services/resembleai/tts.py @@ -23,7 +23,7 @@ from pipecat.frames.frames import ( TTSStartedFrame, TTSStoppedFrame, ) -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import WebsocketTTSService from pipecat.utils.tracing.service_decorators import traced_tts @@ -92,7 +92,7 @@ class ResembleAITTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index df1b269cd..24a5d152b 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -31,7 +31,7 @@ from pipecat.frames.frames import ( TTSStoppedFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import ( InterruptibleTTSService, TextAggregationMode, @@ -241,15 +241,15 @@ class RimeTTSService(WebsocketTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None @@ -744,15 +744,15 @@ class RimeHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else "eng" @@ -974,15 +974,15 @@ class RimeNonJsonTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = ( self.language_to_service_language(params.language) if params.language else None diff --git a/src/pipecat/services/sambanova/llm.py b/src/pipecat/services/sambanova/llm.py index a77252ff8..3c7d76737 100644 --- a/src/pipecat/services/sambanova/llm.py +++ b/src/pipecat/services/sambanova/llm.py @@ -24,7 +24,6 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.llm_service import FunctionCallFromLLM from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param from pipecat.utils.tracing.service_decorators import traced_llm @@ -73,7 +72,7 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/sambanova/stt.py b/src/pipecat/services/sambanova/stt.py index c42a491e1..d3a77b4eb 100644 --- a/src/pipecat/services/sambanova/stt.py +++ b/src/pipecat/services/sambanova/stt.py @@ -11,7 +11,6 @@ from typing import Any, Optional from loguru import logger -from pipecat.services.settings import _warn_deprecated_param from pipecat.services.stt_latency import SAMBANOVA_TTFS_P99 from pipecat.services.whisper.base_stt import ( BaseWhisperSTTService, @@ -90,16 +89,16 @@ class SambaNovaSTTService(BaseWhisperSTTService): # type: ignore # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if language is not None: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = self.language_to_service_language(language) if prompt is not None: - _warn_deprecated_param("prompt", self.Settings, "prompt") + self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", self.Settings, "temperature") + self._warn_init_param_moved_to_settings("temperature", "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/sarvam/stt.py b/src/pipecat/services/sarvam/stt.py index b2c02f7cd..8d0a38810 100644 --- a/src/pipecat/services/sarvam/stt.py +++ b/src/pipecat/services/sarvam/stt.py @@ -36,7 +36,6 @@ from pipecat.services.settings import ( NOT_GIVEN, STTSettings, _NotGiven, - _warn_deprecated_param, is_given, ) from pipecat.services.stt_latency import SARVAM_TTFS_P99 @@ -255,12 +254,12 @@ class SarvamSTTService(STTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # --- 3. Deprecated params overrides --- if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = params.language default_settings.prompt = params.prompt diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index a469dbf33..91ee088cf 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -60,7 +60,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.sarvam._sdk import sdk_headers -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import InterruptibleTTSService, TextAggregationMode, TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -462,15 +462,15 @@ class SarvamHttpTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = ( @@ -874,10 +874,10 @@ class SarvamTTSService(InterruptibleTTSService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # Init-only audio format fields (not runtime-updatable) @@ -886,7 +886,7 @@ class SarvamTTSService(InterruptibleTTSService): # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: default_settings.language = ( diff --git a/src/pipecat/services/settings.py b/src/pipecat/services/settings.py index c9a120972..a0bf3cd58 100644 --- a/src/pipecat/services/settings.py +++ b/src/pipecat/services/settings.py @@ -37,7 +37,6 @@ Key helpers: from __future__ import annotations import copy -import warnings from dataclasses import dataclass, field, fields from typing import TYPE_CHECKING, Any, ClassVar, Dict, Mapping, Optional, Type, TypeVar @@ -49,45 +48,6 @@ if TYPE_CHECKING: from pipecat.turns.user_turn_completion_mixin import UserTurnCompletionConfig -# --------------------------------------------------------------------------- -# Deprecation helper -# --------------------------------------------------------------------------- - - -def _warn_deprecated_param( - param_name: str, - settings_class: type, - settings_field: str | None = None, - stacklevel: int = 3, -): - """Emit DeprecationWarning for a deprecated init parameter. - - Args: - param_name: Name of the deprecated parameter. - settings_class: The settings class to use instead. - settings_field: Specific field on the settings class, if different - from *param_name*. - stacklevel: Stack depth for the warning. Default ``3`` targets - the caller's caller (i.e. user code that instantiated the service). - """ - settings_class_name = settings_class.__name__ - if settings_field: - msg = ( - f"The `{param_name}` parameter is deprecated. " - f"Use `settings={settings_class_name}({settings_field}=...)` instead. " - f"If both are provided, `settings` takes precedence." - ) - else: - msg = ( - f"The `{param_name}` parameter is deprecated. " - f"Use `settings={settings_class_name}(...)` instead. " - f"If both are provided, `settings` takes precedence." - ) - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) - - # --------------------------------------------------------------------------- # NOT_GIVEN sentinel # --------------------------------------------------------------------------- diff --git a/src/pipecat/services/soniox/stt.py b/src/pipecat/services/soniox/stt.py index abf42dcdc..f123d850c 100644 --- a/src/pipecat/services/soniox/stt.py +++ b/src/pipecat/services/soniox/stt.py @@ -24,7 +24,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import SONIOX_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService from pipecat.transcriptions.language import Language @@ -233,12 +233,12 @@ class SonioxSTTService(WebsocketSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # --- 3. Deprecated params overrides --- if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.model = params.model if params.audio_format is not None: diff --git a/src/pipecat/services/speechmatics/stt.py b/src/pipecat/services/speechmatics/stt.py index 2a54b2b08..ae8e35850 100644 --- a/src/pipecat/services/speechmatics/stt.py +++ b/src/pipecat/services/speechmatics/stt.py @@ -33,7 +33,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import SPEECHMATICS_TTFS_P99 from pipecat.services.stt_service import STTService from pipecat.transcriptions.language import Language, resolve_language @@ -454,7 +454,7 @@ class SpeechmaticsSTTService(STTService): # --- 3. Deprecated params overrides --- if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.language = _params.language default_settings.domain = _params.domain diff --git a/src/pipecat/services/speechmatics/tts.py b/src/pipecat/services/speechmatics/tts.py index 243437cb3..64f64378a 100644 --- a/src/pipecat/services/speechmatics/tts.py +++ b/src/pipecat/services/speechmatics/tts.py @@ -20,7 +20,7 @@ from pipecat.frames.frames import ( Frame, TTSAudioRawFrame, ) -from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import TTSService from pipecat.utils.network import exponential_backoff_time from pipecat.utils.tracing.service_decorators import traced_tts @@ -119,12 +119,12 @@ class SpeechmaticsTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. Apply params overrides — only if settings not provided if params is not None: - _warn_deprecated_param("params", self.Settings) + self._warn_init_param_moved_to_settings("params") if not settings: default_settings.max_retries = params.max_retries diff --git a/src/pipecat/services/together/llm.py b/src/pipecat/services/together/llm.py index b1eb83d26..4ec2f8244 100644 --- a/src/pipecat/services/together/llm.py +++ b/src/pipecat/services/together/llm.py @@ -13,7 +13,6 @@ from loguru import logger from pipecat.services.openai.base_llm import BaseOpenAILLMService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.settings import _warn_deprecated_param @dataclass @@ -61,7 +60,7 @@ class TogetherLLMService(OpenAILLMService): # 2. Apply direct init arg overrides (deprecated) if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/whisper/base_stt.py b/src/pipecat/services/whisper/base_stt.py index d045eafc7..93bb5de34 100644 --- a/src/pipecat/services/whisper/base_stt.py +++ b/src/pipecat/services/whisper/base_stt.py @@ -18,7 +18,7 @@ from openai import AsyncOpenAI from openai.types.audio import Transcription from pipecat.frames.frames import ErrorFrame, Frame, TranscriptionFrame -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import WHISPER_TTFS_P99 from pipecat.services.stt_service import SegmentedSTTService from pipecat.transcriptions.language import Language, resolve_language @@ -190,16 +190,16 @@ class BaseWhisperSTTService(SegmentedSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if language is not None: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = self.language_to_service_language(language) if prompt is not None: - _warn_deprecated_param("prompt", self.Settings, "prompt") + self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt if temperature is not None: - _warn_deprecated_param("temperature", self.Settings, "temperature") + self._warn_init_param_moved_to_settings("temperature", "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/whisper/stt.py b/src/pipecat/services/whisper/stt.py index 29574007a..ac5d90c30 100644 --- a/src/pipecat/services/whisper/stt.py +++ b/src/pipecat/services/whisper/stt.py @@ -20,7 +20,7 @@ from loguru import logger from typing_extensions import TYPE_CHECKING, override from pipecat.frames.frames import ErrorFrame, Frame, TranscriptionFrame -from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven, _warn_deprecated_param +from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_service import SegmentedSTTService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.time import time_now_iso8601 @@ -256,13 +256,13 @@ class WhisperSTTService(SegmentedSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if isinstance(model, str) else model.value if no_speech_prob is not None: - _warn_deprecated_param("no_speech_prob", self.Settings, "no_speech_prob") + self._warn_init_param_moved_to_settings("no_speech_prob", "no_speech_prob") default_settings.no_speech_prob = no_speech_prob if language is not None: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = language # --- 3. (no params object for this service) --- @@ -432,16 +432,16 @@ class WhisperSTTServiceMLX(WhisperSTTService): # --- 2. Deprecated direct-arg overrides --- if model is not None: - _warn_deprecated_param("model", self.Settings, "model") + self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model if isinstance(model, str) else model.value if no_speech_prob is not None: - _warn_deprecated_param("no_speech_prob", self.Settings, "no_speech_prob") + self._warn_init_param_moved_to_settings("no_speech_prob", "no_speech_prob") default_settings.no_speech_prob = no_speech_prob if language is not None: - _warn_deprecated_param("language", self.Settings, "language") + self._warn_init_param_moved_to_settings("language", "language") default_settings.language = language if temperature is not None: - _warn_deprecated_param("temperature", self.Settings, "temperature") + self._warn_init_param_moved_to_settings("temperature", "temperature") default_settings.temperature = temperature # --- 3. (no params object for this service) --- diff --git a/src/pipecat/services/xtts/tts.py b/src/pipecat/services/xtts/tts.py index b769742f3..099fce65c 100644 --- a/src/pipecat/services/xtts/tts.py +++ b/src/pipecat/services/xtts/tts.py @@ -23,7 +23,7 @@ from pipecat.frames.frames import ( StartFrame, TTSAudioRawFrame, ) -from pipecat.services.settings import TTSSettings, _warn_deprecated_param +from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import TTSService from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.tracing.service_decorators import traced_tts @@ -122,7 +122,7 @@ class XTTSService(TTSService): # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: - _warn_deprecated_param("voice_id", self.Settings, "voice") + self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id # 3. (No step 3, as there's no params object to apply) From a9e124b84f845b02a67637ffccf45db3dde6751a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 11 Mar 2026 14:17:40 -0400 Subject: [PATCH 024/159] Update sarvamai dependency from 0.1.26a2 to 0.1.26 Bump the Sarvam AI SDK to the stable release version. --- pyproject.toml | 2 +- uv.lock | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d2ff8ec85..48226b14f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,7 +109,7 @@ riva = [ "pipecat-ai[nvidia]" ] runner = [ "python-dotenv>=1.0.0,<2.0.0", "uvicorn>=0.32.0,<1.0.0", "fastapi>=0.115.6,<1", "pipecat-ai-small-webrtc-prebuilt>=2.3.0"] sagemaker = ["aws_sdk_sagemaker_runtime_http2; python_version>='3.12'"] sambanova = [] -sarvam = [ "sarvamai==0.1.26a2", "pipecat-ai[websockets-base]" ] +sarvam = [ "sarvamai==0.1.26", "pipecat-ai[websockets-base]" ] sentry = [ "sentry-sdk>=2.28.0,<3" ] silero = [] simli = [ "simli-ai~=2.0.1"] diff --git a/uv.lock b/uv.lock index eae25a805..b68216261 100644 --- a/uv.lock +++ b/uv.lock @@ -2110,6 +2110,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747, upload-time = "2026-02-20T20:16:21.325Z" }, { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202, upload-time = "2026-02-20T20:47:28.955Z" }, { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620, upload-time = "2026-02-20T20:55:55.581Z" }, + { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132, upload-time = "2026-02-20T21:02:43.261Z" }, { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729, upload-time = "2026-02-20T20:20:58.395Z" }, { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946, upload-time = "2026-02-20T20:49:31.102Z" }, { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494, upload-time = "2026-02-20T20:21:06.541Z" }, @@ -2117,6 +2118,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, @@ -2125,6 +2127,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, @@ -2133,6 +2136,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, + { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, @@ -2141,6 +2145,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, @@ -2149,6 +2154,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, @@ -4864,7 +4870,7 @@ requires-dist = [ { name = "requests", marker = "extra == 'kokoro'", specifier = ">=2.32.5,<3" }, { name = "requests", marker = "extra == 'piper'", specifier = ">=2.32.5,<3" }, { name = "resampy", specifier = "~=0.4.3" }, - { name = "sarvamai", marker = "extra == 'sarvam'", specifier = "==0.1.26a2" }, + { name = "sarvamai", marker = "extra == 'sarvam'", specifier = "==0.1.26" }, { name = "sentry-sdk", marker = "extra == 'sentry'", specifier = ">=2.28.0,<3" }, { name = "simli-ai", marker = "extra == 'simli'", specifier = "~=2.0.1" }, { name = "soundfile", marker = "extra == 'soundfile'", specifier = "~=0.13.1" }, @@ -6380,7 +6386,7 @@ wheels = [ [[package]] name = "sarvamai" -version = "0.1.26a2" +version = "0.1.26" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -6389,9 +6395,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/6c/80ab26743586532a3e9d68385549b0992e5318b5499db815889c8527cce5/sarvamai-0.1.26a2.tar.gz", hash = "sha256:0cbd1a95d13c1f8f0d1bf8fbeb37e86d3c2dc75a7ac402743bf0e571378f79e4", size = 112445, upload-time = "2026-02-16T13:16:28.392Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/31/13f65e8533b667514e1cfe838d12a14494cbc5943fd8f0c101305127459b/sarvamai-0.1.26.tar.gz", hash = "sha256:d51a213c27feb33d65f5b71e4882dcdb873dc5e0d720390b7ba18d1bdeec2471", size = 113050, upload-time = "2026-03-06T16:40:36.647Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/3e/76c8ea81e790a5dab2ec9cd9fdb02cd600f90b1dfd9c895bea2fb5e6aa7f/sarvamai-0.1.26a2-py3-none-any.whl", hash = "sha256:2b0549a18e093ea382725240035a0bea18fff2a0d5207ad6c95ff7189e03264a", size = 227413, upload-time = "2026-02-16T13:16:27.045Z" }, + { url = "https://files.pythonhosted.org/packages/76/c9/c03a807ace9cafbfe26418be995e4959142a55313c9f26564586e111f31d/sarvamai-0.1.26-py3-none-any.whl", hash = "sha256:39e79ba0932f4501a2aa28f84fd2de64d34fc9a7af2b0d4ead1efa617517b3bd", size = 229057, upload-time = "2026-03-06T16:40:35.584Z" }, ] [[package]] @@ -7400,6 +7406,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/8b/4b61d6e13f7108f36910df9ab4b58fd389cc2520d54d81b88660804aad99/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f", size = 79423467, upload-time = "2026-02-10T21:44:48.711Z" }, { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, + { url = "https://files.pythonhosted.org/packages/16/ee/efbd56687be60ef9af0c9c0ebe106964c07400eade5b0af8902a1d8cd58c/torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321", size = 915510070, upload-time = "2026-03-11T14:16:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/36/ab/7b562f1808d3f65414cd80a4f7d4bb00979d9355616c034c171249e1a303/torch-2.10.0-3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac5bdcbb074384c66fa160c15b1ead77839e3fe7ed117d667249afce0acabfac", size = 915518691, upload-time = "2026-03-11T14:15:43.147Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, + { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" }, { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" }, { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" }, { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" }, From 71e61588617781d1535655bf832bb5a3b8d83cc1 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 11 Mar 2026 14:18:47 -0400 Subject: [PATCH 025/159] Add changelog for PR #3997 --- changelog/3997.changed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3997.changed.md diff --git a/changelog/3997.changed.md b/changelog/3997.changed.md new file mode 100644 index 000000000..7d626a5fe --- /dev/null +++ b/changelog/3997.changed.md @@ -0,0 +1 @@ +- Updated `sarvamai` dependency from `0.1.26a2` (alpha) to `0.1.26` (stable release). From 080ed22ff5cf1caaf53625f884386ec4d2c17e9c Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 15:44:05 -0400 Subject: [PATCH 026/159] Override CambTTSSettings.voice type from str to int to match Camb.ai's integer voice IDs --- examples/foundational/55zf-update-settings-camb-tts.py | 6 ++++-- src/pipecat/services/camb/tts.py | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/foundational/55zf-update-settings-camb-tts.py b/examples/foundational/55zf-update-settings-camb-tts.py index 5f0840857..37b845ab4 100644 --- a/examples/foundational/55zf-update-settings-camb-tts.py +++ b/examples/foundational/55zf-update-settings-camb-tts.py @@ -96,9 +96,11 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): await task.queue_frames([LLMRunFrame()]) await asyncio.sleep(10) - logger.info("Updating Camb TTS settings: language -> Spanish") + logger.info("Updating Camb TTS settings: language -> Spanish, voice -> Pirate Captain") await task.queue_frame( - TTSUpdateSettingsFrame(delta=CambTTSService.Settings(language=Language.ES)) + TTSUpdateSettingsFrame( + delta=CambTTSService.Settings(language=Language.ES, voice=147319) + ) ) @transport.event_handler("on_client_disconnected") diff --git a/src/pipecat/services/camb/tts.py b/src/pipecat/services/camb/tts.py index b0c237165..9c360ed37 100644 --- a/src/pipecat/services/camb/tts.py +++ b/src/pipecat/services/camb/tts.py @@ -138,10 +138,13 @@ class CambTTSSettings(TTSSettings): """Settings for CambTTSService. Parameters: + voice: Camb.ai voice ID. Overrides ``TTSSettings.voice`` (str) because + Camb.ai uses integer voice IDs. user_instructions: Custom instructions for mars-instruct model only. Ignored for other models. Max 1000 characters. """ + voice: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN) user_instructions: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) From 4a45145cbaf379e1aedeb1ca1acb902d8a3ec045 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 11 Mar 2026 15:51:09 -0400 Subject: [PATCH 027/159] Restored the default model to gpt-4.1 for OpenAI and Azure LLM services The default model for OpenAILLMService and AzureLLMService was still set to gpt-4o. Restored it to gpt-4.1. Also, removed hardcoded gpt-4o/gpt-4o-mini model references from examples so they pick up the new default. --- changelog/4000.fixed.md | 1 + examples/foundational/04a-transports-daily.py | 1 - examples/foundational/14v-function-calling-openai.py | 1 - examples/foundational/27-simli-layer.py | 1 - examples/foundational/35-pattern-pair-voice-switching.py | 2 +- examples/foundational/37-mem0.py | 3 +-- src/pipecat/services/azure/llm.py | 4 ++-- src/pipecat/services/openai/base_llm.py | 2 +- 8 files changed, 6 insertions(+), 9 deletions(-) create mode 100644 changelog/4000.fixed.md diff --git a/changelog/4000.fixed.md b/changelog/4000.fixed.md new file mode 100644 index 000000000..871d71135 --- /dev/null +++ b/changelog/4000.fixed.md @@ -0,0 +1 @@ +- Fixed an issue where the default model for `OpenAILLMService` and `AzureLLMService` was mistakenly reverted to `gpt-4o`. The defaults are now restored to `gpt-4.1`. diff --git a/examples/foundational/04a-transports-daily.py b/examples/foundational/04a-transports-daily.py index ab986c22f..b73b7bdfe 100644 --- a/examples/foundational/04a-transports-daily.py +++ b/examples/foundational/04a-transports-daily.py @@ -58,7 +58,6 @@ async def main(): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - model="gpt-4o", system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", ), ) diff --git a/examples/foundational/14v-function-calling-openai.py b/examples/foundational/14v-function-calling-openai.py index 639d9bd23..db759b186 100644 --- a/examples/foundational/14v-function-calling-openai.py +++ b/examples/foundational/14v-function-calling-openai.py @@ -79,7 +79,6 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): instructions="Please speak clearly and at a moderate pace.", ) - # model choices: gpt-4o, gpt-4.1, etc. llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index c8a9ad663..b2cbb6837 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -73,7 +73,6 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - model="gpt-4o-mini", system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", ), ) diff --git a/examples/foundational/35-pattern-pair-voice-switching.py b/examples/foundational/35-pattern-pair-voice-switching.py index ed9bb3973..c246ce599 100644 --- a/examples/foundational/35-pattern-pair-voice-switching.py +++ b/examples/foundational/35-pattern-pair-voice-switching.py @@ -24,7 +24,7 @@ The PatternPairAggregator: - Returns processed text at sentence boundaries Requirements: - - OpenAI API key (for GPT-4o) + - OpenAI API key - Cartesia API key (for text-to-speech) - Daily API key (for video/audio transport) diff --git a/examples/foundational/37-mem0.py b/examples/foundational/37-mem0.py index 06883a43e..e572d2dbe 100644 --- a/examples/foundational/37-mem0.py +++ b/examples/foundational/37-mem0.py @@ -24,7 +24,7 @@ Example usage (run from pipecat root directory): $ python examples/foundational/37-mem0.py Requirements: - - OpenAI API key (for GPT-4o-mini) + - OpenAI API key - ElevenLabs API key (for text-to-speech) - Daily API key (for video/audio transport) - Mem0 API key (for cloud-based memory storage) @@ -226,7 +226,6 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - model="gpt-4o-mini", system_instruction="""You are a personal assistant. You can remember things about the person you are talking to. Some Guidelines: - Make sure your responses are friendly yet short and concise. diff --git a/src/pipecat/services/azure/llm.py b/src/pipecat/services/azure/llm.py index ea705eec6..8b5050e5b 100644 --- a/src/pipecat/services/azure/llm.py +++ b/src/pipecat/services/azure/llm.py @@ -47,7 +47,7 @@ class AzureLLMService(OpenAILLMService): Args: api_key: The API key for accessing Azure OpenAI. endpoint: The Azure endpoint URL. - model: The model identifier to use. Defaults to "gpt-4o". + model: The model identifier to use. Defaults to "gpt-4.1". .. deprecated:: 0.0.105 Use ``settings=AzureLLMService.Settings(model=...)`` instead. @@ -58,7 +58,7 @@ class AzureLLMService(OpenAILLMService): **kwargs: Additional keyword arguments passed to OpenAILLMService. """ # 1. Initialize default_settings with hardcoded defaults - default_settings = self.Settings(model="gpt-4o") + default_settings = self.Settings(model="gpt-4.1") # 2. Apply direct init arg overrides (deprecated) if model is not None: diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index d1d695ef6..41b26bd20 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -151,7 +151,7 @@ class BaseOpenAILLMService(LLMService): """ # 1. Initialize default_settings with hardcoded defaults default_settings = self.Settings( - model="gpt-4o", + model="gpt-4.1", system_instruction=None, frequency_penalty=NOT_GIVEN, presence_penalty=NOT_GIVEN, From a54aa2d1f879990826a99b3adaf1fddb1cf40322 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 11 Mar 2026 16:56:41 -0400 Subject: [PATCH 028/159] Migrate SimliVideoService to AIService with Settings pattern Align Simli with HeyGen/Tavus by extending AIService instead of FrameProcessor and using a ServiceSettings dataclass. InputParams is preserved but deprecated; its fields are promoted to direct init params. Lifecycle handling moves to start()/stop()/cancel() methods. --- src/pipecat/services/simli/video.py | 105 +++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 19 deletions(-) diff --git a/src/pipecat/services/simli/video.py b/src/pipecat/services/simli/video.py index 880b40428..8345c0b86 100644 --- a/src/pipecat/services/simli/video.py +++ b/src/pipecat/services/simli/video.py @@ -8,6 +8,7 @@ import asyncio import warnings +from dataclasses import dataclass from typing import Optional import numpy as np @@ -20,11 +21,14 @@ from pipecat.frames.frames import ( Frame, InterruptionFrame, OutputImageRawFrame, + StartFrame, TTSAudioRawFrame, TTSStoppedFrame, UserStartedSpeakingFrame, ) -from pipecat.processors.frame_processor import FrameDirection, FrameProcessor, StartFrame +from pipecat.processors.frame_processor import FrameDirection +from pipecat.services.ai_service import AIService +from pipecat.services.settings import ServiceSettings try: from av.audio.frame import AudioFrame @@ -36,7 +40,14 @@ except ModuleNotFoundError as e: raise Exception(f"Missing module: {e}") -class SimliVideoService(FrameProcessor): +@dataclass +class SimliVideoSettings(ServiceSettings): + """Settings for the Simli video service.""" + + pass + + +class SimliVideoService(AIService): """Simli video service for real-time avatar generation. Provides real-time avatar video generation by processing audio frames @@ -44,9 +55,15 @@ class SimliVideoService(FrameProcessor): audio resampling, video frame processing, and connection management. """ + Settings = SimliVideoSettings + _settings: Settings + class InputParams(BaseModel): """Input parameters for Simli video configuration. + .. deprecated:: 0.0.105 + Use ``SimliVideoService.Settings(...)`` instead. + Parameters: enable_logging: Whether to enable Simli logging. max_session_length: Absolute maximum session duration in seconds. @@ -66,10 +83,13 @@ class SimliVideoService(FrameProcessor): face_id: Optional[str] = None, simli_config: Optional[SimliConfig] = None, use_turn_server: bool = False, - latency_interval: int = 0, simli_url: str = "https://api.simli.ai", is_trinity_avatar: bool = False, params: Optional[InputParams] = None, + max_session_length: Optional[int] = None, + max_idle_time: Optional[int] = None, + enable_logging: Optional[bool] = None, + settings: Optional[Settings] = None, **kwargs, ): """Initialize the Simli video service. @@ -90,18 +110,42 @@ class SimliVideoService(FrameProcessor): .. deprecated:: 0.0.95 The 'use_turn_server' parameter is deprecated and will be removed in a future version. - latency_interval: Latency interval setting for sending health checks to check - the latency to Simli Servers. Defaults to 0. simli_url: URL of the simli servers. Can be changed for custom deployments of enterprise users. is_trinity_avatar: Boolean to tell simli client that this is a Trinity avatar which reduces latency when using Trinity. params: Additional input parameters for session configuration. - **kwargs: Additional arguments passed to the parent FrameProcessor. - """ - super().__init__(**kwargs) - params = params or SimliVideoService.InputParams() + .. deprecated:: 0.0.105 + Use ``settings=SimliVideoService.Settings(...)`` instead. + + max_session_length: Absolute maximum session duration in seconds. + Avatar will disconnect after this time even if it's speaking. + max_idle_time: Maximum duration in seconds the avatar is not speaking + before the avatar disconnects. + enable_logging: Whether to enable Simli logging. + settings: Service settings. + **kwargs: Additional arguments passed to the parent AIService. + """ + # 1. Default settings + default_settings = ServiceSettings(model=None) + + # 2. Apply deprecated params overrides + if params is not None: + self._warn_init_param_moved_to_settings("params") + if max_session_length is None and hasattr(params, "max_session_length"): + max_session_length = params.max_session_length + if max_idle_time is None and hasattr(params, "max_idle_time"): + max_idle_time = params.max_idle_time + if enable_logging is None and hasattr(params, "enable_logging"): + enable_logging = params.enable_logging + + # 3. Apply settings delta + if settings is not None: + default_settings.apply_update(settings) + + # 4. Call super + super().__init__(settings=default_settings, **kwargs) # Handle deprecated simli_config parameter if simli_config is not None: @@ -133,10 +177,10 @@ class SimliVideoService(FrameProcessor): config_kwargs = { "faceId": face_id, } - if params.max_session_length is not None: - config_kwargs["maxSessionLength"] = params.max_session_length - if params.max_idle_time is not None: - config_kwargs["maxIdleTime"] = params.max_idle_time + if max_session_length is not None: + config_kwargs["maxSessionLength"] = max_session_length + if max_idle_time is not None: + config_kwargs["maxIdleTime"] = max_idle_time config = SimliConfig(**config_kwargs) @@ -168,6 +212,33 @@ class SimliVideoService(FrameProcessor): self._previously_interrupted = is_trinity_avatar self._audio_buffer = bytearray() + async def start(self, frame: StartFrame): + """Start the Simli video service. + + Args: + frame: The start frame containing initialization parameters. + """ + await super().start(frame) + await self._start_connection() + + async def stop(self, frame: EndFrame): + """Stop the Simli video service. + + Args: + frame: The end frame. + """ + await super().stop(frame) + await self._stop_connection() + + async def cancel(self, frame: CancelFrame): + """Cancel the Simli video service. + + Args: + frame: The cancel frame. + """ + await super().cancel(frame) + await self._stop_connection() + async def _start_connection(self): """Start the connection to Simli service and begin processing tasks.""" try: @@ -222,9 +293,7 @@ class SimliVideoService(FrameProcessor): direction: The direction of frame processing. """ await super().process_frame(frame, direction) - if isinstance(frame, StartFrame): - await self._start_connection() - elif isinstance(frame, TTSAudioRawFrame): + if isinstance(frame, TTSAudioRawFrame): # Send audio frame to Simli try: old_frame = AudioFrame.from_ndarray( @@ -268,8 +337,6 @@ class SimliVideoService(FrameProcessor): except Exception as e: await self.push_error(error_msg=f"Error stopping TTS: {e}", exception=e) return - elif isinstance(frame, (EndFrame, CancelFrame)): - await self._stop() elif isinstance(frame, (InterruptionFrame, UserStartedSpeakingFrame)): if not self._previously_interrupted: await self._simli_client.clearBuffer() @@ -277,7 +344,7 @@ class SimliVideoService(FrameProcessor): await self.push_frame(frame, direction) - async def _stop(self): + async def _stop_connection(self): """Stop the Simli client and cancel processing tasks.""" await self._simli_client.stop() if self._audio_task: From 2d9dc2fa1c8c89c155ac4404fad9cd8601ef862d Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 11 Mar 2026 17:12:59 -0400 Subject: [PATCH 029/159] Update quickstart example for 0.0.105 --- examples/quickstart/bot.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/quickstart/bot.py b/examples/quickstart/bot.py index fbe27d783..c201edeb3 100644 --- a/examples/quickstart/bot.py +++ b/examples/quickstart/bot.py @@ -63,19 +63,19 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + settings=CartesiaTTSService.Settings( + voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ), ) - llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + llm = OpenAILLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAILLMService.Settings( + system_instruction="You are a friendly AI assistant. Respond naturally and keep your answers conversational.", + ), + ) - messages = [ - { - "role": "system", - "content": "You are a friendly AI assistant. Respond naturally and keep your answers conversational.", - }, - ] - - context = LLMContext(messages) + context = LLMContext() user_aggregator, assistant_aggregator = LLMContextAggregatorPair( context, user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), @@ -105,7 +105,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): async def on_client_connected(transport, client): logger.info(f"Client connected") # Kick off the conversation. - messages.append({"role": "system", "content": "Say hello and briefly introduce yourself."}) + context.add_message( + {"role": "user", "content": "Say hello and briefly introduce yourself."} + ) await task.queue_frames([LLMRunFrame()]) @transport.event_handler("on_client_disconnected") From e456a6bb233f0e67db800f05aa906f233e2acaeb Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 17:17:40 -0400 Subject: [PATCH 030/159] Move away from remaining deprecated `TransportParams.vad_analyzer` usage in example files. Skip updates to deprecated services. --- .../foundational/07zk-interruptible-resemble.py | 8 ++------ examples/foundational/13-whisper-transcription.py | 7 +++---- examples/foundational/13a-whisper-local.py | 5 +++-- examples/foundational/13e-whisper-mlx.py | 9 +++++---- .../foundational/13g-sambanova-transcription.py | 9 +++++---- examples/foundational/13i-soniox-transcription.py | 7 +++---- examples/foundational/13j-azure-transcription.py | 7 +++---- .../foundational/13k-elevenlabs-transcription.py | 12 ++++++------ examples/foundational/13m-openai-transcription.py | 12 ++++++------ examples/foundational/19-openai-realtime.py | 9 +++++---- examples/foundational/19a-azure-realtime.py | 13 ++++++++----- examples/foundational/19b-openai-realtime-text.py | 13 ++++++++----- .../foundational/19c-openai-realtime-live-video.py | 12 ++++++++---- .../20b-persistent-context-openai-realtime.py | 13 ++++++++----- .../20e-persistent-context-aws-nova-sonic.py | 13 ++++++++----- examples/foundational/25-google-audio-in.py | 13 ++++++++----- examples/foundational/40-aws-nova-sonic.py | 9 +++++---- .../55zl-update-settings-azure-realtime.py | 9 +++++---- .../55zl-update-settings-openai-realtime.py | 9 +++++---- .../55zm-update-settings-gemini-live-vertex.py | 13 ++++++++----- .../55zm-update-settings-gemini-live.py | 13 ++++++++----- .../55zn-update-settings-ultravox-realtime.py | 9 +++++---- .../55zo-update-settings-grok-realtime.py | 9 +++++---- 23 files changed, 130 insertions(+), 103 deletions(-) diff --git a/examples/foundational/07zk-interruptible-resemble.py b/examples/foundational/07zk-interruptible-resemble.py index 88bcb724e..a62387815 100644 --- a/examples/foundational/07zk-interruptible-resemble.py +++ b/examples/foundational/07zk-interruptible-resemble.py @@ -30,24 +30,20 @@ from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams load_dotenv(override=True) -# We store functions so objects (e.g. SileroVADAnalyzer) don't get -# instantiated. The function will be called when the desired transport gets -# selected. +# We use lambdas to defer transport parameter creation until the transport +# type is selected at runtime. transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } diff --git a/examples/foundational/13-whisper-transcription.py b/examples/foundational/13-whisper-transcription.py index e2f0f61e0..f61cec132 100644 --- a/examples/foundational/13-whisper-transcription.py +++ b/examples/foundational/13-whisper-transcription.py @@ -13,6 +13,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -40,15 +41,12 @@ class TranscriptionLogger(FrameProcessor): transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -59,8 +57,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): stt = WhisperSTTService() tl = TranscriptionLogger() + vad_processor = VADProcessor(vad_analyzer=SileroVADAnalyzer()) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask( pipeline, diff --git a/examples/foundational/13a-whisper-local.py b/examples/foundational/13a-whisper-local.py index ec9ddb603..0882542fc 100644 --- a/examples/foundational/13a-whisper-local.py +++ b/examples/foundational/13a-whisper-local.py @@ -15,6 +15,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.whisper.stt import WhisperSTTService from pipecat.transports.local.audio import LocalAudioTransport, LocalAudioTransportParams @@ -40,15 +41,15 @@ async def main(): transport = LocalAudioTransport( LocalAudioTransportParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ) ) stt = WhisperSTTService() tl = TranscriptionLogger() + vad_processor = VADProcessor(vad_analyzer=SileroVADAnalyzer()) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask(pipeline) diff --git a/examples/foundational/13e-whisper-mlx.py b/examples/foundational/13e-whisper-mlx.py index f4721a86a..af7d2d79e 100644 --- a/examples/foundational/13e-whisper-mlx.py +++ b/examples/foundational/13e-whisper-mlx.py @@ -15,6 +15,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame, UserStoppedSpeaking from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -61,15 +62,12 @@ class TranscriptionLogger(FrameProcessor): transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), ), } @@ -84,8 +82,11 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) tl = TranscriptionLogger() + vad_processor = VADProcessor( + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)) + ) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask( pipeline, diff --git a/examples/foundational/13g-sambanova-transcription.py b/examples/foundational/13g-sambanova-transcription.py index 404c215fd..26e961c5e 100644 --- a/examples/foundational/13g-sambanova-transcription.py +++ b/examples/foundational/13g-sambanova-transcription.py @@ -16,6 +16,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame, UserStoppedSpeaking from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -62,15 +63,12 @@ class TranscriptionLogger(FrameProcessor): transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), ), } @@ -86,8 +84,11 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) tl = TranscriptionLogger() + vad_processor = VADProcessor( + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)) + ) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask( pipeline, diff --git a/examples/foundational/13i-soniox-transcription.py b/examples/foundational/13i-soniox-transcription.py index d8d46dfc3..9476e9441 100644 --- a/examples/foundational/13i-soniox-transcription.py +++ b/examples/foundational/13i-soniox-transcription.py @@ -14,6 +14,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -39,15 +40,12 @@ class TranscriptionLogger(FrameProcessor): transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -60,8 +58,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) tl = TranscriptionLogger() + vad_processor = VADProcessor(vad_analyzer=SileroVADAnalyzer()) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask( pipeline, diff --git a/examples/foundational/13j-azure-transcription.py b/examples/foundational/13j-azure-transcription.py index d3df106bd..301c6effd 100644 --- a/examples/foundational/13j-azure-transcription.py +++ b/examples/foundational/13j-azure-transcription.py @@ -14,6 +14,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -39,15 +40,12 @@ class TranscriptionLogger(FrameProcessor): transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -61,8 +59,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) tl = TranscriptionLogger() + vad_processor = VADProcessor(vad_analyzer=SileroVADAnalyzer()) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask( pipeline, diff --git a/examples/foundational/13k-elevenlabs-transcription.py b/examples/foundational/13k-elevenlabs-transcription.py index f801944e5..dbfd32ec6 100644 --- a/examples/foundational/13k-elevenlabs-transcription.py +++ b/examples/foundational/13k-elevenlabs-transcription.py @@ -14,6 +14,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -39,11 +40,9 @@ class TranscriptionLogger(FrameProcessor): # We use lambdas to defer transport parameter creation until the transport # type is selected at runtime. transport_params = { - "daily": lambda: DailyParams(audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer()), - "twilio": lambda: FastAPIWebsocketParams( - audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer() - ), - "webrtc": lambda: TransportParams(audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer()), + "daily": lambda: DailyParams(audio_in_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_in_enabled=True), + "webrtc": lambda: TransportParams(audio_in_enabled=True), } @@ -53,8 +52,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): stt = ElevenLabsRealtimeSTTService(api_key=os.getenv("ELEVENLABS_API_KEY")) tl = TranscriptionLogger() + vad_processor = VADProcessor(vad_analyzer=SileroVADAnalyzer()) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask( pipeline, diff --git a/examples/foundational/13m-openai-transcription.py b/examples/foundational/13m-openai-transcription.py index 0fb595881..cbbf0e13d 100644 --- a/examples/foundational/13m-openai-transcription.py +++ b/examples/foundational/13m-openai-transcription.py @@ -14,6 +14,7 @@ from pipecat.frames.frames import Frame, TranscriptionFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask +from pipecat.processors.audio.vad_processor import VADProcessor from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -39,11 +40,9 @@ class TranscriptionLogger(FrameProcessor): # We use lambdas to defer transport parameter creation until the transport # type is selected at runtime. transport_params = { - "daily": lambda: DailyParams(audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer()), - "twilio": lambda: FastAPIWebsocketParams( - audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer() - ), - "webrtc": lambda: TransportParams(audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer()), + "daily": lambda: DailyParams(audio_in_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_in_enabled=True), + "webrtc": lambda: TransportParams(audio_in_enabled=True), } @@ -59,8 +58,9 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) tl = TranscriptionLogger() + vad_processor = VADProcessor(vad_analyzer=SileroVADAnalyzer()) - pipeline = Pipeline([transport.input(), stt, tl]) + pipeline = Pipeline([transport.input(), vad_processor, stt, tl]) task = PipelineTask( pipeline, diff --git a/examples/foundational/19-openai-realtime.py b/examples/foundational/19-openai-realtime.py index 8eeea9e02..cb3abf953 100644 --- a/examples/foundational/19-openai-realtime.py +++ b/examples/foundational/19-openai-realtime.py @@ -24,6 +24,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import ( AssistantTurnStoppedMessage, LLMContextAggregatorPair, + LLMUserAggregatorParams, UserTurnStoppedMessage, ) from pipecat.runner.types import RunnerArguments @@ -119,17 +120,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -187,7 +185,10 @@ Remember, your responses should be short. Just one or two sentences, usually. Re tools, ) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/19a-azure-realtime.py b/examples/foundational/19a-azure-realtime.py index c98dc167a..ffba20f5d 100644 --- a/examples/foundational/19a-azure-realtime.py +++ b/examples/foundational/19a-azure-realtime.py @@ -19,7 +19,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.azure.realtime.llm import AzureRealtimeLLMService @@ -93,17 +96,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -172,7 +172,10 @@ Remember, your responses should be short. Just one or two sentences, usually. Re tools, ) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/19b-openai-realtime-text.py b/examples/foundational/19b-openai-realtime-text.py index deb50e805..1fa6ea545 100644 --- a/examples/foundational/19b-openai-realtime-text.py +++ b/examples/foundational/19b-openai-realtime-text.py @@ -19,7 +19,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService @@ -98,17 +101,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -175,7 +175,10 @@ Remember, your responses should be short. Just one or two sentences, usually. Re tools, ) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/19c-openai-realtime-live-video.py b/examples/foundational/19c-openai-realtime-live-video.py index 3038a57bc..f862b5511 100644 --- a/examples/foundational/19c-openai-realtime-live-video.py +++ b/examples/foundational/19c-openai-realtime-live-video.py @@ -17,7 +17,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import ( create_transport, @@ -46,13 +49,11 @@ transport_params = { audio_in_enabled=True, audio_out_enabled=True, video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -103,7 +104,10 @@ Remember, your responses should be short. Just one or two sentences, usually. Re [{"role": "user", "content": "Say hello!"}], ) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 2b9a8b6bf..de4215ab8 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -21,7 +21,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.deepgram.stt import DeepgramSTTService @@ -153,17 +156,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -214,7 +214,10 @@ Remember, your responses should be short. Just one or two sentences, usually.""" llm.register_function("load_conversation", load_conversation) context = LLMContext([{"role": "user", "content": "Say hello!"}], tools) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index 9614328f2..3d1043d18 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -21,7 +21,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.aws.nova_sonic.llm import AWSNovaSonicLLMService @@ -189,17 +192,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -236,7 +236,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm.register_function("load_conversation", load_conversation) context = LLMContext(tools=tools) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py index 572e0e213..6b549a7fe 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -28,7 +28,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.processors.frame_processor import FrameProcessor from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -270,17 +273,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -324,7 +324,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ] context = LLMContext(messages) - context_aggregator = LLMContextAggregatorPair(context) + context_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) audio_collector = UserAudioCollector(context, context_aggregator.user()) input_transcription_context_filter = InputTranscriptionContextFilter() transcription_frames_emitter = InputTranscriptionFrameEmitter() diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py index 79a3d168c..6f7c745ce 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -24,6 +24,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import ( AssistantTurnStoppedMessage, LLMContextAggregatorPair, + LLMUserAggregatorParams, UserTurnStoppedMessage, ) from pipecat.runner.types import RunnerArguments @@ -87,17 +88,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -147,7 +145,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): # Set up context and context management. context = LLMContext(tools=tools) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) # Build the pipeline pipeline = Pipeline( diff --git a/examples/foundational/55zl-update-settings-azure-realtime.py b/examples/foundational/55zl-update-settings-azure-realtime.py index fcfcdcfdd..a977ea60c 100644 --- a/examples/foundational/55zl-update-settings-azure-realtime.py +++ b/examples/foundational/55zl-update-settings-azure-realtime.py @@ -19,6 +19,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import ( AssistantTurnStoppedMessage, LLMContextAggregatorPair, + LLMUserAggregatorParams, ) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -34,17 +35,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -65,7 +63,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ] context = LLMContext(messages) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/55zl-update-settings-openai-realtime.py b/examples/foundational/55zl-update-settings-openai-realtime.py index 372b1f1e6..9f7281a72 100644 --- a/examples/foundational/55zl-update-settings-openai-realtime.py +++ b/examples/foundational/55zl-update-settings-openai-realtime.py @@ -19,6 +19,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import ( AssistantTurnStoppedMessage, LLMContextAggregatorPair, + LLMUserAggregatorParams, ) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -34,17 +35,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -62,7 +60,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ] context = LLMContext(messages) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/55zm-update-settings-gemini-live-vertex.py b/examples/foundational/55zm-update-settings-gemini-live-vertex.py index 8ff18e1eb..c6a04347a 100644 --- a/examples/foundational/55zm-update-settings-gemini-live-vertex.py +++ b/examples/foundational/55zm-update-settings-gemini-live-vertex.py @@ -16,7 +16,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.google.gemini_live.vertex.llm import GeminiLiveVertexLLMService @@ -30,17 +33,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -58,7 +58,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) context = LLMContext() - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/55zm-update-settings-gemini-live.py b/examples/foundational/55zm-update-settings-gemini-live.py index 0b88c45da..beb7f0c1c 100644 --- a/examples/foundational/55zm-update-settings-gemini-live.py +++ b/examples/foundational/55zm-update-settings-gemini-live.py @@ -16,7 +16,10 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.google.gemini_live.llm import GeminiLiveLLMService @@ -30,17 +33,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -56,7 +56,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) context = LLMContext() - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/55zn-update-settings-ultravox-realtime.py b/examples/foundational/55zn-update-settings-ultravox-realtime.py index 13ce6c1ae..ccf2d2003 100644 --- a/examples/foundational/55zn-update-settings-ultravox-realtime.py +++ b/examples/foundational/55zn-update-settings-ultravox-realtime.py @@ -21,6 +21,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import ( AssistantTurnStoppedMessage, LLMContextAggregatorPair, + LLMUserAggregatorParams, ) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -35,17 +36,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -73,7 +71,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ] context = LLMContext(messages) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ diff --git a/examples/foundational/55zo-update-settings-grok-realtime.py b/examples/foundational/55zo-update-settings-grok-realtime.py index 5ac425de4..60ba18755 100644 --- a/examples/foundational/55zo-update-settings-grok-realtime.py +++ b/examples/foundational/55zo-update-settings-grok-realtime.py @@ -19,6 +19,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import ( AssistantTurnStoppedMessage, LLMContextAggregatorPair, + LLMUserAggregatorParams, ) from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport @@ -34,17 +35,14 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), ), } @@ -62,7 +60,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ] context = LLMContext(messages) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) pipeline = Pipeline( [ From ccc2549c0cebed0ad65a8b13b978b7b7768161b8 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 17:22:11 -0400 Subject: [PATCH 031/159] Broaden the `vad_analyzer` deprecation warning in `BaseInputTransport` to account for use-cases where there is no `LLMUserAggregator` at play --- src/pipecat/transports/base_input.py | 5 +++-- src/pipecat/transports/base_transport.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pipecat/transports/base_input.py b/src/pipecat/transports/base_input.py index 1da672ab7..bcd457564 100644 --- a/src/pipecat/transports/base_input.py +++ b/src/pipecat/transports/base_input.py @@ -150,8 +150,9 @@ class BaseInputTransport(FrameProcessor): with warnings.catch_warnings(): warnings.simplefilter("always") warnings.warn( - "Parameter 'vad_analyzer' is deprecated, use `LLMUserAggregator`'s new " - "`vad_analyzer` parameter instead.", + "Parameter 'vad_analyzer' is deprecated. Use `LLMUserAggregator`'s " + "`vad_analyzer` parameter, or `VADProcessor` if no `LLMUserAggregator` " + "is needed.", DeprecationWarning, ) diff --git a/src/pipecat/transports/base_transport.py b/src/pipecat/transports/base_transport.py index 7879976ba..a78e1046c 100644 --- a/src/pipecat/transports/base_transport.py +++ b/src/pipecat/transports/base_transport.py @@ -115,8 +115,9 @@ class TransportParams(BaseModel): vad_analyzer: Voice Activity Detection analyzer instance. .. deprecated:: 0.0.101 - The `vad_analyzer` parameter is deprecated, use `LLMUSerAggregator`'s - new `vad_analyzer` parameter instead. + The `vad_analyzer` parameter is deprecated. Use `LLMUserAggregator`'s + `vad_analyzer` parameter, or `VADProcessor` if no `LLMUserAggregator` + is needed. turn_analyzer: Turn-taking analyzer instance for conversation management. From 9a0568e6fe1e3b5afb56304c3067139fc3d152b3 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 17:32:39 -0400 Subject: [PATCH 032/159] Add changelog for #4003 --- changelog/4003.changed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4003.changed.md diff --git a/changelog/4003.changed.md b/changelog/4003.changed.md new file mode 100644 index 000000000..26269ba68 --- /dev/null +++ b/changelog/4003.changed.md @@ -0,0 +1 @@ +- The `TransportParams.vad_analyzer` deprecation warning now also suggests `VADProcessor` as an alternative for pipelines that don't use an `LLMUserAggregator`. From 69e7677f4f477e39d8c7257443d664e1a0dee089 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 17:33:20 -0400 Subject: [PATCH 033/159] Remove changelog for #4003 --- changelog/4003.changed.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 changelog/4003.changed.md diff --git a/changelog/4003.changed.md b/changelog/4003.changed.md deleted file mode 100644 index 26269ba68..000000000 --- a/changelog/4003.changed.md +++ /dev/null @@ -1 +0,0 @@ -- The `TransportParams.vad_analyzer` deprecation warning now also suggests `VADProcessor` as an alternative for pipelines that don't use an `LLMUserAggregator`. From 11b14b7857a86b920aeb16dc1b83897695fde2df Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 11 Mar 2026 17:01:19 -0400 Subject: [PATCH 034/159] Add changelog for PR #4001 --- changelog/4001.changed.md | 1 + changelog/4001.deprecated.md | 1 + src/pipecat/services/simli/video.py | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelog/4001.changed.md create mode 100644 changelog/4001.deprecated.md diff --git a/changelog/4001.changed.md b/changelog/4001.changed.md new file mode 100644 index 000000000..1d0d4e4ef --- /dev/null +++ b/changelog/4001.changed.md @@ -0,0 +1 @@ +- `SimliVideoService` now extends `AIService` instead of `FrameProcessor`, aligning it with the HeyGen and Tavus video services. It supports `SimliVideoService.Settings(...)` for configuration and uses `start()`/`stop()`/`cancel()` lifecycle methods. Existing constructor usage (`api_key`, `face_id`, etc.) remains unchanged. diff --git a/changelog/4001.deprecated.md b/changelog/4001.deprecated.md new file mode 100644 index 000000000..749e7eea5 --- /dev/null +++ b/changelog/4001.deprecated.md @@ -0,0 +1 @@ +- `SimliVideoService.InputParams` is deprecated. Use the direct constructor parameters `max_session_length`, `max_idle_time`, and `enable_logging` instead. diff --git a/src/pipecat/services/simli/video.py b/src/pipecat/services/simli/video.py index 8345c0b86..f994ef0dc 100644 --- a/src/pipecat/services/simli/video.py +++ b/src/pipecat/services/simli/video.py @@ -61,7 +61,7 @@ class SimliVideoService(AIService): class InputParams(BaseModel): """Input parameters for Simli video configuration. - .. deprecated:: 0.0.105 + .. deprecated:: 0.0.106 Use ``SimliVideoService.Settings(...)`` instead. Parameters: @@ -116,7 +116,7 @@ class SimliVideoService(AIService): which reduces latency when using Trinity. params: Additional input parameters for session configuration. - .. deprecated:: 0.0.105 + .. deprecated:: 0.0.106 Use ``settings=SimliVideoService.Settings(...)`` instead. max_session_length: Absolute maximum session duration in seconds. From 65e4e365dcc5e451d6ffbc7d89d4686f399604d4 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 21:32:39 -0400 Subject: [PATCH 035/159] Add optional `service` field to `ServiceUpdateSettingsFrame` for targeting a specific service instance When `service` is set and doesn't match, the service forwards the frame instead of consuming it. This allows targeting a specific service when multiple services of the same type exist in the pipeline. --- src/pipecat/frames/frames.py | 5 +++++ src/pipecat/services/llm_service.py | 4 +++- src/pipecat/services/openai_realtime_beta/openai.py | 6 +++++- src/pipecat/services/stt_service.py | 4 +++- src/pipecat/services/tts_service.py | 4 +++- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 390eb93dd..f58dc957f 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -2154,10 +2154,15 @@ class ServiceUpdateSettingsFrame(ControlFrame, UninterruptibleFrame): delta: :class:`~pipecat.services.settings.ServiceSettings` delta-mode object describing the fields to change. + + service: Optional target service instance. When provided, only that + service will apply the settings; other services will forward the + frame unchanged. """ settings: Mapping[str, Any] = field(default_factory=dict) delta: Optional["ServiceSettings"] = None + service: Optional["FrameProcessor"] = None @dataclass diff --git a/src/pipecat/services/llm_service.py b/src/pipecat/services/llm_service.py index 7944f413a..a479fcfc6 100644 --- a/src/pipecat/services/llm_service.py +++ b/src/pipecat/services/llm_service.py @@ -403,7 +403,9 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService): elif isinstance(frame, LLMConfigureOutputFrame): self._skip_tts = frame.skip_tts elif isinstance(frame, LLMUpdateSettingsFrame): - if frame.delta is not None: + if frame.service is not None and frame.service is not self: + await self.push_frame(frame, direction) + elif frame.delta is not None: await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index 0a6315986..cacf8debd 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -386,7 +386,11 @@ class OpenAIRealtimeBetaLLMService(LLMService): # fields, not our Settings fields, so we construct SessionProperties # directly. The frame.delta path falls through to super, which calls # _update_settings → our override handles the rest. - if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: + if ( + isinstance(frame, LLMUpdateSettingsFrame) + and frame.delta is None + and (frame.service is None or frame.service is self) + ): self._session_properties = events.SessionProperties(**frame.settings) await self._send_session_update() await self.push_frame(frame, direction) diff --git a/src/pipecat/services/stt_service.py b/src/pipecat/services/stt_service.py index ebf007f6f..a16aa0eaa 100644 --- a/src/pipecat/services/stt_service.py +++ b/src/pipecat/services/stt_service.py @@ -357,7 +357,9 @@ class STTService(AIService): await self._handle_vad_user_stopped_speaking(frame) await self.push_frame(frame, direction) elif isinstance(frame, STTUpdateSettingsFrame): - if frame.delta is not None: + if frame.service is not None and frame.service is not self: + await self.push_frame(frame, direction) + elif frame.delta is not None: await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index da1bcaf87..a79156018 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -738,7 +738,9 @@ class TTSService(AIService): self._turn_context_id = saved_turn_context_id self._processing_text = processing_text elif isinstance(frame, TTSUpdateSettingsFrame): - if frame.delta is not None: + if frame.service is not None and frame.service is not self: + await self.push_frame(frame, direction) + elif frame.delta is not None: await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. From 36b57252b4d6807e38ddf443343cb579f2239d13 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 11 Mar 2026 21:47:51 -0400 Subject: [PATCH 036/159] Add changelog for PR #4004 --- changelog/4004.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4004.added.md diff --git a/changelog/4004.added.md b/changelog/4004.added.md new file mode 100644 index 000000000..f0fd28767 --- /dev/null +++ b/changelog/4004.added.md @@ -0,0 +1 @@ +- Added optional `service` field to `ServiceUpdateSettingsFrame` (and its subclasses `LLMUpdateSettingsFrame`, `TTSUpdateSettingsFrame`, `STTUpdateSettingsFrame`) to target a specific service instance. When `service` is set, only the matching service applies the settings; others forward the frame unchanged. This enables updating a single service when multiple services of the same type exist in the pipeline. From 7a7d600985d09f642d80e04fe4853426b7d93399 Mon Sep 17 00:00:00 2001 From: Varun Singh <382354+vr000m@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:50:10 -0700 Subject: [PATCH 037/159] Add sip_provider and room_geo parameters to configure() Add convenience parameters to configure() so callers don't need to manually construct DailyRoomProperties/DailyRoomSipParams for common SIP provider and geo configuration. --- src/pipecat/runner/daily.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pipecat/runner/daily.py b/src/pipecat/runner/daily.py index b2090b094..cfb754dfd 100644 --- a/src/pipecat/runner/daily.py +++ b/src/pipecat/runner/daily.py @@ -85,6 +85,8 @@ async def configure( sip_enable_video: Optional[bool] = False, sip_num_endpoints: Optional[int] = 1, sip_codecs: Optional[Dict[str, List[str]]] = None, + sip_provider: Optional[str] = None, + room_geo: Optional[str] = None, room_properties: Optional[DailyRoomProperties] = None, token_properties: Optional["DailyMeetingTokenProperties"] = None, ) -> DailyRoomConfig: @@ -105,6 +107,10 @@ async def configure( sip_num_endpoints: Number of allowed SIP endpoints. sip_codecs: Codecs to support for audio and video. If None, uses Daily defaults. Example: {"audio": ["OPUS"], "video": ["H264"]} + sip_provider: SIP provider name (e.g., "daily"). Only used when + sip_caller_phone is provided and room_properties is not. + room_geo: Daily room geographic region (e.g., "us-east-1"). Only used + when room_properties is not provided. room_properties: Optional DailyRoomProperties to use instead of building from individual parameters. When provided, this overrides room_exp_duration and SIP-related parameters. If not provided, properties are built from the @@ -154,6 +160,8 @@ async def configure( sip_enable_video is not False, sip_num_endpoints != 1, sip_codecs is not None, + sip_provider is not None, + room_geo is not None, ] ) if individual_params_provided: @@ -207,6 +215,9 @@ async def configure( eject_at_room_exp=True, ) + if room_geo: + room_properties.geo = room_geo + # Add SIP configuration if enabled if sip_enabled: sip_params = DailyRoomSipParams( @@ -215,6 +226,7 @@ async def configure( sip_mode="dial-in", num_endpoints=sip_num_endpoints, codecs=sip_codecs, + provider=sip_provider, ) room_properties.sip = sip_params room_properties.enable_dialout = True # Enable outbound calls if needed From bf66ae7e46db3eb384eddbdc17e8bda02a0f81ce Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 12 Mar 2026 09:22:31 -0400 Subject: [PATCH 038/159] Add changelog for #4005 --- changelog/4005.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4005.added.md diff --git a/changelog/4005.added.md b/changelog/4005.added.md new file mode 100644 index 000000000..0a023f104 --- /dev/null +++ b/changelog/4005.added.md @@ -0,0 +1 @@ +- Added `sip_provider` and `room_geo` parameters to `configure()` in the Daily runner. These convenience parameters let callers specify a SIP provider name and geographic region directly without manually constructing `DailyRoomProperties` and `DailyRoomSipParams`. From 84538b0ca89bb6770bc97246dd2dfbe83d83b631 Mon Sep 17 00:00:00 2001 From: Ali Alhoshaiyan Date: Thu, 15 Jan 2026 09:52:44 +0300 Subject: [PATCH 039/159] Reduce Call Tool Result Context Size by Allowing UTF-8 in JSON Serialization --- src/pipecat/services/anthropic/llm.py | 2 +- src/pipecat/services/aws/llm.py | 2 +- src/pipecat/services/openai/llm.py | 2 +- src/pipecat/services/openai_realtime_beta/openai.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index fcaed1dfe..2f375df82 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -1246,7 +1246,7 @@ class AnthropicAssistantContextAggregator(LLMAssistantContextAggregator): frame: Frame containing function call result. """ if frame.result: - result = json.dumps(frame.result) + result = json.dumps(frame.result, ensure_ascii=False) await self._update_function_call_result(frame.function_name, frame.tool_call_id, result) else: await self._update_function_call_result( diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index 59859abf7..92049dffb 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -691,7 +691,7 @@ class AWSBedrockAssistantContextAggregator(LLMAssistantContextAggregator): frame: The function call result frame to handle. """ if frame.result: - result = json.dumps(frame.result) + result = json.dumps(frame.result, ensure_ascii=False) await self._update_function_call_result(frame.function_name, frame.tool_call_id, result) else: await self._update_function_call_result( diff --git a/src/pipecat/services/openai/llm.py b/src/pipecat/services/openai/llm.py index abb3e7eec..553733922 100644 --- a/src/pipecat/services/openai/llm.py +++ b/src/pipecat/services/openai/llm.py @@ -255,7 +255,7 @@ class OpenAIAssistantContextAggregator(LLMAssistantContextAggregator): frame: Frame containing the function call result. """ if frame.result: - result = json.dumps(frame.result) + result = json.dumps(frame.result, ensure_ascii=False) await self._update_function_call_result(frame.function_name, frame.tool_call_id, result) else: await self._update_function_call_result( diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index 0a6315986..0d20039b1 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -441,7 +441,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): item = events.ConversationItem( type="function_call_output", call_id=frame.tool_call_id, - output=json.dumps(frame.result), + output=json.dumps(frame.result, ensure_ascii=False), ) await self.send_client_event(events.ConversationItemCreateEvent(item=item)) From 765fbeec630ef9ed2bf70bc8becdd80443aef4c8 Mon Sep 17 00:00:00 2001 From: Ali Alhoshaiyan Date: Thu, 15 Jan 2026 10:11:20 +0300 Subject: [PATCH 040/159] Add changelog --- changelog/3457.changed.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 changelog/3457.changed.md diff --git a/changelog/3457.changed.md b/changelog/3457.changed.md new file mode 100644 index 000000000..afd607d7a --- /dev/null +++ b/changelog/3457.changed.md @@ -0,0 +1,16 @@ +# Reduce Call Tool Result Context Size by Allowing UTF-8 in JSON Serialization + +This PR changes tool result serialization to prevent UTF-8 code points from being escaped during serialization. This drastically reduces the context size when returning a response that contains languages other than English. + +We have been running a monkey-patched version in production and it helped us improve the agent accuracy and control cost better. + +``` +>>> data = { "message": "أهلًا بالعالم" } +>>> json.dumps(data) +'{"message": "\\u0623\\u0647\\u0644\\u064b\\u0627 \\u0628\\u0627\\u0644\\u0639\\u0627\\u0644\\u0645"}' +>>> +>>> +>>> +>>> json.dumps(data, ensure_ascii=False) +'{"message": "أهلًا بالعالم"}' +``` From 1fe1f0f43929892c47379985ea854010ebd25e4a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 12 Mar 2026 10:34:29 -0400 Subject: [PATCH 041/159] Apply ensure_ascii=False to remaining LLM services and fix changelog format --- changelog/3457.changed.md | 17 +---------------- src/pipecat/services/aws/nova_sonic/llm.py | 4 +++- src/pipecat/services/google/llm.py | 4 +++- src/pipecat/services/grok/realtime/llm.py | 2 +- src/pipecat/services/openai/realtime/llm.py | 2 +- 5 files changed, 9 insertions(+), 20 deletions(-) diff --git a/changelog/3457.changed.md b/changelog/3457.changed.md index afd607d7a..d0d82ad2d 100644 --- a/changelog/3457.changed.md +++ b/changelog/3457.changed.md @@ -1,16 +1 @@ -# Reduce Call Tool Result Context Size by Allowing UTF-8 in JSON Serialization - -This PR changes tool result serialization to prevent UTF-8 code points from being escaped during serialization. This drastically reduces the context size when returning a response that contains languages other than English. - -We have been running a monkey-patched version in production and it helped us improve the agent accuracy and control cost better. - -``` ->>> data = { "message": "أهلًا بالعالم" } ->>> json.dumps(data) -'{"message": "\\u0623\\u0647\\u0644\\u064b\\u0627 \\u0628\\u0627\\u0644\\u0639\\u0627\\u0644\\u0645"}' ->>> ->>> ->>> ->>> json.dumps(data, ensure_ascii=False) -'{"message": "أهلًا بالعالم"}' -``` +- Changed tool result JSON serialization to use `ensure_ascii=False`, preserving UTF-8 characters instead of escaping them. This reduces context size and token usage for non-English languages. diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index b1f56dbbd..0947ba1d7 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -1044,7 +1044,9 @@ class AWSNovaSonicLLMService(LLMService): "toolResult": { "promptName": self._prompt_name, "contentName": content_name, - "content": json.dumps(result) if isinstance(result, dict) else result, + "content": json.dumps(result, ensure_ascii=False) + if isinstance(result, dict) + else result, } } } diff --git a/src/pipecat/services/google/llm.py b/src/pipecat/services/google/llm.py index 698b9f97f..26ad46311 100644 --- a/src/pipecat/services/google/llm.py +++ b/src/pipecat/services/google/llm.py @@ -200,7 +200,9 @@ class GoogleAssistantContextAggregator(OpenAIAssistantContextAggregator): if message.role == "user": for part in message.parts: if part.function_response and part.function_response.id == tool_call_id: - part.function_response.response = {"value": json.dumps(result)} + part.function_response.response = { + "value": json.dumps(result, ensure_ascii=False) + } @dataclass diff --git a/src/pipecat/services/grok/realtime/llm.py b/src/pipecat/services/grok/realtime/llm.py index bfa192aad..6e37d21d2 100644 --- a/src/pipecat/services/grok/realtime/llm.py +++ b/src/pipecat/services/grok/realtime/llm.py @@ -939,7 +939,7 @@ class GrokRealtimeLLMService(LLMService): item = events.ConversationItem( type="function_call_output", call_id=tool_call_id, - output=json.dumps(result), + output=json.dumps(result, ensure_ascii=False), ) await self.send_client_event(events.ConversationItemCreateEvent(item=item)) diff --git a/src/pipecat/services/openai/realtime/llm.py b/src/pipecat/services/openai/realtime/llm.py index faaa19884..bd31369ae 100644 --- a/src/pipecat/services/openai/realtime/llm.py +++ b/src/pipecat/services/openai/realtime/llm.py @@ -1128,7 +1128,7 @@ class OpenAIRealtimeLLMService(LLMService): item = events.ConversationItem( type="function_call_output", call_id=tool_call_id, - output=json.dumps(result), + output=json.dumps(result, ensure_ascii=False), ) await self.send_client_event(events.ConversationItemCreateEvent(item=item)) From 27b686db8c6369ead5b28fc9b3f572351caba890 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 12 Mar 2026 11:04:49 -0400 Subject: [PATCH 042/159] Don't bother honoring the new `LLMUpdateSettingsFrame.service` field in the deprecated `OpenAIRealtimeBetaLLMService` --- src/pipecat/services/openai_realtime_beta/openai.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index cacf8debd..0a6315986 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -386,11 +386,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): # fields, not our Settings fields, so we construct SessionProperties # directly. The frame.delta path falls through to super, which calls # _update_settings → our override handles the rest. - if ( - isinstance(frame, LLMUpdateSettingsFrame) - and frame.delta is None - and (frame.service is None or frame.service is self) - ): + if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: self._session_properties = events.SessionProperties(**frame.settings) await self._send_session_update() await self.push_frame(frame, direction) From 73a56f5d81b84bbc5442c60cffe6ffb500d4ff80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 12 Mar 2026 00:15:01 -0700 Subject: [PATCH 043/159] Fix ParallelPipeline flush ordering and buffered frame handling Flush buffered frames before pushing the synchronization frame so downstream processors see the buffered frames first. Switch to a while-loop with pop(0) so frames added to the buffer during flush are also drained. --- src/pipecat/pipeline/parallel_pipeline.py | 27 ++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/pipecat/pipeline/parallel_pipeline.py b/src/pipecat/pipeline/parallel_pipeline.py index 88ea04638..1e2e03a8f 100644 --- a/src/pipecat/pipeline/parallel_pipeline.py +++ b/src/pipecat/pipeline/parallel_pipeline.py @@ -143,6 +143,19 @@ class ParallelPipeline(BasePipeline): await super().process_frame(frame, direction) # Parallel pipeline synchronized frames. + # + # - StartFrame: If a fast branch completes first, processors in + # other branches that haven't received StartFrame yet could + # receive other frames before it, causing errors. + # + # - EndFrame: If EndFrame escapes from a fast branch, downstream + # processors (e.g. output transport) begin shutting down while + # other branches still have frames to flush, causing lost output. + # + # - CancelFrame: PipelineTask waits for CancelFrame to reach the + # pipeline sink. If it escapes from a fast branch while slower + # branches are still running, the task considers cancellation + # complete prematurely. if isinstance(frame, (StartFrame, EndFrame, CancelFrame)): self._frame_counter[frame.id] = len(self._pipelines) self._synchronizing = True @@ -179,8 +192,13 @@ class ParallelPipeline(BasePipeline): # Only push the frame when all pipelines have processed it. if frame_counter == 0: self._synchronizing = False - await self._parallel_push_frame(frame, direction) - await self._flush_buffered_frames() + # StartFrame should always go before any other frame. + if isinstance(frame, StartFrame): + await self._parallel_push_frame(frame, direction) + await self._flush_buffered_frames() + else: + await self._flush_buffered_frames() + await self._parallel_push_frame(frame, direction) await self.resume_processing_system_frames() await self.resume_processing_frames() else: @@ -188,7 +206,6 @@ class ParallelPipeline(BasePipeline): async def _flush_buffered_frames(self): """Flush frames that were buffered during lifecycle frame synchronization.""" - frames = self._buffered_frames - self._buffered_frames = [] - for frame, direction in frames: + while len(self._buffered_frames) > 0: + frame, direction = self._buffered_frames.pop(0) await self.push_frame(frame, direction) From 1a66bdef8ed36722169752041051a62d844f85a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 12 Mar 2026 00:15:03 -0700 Subject: [PATCH 044/159] Fix TTS stop ordering to drain audio contexts before canceling Wait for _audio_context_task to finish draining the contexts queue before canceling _stop_frame_task, ensuring all pending audio contexts are processed during shutdown. --- src/pipecat/services/tts_service.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index da1bcaf87..7cfb9d738 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -536,15 +536,15 @@ class TTSService(AIService): frame: The end frame. """ await super().stop(frame) - if self._stop_frame_task: - await self.cancel_task(self._stop_frame_task) - self._stop_frame_task = None if self._audio_context_task: # Indicate no more audio contexts are available; this will end the # task cleanly after all contexts have been processed. await self._contexts_queue.put(None) await self._audio_context_task self._audio_context_task = None + if self._stop_frame_task: + await self.cancel_task(self._stop_frame_task) + self._stop_frame_task = None async def cancel(self, frame: CancelFrame): """Cancel the TTS service. From a461b2b9e6bb7306596d15b921e3753de2ffc717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 12 Mar 2026 00:16:26 -0700 Subject: [PATCH 045/159] Add changelog entries for PR #4007 --- changelog/4007.fixed.2.md | 1 + changelog/4007.fixed.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog/4007.fixed.2.md create mode 100644 changelog/4007.fixed.md diff --git a/changelog/4007.fixed.2.md b/changelog/4007.fixed.2.md new file mode 100644 index 000000000..0c50b83e9 --- /dev/null +++ b/changelog/4007.fixed.2.md @@ -0,0 +1 @@ +- Fixed `TTSService` potentially canceling in-flight audio during shutdown. The stop sequence now waits for all queued audio contexts to finish processing before canceling the stop frame task. diff --git a/changelog/4007.fixed.md b/changelog/4007.fixed.md new file mode 100644 index 000000000..8a90aea43 --- /dev/null +++ b/changelog/4007.fixed.md @@ -0,0 +1 @@ +- Fixed `ParallelPipeline` dropping or misordering frames during lifecycle synchronization. Buffered frames are now flushed in the correct order relative to synchronization frames (`StartFrame` goes first, `EndFrame`/`CancelFrame` go after), and frames added to the buffer during flush are also drained. From 2eccd28cf0aaa6362ff117dd3b6de7539b809d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 11 Mar 2026 23:16:15 -0700 Subject: [PATCH 046/159] handle EndTaskFrame, StopTaskFrame and CancelTaskFrame downstream EndTaskFrame and StopTaskFrame are now ControlFrames instead of SystemFrames, so they flow through the pipeline and queue behind pending work. This prevents races where EndFrame could overtake in-flight frames (e.g. function call responses). CancelTaskFrame and InterruptionTaskFrame remain SystemFrames (via new TaskSystemFrame base): since they need immediate propagation. The sink now catches EndTaskFrame, StopTaskFrame and CancelTaskFrame downstream and re-queues it upstream to the task, ensuring the full pipeline drains before shutdown begins. --- src/pipecat/frames/frames.py | 48 +++++++++++++++++++++++------------- src/pipecat/pipeline/task.py | 20 ++++++++++++--- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index f58dc957f..1ef7bdb4a 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -1742,7 +1742,7 @@ class ServiceSwitcherRequestMetadataFrame(ControlFrame): @dataclass -class TaskFrame(SystemFrame): +class TaskFrame(ControlFrame): """Base frame for task frames. This is a base class for frames that are meant to be sent and handled @@ -1756,7 +1756,21 @@ class TaskFrame(SystemFrame): @dataclass -class EndTaskFrame(TaskFrame): +class TaskSystemFrame(SystemFrame): + """Base frame for task system frames. + + This is a base class for frames that are meant to be sent and handled + upstream by the pipeline task. This might result in a corresponding frame + sent downstream (e.g. `InterruptionTaskFrame` / `InterruptionFrame` or + `EndTaskFrame` / `EndFrame`). + + """ + + pass + + +@dataclass +class EndTaskFrame(TaskFrame, UninterruptibleFrame): """Frame to request graceful pipeline task closure. This is used to notify the pipeline task that the pipeline should be @@ -1774,7 +1788,20 @@ class EndTaskFrame(TaskFrame): @dataclass -class CancelTaskFrame(TaskFrame): +class StopTaskFrame(TaskFrame, UninterruptibleFrame): + """Frame to request pipeline task stop while keeping processors running. + + This is used to notify the pipeline task that it should be stopped as + soon as possible (flushing all the queued frames) but that the pipeline + processors should be kept in a running state. This frame should be pushed + upstream. + """ + + pass + + +@dataclass +class CancelTaskFrame(TaskSystemFrame): """Frame to request immediate pipeline task cancellation. This is used to notify the pipeline task that the pipeline should be @@ -1792,20 +1819,7 @@ class CancelTaskFrame(TaskFrame): @dataclass -class StopTaskFrame(TaskFrame): - """Frame to request pipeline task stop while keeping processors running. - - This is used to notify the pipeline task that it should be stopped as - soon as possible (flushing all the queued frames) but that the pipeline - processors should be kept in a running state. This frame should be pushed - upstream. - """ - - pass - - -@dataclass -class InterruptionTaskFrame(TaskFrame): +class InterruptionTaskFrame(TaskSystemFrame): """Frame indicating the pipeline should be interrupted. This frame should be pushed upstream to indicate the pipeline should be diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index e795961a1..56df719d5 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -876,22 +876,22 @@ class PipelineTask(BasePipelineTask): if isinstance(frame, EndTaskFrame): # Tell the task we should end nicely. - logger.debug(f"{self}: received end task frame {frame}") + logger.debug(f"{self}: received end task frame upstream {frame}") await self.queue_frame(EndFrame(reason=frame.reason)) elif isinstance(frame, CancelTaskFrame): # Tell the task we should end right away. - logger.debug(f"{self}: received cancel task frame {frame}") + logger.debug(f"{self}: received cancel task frame upstream {frame}") await self.queue_frame(CancelFrame(reason=frame.reason)) elif isinstance(frame, StopTaskFrame): # Tell the task we should stop nicely. - logger.debug(f"{self}: received stop task frame {frame}") + logger.debug(f"{self}: received stop task frame upstream {frame}") await self.queue_frame(StopFrame()) elif isinstance(frame, InterruptionTaskFrame): # Tell the task we should interrupt the pipeline. Note that we are # bypassing the push queue and directly queue into the # pipeline. This is in case the push task is blocked waiting for a # pipeline-ending frame to finish traversing the pipeline. - logger.debug(f"{self}: received interruption task frame {frame}") + logger.debug(f"{self}: received interruption task frame upstream {frame}") await self._pipeline.queue_frame(InterruptionFrame()) elif isinstance(frame, ErrorFrame): await self._call_event_handler("on_pipeline_error", frame) @@ -934,6 +934,18 @@ class PipelineTask(BasePipelineTask): self._pipeline_end_event.set() elif isinstance(frame, HeartbeatFrame): await self._heartbeat_queue.put(frame) + elif isinstance(frame, EndTaskFrame): + logger.debug(f"{self}: received end task frame downstream {frame}") + await self.queue_frame(EndTaskFrame(reason=frame.reason), FrameDirection.UPSTREAM) + elif isinstance(frame, StopTaskFrame): + logger.debug(f"{self}: received stop task frame downstream {frame}") + await self.queue_frame(StopTaskFrame(), FrameDirection.UPSTREAM) + elif isinstance(frame, CancelTaskFrame): + logger.debug(f"{self}: received cancel task frame downstream {frame}") + await self.queue_frame(CancelTaskFrame(reason=frame.reason), FrameDirection.UPSTREAM) + elif isinstance(frame, InterruptionTaskFrame): + logger.debug(f"{self}: received interruption task frame downstream {frame}") + await self.queue_frame(InterruptionTaskFrame(), FrameDirection.UPSTREAM) async def _heartbeat_push_handler(self): """Push heartbeat frames at regular intervals.""" From f6f08d19a86acb72b1c984810a9b2038b6174f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 11 Mar 2026 23:16:45 -0700 Subject: [PATCH 047/159] Add changelog for #4006 --- changelog/4006.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4006.fixed.md diff --git a/changelog/4006.fixed.md b/changelog/4006.fixed.md new file mode 100644 index 000000000..ba12beea7 --- /dev/null +++ b/changelog/4006.fixed.md @@ -0,0 +1 @@ +- Fixed a race condition where `EndTaskFrame` could cause the pipeline to shut down before in-flight frames (e.g. LLM function call responses) finished processing. `EndTaskFrame` and `StopTaskFrame` now flow through the pipeline as `ControlFrame`s, ensuring all pending work is flushed before shutdown begins. `CancelTaskFrame` and `InterruptionTaskFrame` remain immediate (`SystemFrame`). From 38a4d4ff239c6a629c0eeb2677f50530eac4341d Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 12 Mar 2026 14:46:49 -0400 Subject: [PATCH 048/159] Update quickstart to use cloud builds --- examples/quickstart/README.md | 28 +++++----------------------- examples/quickstart/pcc-deploy.toml | 9 ++------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/examples/quickstart/README.md b/examples/quickstart/README.md index 91a3fd888..6374d622c 100644 --- a/examples/quickstart/README.md +++ b/examples/quickstart/README.md @@ -81,23 +81,12 @@ Transform your local bot into a production-ready service. Pipecat Cloud handles > 💡 Tip: You can run the `pipecat` CLI using the `pc` alias. -3. Set up Docker for building your bot image: - - - **Install [Docker](https://www.docker.com/)** on your system - - **Create a [Docker Hub](https://hub.docker.com/) account** - - **Login to Docker Hub:** - - ```bash - docker login - ``` - ### Configure your deployment -The `pcc-deploy.toml` file tells Pipecat Cloud how to run your bot. **Update the image field** with your Docker Hub username by editing `pcc-deploy.toml`. +The `pcc-deploy.toml` file tells Pipecat Cloud how to run your bot. ```ini agent_name = "quickstart" -image = "YOUR_DOCKERHUB_USERNAME/quickstart:0.1" # 👈 Update this line secret_set = "quickstart-secrets" [scaling] @@ -107,12 +96,9 @@ secret_set = "quickstart-secrets" **Understanding the TOML file settings:** - `agent_name`: Your bot's name in Pipecat Cloud -- `image`: The Docker image to deploy (format: `username/image:version`) - `secret_set`: Where your API keys are stored securely - `min_agents`: Number of bot instances to keep ready (1 = instant start) -> 💡 Tip: [Set up `image_credentials`](https://docs.pipecat.ai/deployment/pipecat-cloud/fundamentals/secrets#image-pull-secrets) in your TOML file for authenticated image pulls - ### Log in to Pipecat Cloud To start using the CLI, authenticate to Pipecat Cloud: @@ -121,7 +107,7 @@ To start using the CLI, authenticate to Pipecat Cloud: pipecat cloud auth login ``` -You'll be presented with a link that you can click to authenticate your client. +You'll be presented with a link and six-digit code that you can click to authenticate your client. ### Configure secrets @@ -133,13 +119,7 @@ pipecat cloud secrets set quickstart-secrets --file .env This creates a secret set called `quickstart-secrets` (matching your TOML file) and uploads all your API keys from `.env`. -### Build and deploy - -Build your Docker image and push to Docker Hub: - -```bash -pipecat cloud docker build-push -``` +### Deploy Deploy to Pipecat Cloud: @@ -147,6 +127,8 @@ Deploy to Pipecat Cloud: pipecat cloud deploy ``` +This pushes your project files to Pipecat Cloud where a docker image is built and deployed into production. + ### Connect to your agent 1. Open your [Pipecat Cloud dashboard](https://pipecat.daily.co/) diff --git a/examples/quickstart/pcc-deploy.toml b/examples/quickstart/pcc-deploy.toml index f08e8a624..5562f117e 100644 --- a/examples/quickstart/pcc-deploy.toml +++ b/examples/quickstart/pcc-deploy.toml @@ -1,11 +1,6 @@ -agent_name = "quickstart-test" -image = "your_username/quickstart-test:latest" -secret_set = "quickstart-test-secrets" +agent_name = "quickstart" +secret_set = "quickstart-secrets" agent_profile = "agent-1x" -# RECOMMENDED: Set an image pull secret: -# https://docs.pipecat.ai/deployment/pipecat-cloud/fundamentals/secrets#image-pull-secrets -image_credentials = "dockerhub-access" - [scaling] min_agents = 1 From 0373f85b85c92d31f252971f1c10c899412a6750 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 12 Mar 2026 14:56:30 -0400 Subject: [PATCH 049/159] Add PerplexityLLMAdapter to enforce Perplexity's message ordering constraints Perplexity's API is stricter than OpenAI about conversation history: - Requires strict alternation between user/tool and assistant messages - Disallows system messages except as the initial message - Requires the last message to be user or tool The new adapter transforms messages before sending to satisfy all three constraints: merging consecutive initial system messages, converting non-initial system to user, merging consecutive same-role messages, and removing trailing assistant messages. Also adds dual-system-instruction warnings to Cerebras, Fireworks, Mistral, Perplexity, and SambaNova services (matching the existing BaseOpenAILLMService pattern), and updates the warning text in BaseOpenAILLMService to be more descriptive. --- .../adapters/services/perplexity_adapter.py | 169 +++++++++++++++ src/pipecat/services/cerebras/llm.py | 4 + src/pipecat/services/fireworks/llm.py | 4 + src/pipecat/services/mistral/llm.py | 4 + src/pipecat/services/openai/base_llm.py | 6 +- src/pipecat/services/perplexity/llm.py | 9 + src/pipecat/services/sambanova/llm.py | 4 + tests/test_get_llm_invocation_params.py | 197 ++++++++++++++++++ 8 files changed, 393 insertions(+), 4 deletions(-) create mode 100644 src/pipecat/adapters/services/perplexity_adapter.py diff --git a/src/pipecat/adapters/services/perplexity_adapter.py b/src/pipecat/adapters/services/perplexity_adapter.py new file mode 100644 index 000000000..0c5de9a63 --- /dev/null +++ b/src/pipecat/adapters/services/perplexity_adapter.py @@ -0,0 +1,169 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Perplexity LLM adapter for Pipecat. + +Perplexity's API uses an OpenAI-compatible interface but enforces stricter +constraints on conversation history structure: + +1. **Strict role alternation** — Messages must alternate between "user"/"tool" + and "assistant" roles. Consecutive messages with the same role (e.g. two + "user" messages in a row) are rejected with: + ``"messages must be an alternating sequence of user/tool and assistant messages"`` + +2. **No non-initial system messages** — "system" messages are only allowed as + the very first message. A system message anywhere else causes: + ``"only the initial message can have the system role"`` + +3. **Last message must be user/tool** — The final message in the conversation + must have role "user" or "tool". A trailing "assistant" message causes: + ``"the last message must have the user or tool role"`` + +This adapter transforms the message list to satisfy all three constraints before +the messages are sent to Perplexity's API. +""" + +import copy +from typing import List + +from openai.types.chat import ChatCompletionMessageParam + +from pipecat.adapters.services.open_ai_adapter import OpenAILLMAdapter, OpenAILLMInvocationParams +from pipecat.processors.aggregators.llm_context import LLMContext + + +class PerplexityLLMAdapter(OpenAILLMAdapter): + """Adapter that transforms messages to satisfy Perplexity's API constraints. + + Perplexity's API is stricter than standard OpenAI about message structure. + This adapter extends ``OpenAILLMAdapter`` and applies message transformations + to ensure compliance with Perplexity's three constraints (role alternation, + no non-initial system messages, last message must be user/tool). + + The transformations are applied in ``get_llm_invocation_params`` after the + parent adapter extracts messages from the LLM context, and before + ``build_chat_completion_params`` prepends ``system_instruction``. + """ + + def get_llm_invocation_params(self, context: LLMContext) -> OpenAILLMInvocationParams: + """Get OpenAI-compatible invocation parameters with Perplexity message fixes applied. + + Args: + context: The LLM context containing messages, tools, etc. + + Returns: + Dictionary of parameters for Perplexity's ChatCompletion API, with + messages transformed to satisfy Perplexity's constraints. + """ + params = super().get_llm_invocation_params(context) + params["messages"] = self._transform_messages(list(params["messages"])) + return params + + def _transform_messages( + self, messages: List[ChatCompletionMessageParam] + ) -> List[ChatCompletionMessageParam]: + """Transform messages to satisfy Perplexity's API constraints. + + Applies four transformation steps in order: + + 1. **Merge consecutive initial system messages** — If the conversation + starts with multiple system messages, merge them into a single system + message using list-of-dicts content format. This addresses + Perplexity's constraint that only the initial message can be system. + + 2. **Convert non-initial system messages to user** — Any system message + after the initial position is converted to role "user", since + Perplexity rejects non-initial system messages. + + 3. **Merge consecutive same-role messages** — After the above + conversions, adjacent messages with the same role are merged using + list-of-dicts content format. This ensures strict role alternation + (e.g. a converted system→user message adjacent to an existing user + message gets merged). + + 4. **Remove trailing assistant messages** — If the last message is + "assistant", remove it. OpenAI appears to silently ignore trailing + assistant messages server-side, so removing them preserves equivalent + behavior while satisfying Perplexity's "last message must be + user/tool" constraint. If the only remaining message is "system" + (possible when the context contains just a single system message), + convert it to "user" since Perplexity requires the last message to + be "user" or "tool". + + Args: + messages: List of message dicts with "role" and "content" keys. + + Returns: + Transformed list of message dicts satisfying Perplexity's constraints. + """ + if not messages: + return messages + + messages = copy.deepcopy(messages) + + # Step 1: Merge consecutive system messages at the start into one. + # Perplexity only allows a single initial system message, so if there + # are multiple consecutive system messages at the start, we merge them. + if messages[0].get("role") == "system": + system_end = 1 + while system_end < len(messages) and messages[system_end].get("role") == "system": + system_end += 1 + + if system_end > 1: + # Merge all initial system messages into a single message using + # list-of-dicts content format (same approach as Anthropic adapter). + merged_content = [] + for msg in messages[:system_end]: + content = msg.get("content", "") + if isinstance(content, str): + merged_content.append({"type": "text", "text": content}) + elif isinstance(content, list): + merged_content.extend(content) + messages = [{"role": "system", "content": merged_content}] + messages[system_end:] + + # Step 2: Convert non-initial system messages to "user". + # Perplexity only allows system role for the very first message. + for i in range(1, len(messages)): + if messages[i].get("role") == "system": + messages[i]["role"] = "user" + + # Step 3: Merge consecutive same-role messages. + # After system→user conversions above, we may have adjacent same-role + # messages that violate Perplexity's strict alternation requirement. + i = 0 + while i < len(messages) - 1: + current = messages[i] + next_msg = messages[i + 1] + if current["role"] == next_msg["role"]: + # Convert string content to list-of-dicts format for merging + if isinstance(current.get("content"), str): + current["content"] = [{"type": "text", "text": current["content"]}] + if isinstance(next_msg.get("content"), str): + next_msg["content"] = [{"type": "text", "text": next_msg["content"]}] + # Merge content from next message into current + if isinstance(current.get("content"), list) and isinstance( + next_msg.get("content"), list + ): + current["content"].extend(next_msg["content"]) + messages.pop(i + 1) + else: + i += 1 + + # Step 4: Handle trailing messages. + # Perplexity requires the last message to be "user" or "tool". + if messages: + # Remove trailing assistant messages. OpenAI appears to silently + # ignore trailing assistant messages server-side, so removing them + # preserves equivalent behavior. + while messages and messages[-1].get("role") == "assistant": + messages.pop() + + # If the only remaining message is "system" (single system message + # in the context), convert it to "user". + if messages and len(messages) == 1 and messages[0].get("role") == "system": + messages[0]["role"] = "user" + + return messages diff --git a/src/pipecat/services/cerebras/llm.py b/src/pipecat/services/cerebras/llm.py index 7c31a6857..dfb62baf8 100644 --- a/src/pipecat/services/cerebras/llm.py +++ b/src/pipecat/services/cerebras/llm.py @@ -117,6 +117,10 @@ class CerebrasLLMService(OpenAILLMService): # Prepend system instruction if set if self._settings.system_instruction: messages = params.get("messages", []) + if messages and messages[0].get("role") == "system": + logger.warning( + f"{self}: Both system_instruction and an initial system message in context are set. This may be unintended." + ) params["messages"] = [ {"role": "system", "content": self._settings.system_instruction} ] + messages diff --git a/src/pipecat/services/fireworks/llm.py b/src/pipecat/services/fireworks/llm.py index bf141fac1..5efa60793 100644 --- a/src/pipecat/services/fireworks/llm.py +++ b/src/pipecat/services/fireworks/llm.py @@ -118,6 +118,10 @@ class FireworksLLMService(OpenAILLMService): # Prepend system instruction if set if self._settings.system_instruction: messages = params.get("messages", []) + if messages and messages[0].get("role") == "system": + logger.warning( + f"{self}: Both system_instruction and an initial system message in context are set. This may be unintended." + ) params["messages"] = [ {"role": "system", "content": self._settings.system_instruction} ] + messages diff --git a/src/pipecat/services/mistral/llm.py b/src/pipecat/services/mistral/llm.py index 3ee1b2623..063dac3aa 100644 --- a/src/pipecat/services/mistral/llm.py +++ b/src/pipecat/services/mistral/llm.py @@ -236,6 +236,10 @@ class MistralLLMService(OpenAILLMService): # Prepend system instruction if set if self._settings.system_instruction: messages = params.get("messages", []) + if messages and messages[0].get("role") == "system": + logger.warning( + f"{self}: Both system_instruction and an initial system message in context are set. This may be unintended." + ) params["messages"] = [ {"role": "system", "content": self._settings.system_instruction} ] + messages diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 41b26bd20..eb8ce3cc6 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -332,8 +332,7 @@ class BaseOpenAILLMService(LLMService): messages = params.get("messages", []) if messages and messages[0].get("role") == "system": logger.warning( - f"{self}: Both system_instruction and a system message in context are set." - " Using system_instruction." + f"{self}: Both system_instruction and an initial system message in context are set. This may be unintended." ) params["messages"] = [ {"role": "system", "content": self._settings.system_instruction} @@ -381,8 +380,7 @@ class BaseOpenAILLMService(LLMService): messages = params.get("messages", []) if messages and messages[0].get("role") == "system": logger.warning( - f"{self}: Both system_instruction and a system message in context are set." - " Using system_instruction." + f"{self}: Both system_instruction and an initial system message in context are set. This may be unintended." ) params["messages"] = [{"role": "system", "content": system_instruction}] + messages diff --git a/src/pipecat/services/perplexity/llm.py b/src/pipecat/services/perplexity/llm.py index 6c2ceba35..9ea323c5d 100644 --- a/src/pipecat/services/perplexity/llm.py +++ b/src/pipecat/services/perplexity/llm.py @@ -14,7 +14,10 @@ reporting patterns while maintaining compatibility with the Pipecat framework. from dataclasses import dataclass from typing import Optional +from loguru import logger + from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams +from pipecat.adapters.services.perplexity_adapter import PerplexityLLMAdapter from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext @@ -37,6 +40,8 @@ class PerplexityLLMService(OpenAILLMService): in token usage reporting between Perplexity (incremental) and OpenAI (final summary). """ + adapter_class = PerplexityLLMAdapter + Settings = PerplexityLLMSettings _settings: Settings @@ -119,6 +124,10 @@ class PerplexityLLMService(OpenAILLMService): # Prepend system instruction if set if self._settings.system_instruction: messages = params.get("messages", []) + if messages and messages[0].get("role") == "system": + logger.warning( + f"{self}: Both system_instruction and an initial system message in context are set. This may be unintended." + ) params["messages"] = [ {"role": "system", "content": self._settings.system_instruction} ] + messages diff --git a/src/pipecat/services/sambanova/llm.py b/src/pipecat/services/sambanova/llm.py index 3c7d76737..710a22db2 100644 --- a/src/pipecat/services/sambanova/llm.py +++ b/src/pipecat/services/sambanova/llm.py @@ -134,6 +134,10 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore # Prepend system instruction if set if self._settings.system_instruction: messages = params.get("messages", []) + if messages and messages[0].get("role") == "system": + logger.warning( + f"{self}: Both system_instruction and an initial system message in context are set. This may be unintended." + ) params["messages"] = [ {"role": "system", "content": self._settings.system_instruction} ] + messages diff --git a/tests/test_get_llm_invocation_params.py b/tests/test_get_llm_invocation_params.py index c93275b67..f534d7109 100644 --- a/tests/test_get_llm_invocation_params.py +++ b/tests/test_get_llm_invocation_params.py @@ -48,6 +48,7 @@ from pipecat.adapters.services.anthropic_adapter import AnthropicLLMAdapter from pipecat.adapters.services.bedrock_adapter import AWSBedrockLLMAdapter from pipecat.adapters.services.gemini_adapter import GeminiLLMAdapter from pipecat.adapters.services.open_ai_adapter import OpenAILLMAdapter +from pipecat.adapters.services.perplexity_adapter import PerplexityLLMAdapter from pipecat.processors.aggregators.llm_context import ( LLMContext, LLMStandardMessage, @@ -992,5 +993,201 @@ class TestAWSBedrockGetLLMInvocationParams(unittest.TestCase): self.assertEqual(len(params["messages"]), 0) +class TestPerplexityGetLLMInvocationParams(unittest.TestCase): + def setUp(self) -> None: + """Sets up a common adapter instance for all tests.""" + self.adapter = PerplexityLLMAdapter() + + def test_standard_messages_pass_through(self): + """Test that a valid [user, assistant, user] sequence passes through unchanged.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there!"}, + {"role": "user", "content": "How are you?"}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 3) + self.assertEqual(params["messages"][0]["role"], "user") + self.assertEqual(params["messages"][0]["content"], "Hello") + self.assertEqual(params["messages"][1]["role"], "assistant") + self.assertEqual(params["messages"][1]["content"], "Hi there!") + self.assertEqual(params["messages"][2]["role"], "user") + self.assertEqual(params["messages"][2]["content"], "How are you?") + + def test_initial_system_message_preserved(self): + """Test that a valid [system, user, assistant, user] sequence passes through unchanged.""" + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi!"}, + {"role": "user", "content": "Bye"}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 4) + self.assertEqual(params["messages"][0]["role"], "system") + self.assertEqual(params["messages"][0]["content"], "You are a helpful assistant.") + self.assertEqual(params["messages"][1]["role"], "user") + self.assertEqual(params["messages"][2]["role"], "assistant") + self.assertEqual(params["messages"][3]["role"], "user") + + def test_consecutive_same_role_messages_merged(self): + """Test that consecutive user messages are merged into list-of-dicts content.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "First message"}, + {"role": "user", "content": "Second message"}, + {"role": "assistant", "content": "Response"}, + {"role": "user", "content": "Third message"}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 3) + + # First message should be merged users + merged = params["messages"][0] + self.assertEqual(merged["role"], "user") + self.assertIsInstance(merged["content"], list) + self.assertEqual(len(merged["content"]), 2) + self.assertEqual(merged["content"][0]["type"], "text") + self.assertEqual(merged["content"][0]["text"], "First message") + self.assertEqual(merged["content"][1]["type"], "text") + self.assertEqual(merged["content"][1]["text"], "Second message") + + self.assertEqual(params["messages"][1]["role"], "assistant") + self.assertEqual(params["messages"][2]["role"], "user") + + def test_non_initial_system_converted_to_user(self): + """Test that non-initial system messages are converted to user and merged with adjacent user.""" + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are helpful."}, + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi!"}, + {"role": "system", "content": "Be concise."}, + {"role": "user", "content": "Tell me about Python."}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + # system(initial), user, assistant, merged(system→user + user) + self.assertEqual(len(params["messages"]), 4) + self.assertEqual(params["messages"][0]["role"], "system") + self.assertEqual(params["messages"][1]["role"], "user") + self.assertEqual(params["messages"][2]["role"], "assistant") + + # The converted system→user and the following user should be merged + merged = params["messages"][3] + self.assertEqual(merged["role"], "user") + self.assertIsInstance(merged["content"], list) + self.assertEqual(len(merged["content"]), 2) + self.assertEqual(merged["content"][0]["text"], "Be concise.") + self.assertEqual(merged["content"][1]["text"], "Tell me about Python.") + + def test_multiple_system_messages_at_start_merged(self): + """Test that multiple consecutive system messages at start are merged into one.""" + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "system", "content": "Always be polite."}, + {"role": "user", "content": "Hello"}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 2) + + # First message should be merged system + system_msg = params["messages"][0] + self.assertEqual(system_msg["role"], "system") + self.assertIsInstance(system_msg["content"], list) + self.assertEqual(len(system_msg["content"]), 2) + self.assertEqual(system_msg["content"][0]["text"], "You are a helpful assistant.") + self.assertEqual(system_msg["content"][1]["text"], "Always be polite.") + + self.assertEqual(params["messages"][1]["role"], "user") + self.assertEqual(params["messages"][1]["content"], "Hello") + + def test_trailing_assistant_removed(self): + """Test that a trailing assistant message is removed.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there!"}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 1) + self.assertEqual(params["messages"][0]["role"], "user") + self.assertEqual(params["messages"][0]["content"], "Hello") + + def test_only_system_message_converted_to_user(self): + """Test that a single system message is converted to user role.""" + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are a helpful assistant."}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 1) + self.assertEqual(params["messages"][0]["role"], "user") + self.assertEqual(params["messages"][0]["content"], "You are a helpful assistant.") + + def test_consecutive_assistants_merged_then_trailing_removed(self): + """Test that consecutive assistant messages are merged, then trailing assistant is removed.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "First response"}, + {"role": "assistant", "content": "Second response"}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + # After merging assistants we get [user, assistant(merged)], then trailing + # assistant is removed, leaving just [user] + self.assertEqual(len(params["messages"]), 1) + self.assertEqual(params["messages"][0]["role"], "user") + self.assertEqual(params["messages"][0]["content"], "Hello") + + def test_tool_messages_preserved(self): + """Test that tool messages pass through without modification.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "What's the weather?"}, + { + "role": "assistant", + "content": "Let me check.", + "tool_calls": [{"id": "1", "function": {"name": "get_weather", "arguments": "{}"}}], + }, + {"role": "tool", "content": "Sunny, 72F", "tool_call_id": "1"}, + {"role": "user", "content": "Thanks!"}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 4) + self.assertEqual(params["messages"][0]["role"], "user") + self.assertEqual(params["messages"][1]["role"], "assistant") + self.assertEqual(params["messages"][2]["role"], "tool") + self.assertEqual(params["messages"][2]["content"], "Sunny, 72F") + self.assertEqual(params["messages"][3]["role"], "user") + + def test_empty_messages(self): + """Test that empty messages list returns empty.""" + context = LLMContext(messages=[]) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(params["messages"], []) + + if __name__ == "__main__": unittest.main() From e4bf6281c6fb136cbed12dfdf7ca6f671caae718 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 12 Mar 2026 14:56:37 -0400 Subject: [PATCH 050/159] Add changelog for #4009 --- changelog/4009.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4009.added.md diff --git a/changelog/4009.added.md b/changelog/4009.added.md new file mode 100644 index 000000000..9ebbec7dd --- /dev/null +++ b/changelog/4009.added.md @@ -0,0 +1 @@ +- Added `PerplexityLLMAdapter` that automatically transforms conversation messages to satisfy Perplexity's stricter API constraints (strict role alternation, no non-initial system messages, last message must be user/tool). Previously, certain conversation histories could cause Perplexity API errors that didn't occur with OpenAI (`PerplexityLLMService` subclasses `OpenAILLMService` since Perplexity uses an OpenAI-compatible API). From 7f98cc9921fe7fe44f1e64ffb71524e592b16ceb Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 12 Mar 2026 15:14:56 -0400 Subject: [PATCH 051/159] Remove initial system message merging, handle trailing system messages Perplexity allows multiple initial system messages, so don't merge them. Instead, skip system-system pairs during the consecutive same-role merge step. Broaden the trailing message fix to convert any trailing system message to user (not just a lone system message), so contexts with only system messages don't fail. --- .../adapters/services/perplexity_adapter.py | 85 ++++++++----------- tests/test_get_llm_invocation_params.py | 39 ++++++--- 2 files changed, 59 insertions(+), 65 deletions(-) diff --git a/src/pipecat/adapters/services/perplexity_adapter.py b/src/pipecat/adapters/services/perplexity_adapter.py index 0c5de9a63..754716a19 100644 --- a/src/pipecat/adapters/services/perplexity_adapter.py +++ b/src/pipecat/adapters/services/perplexity_adapter.py @@ -14,8 +14,9 @@ constraints on conversation history structure: "user" messages in a row) are rejected with: ``"messages must be an alternating sequence of user/tool and assistant messages"`` -2. **No non-initial system messages** — "system" messages are only allowed as - the very first message. A system message anywhere else causes: +2. **No non-initial system messages** — "system" messages are only allowed at + the start of the conversation. A system message after a non-system message + causes: ``"only the initial message can have the system role"`` 3. **Last message must be user/tool** — The final message in the conversation @@ -38,9 +39,9 @@ from pipecat.processors.aggregators.llm_context import LLMContext class PerplexityLLMAdapter(OpenAILLMAdapter): """Adapter that transforms messages to satisfy Perplexity's API constraints. - Perplexity's API is stricter than standard OpenAI about message structure. - This adapter extends ``OpenAILLMAdapter`` and applies message transformations - to ensure compliance with Perplexity's three constraints (role alternation, + Perplexity's API is stricter than OpenAI about message structure. This + adapter extends ``OpenAILLMAdapter`` and applies message transformations + to ensure compliance with Perplexity's constraints (role alternation, no non-initial system messages, last message must be user/tool). The transformations are applied in ``get_llm_invocation_params`` after the @@ -67,31 +68,24 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): ) -> List[ChatCompletionMessageParam]: """Transform messages to satisfy Perplexity's API constraints. - Applies four transformation steps in order: + Applies three transformation steps in order: - 1. **Merge consecutive initial system messages** — If the conversation - starts with multiple system messages, merge them into a single system - message using list-of-dicts content format. This addresses - Perplexity's constraint that only the initial message can be system. + 1. **Convert non-initial system messages to user** — Any system message + after the initial system message block is converted to role "user", + since Perplexity rejects system messages after a non-system message. - 2. **Convert non-initial system messages to user** — Any system message - after the initial position is converted to role "user", since - Perplexity rejects non-initial system messages. - - 3. **Merge consecutive same-role messages** — After the above + 2. **Merge consecutive same-role messages** — After the above conversions, adjacent messages with the same role are merged using list-of-dicts content format. This ensures strict role alternation (e.g. a converted system→user message adjacent to an existing user message gets merged). - 4. **Remove trailing assistant messages** — If the last message is + 3. **Ensure last message is user/tool** — If the last message is "assistant", remove it. OpenAI appears to silently ignore trailing assistant messages server-side, so removing them preserves equivalent behavior while satisfying Perplexity's "last message must be - user/tool" constraint. If the only remaining message is "system" - (possible when the context contains just a single system message), - convert it to "user" since Perplexity requires the last message to - be "user" or "tool". + user/tool" constraint. If the last message is "system" (e.g. the + context only contains system messages), convert it to "user". Args: messages: List of message dicts with "role" and "content" keys. @@ -104,40 +98,29 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): messages = copy.deepcopy(messages) - # Step 1: Merge consecutive system messages at the start into one. - # Perplexity only allows a single initial system message, so if there - # are multiple consecutive system messages at the start, we merge them. - if messages[0].get("role") == "system": - system_end = 1 - while system_end < len(messages) and messages[system_end].get("role") == "system": - system_end += 1 - - if system_end > 1: - # Merge all initial system messages into a single message using - # list-of-dicts content format (same approach as Anthropic adapter). - merged_content = [] - for msg in messages[:system_end]: - content = msg.get("content", "") - if isinstance(content, str): - merged_content.append({"type": "text", "text": content}) - elif isinstance(content, list): - merged_content.extend(content) - messages = [{"role": "system", "content": merged_content}] + messages[system_end:] - - # Step 2: Convert non-initial system messages to "user". - # Perplexity only allows system role for the very first message. - for i in range(1, len(messages)): + # Step 1: Convert non-initial system messages to "user". + # Perplexity allows system messages at the start, but rejects them + # after any non-system message. + in_initial_system_block = True + for i in range(len(messages)): if messages[i].get("role") == "system": - messages[i]["role"] = "user" + if not in_initial_system_block: + messages[i]["role"] = "user" + else: + in_initial_system_block = False - # Step 3: Merge consecutive same-role messages. + # Step 2: Merge consecutive same-role messages. # After system→user conversions above, we may have adjacent same-role # messages that violate Perplexity's strict alternation requirement. + # Skip consecutive system messages at the start — Perplexity allows those. i = 0 while i < len(messages) - 1: current = messages[i] next_msg = messages[i + 1] - if current["role"] == next_msg["role"]: + if current["role"] == next_msg["role"] == "system": + # Perplexity allows multiple initial system messages, don't merge + i += 1 + elif current["role"] == next_msg["role"]: # Convert string content to list-of-dicts format for merging if isinstance(current.get("content"), str): current["content"] = [{"type": "text", "text": current["content"]}] @@ -152,7 +135,7 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): else: i += 1 - # Step 4: Handle trailing messages. + # Step 3: Handle trailing messages. # Perplexity requires the last message to be "user" or "tool". if messages: # Remove trailing assistant messages. OpenAI appears to silently @@ -161,9 +144,9 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): while messages and messages[-1].get("role") == "assistant": messages.pop() - # If the only remaining message is "system" (single system message - # in the context), convert it to "user". - if messages and len(messages) == 1 and messages[0].get("role") == "system": - messages[0]["role"] = "user" + # If the last message is "system" (e.g. the context only contains + # system messages), convert it to "user". + if messages and messages[-1].get("role") == "system": + messages[-1]["role"] = "user" return messages diff --git a/tests/test_get_llm_invocation_params.py b/tests/test_get_llm_invocation_params.py index f534d7109..6e37873d4 100644 --- a/tests/test_get_llm_invocation_params.py +++ b/tests/test_get_llm_invocation_params.py @@ -1090,8 +1090,8 @@ class TestPerplexityGetLLMInvocationParams(unittest.TestCase): self.assertEqual(merged["content"][0]["text"], "Be concise.") self.assertEqual(merged["content"][1]["text"], "Tell me about Python.") - def test_multiple_system_messages_at_start_merged(self): - """Test that multiple consecutive system messages at start are merged into one.""" + def test_multiple_system_messages_at_start_preserved(self): + """Test that multiple consecutive system messages at start pass through unchanged.""" messages: list[LLMStandardMessage] = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "system", "content": "Always be polite."}, @@ -1101,18 +1101,13 @@ class TestPerplexityGetLLMInvocationParams(unittest.TestCase): context = LLMContext(messages=messages) params = self.adapter.get_llm_invocation_params(context) - self.assertEqual(len(params["messages"]), 2) - - # First message should be merged system - system_msg = params["messages"][0] - self.assertEqual(system_msg["role"], "system") - self.assertIsInstance(system_msg["content"], list) - self.assertEqual(len(system_msg["content"]), 2) - self.assertEqual(system_msg["content"][0]["text"], "You are a helpful assistant.") - self.assertEqual(system_msg["content"][1]["text"], "Always be polite.") - - self.assertEqual(params["messages"][1]["role"], "user") - self.assertEqual(params["messages"][1]["content"], "Hello") + self.assertEqual(len(params["messages"]), 3) + self.assertEqual(params["messages"][0]["role"], "system") + self.assertEqual(params["messages"][0]["content"], "You are a helpful assistant.") + self.assertEqual(params["messages"][1]["role"], "system") + self.assertEqual(params["messages"][1]["content"], "Always be polite.") + self.assertEqual(params["messages"][2]["role"], "user") + self.assertEqual(params["messages"][2]["content"], "Hello") def test_trailing_assistant_removed(self): """Test that a trailing assistant message is removed.""" @@ -1141,6 +1136,22 @@ class TestPerplexityGetLLMInvocationParams(unittest.TestCase): self.assertEqual(params["messages"][0]["role"], "user") self.assertEqual(params["messages"][0]["content"], "You are a helpful assistant.") + def test_only_system_messages_last_converted_to_user(self): + """Test that when only system messages exist, the last one is converted to user.""" + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "system", "content": "Always be polite."}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["messages"]), 2) + self.assertEqual(params["messages"][0]["role"], "system") + self.assertEqual(params["messages"][0]["content"], "You are a helpful assistant.") + self.assertEqual(params["messages"][1]["role"], "user") + self.assertEqual(params["messages"][1]["content"], "Always be polite.") + def test_consecutive_assistants_merged_then_trailing_removed(self): """Test that consecutive assistant messages are merged, then trailing assistant is removed.""" messages: list[LLMStandardMessage] = [ From e69f5a76e1e596bb4ebfac7f93bcb0151dab04f0 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 12 Mar 2026 15:24:17 -0400 Subject: [PATCH 052/159] Add test for trailing assistant+system ordering, improve docstring Add test exercising the step 3 ordering where stripping a trailing assistant exposes a system message that then gets converted to user. Move the reasoning about when a trailing system message can occur into the docstring. --- .../adapters/services/perplexity_adapter.py | 10 ++++++---- tests/test_get_llm_invocation_params.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/pipecat/adapters/services/perplexity_adapter.py b/src/pipecat/adapters/services/perplexity_adapter.py index 754716a19..b55696893 100644 --- a/src/pipecat/adapters/services/perplexity_adapter.py +++ b/src/pipecat/adapters/services/perplexity_adapter.py @@ -84,8 +84,11 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): "assistant", remove it. OpenAI appears to silently ignore trailing assistant messages server-side, so removing them preserves equivalent behavior while satisfying Perplexity's "last message must be - user/tool" constraint. If the last message is "system" (e.g. the - context only contains system messages), convert it to "user". + user/tool" constraint. If the last message is "system", convert it + to "user". A trailing system message can only occur when the context + consists entirely of system messages (possibly followed by assistant + messages that were just removed), because step 1 converts any system + message that appears after a non-system message to "user". Args: messages: List of message dicts with "role" and "content" keys. @@ -144,8 +147,7 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): while messages and messages[-1].get("role") == "assistant": messages.pop() - # If the last message is "system" (e.g. the context only contains - # system messages), convert it to "user". + # If the last message is "system", convert it to "user". if messages and messages[-1].get("role") == "system": messages[-1]["role"] = "user" diff --git a/tests/test_get_llm_invocation_params.py b/tests/test_get_llm_invocation_params.py index 6e37873d4..08710d77d 100644 --- a/tests/test_get_llm_invocation_params.py +++ b/tests/test_get_llm_invocation_params.py @@ -1152,6 +1152,25 @@ class TestPerplexityGetLLMInvocationParams(unittest.TestCase): self.assertEqual(params["messages"][1]["role"], "user") self.assertEqual(params["messages"][1]["content"], "Always be polite.") + def test_trailing_assistant_removed_then_system_converted(self): + """Test that trailing assistant is removed, exposing a system message that becomes user. + + This exercises the ordering of step 3: strip trailing assistants first, + then convert a trailing system to user. + """ + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are helpful."}, + {"role": "assistant", "content": "Sure thing."}, + ] + + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + # Trailing assistant removed → [system] → system converted to user → [user] + self.assertEqual(len(params["messages"]), 1) + self.assertEqual(params["messages"][0]["role"], "user") + self.assertEqual(params["messages"][0]["content"], "You are helpful.") + def test_consecutive_assistants_merged_then_trailing_removed(self): """Test that consecutive assistant messages are merged, then trailing assistant is removed.""" messages: list[LLMStandardMessage] = [ From 99f28120b70b5a295ad2f7dd659023764e8075f3 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 12 Mar 2026 16:04:46 -0400 Subject: [PATCH 053/159] =?UTF-8?q?Remove=20trailing=20system=E2=86=92user?= =?UTF-8?q?=20conversion=20for=20cross-call=20stability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perplexity appears to have statefulness within a conversation, so converting a system message to "user" in one call and then back to "system" in the next (after more messages are appended) causes API errors. Remove the trailing system→user conversion entirely — if the context only has system messages, the API call will fail but the mistake will be caught right away. --- .../adapters/services/perplexity_adapter.py | 32 +++++++-------- tests/test_get_llm_invocation_params.py | 41 ++++++++----------- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/src/pipecat/adapters/services/perplexity_adapter.py b/src/pipecat/adapters/services/perplexity_adapter.py index b55696893..a8fbe3c18 100644 --- a/src/pipecat/adapters/services/perplexity_adapter.py +++ b/src/pipecat/adapters/services/perplexity_adapter.py @@ -80,15 +80,19 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): (e.g. a converted system→user message adjacent to an existing user message gets merged). - 3. **Ensure last message is user/tool** — If the last message is + 3. **Remove trailing assistant messages** — If the last message is "assistant", remove it. OpenAI appears to silently ignore trailing assistant messages server-side, so removing them preserves equivalent behavior while satisfying Perplexity's "last message must be - user/tool" constraint. If the last message is "system", convert it - to "user". A trailing system message can only occur when the context - consists entirely of system messages (possibly followed by assistant - messages that were just removed), because step 1 converts any system - message that appears after a non-system message to "user". + user/tool" constraint. + + Note: we intentionally do *not* convert a trailing system message to + "user". That would make the transformation unstable across calls — + Perplexity appears to have statefulness/caching within a conversation, + so a message that was sent as "user" in one call but becomes "system" + in the next (once more messages are appended) causes errors. If the + context consists entirely of system messages, the Perplexity API call + will fail, but that mistake will be caught right away. Args: messages: List of message dicts with "role" and "content" keys. @@ -138,17 +142,11 @@ class PerplexityLLMAdapter(OpenAILLMAdapter): else: i += 1 - # Step 3: Handle trailing messages. + # Step 3: Remove trailing assistant messages. # Perplexity requires the last message to be "user" or "tool". - if messages: - # Remove trailing assistant messages. OpenAI appears to silently - # ignore trailing assistant messages server-side, so removing them - # preserves equivalent behavior. - while messages and messages[-1].get("role") == "assistant": - messages.pop() - - # If the last message is "system", convert it to "user". - if messages and messages[-1].get("role") == "system": - messages[-1]["role"] = "user" + # OpenAI appears to silently ignore trailing assistant messages + # server-side, so removing them preserves equivalent behavior. + while messages and messages[-1].get("role") == "assistant": + messages.pop() return messages diff --git a/tests/test_get_llm_invocation_params.py b/tests/test_get_llm_invocation_params.py index 08710d77d..9cfeb8933 100644 --- a/tests/test_get_llm_invocation_params.py +++ b/tests/test_get_llm_invocation_params.py @@ -1123,8 +1123,14 @@ class TestPerplexityGetLLMInvocationParams(unittest.TestCase): self.assertEqual(params["messages"][0]["role"], "user") self.assertEqual(params["messages"][0]["content"], "Hello") - def test_only_system_message_converted_to_user(self): - """Test that a single system message is converted to user role.""" + def test_only_system_messages_preserved(self): + """Test that system-only contexts are left unchanged (no system→user conversion). + + We intentionally do not convert trailing system messages to "user" + because that would make the transformation unstable across calls — + Perplexity has statefulness within a conversation, so a message that + was "user" in one call but becomes "system" in the next causes errors. + """ messages: list[LLMStandardMessage] = [ {"role": "system", "content": "You are a helpful assistant."}, ] @@ -1133,30 +1139,15 @@ class TestPerplexityGetLLMInvocationParams(unittest.TestCase): params = self.adapter.get_llm_invocation_params(context) self.assertEqual(len(params["messages"]), 1) - self.assertEqual(params["messages"][0]["role"], "user") - self.assertEqual(params["messages"][0]["content"], "You are a helpful assistant.") - - def test_only_system_messages_last_converted_to_user(self): - """Test that when only system messages exist, the last one is converted to user.""" - messages: list[LLMStandardMessage] = [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "system", "content": "Always be polite."}, - ] - - context = LLMContext(messages=messages) - params = self.adapter.get_llm_invocation_params(context) - - self.assertEqual(len(params["messages"]), 2) self.assertEqual(params["messages"][0]["role"], "system") - self.assertEqual(params["messages"][0]["content"], "You are a helpful assistant.") - self.assertEqual(params["messages"][1]["role"], "user") - self.assertEqual(params["messages"][1]["content"], "Always be polite.") - def test_trailing_assistant_removed_then_system_converted(self): - """Test that trailing assistant is removed, exposing a system message that becomes user. + def test_system_exposed_after_trailing_assistant_removed(self): + """Test that a system message exposed by trailing assistant removal stays system. - This exercises the ordering of step 3: strip trailing assistants first, - then convert a trailing system to user. + It's important that initial system messages are never converted to + "user", because Perplexity has statefulness within a conversation — if + a message was sent as "system" in one call and then becomes "user" in a + later call (after more messages are appended), the API rejects it. """ messages: list[LLMStandardMessage] = [ {"role": "system", "content": "You are helpful."}, @@ -1166,9 +1157,9 @@ class TestPerplexityGetLLMInvocationParams(unittest.TestCase): context = LLMContext(messages=messages) params = self.adapter.get_llm_invocation_params(context) - # Trailing assistant removed → [system] → system converted to user → [user] + # Trailing assistant removed → [system], system stays as-is self.assertEqual(len(params["messages"]), 1) - self.assertEqual(params["messages"][0]["role"], "user") + self.assertEqual(params["messages"][0]["role"], "system") self.assertEqual(params["messages"][0]["content"], "You are helpful.") def test_consecutive_assistants_merged_then_trailing_removed(self): From de38ca626d6b11d6d11c90e88145fc8d23133da0 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 12 Mar 2026 17:19:32 -0400 Subject: [PATCH 054/159] Deprecate LocalSmartTurnAnalyzerV2 and LocalCoreMLSmartTurnAnalyzer Both analyzers are superseded by LocalSmartTurnAnalyzerV3. Added deprecation warnings and docstring notices following the existing pattern from LocalSmartTurnAnalyzer. --- .../turn/smart_turn/local_coreml_smart_turn.py | 14 ++++++++++++++ .../audio/turn/smart_turn/local_smart_turn.py | 2 +- .../audio/turn/smart_turn/local_smart_turn_v2.py | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/pipecat/audio/turn/smart_turn/local_coreml_smart_turn.py b/src/pipecat/audio/turn/smart_turn/local_coreml_smart_turn.py index be4744c27..18310c386 100644 --- a/src/pipecat/audio/turn/smart_turn/local_coreml_smart_turn.py +++ b/src/pipecat/audio/turn/smart_turn/local_coreml_smart_turn.py @@ -10,6 +10,7 @@ This module provides a smart turn analyzer that uses CoreML models for local end-of-turn detection without requiring network connectivity. """ +import warnings from typing import Any, Dict import numpy as np @@ -35,6 +36,10 @@ class LocalCoreMLSmartTurnAnalyzer(BaseSmartTurn): Provides end-of-turn detection using locally-stored CoreML models, enabling offline operation without network dependencies. Optimized for Apple Silicon and other CoreML-compatible hardware. + + .. deprecated:: 0.0.106 + LocalCoreMLSmartTurnAnalyzer is deprecated and will be removed in a future version. + Use LocalSmartTurnAnalyzerV3 instead. """ def __init__(self, *, smart_turn_model_path: str, **kwargs): @@ -50,6 +55,15 @@ class LocalCoreMLSmartTurnAnalyzer(BaseSmartTurn): """ super().__init__(**kwargs) + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "LocalCoreMLSmartTurnAnalyzer is deprecated and will be removed in a future " + "version. Use LocalSmartTurnAnalyzerV3 instead.", + DeprecationWarning, + stacklevel=2, + ) + if not smart_turn_model_path: logger.error("smart_turn_model_path is not set.") raise Exception("smart_turn_model_path must be provided.") diff --git a/src/pipecat/audio/turn/smart_turn/local_smart_turn.py b/src/pipecat/audio/turn/smart_turn/local_smart_turn.py index e98c345a1..791b63af1 100644 --- a/src/pipecat/audio/turn/smart_turn/local_smart_turn.py +++ b/src/pipecat/audio/turn/smart_turn/local_smart_turn.py @@ -36,7 +36,7 @@ class LocalSmartTurnAnalyzer(BaseSmartTurn): enabling offline operation without network dependencies. Uses Wav2Vec2-BERT architecture for audio sequence classification. - .. deprecated:: 0.98.0 + .. deprecated:: 0.0.98 LocalSmartTurnAnalyzer is deprecated and will be removed in a future version. Use LocalSmartTurnAnalyzerV3 instead. """ diff --git a/src/pipecat/audio/turn/smart_turn/local_smart_turn_v2.py b/src/pipecat/audio/turn/smart_turn/local_smart_turn_v2.py index 0b2f21cba..8d584ecd2 100644 --- a/src/pipecat/audio/turn/smart_turn/local_smart_turn_v2.py +++ b/src/pipecat/audio/turn/smart_turn/local_smart_turn_v2.py @@ -10,6 +10,7 @@ This module provides a smart turn analyzer that uses PyTorch models for local end-of-turn detection without requiring network connectivity. """ +import warnings from typing import Any, Dict import numpy as np @@ -41,6 +42,10 @@ class LocalSmartTurnAnalyzerV2(BaseSmartTurn): Provides end-of-turn detection using locally-stored PyTorch models, enabling offline operation without network dependencies. Uses Wav2Vec2 architecture for audio sequence classification. + + .. deprecated:: 0.0.106 + LocalSmartTurnAnalyzerV2 is deprecated and will be removed in a future version. + Use LocalSmartTurnAnalyzerV3 instead. """ def __init__(self, *, smart_turn_model_path: str, **kwargs): @@ -53,6 +58,15 @@ class LocalSmartTurnAnalyzerV2(BaseSmartTurn): """ super().__init__(**kwargs) + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "LocalSmartTurnAnalyzerV2 is deprecated and will be removed in a future version. " + "Use LocalSmartTurnAnalyzerV3 instead.", + DeprecationWarning, + stacklevel=2, + ) + if not smart_turn_model_path: # Define the path to the pretrained model on Hugging Face smart_turn_model_path = "pipecat-ai/smart-turn-v2" From ed0b8dadb51dfe6a95feff693d5f8073451aa118 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 12 Mar 2026 17:22:13 -0400 Subject: [PATCH 055/159] Add changelog for #4012 --- changelog/4012.deprecated.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4012.deprecated.md diff --git a/changelog/4012.deprecated.md b/changelog/4012.deprecated.md new file mode 100644 index 000000000..4310b8aba --- /dev/null +++ b/changelog/4012.deprecated.md @@ -0,0 +1 @@ +- Deprecated `LocalSmartTurnAnalyzerV2` and `LocalCoreMLSmartTurnAnalyzer`. Use `LocalSmartTurnAnalyzerV3` instead. Instantiating these analyzers will now emit a `DeprecationWarning`. From 1064482ade2340549fe1d90030f783472c8dff4f Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 10:20:51 -0400 Subject: [PATCH 056/159] Update pipecat-ai-small-webrtc-prebuilt to 2.4.0 --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 48226b14f..5a4b45646 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,7 +106,7 @@ remote-smart-turn = [] resembleai = [ "pipecat-ai[websockets-base]" ] rime = [ "pipecat-ai[websockets-base]" ] riva = [ "pipecat-ai[nvidia]" ] -runner = [ "python-dotenv>=1.0.0,<2.0.0", "uvicorn>=0.32.0,<1.0.0", "fastapi>=0.115.6,<1", "pipecat-ai-small-webrtc-prebuilt>=2.3.0"] +runner = [ "python-dotenv>=1.0.0,<2.0.0", "uvicorn>=0.32.0,<1.0.0", "fastapi>=0.115.6,<1", "pipecat-ai-small-webrtc-prebuilt>=2.4.0"] sagemaker = ["aws_sdk_sagemaker_runtime_http2; python_version>='3.12'"] sambanova = [] sarvam = [ "sarvamai==0.1.26", "pipecat-ai[websockets-base]" ] diff --git a/uv.lock b/uv.lock index b68216261..079d7df2a 100644 --- a/uv.lock +++ b/uv.lock @@ -4855,7 +4855,7 @@ requires-dist = [ { name = "pipecat-ai", extras = ["websockets-base"], marker = "extra == 'ultravox'" }, { name = "pipecat-ai", extras = ["websockets-base"], marker = "extra == 'websocket'" }, { name = "pipecat-ai-krisp", marker = "extra == 'krisp'", specifier = "~=0.4.0" }, - { name = "pipecat-ai-small-webrtc-prebuilt", marker = "extra == 'runner'", specifier = ">=2.3.0" }, + { name = "pipecat-ai-small-webrtc-prebuilt", marker = "extra == 'runner'", specifier = ">=2.4.0" }, { name = "piper-tts", marker = "extra == 'piper'", specifier = ">=1.3.0,<2" }, { name = "protobuf", specifier = "~=5.29.6" }, { name = "pvkoala", marker = "extra == 'koala'", specifier = "~=2.0.3" }, @@ -4927,14 +4927,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/1d/37/0f1d11d1dc33234a3 [[package]] name = "pipecat-ai-small-webrtc-prebuilt" -version = "2.3.0" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastapi", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/5f/b0f73bbc6997c22655f0495ce21a4cb176e192df1b5407f66fad8101c697/pipecat_ai_small_webrtc_prebuilt-2.3.0.tar.gz", hash = "sha256:10dc31db9978d68001ae941066fe460c533412a8984df71e5416d4ebeb9c0371", size = 469001, upload-time = "2026-02-25T17:18:43.316Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/02/1e6e90f084ebb1fc954f37661c4614219e4c9fec3d305c8abe5141707b0c/pipecat_ai_small_webrtc_prebuilt-2.4.0.tar.gz", hash = "sha256:c5eddca4e061afb7c5f98cf52ccb85511978a8c834447f6c6d662029e02950c4", size = 472449, upload-time = "2026-03-13T14:17:08.164Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/bc/6193b639a53f4bac1c0fe29b1f8e0d49085c60e457b02a01e725eb7c093f/pipecat_ai_small_webrtc_prebuilt-2.3.0-py3-none-any.whl", hash = "sha256:b3ddaff8bbd56746fe3c58a2d721d3ccc94d17a33c16d78dcbce73d7526c1a05", size = 468881, upload-time = "2026-02-25T17:18:41.869Z" }, + { url = "https://files.pythonhosted.org/packages/25/77/8f6f67142a153943fff31530d51dcf7a2374c39dfa9aba6ef163bf0c622f/pipecat_ai_small_webrtc_prebuilt-2.4.0-py3-none-any.whl", hash = "sha256:9e9a3aa24231b1bf4101a6a2b42c4164a186c0c3d3e49bd51f77280eaa402d12", size = 472792, upload-time = "2026-03-13T14:17:06.556Z" }, ] [[package]] From 7365ebfdf937832986395191e6d5f3a62585221c Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 10:22:58 -0400 Subject: [PATCH 057/159] Add changelog for #4023 --- changelog/4023.changed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4023.changed.md diff --git a/changelog/4023.changed.md b/changelog/4023.changed.md new file mode 100644 index 000000000..7756f20b8 --- /dev/null +++ b/changelog/4023.changed.md @@ -0,0 +1 @@ +- Update `pipecat-ai-small-webrtc-prebuilt` to `2.4.0`. From 8467058e48e8206961598b1e5f991cb20cd19d33 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 10:56:33 -0400 Subject: [PATCH 058/159] Fix Language enum conversion at init time in base TTS/STT services When a Language enum (e.g. Language.ES) is passed via settings=Service.Settings(language=Language.ES), it gets stored as-is without conversion to the service-specific code. The base _update_settings() handles this for runtime updates, but at init time apply_update() copies the raw enum. This causes API errors because services send the unconverted enum value. Add language conversion in TTSService.__init__ and STTService.__init__ after super().__init__(), using the subclass language_to_service_language() via normal method resolution. --- src/pipecat/services/stt_service.py | 9 +++++++++ src/pipecat/services/tts_service.py | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/pipecat/services/stt_service.py b/src/pipecat/services/stt_service.py index a16aa0eaa..c442c41eb 100644 --- a/src/pipecat/services/stt_service.py +++ b/src/pipecat/services/stt_service.py @@ -120,6 +120,15 @@ class STTService(AIService): or STTSettings(), **kwargs, ) + + # Convert Language enum to service-specific format at init time. + # Runtime updates are handled by _update_settings(), but init-time + # settings bypass that path and need explicit conversion. + if isinstance(self._settings.language, Language): + converted = self.language_to_service_language(self._settings.language) + if converted is not None: + self._settings.language = converted + self._audio_passthrough = audio_passthrough self._init_sample_rate = sample_rate self._sample_rate = 0 diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index 44fb98826..e2a9190ab 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -245,6 +245,14 @@ class TTSService(AIService): **kwargs, ) + # Convert Language enum to service-specific format at init time. + # Runtime updates are handled by _update_settings(), but init-time + # settings bypass that path and need explicit conversion. + if isinstance(self._settings.language, Language): + converted = self.language_to_service_language(self._settings.language) + if converted is not None: + self._settings.language = converted + # Resolve text_aggregation_mode from the new param or deprecated aggregate_sentences if aggregate_sentences is not None: import warnings From 9f2f73b6b4a14af8ab077a464d8d9bab6f211eec Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 10:57:04 -0400 Subject: [PATCH 059/159] Remove redundant per-service language conversion from subclasses Now that the base TTSService and STTService handle Language enum conversion at init time, subclasses no longer need to convert in their own __init__ methods. Remove conversion calls from hardcoded defaults, params paths, and deprecated direct arg paths across 22 service files. Services just pass raw Language enums and let the base class convert via language_to_service_language() polymorphic dispatch. --- src/pipecat/services/asyncai/tts.py | 8 ++------ src/pipecat/services/aws/stt.py | 4 ++-- src/pipecat/services/aws/tts.py | 6 +----- src/pipecat/services/azure/stt.py | 4 ++-- src/pipecat/services/azure/tts.py | 12 ++---------- src/pipecat/services/camb/tts.py | 4 +--- src/pipecat/services/cartesia/tts.py | 8 ++++---- src/pipecat/services/elevenlabs/stt.py | 4 ++-- src/pipecat/services/elevenlabs/tts.py | 4 ++-- src/pipecat/services/fal/stt.py | 4 ++-- src/pipecat/services/google/tts.py | 6 +++--- src/pipecat/services/groq/stt.py | 4 ++-- src/pipecat/services/kokoro/tts.py | 4 ++-- src/pipecat/services/lmnt/tts.py | 2 +- src/pipecat/services/neuphonic/tts.py | 8 ++++---- src/pipecat/services/nvidia/stt.py | 6 ++---- src/pipecat/services/openai/stt.py | 2 +- src/pipecat/services/rime/tts.py | 12 +++--------- src/pipecat/services/sambanova/stt.py | 4 ++-- src/pipecat/services/sarvam/tts.py | 16 ++-------------- src/pipecat/services/whisper/base_stt.py | 2 +- src/pipecat/services/xtts/tts.py | 2 +- 22 files changed, 44 insertions(+), 82 deletions(-) diff --git a/src/pipecat/services/asyncai/tts.py b/src/pipecat/services/asyncai/tts.py index 42c0a09a4..d2ac74445 100644 --- a/src/pipecat/services/asyncai/tts.py +++ b/src/pipecat/services/asyncai/tts.py @@ -171,9 +171,7 @@ class AsyncAITTSService(WebsocketTTSService): if params is not None: self._warn_init_param_moved_to_settings("params") if not settings: - default_settings.language = ( - self.language_to_service_language(params.language) if params.language else None - ) + default_settings.language = params.language # 4. Apply settings delta (canonical API, always wins) if settings is not None: @@ -565,9 +563,7 @@ class AsyncAIHttpTTSService(TTSService): if params is not None: self._warn_init_param_moved_to_settings("params") if not settings: - default_settings.language = ( - self.language_to_service_language(params.language) if params.language else None - ) + default_settings.language = params.language # 4. Apply settings delta (canonical API, always wins) if settings is not None: diff --git a/src/pipecat/services/aws/stt.py b/src/pipecat/services/aws/stt.py index eed1a321d..ace05090d 100644 --- a/src/pipecat/services/aws/stt.py +++ b/src/pipecat/services/aws/stt.py @@ -100,13 +100,13 @@ class AWSTranscribeSTTService(WebsocketSTTService): # 1. Initialize default_settings with hardcoded defaults default_settings = self.Settings( model=None, - language=self.language_to_service_language(Language.EN), + language=Language.EN, ) # 2. Apply direct init arg overrides (deprecated) if language is not None: self._warn_init_param_moved_to_settings("language", "language") - default_settings.language = self.language_to_service_language(language) + default_settings.language = language # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/aws/tts.py b/src/pipecat/services/aws/tts.py index 9648c45c8..32266886e 100644 --- a/src/pipecat/services/aws/tts.py +++ b/src/pipecat/services/aws/tts.py @@ -230,11 +230,7 @@ class AWSPollyTTSService(TTSService): self._warn_init_param_moved_to_settings("params") if not settings: default_settings.engine = params.engine - default_settings.language = ( - self.language_to_service_language(params.language) - if params.language - else "en-US" - ) + default_settings.language = params.language if params.language else "en-US" default_settings.pitch = params.pitch default_settings.rate = params.rate default_settings.volume = params.volume diff --git a/src/pipecat/services/azure/stt.py b/src/pipecat/services/azure/stt.py index 857b166cb..57306e06a 100644 --- a/src/pipecat/services/azure/stt.py +++ b/src/pipecat/services/azure/stt.py @@ -106,13 +106,13 @@ class AzureSTTService(STTService): # 1. Initialize default_settings with hardcoded defaults default_settings = self.Settings( model=None, - language=language_to_azure_language(Language.EN_US), + language=Language.EN_US, ) # 2. Apply direct init arg overrides (deprecated) if language is not None and language != Language.EN_US: self._warn_init_param_moved_to_settings("language", "language") - default_settings.language = language_to_azure_language(language) + default_settings.language = language # 3. (No step 3, as there's no params object to apply) diff --git a/src/pipecat/services/azure/tts.py b/src/pipecat/services/azure/tts.py index 0130ef5cb..a9491e9aa 100644 --- a/src/pipecat/services/azure/tts.py +++ b/src/pipecat/services/azure/tts.py @@ -312,11 +312,7 @@ class AzureTTSService(TTSService, AzureBaseTTSService): self._warn_init_param_moved_to_settings("params") if not settings: default_settings.emphasis = params.emphasis - default_settings.language = ( - self.language_to_service_language(params.language) - if params.language - else "en-US" - ) + default_settings.language = params.language if params.language else "en-US" default_settings.pitch = params.pitch default_settings.rate = params.rate default_settings.role = params.role @@ -809,11 +805,7 @@ class AzureHttpTTSService(TTSService, AzureBaseTTSService): self._warn_init_param_moved_to_settings("params") if not settings: default_settings.emphasis = params.emphasis - default_settings.language = ( - self.language_to_service_language(params.language) - if params.language - else "en-US" - ) + default_settings.language = params.language if params.language else "en-US" default_settings.pitch = params.pitch default_settings.rate = params.rate default_settings.role = params.role diff --git a/src/pipecat/services/camb/tts.py b/src/pipecat/services/camb/tts.py index 9c360ed37..b6b83a928 100644 --- a/src/pipecat/services/camb/tts.py +++ b/src/pipecat/services/camb/tts.py @@ -260,9 +260,7 @@ class CambTTSService(TTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = ( - self.language_to_service_language(params.language) or "en-us" - ) + default_settings.language = params.language if params.user_instructions is not None: default_settings.user_instructions = params.user_instructions diff --git a/src/pipecat/services/cartesia/tts.py b/src/pipecat/services/cartesia/tts.py index aca5c46c6..b713a0d9a 100644 --- a/src/pipecat/services/cartesia/tts.py +++ b/src/pipecat/services/cartesia/tts.py @@ -302,7 +302,7 @@ class CartesiaTTSService(WebsocketTTSService): default_settings = self.Settings( model="sonic-3", voice=None, - language=language_to_cartesia_language(Language.EN), + language=Language.EN, generation_config=None, pronunciation_dict_id=None, ) @@ -320,7 +320,7 @@ class CartesiaTTSService(WebsocketTTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.generation_config is not None: default_settings.generation_config = params.generation_config if params.pronunciation_dict_id is not None: @@ -749,7 +749,7 @@ class CartesiaHttpTTSService(TTSService): default_settings = self.Settings( model="sonic-3", voice=None, - language=language_to_cartesia_language(Language.EN), + language=Language.EN, generation_config=None, pronunciation_dict_id=None, ) @@ -767,7 +767,7 @@ class CartesiaHttpTTSService(TTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.generation_config is not None: default_settings.generation_config = params.generation_config if params.pronunciation_dict_id is not None: diff --git a/src/pipecat/services/elevenlabs/stt.py b/src/pipecat/services/elevenlabs/stt.py index daca9be3d..aa7fd0659 100644 --- a/src/pipecat/services/elevenlabs/stt.py +++ b/src/pipecat/services/elevenlabs/stt.py @@ -272,7 +272,7 @@ class ElevenLabsSTTService(SegmentedSTTService): # 1. Initialize default_settings with hardcoded defaults default_settings = self.Settings( model="scribe_v2", - language=language_to_elevenlabs_language(Language.EN), + language=Language.EN, tag_audio_events=None, ) @@ -286,7 +286,7 @@ class ElevenLabsSTTService(SegmentedSTTService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = language_to_elevenlabs_language(params.language) + default_settings.language = params.language default_settings.tag_audio_events = params.tag_audio_events # 4. Apply settings delta (canonical API, always wins) diff --git a/src/pipecat/services/elevenlabs/tts.py b/src/pipecat/services/elevenlabs/tts.py index 8cfd1abe2..866d0405f 100644 --- a/src/pipecat/services/elevenlabs/tts.py +++ b/src/pipecat/services/elevenlabs/tts.py @@ -449,7 +449,7 @@ class ElevenLabsTTSService(WebsocketTTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.stability is not None: default_settings.stability = params.stability if params.similarity_boost is not None: @@ -1014,7 +1014,7 @@ class ElevenLabsHttpTTSService(TTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.optimize_streaming_latency is not None: default_settings.optimize_streaming_latency = params.optimize_streaming_latency if params.stability is not None: diff --git a/src/pipecat/services/fal/stt.py b/src/pipecat/services/fal/stt.py index 7bfcfbcfd..65df7e3ab 100644 --- a/src/pipecat/services/fal/stt.py +++ b/src/pipecat/services/fal/stt.py @@ -216,7 +216,7 @@ class FalSTTService(SegmentedSTTService): # 1. Initialize default_settings with hardcoded defaults default_settings = self.Settings( model=None, - language=language_to_fal_language(Language.EN), + language=Language.EN, ) # 2. (no deprecated direct args for this service) @@ -226,7 +226,7 @@ class FalSTTService(SegmentedSTTService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = language_to_fal_language(params.language) + default_settings.language = params.language if params.task != "transcribe": task = params.task if params.chunk_level != "segment": diff --git a/src/pipecat/services/google/tts.py b/src/pipecat/services/google/tts.py index 3455a8125..93053cc94 100644 --- a/src/pipecat/services/google/tts.py +++ b/src/pipecat/services/google/tts.py @@ -653,7 +653,7 @@ class GoogleHttpTTSService(TTSService): if params.emphasis is not None: default_settings.emphasis = params.emphasis if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.gender is not None: default_settings.gender = params.gender if params.google_style is not None: @@ -1090,7 +1090,7 @@ class GoogleTTSService(GoogleBaseTTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.speaking_rate is not None: default_settings.speaking_rate = params.speaking_rate @@ -1346,7 +1346,7 @@ class GeminiTTSService(GoogleBaseTTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.prompt is not None: default_settings.prompt = params.prompt if params.multi_speaker is not None: diff --git a/src/pipecat/services/groq/stt.py b/src/pipecat/services/groq/stt.py index 6ff4075ae..3f6c23774 100644 --- a/src/pipecat/services/groq/stt.py +++ b/src/pipecat/services/groq/stt.py @@ -85,7 +85,7 @@ class GroqSTTService(BaseWhisperSTTService): # --- 1. Hardcoded defaults --- default_settings = self.Settings( model="whisper-large-v3-turbo", - language=self.language_to_service_language(Language.EN), + language=Language.EN, prompt=None, temperature=None, ) @@ -96,7 +96,7 @@ class GroqSTTService(BaseWhisperSTTService): default_settings.model = model if language is not None: self._warn_init_param_moved_to_settings("language", "language") - default_settings.language = self.language_to_service_language(language) + default_settings.language = language if prompt is not None: self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt diff --git a/src/pipecat/services/kokoro/tts.py b/src/pipecat/services/kokoro/tts.py index 34e703dab..4756d4e74 100644 --- a/src/pipecat/services/kokoro/tts.py +++ b/src/pipecat/services/kokoro/tts.py @@ -150,7 +150,7 @@ class KokoroTTSService(TTSService): default_settings = self.Settings( model=None, voice=None, - language=language_to_kokoro_language(Language.EN), + language=Language.EN, ) # 2. Apply direct init arg overrides (deprecated) @@ -162,7 +162,7 @@ class KokoroTTSService(TTSService): if params is not None: self._warn_init_param_moved_to_settings("params") if not settings: - default_settings.language = language_to_kokoro_language(params.language) + default_settings.language = params.language # 4. Apply settings delta (canonical API, always wins) if settings is not None: diff --git a/src/pipecat/services/lmnt/tts.py b/src/pipecat/services/lmnt/tts.py index 0ca91a107..5fe574c5a 100644 --- a/src/pipecat/services/lmnt/tts.py +++ b/src/pipecat/services/lmnt/tts.py @@ -129,7 +129,7 @@ class LmntTTSService(InterruptibleTTSService): default_settings = self.Settings( model="aurora", voice=None, - language=self.language_to_service_language(language), + language=language, ) # 2. Apply direct init arg overrides (deprecated) diff --git a/src/pipecat/services/neuphonic/tts.py b/src/pipecat/services/neuphonic/tts.py index b345a0ff4..6fb9d3e28 100644 --- a/src/pipecat/services/neuphonic/tts.py +++ b/src/pipecat/services/neuphonic/tts.py @@ -153,7 +153,7 @@ class NeuphonicTTSService(InterruptibleTTSService): default_settings = self.Settings( model=None, voice=None, - language=self.language_to_service_language(Language.EN), + language=Language.EN, speed=1.0, ) @@ -167,7 +167,7 @@ class NeuphonicTTSService(InterruptibleTTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.speed is not None: default_settings.speed = params.speed @@ -487,7 +487,7 @@ class NeuphonicHttpTTSService(TTSService): default_settings = self.Settings( model=None, voice=None, - language=self.language_to_service_language(Language.EN), + language=Language.EN, speed=1.0, ) @@ -501,7 +501,7 @@ class NeuphonicHttpTTSService(TTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = self.language_to_service_language(params.language) + default_settings.language = params.language if params.speed is not None: default_settings.speed = params.speed diff --git a/src/pipecat/services/nvidia/stt.py b/src/pipecat/services/nvidia/stt.py index fcca14741..50a654191 100644 --- a/src/pipecat/services/nvidia/stt.py +++ b/src/pipecat/services/nvidia/stt.py @@ -503,7 +503,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): # 1. Initialize default_settings with hardcoded defaults default_settings = self.Settings( model=model_function_map.get("model_name"), - language=language_to_nvidia_riva_language(Language.EN_US) or "en-US", + language=Language.EN_US, profanity_filter=False, automatic_punctuation=True, verbatim_transcripts=False, @@ -517,9 +517,7 @@ class NvidiaSegmentedSTTService(SegmentedSTTService): if params is not None: self._warn_init_param_moved_to_settings("params") if not settings: - default_settings.language = ( - language_to_nvidia_riva_language(params.language or Language.EN_US) or "en-US" - ) + default_settings.language = params.language or Language.EN_US default_settings.profanity_filter = params.profanity_filter default_settings.automatic_punctuation = params.automatic_punctuation default_settings.verbatim_transcripts = params.verbatim_transcripts diff --git a/src/pipecat/services/openai/stt.py b/src/pipecat/services/openai/stt.py index 39ca60b25..68ab7e067 100644 --- a/src/pipecat/services/openai/stt.py +++ b/src/pipecat/services/openai/stt.py @@ -119,7 +119,7 @@ class OpenAISTTService(BaseWhisperSTTService): _language = language or Language.EN default_settings = self.Settings( model="gpt-4o-transcribe", - language=self.language_to_service_language(_language), + language=_language, prompt=None, temperature=None, ) diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index 24a5d152b..aec5630b6 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -251,9 +251,7 @@ class RimeTTSService(WebsocketTTSService): if params is not None: self._warn_init_param_moved_to_settings("params") if not settings: - default_settings.language = ( - self.language_to_service_language(params.language) if params.language else None - ) + default_settings.language = params.language default_settings.segment = params.segment default_settings.speedAlpha = params.speed_alpha # Arcana params @@ -754,9 +752,7 @@ class RimeHttpTTSService(TTSService): if params is not None: self._warn_init_param_moved_to_settings("params") if not settings: - default_settings.language = ( - self.language_to_service_language(params.language) if params.language else "eng" - ) + default_settings.language = params.language default_settings.speedAlpha = params.speed_alpha default_settings.reduceLatency = params.reduce_latency default_settings.pauseBetweenBrackets = params.pause_between_brackets @@ -984,9 +980,7 @@ class RimeNonJsonTTSService(InterruptibleTTSService): if params is not None: self._warn_init_param_moved_to_settings("params") if not settings: - default_settings.language = ( - self.language_to_service_language(params.language) if params.language else None - ) + default_settings.language = params.language default_settings.segment = params.segment default_settings.repetition_penalty = params.repetition_penalty default_settings.temperature = params.temperature diff --git a/src/pipecat/services/sambanova/stt.py b/src/pipecat/services/sambanova/stt.py index d3a77b4eb..5cf12d771 100644 --- a/src/pipecat/services/sambanova/stt.py +++ b/src/pipecat/services/sambanova/stt.py @@ -82,7 +82,7 @@ class SambaNovaSTTService(BaseWhisperSTTService): # type: ignore # --- 1. Hardcoded defaults --- default_settings = self.Settings( model="Whisper-Large-v3", - language=self.language_to_service_language(Language.EN), + language=Language.EN, prompt=None, temperature=None, ) @@ -93,7 +93,7 @@ class SambaNovaSTTService(BaseWhisperSTTService): # type: ignore default_settings.model = model if language is not None: self._warn_init_param_moved_to_settings("language", "language") - default_settings.language = self.language_to_service_language(language) + default_settings.language = language if prompt is not None: self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index 91ee088cf..c926270de 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -473,9 +473,7 @@ class SarvamHttpTTSService(TTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = ( - self.language_to_service_language(params.language) or "en-IN" - ) + default_settings.language = params.language if params.enable_preprocessing is not None: default_settings.enable_preprocessing = params.enable_preprocessing if params.pace is not None: @@ -491,10 +489,6 @@ class SarvamHttpTTSService(TTSService): if settings is not None: default_settings.apply_update(settings) - # Convert Language enum to service-specific string - if isinstance(default_settings.language, Language): - default_settings.language = self.language_to_service_language(default_settings.language) - # Get model configuration (validates model exists) resolved_model = default_settings.model if resolved_model not in TTS_MODEL_CONFIGS: @@ -889,9 +883,7 @@ class SarvamTTSService(InterruptibleTTSService): self._warn_init_param_moved_to_settings("params") if not settings: if params.language is not None: - default_settings.language = ( - self.language_to_service_language(params.language) or "en-IN" - ) + default_settings.language = params.language if params.enable_preprocessing is not None: default_settings.enable_preprocessing = params.enable_preprocessing if params.min_buffer_size is not None: @@ -915,10 +907,6 @@ class SarvamTTSService(InterruptibleTTSService): if settings is not None: default_settings.apply_update(settings) - # Convert Language enum to service-specific string - if isinstance(default_settings.language, Language): - default_settings.language = self.language_to_service_language(default_settings.language) - # Get model configuration (validates model exists) resolved_model = default_settings.model if resolved_model not in TTS_MODEL_CONFIGS: diff --git a/src/pipecat/services/whisper/base_stt.py b/src/pipecat/services/whisper/base_stt.py index 93bb5de34..33d19b0aa 100644 --- a/src/pipecat/services/whisper/base_stt.py +++ b/src/pipecat/services/whisper/base_stt.py @@ -194,7 +194,7 @@ class BaseWhisperSTTService(SegmentedSTTService): default_settings.model = model if language is not None: self._warn_init_param_moved_to_settings("language", "language") - default_settings.language = self.language_to_service_language(language) + default_settings.language = language if prompt is not None: self._warn_init_param_moved_to_settings("prompt", "prompt") default_settings.prompt = prompt diff --git a/src/pipecat/services/xtts/tts.py b/src/pipecat/services/xtts/tts.py index 099fce65c..1c3feedb6 100644 --- a/src/pipecat/services/xtts/tts.py +++ b/src/pipecat/services/xtts/tts.py @@ -117,7 +117,7 @@ class XTTSService(TTSService): default_settings = self.Settings( model=None, voice=None, - language=self.language_to_service_language(language), + language=language, ) # 2. Apply direct init arg overrides (deprecated) From 1ea23ad362ea264f6b7a1f34a3e7fc90089496cd Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 10:58:51 -0400 Subject: [PATCH 060/159] Add changelog for #4024 --- changelog/4024.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4024.fixed.md diff --git a/changelog/4024.fixed.md b/changelog/4024.fixed.md new file mode 100644 index 000000000..7654cbdf5 --- /dev/null +++ b/changelog/4024.fixed.md @@ -0,0 +1 @@ +- Fixed `Language` enum values (e.g. `Language.ES`) not being converted to service-specific codes when passed via `settings=Service.Settings(language=Language.ES)` at init time. This caused API errors (e.g. 400 from Rime) because the raw enum was sent instead of the expected language code (e.g. `"spa"`). Runtime updates via `UpdateSettingsFrame` were unaffected. The fix centralizes conversion in the base `TTSService` and `STTService` classes so all services handle this consistently. From 0ec5f5e5ac528c89872c49a5fb9c8a3fde8fd72c Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 11:33:59 -0400 Subject: [PATCH 061/159] Add missing language deprecations for XTTSService, LmntTTSService --- src/pipecat/services/lmnt/tts.py | 9 ++++++++- src/pipecat/services/xtts/tts.py | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/lmnt/tts.py b/src/pipecat/services/lmnt/tts.py index 5fe574c5a..29d0b60ca 100644 --- a/src/pipecat/services/lmnt/tts.py +++ b/src/pipecat/services/lmnt/tts.py @@ -114,6 +114,10 @@ class LmntTTSService(InterruptibleTTSService): sample_rate: Audio sample rate. If None, uses default. language: Language for synthesis. Defaults to English. + + .. deprecated:: 0.0.106 + Use ``settings=LmntTTSService.Settings(language=...)`` instead. + output_format: Audio output format. One of "pcm_s16le", "pcm_f32le", "mp3", "ulaw", "webm". Defaults to "pcm_s16le". model: TTS model to use. @@ -129,13 +133,16 @@ class LmntTTSService(InterruptibleTTSService): default_settings = self.Settings( model="aurora", voice=None, - language=language, + language=Language.EN, ) # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id + if language is not None: + self._warn_init_param_moved_to_settings("language", "language") + default_settings.language = language if model is not None: self._warn_init_param_moved_to_settings("model", "model") default_settings.model = model diff --git a/src/pipecat/services/xtts/tts.py b/src/pipecat/services/xtts/tts.py index 1c3feedb6..b164f8945 100644 --- a/src/pipecat/services/xtts/tts.py +++ b/src/pipecat/services/xtts/tts.py @@ -108,6 +108,10 @@ class XTTSService(TTSService): base_url: Base URL of the XTTS streaming server. aiohttp_session: HTTP session for making requests to the server. language: Language for synthesis. Defaults to English. + + .. deprecated:: 0.0.106 + Use ``settings=XTTSService.Settings(language=...)`` instead. + sample_rate: Audio sample rate. If None, uses default. settings: Runtime-updatable settings. When provided alongside deprecated parameters, ``settings`` values take precedence. @@ -117,13 +121,16 @@ class XTTSService(TTSService): default_settings = self.Settings( model=None, voice=None, - language=language, + language=Language.EN, ) # 2. Apply direct init arg overrides (deprecated) if voice_id is not None: self._warn_init_param_moved_to_settings("voice_id", "voice") default_settings.voice = voice_id + if language is not None: + self._warn_init_param_moved_to_settings("language", "language") + default_settings.language = language # 3. (No step 3, as there's no params object to apply) From 978a1a2083bb5ee3e29342522e7580cd97be108a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 12:22:10 -0400 Subject: [PATCH 062/159] Update the system_instruction wording in the foundational examples to not mention WebRTC call --- examples/foundational/02-llm-say-one-thing.py | 2 +- examples/foundational/04-transports-small-webrtc.py | 2 +- examples/foundational/04a-transports-daily.py | 2 +- examples/foundational/04b-transports-livekit.py | 2 +- examples/foundational/06-listen-and-respond.py | 2 +- examples/foundational/06a-image-sync.py | 2 +- examples/foundational/07-interruptible-cartesia-http.py | 2 +- examples/foundational/07-interruptible.py | 2 +- examples/foundational/07a-interruptible-speechmatics-vad.py | 2 +- examples/foundational/07a-interruptible-speechmatics.py | 2 +- examples/foundational/07b-interruptible-langchain.py | 3 +-- examples/foundational/07c-interruptible-deepgram-flux.py | 2 +- examples/foundational/07c-interruptible-deepgram-http.py | 2 +- examples/foundational/07c-interruptible-deepgram-sagemaker.py | 2 +- examples/foundational/07c-interruptible-deepgram-vad.py | 2 +- examples/foundational/07c-interruptible-deepgram.py | 2 +- examples/foundational/07d-interruptible-elevenlabs-http.py | 2 +- examples/foundational/07d-interruptible-elevenlabs.py | 2 +- examples/foundational/07f-interruptible-azure-http.py | 2 +- examples/foundational/07f-interruptible-azure.py | 2 +- examples/foundational/07h-interruptible-openpipe.py | 2 +- examples/foundational/07i-interruptible-xtts.py | 2 +- examples/foundational/07j-interruptible-gladia-vad.py | 2 +- examples/foundational/07j-interruptible-gladia.py | 2 +- examples/foundational/07k-interruptible-lmnt.py | 2 +- examples/foundational/07l-interruptible-groq.py | 2 +- examples/foundational/07m-interruptible-aws.py | 2 +- examples/foundational/07n-interruptible-gemini-image.py | 2 +- examples/foundational/07n-interruptible-gemini.py | 4 ++-- examples/foundational/07n-interruptible-google-http.py | 2 +- examples/foundational/07n-interruptible-google.py | 2 +- .../07o-interruptible-assemblyai-turn-detection.py | 2 +- examples/foundational/07o-interruptible-assemblyai.py | 2 +- examples/foundational/07p-interruptible-krisp-viva.py | 2 +- examples/foundational/07p-interruptible-krisp.py | 2 +- examples/foundational/07q-interruptible-rime-http.py | 2 +- examples/foundational/07q-interruptible-rime.py | 2 +- examples/foundational/07r-interruptible-nvidia.py | 2 +- examples/foundational/07s-interruptible-google-audio-in.py | 2 +- examples/foundational/07t-interruptible-fish.py | 2 +- examples/foundational/07v-interruptible-neuphonic-http.py | 2 +- examples/foundational/07v-interruptible-neuphonic.py | 2 +- examples/foundational/07w-interruptible-fal.py | 2 +- examples/foundational/07x-interruptible-local.py | 2 +- examples/foundational/07y-interruptible-minimax.py | 2 +- examples/foundational/07z-interruptible-sarvam-http.py | 2 +- examples/foundational/07z-interruptible-sarvam.py | 2 +- examples/foundational/07za-interruptible-soniox.py | 2 +- examples/foundational/07zc-interruptible-asyncai-http.py | 2 +- examples/foundational/07zc-interruptible-asyncai.py | 2 +- examples/foundational/07zd-interruptible-aicoustics.py | 2 +- examples/foundational/07ze-interruptible-hume.py | 2 +- examples/foundational/07zf-interruptible-gradium.py | 2 +- examples/foundational/07zg-interruptible-camb.py | 2 +- examples/foundational/07zi-interruptible-piper.py | 2 +- examples/foundational/07zj-interruptible-kokoro.py | 2 +- examples/foundational/07zk-interruptible-resemble.py | 2 +- examples/foundational/08-custom-frame-processor.py | 2 +- examples/foundational/10-wake-phrase.py | 2 +- examples/foundational/11-sound-effects.py | 2 +- examples/foundational/12-describe-image-openai.py | 2 +- examples/foundational/12a-describe-image-anthropic.py | 2 +- examples/foundational/12b-describe-image-aws.py | 2 +- examples/foundational/12c-describe-image-gemini-flash.py | 2 +- examples/foundational/14-function-calling.py | 2 +- examples/foundational/14a-function-calling-anthropic.py | 2 +- examples/foundational/14c-function-calling-together.py | 2 +- examples/foundational/14d-function-calling-anthropic-video.py | 2 +- examples/foundational/14d-function-calling-aws-video.py | 2 +- .../foundational/14d-function-calling-gemini-flash-video.py | 2 +- examples/foundational/14d-function-calling-moondream-video.py | 2 +- examples/foundational/14d-function-calling-openai-video.py | 2 +- examples/foundational/14f-function-calling-groq.py | 2 +- examples/foundational/14g-function-calling-grok.py | 2 +- examples/foundational/14h-function-calling-azure.py | 2 +- examples/foundational/14i-function-calling-fireworks.py | 2 +- examples/foundational/14j-function-calling-nvidia.py | 2 +- examples/foundational/14k-function-calling-cerebras.py | 2 +- examples/foundational/14l-function-calling-deepseek.py | 2 +- examples/foundational/14m-function-calling-openrouter.py | 2 +- examples/foundational/14n-function-calling-perplexity.py | 2 +- .../foundational/14o-function-calling-gemini-openai-format.py | 2 +- .../foundational/14p-function-calling-gemini-vertex-ai.py | 2 +- examples/foundational/14q-function-calling-qwen.py | 2 +- examples/foundational/14r-function-calling-aws.py | 2 +- examples/foundational/14s-function-calling-sambanova.py | 2 +- examples/foundational/14t-function-calling-direct.py | 2 +- examples/foundational/14u-function-calling-ollama.py | 2 +- examples/foundational/14v-function-calling-openai.py | 2 +- examples/foundational/14w-function-calling-mistral.py | 2 +- examples/foundational/14x-function-calling-openpipe.py | 2 +- examples/foundational/15-switch-voices.py | 2 +- examples/foundational/15a-switch-languages.py | 2 +- examples/foundational/16-gpu-container-local-bot.py | 2 +- examples/foundational/17-detect-user-idle.py | 2 +- examples/foundational/20a-persistent-context-openai.py | 2 +- examples/foundational/20c-persistent-context-anthropic.py | 2 +- examples/foundational/20d-persistent-context-gemini.py | 4 ++-- examples/foundational/21-tavus-transport.py | 2 +- examples/foundational/21a-tavus-video-service.py | 2 +- examples/foundational/22-filter-incomplete-turns.py | 2 +- examples/foundational/23-bot-background-sound.py | 2 +- examples/foundational/25-google-audio-in.py | 2 +- examples/foundational/27-simli-layer.py | 2 +- examples/foundational/28-user-assistant-turns.py | 2 +- examples/foundational/29-turn-tracking-observer.py | 2 +- examples/foundational/30-observer.py | 2 +- examples/foundational/34-audio-recording.py | 2 +- examples/foundational/38-smart-turn-fal.py | 2 +- examples/foundational/38a-smart-turn-local-coreml.py | 2 +- examples/foundational/38b-smart-turn-local.py | 2 +- examples/foundational/39-mcp-stdio.py | 2 +- examples/foundational/39a-mcp-streamable-http.py | 2 +- examples/foundational/39b-mcp-streamable-http-gemini-live.py | 2 +- examples/foundational/39c-multiple-mcp.py | 2 +- examples/foundational/42-interruption-config.py | 2 +- examples/foundational/44-voicemail-detection.py | 2 +- examples/foundational/45-before-and-after-events.py | 2 +- examples/foundational/47-sentry-metrics.py | 2 +- examples/foundational/48-service-switcher.py | 2 +- examples/foundational/49a-thinking-anthropic.py | 2 +- examples/foundational/49b-thinking-google.py | 2 +- examples/foundational/49c-thinking-functions-anthropic.py | 2 +- examples/foundational/49d-thinking-functions-google.py | 2 +- examples/foundational/53-concurrent-llm-evaluation.py | 2 +- .../foundational/53-concurrent-llm-rtvi-ignored-sources.py | 2 +- examples/foundational/54-context-summarization-openai.py | 2 +- examples/foundational/54a-context-summarization-google.py | 2 +- .../foundational/54b-context-summarization-manual-openai.py | 2 +- .../foundational/54c-context-summarization-dedicated-llm.py | 2 +- .../foundational/55a-update-settings-deepgram-flux-stt.py | 2 +- .../55a-update-settings-deepgram-sagemaker-stt.py | 2 +- examples/foundational/55a-update-settings-deepgram-stt.py | 2 +- examples/foundational/55b-update-settings-azure-stt.py | 2 +- examples/foundational/55c-update-settings-google-stt.py | 2 +- examples/foundational/55d-update-settings-assemblyai-stt.py | 2 +- examples/foundational/55e-update-settings-gladia-stt.py | 2 +- .../55f-update-settings-elevenlabs-realtime-stt.py | 2 +- examples/foundational/55g-update-settings-elevenlabs-stt.py | 2 +- examples/foundational/55h-update-settings-speechmatics-stt.py | 2 +- examples/foundational/55i-update-settings-whisper-api-stt.py | 2 +- examples/foundational/55j-update-settings-sarvam-stt.py | 2 +- examples/foundational/55k-update-settings-soniox-stt.py | 2 +- .../foundational/55l-update-settings-aws-transcribe-stt.py | 2 +- examples/foundational/55m-update-settings-cartesia-stt.py | 2 +- .../foundational/55n-update-settings-cartesia-http-tts.py | 2 +- examples/foundational/55n-update-settings-cartesia-tts.py | 2 +- .../foundational/55o-update-settings-elevenlabs-http-tts.py | 2 +- examples/foundational/55o-update-settings-elevenlabs-tts.py | 2 +- examples/foundational/55p-update-settings-openai-tts.py | 2 +- .../foundational/55q-update-settings-deepgram-http-tts.py | 2 +- .../55q-update-settings-deepgram-sagemaker-tts.py | 2 +- examples/foundational/55q-update-settings-deepgram-tts.py | 2 +- examples/foundational/55r-update-settings-azure-http-tts.py | 2 +- examples/foundational/55r-update-settings-azure-tts.py | 2 +- examples/foundational/55s-update-settings-google-http-tts.py | 2 +- .../foundational/55s-update-settings-google-stream-tts.py | 2 +- examples/foundational/55t-update-settings-piper-http-tts.py | 2 +- examples/foundational/55t-update-settings-piper-tts.py | 2 +- examples/foundational/55u-update-settings-rime-http-tts.py | 2 +- examples/foundational/55u-update-settings-rime-tts.py | 2 +- examples/foundational/55v-update-settings-lmnt-tts.py | 2 +- examples/foundational/55w-update-settings-fish-tts.py | 2 +- examples/foundational/55x-update-settings-minimax-tts.py | 2 +- examples/foundational/55y-update-settings-groq-tts.py | 2 +- examples/foundational/55z-update-settings-hume-tts.py | 2 +- .../foundational/55za-update-settings-neuphonic-http-tts.py | 2 +- examples/foundational/55za-update-settings-neuphonic-tts.py | 2 +- .../foundational/55zb-update-settings-inworld-http-tts.py | 2 +- examples/foundational/55zb-update-settings-inworld-tts.py | 2 +- examples/foundational/55zc-update-settings-gemini-tts.py | 2 +- examples/foundational/55zd-update-settings-aws-polly-tts.py | 2 +- examples/foundational/55ze-update-settings-sarvam-http-tts.py | 2 +- examples/foundational/55ze-update-settings-sarvam-tts.py | 2 +- examples/foundational/55zf-update-settings-camb-tts.py | 2 +- examples/foundational/55zg-update-settings-kokoro-tts.py | 2 +- examples/foundational/55zh-update-settings-resembleai-tts.py | 2 +- examples/foundational/55zi-update-settings-azure-llm.py | 2 +- examples/foundational/55zi-update-settings-openai-llm.py | 2 +- examples/foundational/55zj-update-settings-anthropic-llm.py | 2 +- examples/foundational/55zk-update-settings-google-llm.py | 2 +- .../foundational/55zk-update-settings-google-vertex-llm.py | 2 +- examples/foundational/55zl-update-settings-azure-realtime.py | 2 +- examples/foundational/55zl-update-settings-openai-realtime.py | 2 +- .../foundational/55zm-update-settings-gemini-live-vertex.py | 2 +- examples/foundational/55zm-update-settings-gemini-live.py | 2 +- .../foundational/55zn-update-settings-ultravox-realtime.py | 2 +- examples/foundational/55zo-update-settings-grok-realtime.py | 2 +- examples/foundational/55zp-update-settings-aws-bedrock-llm.py | 2 +- examples/foundational/55zq-update-settings-fal-stt.py | 2 +- examples/foundational/55zr-update-settings-gradium-stt.py | 2 +- examples/foundational/55zs-update-settings-whisper-mlx-stt.py | 2 +- examples/foundational/55zs-update-settings-whisper-stt.py | 2 +- .../foundational/55zt-update-settings-nvidia-segmented-stt.py | 2 +- examples/foundational/55zt-update-settings-nvidia-stt.py | 2 +- .../foundational/55zu-update-settings-openai-realtime-stt.py | 2 +- .../foundational/55zv-update-settings-asyncai-http-tts.py | 2 +- examples/foundational/55zv-update-settings-asyncai-tts.py | 2 +- examples/foundational/55zw-update-settings-gradium-tts.py | 2 +- examples/foundational/55zx-update-settings-cerebras-llm.py | 2 +- examples/foundational/55zy-update-settings-deepseek-llm.py | 2 +- examples/foundational/55zz-update-settings-fireworks-llm.py | 2 +- examples/foundational/55zza-update-settings-grok-llm.py | 2 +- examples/foundational/55zzb-update-settings-groq-llm.py | 2 +- examples/foundational/55zzc-update-settings-mistral-llm.py | 2 +- examples/foundational/55zzd-update-settings-nvidia-llm.py | 2 +- examples/foundational/55zze-update-settings-ollama-llm.py | 2 +- examples/foundational/55zzf-update-settings-openrouter-llm.py | 2 +- examples/foundational/55zzg-update-settings-perplexity-llm.py | 2 +- examples/foundational/55zzh-update-settings-qwen-llm.py | 2 +- examples/foundational/55zzi-update-settings-sambanova-llm.py | 2 +- examples/foundational/55zzj-update-settings-together-llm.py | 2 +- .../foundational/55zzk-update-settings-aws-nova-sonic-llm.py | 2 +- examples/foundational/55zzl-update-settings-nvidia-tts.py | 2 +- .../foundational/55zzm-update-settings-speechmatics-tts.py | 2 +- examples/foundational/55zzn-update-settings-groq-stt.py | 2 +- examples/foundational/55zzo-update-settings-openpipe-llm.py | 2 +- examples/foundational/55zzp-update-settings-xtts-tts.py | 2 +- examples/foundational/56-lemonslice-transport.py | 2 +- 219 files changed, 221 insertions(+), 222 deletions(-) diff --git a/examples/foundational/02-llm-say-one-thing.py b/examples/foundational/02-llm-say-one-thing.py index cebea9dcf..d699277ce 100644 --- a/examples/foundational/02-llm-say-one-thing.py +++ b/examples/foundational/02-llm-say-one-thing.py @@ -47,7 +47,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are an LLM in a WebRTC session, and this is a 'hello world' demo.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/04-transports-small-webrtc.py b/examples/foundational/04-transports-small-webrtc.py index ba4749919..cc768a751 100644 --- a/examples/foundational/04-transports-small-webrtc.py +++ b/examples/foundational/04-transports-small-webrtc.py @@ -75,7 +75,7 @@ async def run_example(webrtc_connection: SmallWebRTCConnection): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/04a-transports-daily.py b/examples/foundational/04a-transports-daily.py index b73b7bdfe..83bc27aca 100644 --- a/examples/foundational/04a-transports-daily.py +++ b/examples/foundational/04a-transports-daily.py @@ -58,7 +58,7 @@ async def main(): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/04b-transports-livekit.py b/examples/foundational/04b-transports-livekit.py index 09262f767..b2ec6b49c 100644 --- a/examples/foundational/04b-transports-livekit.py +++ b/examples/foundational/04b-transports-livekit.py @@ -58,7 +58,7 @@ async def main(): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/06-listen-and-respond.py b/examples/foundational/06-listen-and-respond.py index 7a73ad876..769975b9a 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -91,7 +91,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/06a-image-sync.py b/examples/foundational/06a-image-sync.py index 3f80380dc..473441fac 100644 --- a/examples/foundational/06a-image-sync.py +++ b/examples/foundational/06a-image-sync.py @@ -108,7 +108,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07-interruptible-cartesia-http.py b/examples/foundational/07-interruptible-cartesia-http.py index be6bc07a6..6b3daecf0 100644 --- a/examples/foundational/07-interruptible-cartesia-http.py +++ b/examples/foundational/07-interruptible-cartesia-http.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 17aec31e1..c0b410f50 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07a-interruptible-speechmatics-vad.py b/examples/foundational/07a-interruptible-speechmatics-vad.py index cece246d8..327701ff2 100644 --- a/examples/foundational/07a-interruptible-speechmatics-vad.py +++ b/examples/foundational/07a-interruptible-speechmatics-vad.py @@ -113,7 +113,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( temperature=0.75, - system_instruction="You are a helpful British assistant called Sarah. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Always include punctuation in your responses. Give very short replies - do not give longer replies unless strictly necessary. Respond to what the user said in a concise, funny, creative and helpful way. Use `` tags to identify different speakers - do not use tags in your replies. Do not respond to speakers within `` tags unless explicitly asked to.", + system_instruction="You are a helpful British assistant called Sarah in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Always include punctuation in your responses. Give very short replies - do not give longer replies unless strictly necessary. Respond to what the user said in a concise, funny, creative and helpful way. Use `` tags to identify different speakers - do not use tags in your replies. Do not respond to speakers within `` tags unless explicitly asked to.", ), ) diff --git a/examples/foundational/07a-interruptible-speechmatics.py b/examples/foundational/07a-interruptible-speechmatics.py index 3d6395f73..5181bf36f 100644 --- a/examples/foundational/07a-interruptible-speechmatics.py +++ b/examples/foundational/07a-interruptible-speechmatics.py @@ -93,7 +93,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( temperature=0.75, - system_instruction="You are a helpful British assistant called Sarah. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Always include punctuation in your responses. Give very short replies - do not give longer replies unless strictly necessary. Respond to what the user said in a concise, funny, creative and helpful way. Use `` tags to identify different speakers - do not use tags in your replies. Do not respond to speakers within `` tags unless explicitly asked to.", + system_instruction="You are a helpful British assistant called Sarah in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Always include punctuation in your responses. Give very short replies - do not give longer replies unless strictly necessary. Respond to what the user said in a concise, funny, creative and helpful way. Use `` tags to identify different speakers - do not use tags in your replies. Do not respond to speakers within `` tags unless explicitly asked to.", ), ) diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py index 1a85bf7ad..d78e678a2 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -80,8 +80,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): [ ( "system", - "Be nice and helpful. Answer very briefly and without special characters like `#` or `*`. " - "Your response will be synthesized to voice and those characters will create unnatural sounds.", + "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), MessagesPlaceholder("chat_history"), ("human", "{input}"), diff --git a/examples/foundational/07c-interruptible-deepgram-flux.py b/examples/foundational/07c-interruptible-deepgram-flux.py index da027b4f8..2c4e63bb7 100644 --- a/examples/foundational/07c-interruptible-deepgram-flux.py +++ b/examples/foundational/07c-interruptible-deepgram-flux.py @@ -71,7 +71,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07c-interruptible-deepgram-http.py b/examples/foundational/07c-interruptible-deepgram-http.py index ee6733165..cb1026d7f 100644 --- a/examples/foundational/07c-interruptible-deepgram-http.py +++ b/examples/foundational/07c-interruptible-deepgram-http.py @@ -68,7 +68,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07c-interruptible-deepgram-sagemaker.py b/examples/foundational/07c-interruptible-deepgram-sagemaker.py index 91b1c95aa..b3b53b3db 100644 --- a/examples/foundational/07c-interruptible-deepgram-sagemaker.py +++ b/examples/foundational/07c-interruptible-deepgram-sagemaker.py @@ -79,7 +79,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): settings=AWSBedrockLLMSettings( model="us.amazon.nova-pro-v1:0", temperature=0.8, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py index 0a4dc0125..420ec795b 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -71,7 +71,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py index e00a9f191..a4d94f915 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07d-interruptible-elevenlabs-http.py b/examples/foundational/07d-interruptible-elevenlabs-http.py index cf60ac3e7..a83df1465 100644 --- a/examples/foundational/07d-interruptible-elevenlabs-http.py +++ b/examples/foundational/07d-interruptible-elevenlabs-http.py @@ -72,7 +72,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py index d14bc71cd..ad1873788 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07f-interruptible-azure-http.py b/examples/foundational/07f-interruptible-azure-http.py index 38a2410fc..c372c7c7e 100644 --- a/examples/foundational/07f-interruptible-azure-http.py +++ b/examples/foundational/07f-interruptible-azure-http.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"), settings=AzureLLMService.Settings( model=os.getenv("AZURE_CHATGPT_MODEL"), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py index b7ace8548..6a0b39bb5 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"), settings=AzureLLMService.Settings( model=os.getenv("AZURE_CHATGPT_MODEL"), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py index eaaa6fedd..8d31125f2 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -68,7 +68,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): openpipe_api_key=os.getenv("OPENPIPE_API_KEY"), tags={"conversation_id": f"pipecat-{timestamp}"}, settings=OpenPipeLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py index 3246d5da9..0f51ff03e 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -68,7 +68,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07j-interruptible-gladia-vad.py b/examples/foundational/07j-interruptible-gladia-vad.py index b25ab7275..ec3cc80e1 100644 --- a/examples/foundational/07j-interruptible-gladia-vad.py +++ b/examples/foundational/07j-interruptible-gladia-vad.py @@ -76,7 +76,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY", ""), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py index d1fee759b..5ab2e16a3 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -74,7 +74,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY", ""), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py index a41f1ae2e..c6f931413 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07l-interruptible-groq.py b/examples/foundational/07l-interruptible-groq.py index a4bfee8dc..59e1a7fca 100644 --- a/examples/foundational/07l-interruptible-groq.py +++ b/examples/foundational/07l-interruptible-groq.py @@ -58,7 +58,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("GROQ_API_KEY"), settings=GroqLLMService.Settings( model="llama-3.1-8b-instant", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07m-interruptible-aws.py b/examples/foundational/07m-interruptible-aws.py index ff3b6a789..ca44fb448 100644 --- a/examples/foundational/07m-interruptible-aws.py +++ b/examples/foundational/07m-interruptible-aws.py @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): settings=AWSBedrockLLMService.Settings( model="us.anthropic.claude-sonnet-4-6", temperature=0.8, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07n-interruptible-gemini-image.py b/examples/foundational/07n-interruptible-gemini-image.py index ad292ab03..461e2d8fa 100644 --- a/examples/foundational/07n-interruptible-gemini-image.py +++ b/examples/foundational/07n-interruptible-gemini-image.py @@ -89,7 +89,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): settings=GoogleLLMService.Settings( model="gemini-2.5-flash-image", # model="gemini-3-pro-image-preview", # A more powerful model, but slower, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07n-interruptible-gemini.py b/examples/foundational/07n-interruptible-gemini.py index 3d8857708..00290bd3a 100644 --- a/examples/foundational/07n-interruptible-gemini.py +++ b/examples/foundational/07n-interruptible-gemini.py @@ -74,7 +74,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.5-flash", settings=GoogleLLMService.Settings( - system_instruction="""You are a helpful AI assistant in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. + system_instruction="""You are a helpful assistant in a voice conversation. IMPORTANT: You're using Gemini TTS which supports expressive markup tags. You can use these tags in your responses: - [sigh] - Insert a sigh sound @@ -91,7 +91,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): - "[whispering] Let me tell you a secret." - "The answer is... [long pause] ...42!" - Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.""", + Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Keep responses concise. Respond to what the user said in a creative and helpful way.""", ), ) diff --git a/examples/foundational/07n-interruptible-google-http.py b/examples/foundational/07n-interruptible-google-http.py index 35c80a972..d627f431f 100644 --- a/examples/foundational/07n-interruptible-google-http.py +++ b/examples/foundational/07n-interruptible-google-http.py @@ -77,7 +77,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): model="gemini-2.5-flash", # force a certain amount of thinking if you want it # thinking=GoogleLLMService.ThinkingConfig(thinking_budget=4096) - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py index 2f148bb13..f8ec7b037 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -77,7 +77,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): model="gemini-2.5-flash", # force a certain amount of thinking if you want it # thinking=GoogleLLMService.ThinkingConfig(thinking_budget=4096), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07o-interruptible-assemblyai-turn-detection.py b/examples/foundational/07o-interruptible-assemblyai-turn-detection.py index 7f8c8169e..14ec1af21 100644 --- a/examples/foundational/07o-interruptible-assemblyai-turn-detection.py +++ b/examples/foundational/07o-interruptible-assemblyai-turn-detection.py @@ -115,7 +115,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py index 700ec404e..dc8ecee12 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07p-interruptible-krisp-viva.py b/examples/foundational/07p-interruptible-krisp-viva.py index b9eda804c..2b8e7f2f0 100644 --- a/examples/foundational/07p-interruptible-krisp-viva.py +++ b/examples/foundational/07p-interruptible-krisp-viva.py @@ -93,7 +93,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py index 800bc6fbc..229d50d17 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -68,7 +68,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07q-interruptible-rime-http.py b/examples/foundational/07q-interruptible-rime-http.py index f143ee287..f661c112b 100644 --- a/examples/foundational/07q-interruptible-rime-http.py +++ b/examples/foundational/07q-interruptible-rime-http.py @@ -71,7 +71,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py index dde507ee1..694f25c25 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07r-interruptible-nvidia.py b/examples/foundational/07r-interruptible-nvidia.py index 2e69dd50f..ed0918ec1 100644 --- a/examples/foundational/07r-interruptible-nvidia.py +++ b/examples/foundational/07r-interruptible-nvidia.py @@ -58,7 +58,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("NVIDIA_API_KEY"), settings=NvidiaLLMService.Settings( model="meta/llama-3.3-70b-instruct", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py index 6de7e1966..3f92872f0 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -48,7 +48,7 @@ load_dotenv(override=True) marker = "|----|" system_message = f""" -You are a helpful LLM in a WebRTC call. Your goals are to be helpful and brief in your responses. +You are a helpful LLM in a voice call. Your goals are to be helpful and brief in your responses. You are expert at transcribing audio to text. You will receive a mixture of audio and text input. When asked to transcribe what the user said, output an exact, word-for-word transcription. diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py index d51e3a71e..13612a887 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07v-interruptible-neuphonic-http.py b/examples/foundational/07v-interruptible-neuphonic-http.py index bc2bbe983..ad5b7e996 100644 --- a/examples/foundational/07v-interruptible-neuphonic-http.py +++ b/examples/foundational/07v-interruptible-neuphonic-http.py @@ -69,7 +69,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07v-interruptible-neuphonic.py b/examples/foundational/07v-interruptible-neuphonic.py index b44632286..ba3350754 100644 --- a/examples/foundational/07v-interruptible-neuphonic.py +++ b/examples/foundational/07v-interruptible-neuphonic.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07w-interruptible-fal.py b/examples/foundational/07w-interruptible-fal.py index 89a38d23c..08f24fd79 100644 --- a/examples/foundational/07w-interruptible-fal.py +++ b/examples/foundational/07w-interruptible-fal.py @@ -70,7 +70,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07x-interruptible-local.py b/examples/foundational/07x-interruptible-local.py index e0ace854b..28e970403 100644 --- a/examples/foundational/07x-interruptible-local.py +++ b/examples/foundational/07x-interruptible-local.py @@ -52,7 +52,7 @@ async def main(): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07y-interruptible-minimax.py b/examples/foundational/07y-interruptible-minimax.py index d9bcef371..f8323369e 100644 --- a/examples/foundational/07y-interruptible-minimax.py +++ b/examples/foundational/07y-interruptible-minimax.py @@ -71,7 +71,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07z-interruptible-sarvam-http.py b/examples/foundational/07z-interruptible-sarvam-http.py index 76c892fd9..f8a806493 100644 --- a/examples/foundational/07z-interruptible-sarvam-http.py +++ b/examples/foundational/07z-interruptible-sarvam-http.py @@ -75,7 +75,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07z-interruptible-sarvam.py b/examples/foundational/07z-interruptible-sarvam.py index 915ea2c3d..5f144f73f 100644 --- a/examples/foundational/07z-interruptible-sarvam.py +++ b/examples/foundational/07z-interruptible-sarvam.py @@ -69,7 +69,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07za-interruptible-soniox.py b/examples/foundational/07za-interruptible-soniox.py index d425be966..c29fea9fb 100644 --- a/examples/foundational/07za-interruptible-soniox.py +++ b/examples/foundational/07za-interruptible-soniox.py @@ -71,7 +71,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07zc-interruptible-asyncai-http.py b/examples/foundational/07zc-interruptible-asyncai-http.py index e95bd60b2..96964f5e8 100644 --- a/examples/foundational/07zc-interruptible-asyncai-http.py +++ b/examples/foundational/07zc-interruptible-asyncai-http.py @@ -69,7 +69,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07zc-interruptible-asyncai.py b/examples/foundational/07zc-interruptible-asyncai.py index 40cdcf7ab..39052720c 100644 --- a/examples/foundational/07zc-interruptible-asyncai.py +++ b/examples/foundational/07zc-interruptible-asyncai.py @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07zd-interruptible-aicoustics.py b/examples/foundational/07zd-interruptible-aicoustics.py index 923530943..eeb45a4c7 100644 --- a/examples/foundational/07zd-interruptible-aicoustics.py +++ b/examples/foundational/07zd-interruptible-aicoustics.py @@ -85,7 +85,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07ze-interruptible-hume.py b/examples/foundational/07ze-interruptible-hume.py index ef19e3619..a5e4253f6 100644 --- a/examples/foundational/07ze-interruptible-hume.py +++ b/examples/foundational/07ze-interruptible-hume.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07zf-interruptible-gradium.py b/examples/foundational/07zf-interruptible-gradium.py index 4b7e2f32b..1a067d1b2 100644 --- a/examples/foundational/07zf-interruptible-gradium.py +++ b/examples/foundational/07zf-interruptible-gradium.py @@ -71,7 +71,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07zg-interruptible-camb.py b/examples/foundational/07zg-interruptible-camb.py index e0f764f4e..ff9b7162d 100644 --- a/examples/foundational/07zg-interruptible-camb.py +++ b/examples/foundational/07zg-interruptible-camb.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful voice assistant powered by Camb AI text-to-speech. ", + system_instruction="You are a helpful voice assistant powered by Camb AI text-to-speech. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Keep responses concise.", ), ) diff --git a/examples/foundational/07zi-interruptible-piper.py b/examples/foundational/07zi-interruptible-piper.py index 44b7d0efd..53f21811c 100644 --- a/examples/foundational/07zi-interruptible-piper.py +++ b/examples/foundational/07zi-interruptible-piper.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07zj-interruptible-kokoro.py b/examples/foundational/07zj-interruptible-kokoro.py index 0978ba720..5fb0ca55b 100644 --- a/examples/foundational/07zj-interruptible-kokoro.py +++ b/examples/foundational/07zj-interruptible-kokoro.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/07zk-interruptible-resemble.py b/examples/foundational/07zk-interruptible-resemble.py index a62387815..60d1d8495 100644 --- a/examples/foundational/07zk-interruptible-resemble.py +++ b/examples/foundational/07zk-interruptible-resemble.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/08-custom-frame-processor.py b/examples/foundational/08-custom-frame-processor.py index b6b1ef42c..f07711657 100644 --- a/examples/foundational/08-custom-frame-processor.py +++ b/examples/foundational/08-custom-frame-processor.py @@ -103,7 +103,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py index df74e4be4..0a4244c33 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful assistant. Respond to what the user said in a creative and helpful way. Keep your responses brief.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/11-sound-effects.py b/examples/foundational/11-sound-effects.py index 87cb3f333..1f7fdd339 100644 --- a/examples/foundational/11-sound-effects.py +++ b/examples/foundational/11-sound-effects.py @@ -107,7 +107,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/12-describe-image-openai.py b/examples/foundational/12-describe-image-openai.py index 0888fa6bf..8c8af6352 100644 --- a/examples/foundational/12-describe-image-openai.py +++ b/examples/foundational/12-describe-image-openai.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are also able to describe images.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are also able to describe images.", ), ) diff --git a/examples/foundational/12a-describe-image-anthropic.py b/examples/foundational/12a-describe-image-anthropic.py index 086e282dc..642885dcf 100644 --- a/examples/foundational/12a-describe-image-anthropic.py +++ b/examples/foundational/12a-describe-image-anthropic.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AnthropicLLMService( api_key=os.getenv("ANTHROPIC_API_KEY"), settings=AnthropicLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are also able to describe images.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are also able to describe images.", ), ) diff --git a/examples/foundational/12b-describe-image-aws.py b/examples/foundational/12b-describe-image-aws.py index d42bc6ef0..47d2b7970 100644 --- a/examples/foundational/12b-describe-image-aws.py +++ b/examples/foundational/12b-describe-image-aws.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): settings=AWSBedrockLLMService.Settings( model="us.anthropic.claude-sonnet-4-6", temperature=0.8, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are also able to describe images.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are also able to describe images.", ), ) diff --git a/examples/foundational/12c-describe-image-gemini-flash.py b/examples/foundational/12c-describe-image-gemini-flash.py index acb481d3e..248bc08a7 100644 --- a/examples/foundational/12c-describe-image-gemini-flash.py +++ b/examples/foundational/12c-describe-image-gemini-flash.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are also able to describe images.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are also able to describe images.", ), ) diff --git a/examples/foundational/14-function-calling.py b/examples/foundational/14-function-calling.py index 7b7e11edf..5001d5dad 100644 --- a/examples/foundational/14-function-calling.py +++ b/examples/foundational/14-function-calling.py @@ -75,7 +75,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/14a-function-calling-anthropic.py b/examples/foundational/14a-function-calling-anthropic.py index c0ad9102d..6cf1e228f 100644 --- a/examples/foundational/14a-function-calling-anthropic.py +++ b/examples/foundational/14a-function-calling-anthropic.py @@ -77,7 +77,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AnthropicLLMService( api_key=os.getenv("ANTHROPIC_API_KEY"), settings=AnthropicLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) llm.register_function("get_weather", get_weather) diff --git a/examples/foundational/14c-function-calling-together.py b/examples/foundational/14c-function-calling-together.py index 3d2034cb4..5bd176237 100644 --- a/examples/foundational/14c-function-calling-together.py +++ b/examples/foundational/14c-function-calling-together.py @@ -73,7 +73,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("TOGETHER_API_KEY"), settings=TogetherLLMService.Settings( model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14d-function-calling-anthropic-video.py b/examples/foundational/14d-function-calling-anthropic-video.py index ce51ddb22..f2653101e 100644 --- a/examples/foundational/14d-function-calling-anthropic-video.py +++ b/examples/foundational/14d-function-calling-anthropic-video.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AnthropicLLMService( api_key=os.getenv("ANTHROPIC_API_KEY"), settings=AnthropicLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are able to describe images from the user camera.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are able to describe images from the user camera.", ), ) llm.register_function("fetch_user_image", fetch_user_image) diff --git a/examples/foundational/14d-function-calling-aws-video.py b/examples/foundational/14d-function-calling-aws-video.py index 2c54e83f4..9fccdecdf 100644 --- a/examples/foundational/14d-function-calling-aws-video.py +++ b/examples/foundational/14d-function-calling-aws-video.py @@ -104,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): # Here we can't because AWS Bedrock doesn't support it for Claude 3.7, # which we need for image input. temperature=0.8, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are able to describe images from the user camera.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are able to describe images from the user camera.", ), ) llm.register_function("fetch_user_image", fetch_user_image) diff --git a/examples/foundational/14d-function-calling-gemini-flash-video.py b/examples/foundational/14d-function-calling-gemini-flash-video.py index 16d82e002..ba76de44b 100644 --- a/examples/foundational/14d-function-calling-gemini-flash-video.py +++ b/examples/foundational/14d-function-calling-gemini-flash-video.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are able to describe images from the user camera.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are able to describe images from the user camera.", ), ) llm.register_function("fetch_user_image", fetch_user_image) diff --git a/examples/foundational/14d-function-calling-moondream-video.py b/examples/foundational/14d-function-calling-moondream-video.py index df7fc6014..2bbfbd426 100644 --- a/examples/foundational/14d-function-calling-moondream-video.py +++ b/examples/foundational/14d-function-calling-moondream-video.py @@ -129,7 +129,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are able to describe images from the user camera.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are able to describe images from the user camera.", ), ) llm.register_function("fetch_user_image", fetch_user_image) diff --git a/examples/foundational/14d-function-calling-openai-video.py b/examples/foundational/14d-function-calling-openai-video.py index 1eac522e8..59bef64b6 100644 --- a/examples/foundational/14d-function-calling-openai-video.py +++ b/examples/foundational/14d-function-calling-openai-video.py @@ -99,7 +99,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You are able to describe images from the user camera.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are able to describe images from the user camera.", ), ) llm.register_function("fetch_user_image", fetch_user_image) diff --git a/examples/foundational/14f-function-calling-groq.py b/examples/foundational/14f-function-calling-groq.py index a6f383a1f..1e6e84338 100644 --- a/examples/foundational/14f-function-calling-groq.py +++ b/examples/foundational/14f-function-calling-groq.py @@ -72,7 +72,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GroqLLMService( api_key=os.getenv("GROQ_API_KEY"), settings=GroqLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14g-function-calling-grok.py b/examples/foundational/14g-function-calling-grok.py index 283fd40fd..4de1f6528 100644 --- a/examples/foundational/14g-function-calling-grok.py +++ b/examples/foundational/14g-function-calling-grok.py @@ -72,7 +72,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GrokLLMService( api_key=os.getenv("GROK_API_KEY"), settings=GrokLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14h-function-calling-azure.py b/examples/foundational/14h-function-calling-azure.py index 3aae0c490..23c156da4 100644 --- a/examples/foundational/14h-function-calling-azure.py +++ b/examples/foundational/14h-function-calling-azure.py @@ -74,7 +74,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"), settings=AzureLLMService.Settings( model=os.getenv("AZURE_CHATGPT_MODEL"), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14i-function-calling-fireworks.py b/examples/foundational/14i-function-calling-fireworks.py index c82165b17..f58ef73d9 100644 --- a/examples/foundational/14i-function-calling-fireworks.py +++ b/examples/foundational/14i-function-calling-fireworks.py @@ -73,7 +73,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("FIREWORKS_API_KEY"), settings=FireworksLLMService.Settings( model="accounts/fireworks/models/gpt-oss-20b", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14j-function-calling-nvidia.py b/examples/foundational/14j-function-calling-nvidia.py index 7dd772c03..e3db6db8d 100644 --- a/examples/foundational/14j-function-calling-nvidia.py +++ b/examples/foundational/14j-function-calling-nvidia.py @@ -75,7 +75,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): model="nvidia/llama-3.3-nemotron-super-49b-v1.5", # Recommended when turning thinking off temperature=0.0, - system_instruction="/no_think You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="/no_think You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14k-function-calling-cerebras.py b/examples/foundational/14k-function-calling-cerebras.py index 51b9c9bf9..ad8475185 100644 --- a/examples/foundational/14k-function-calling-cerebras.py +++ b/examples/foundational/14k-function-calling-cerebras.py @@ -72,7 +72,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = CerebrasLLMService( api_key=os.getenv("CEREBRAS_API_KEY"), settings=CerebrasLLMService.Settings( - system_instruction="""You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. + system_instruction="""You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You have one functions available: diff --git a/examples/foundational/14l-function-calling-deepseek.py b/examples/foundational/14l-function-calling-deepseek.py index 4af091b7a..f115299e2 100644 --- a/examples/foundational/14l-function-calling-deepseek.py +++ b/examples/foundational/14l-function-calling-deepseek.py @@ -73,7 +73,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("DEEPSEEK_API_KEY"), settings=DeepSeekLLMService.Settings( model="deepseek-chat", - system_instruction="""You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. + system_instruction="""You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You have one functions available: diff --git a/examples/foundational/14m-function-calling-openrouter.py b/examples/foundational/14m-function-calling-openrouter.py index e23865435..b341ca71c 100644 --- a/examples/foundational/14m-function-calling-openrouter.py +++ b/examples/foundational/14m-function-calling-openrouter.py @@ -77,7 +77,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("OPENROUTER_API_KEY"), settings=OpenRouterLLMService.Settings( model="openai/gpt-4o-2024-11-20", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14n-function-calling-perplexity.py b/examples/foundational/14n-function-calling-perplexity.py index 2c1ba9e00..a67e93ab8 100644 --- a/examples/foundational/14n-function-calling-perplexity.py +++ b/examples/foundational/14n-function-calling-perplexity.py @@ -70,7 +70,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = PerplexityLLMService( api_key=os.getenv("PERPLEXITY_API_KEY"), settings=PerplexityLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way, but try to be brief.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/14o-function-calling-gemini-openai-format.py b/examples/foundational/14o-function-calling-gemini-openai-format.py index 8ab53d9cf..70e8af3d3 100644 --- a/examples/foundational/14o-function-calling-gemini-openai-format.py +++ b/examples/foundational/14o-function-calling-gemini-openai-format.py @@ -72,7 +72,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleLLMOpenAIBetaService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMOpenAIBetaService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can aslo register a function_name of None to get all functions diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py index c6259b992..fb3910ed3 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -74,7 +74,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), location=os.getenv("GOOGLE_CLOUD_LOCATION"), settings=GoogleVertexLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can aslo register a function_name of None to get all functions diff --git a/examples/foundational/14q-function-calling-qwen.py b/examples/foundational/14q-function-calling-qwen.py index bf866a519..9a6bf6d8b 100644 --- a/examples/foundational/14q-function-calling-qwen.py +++ b/examples/foundational/14q-function-calling-qwen.py @@ -73,7 +73,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("QWEN_API_KEY"), model="qwen2.5-72b-instruct", settings=QwenLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/14r-function-calling-aws.py b/examples/foundational/14r-function-calling-aws.py index 29591c80a..86ee0f0dc 100644 --- a/examples/foundational/14r-function-calling-aws.py +++ b/examples/foundational/14r-function-calling-aws.py @@ -78,7 +78,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): settings=AWSBedrockLLMService.Settings( model="us.anthropic.claude-sonnet-4-6", temperature=0.8, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/14s-function-calling-sambanova.py b/examples/foundational/14s-function-calling-sambanova.py index c329135f9..3854d2d6e 100644 --- a/examples/foundational/14s-function-calling-sambanova.py +++ b/examples/foundational/14s-function-calling-sambanova.py @@ -75,7 +75,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = SambaNovaLLMService( api_key=os.getenv("SAMBANOVA_API_KEY"), settings=SambaNovaLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # You can also register a function_name of None to get all functions diff --git a/examples/foundational/14t-function-calling-direct.py b/examples/foundational/14t-function-calling-direct.py index 7b4485226..1c6ebe072 100644 --- a/examples/foundational/14t-function-calling-direct.py +++ b/examples/foundational/14t-function-calling-direct.py @@ -88,7 +88,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/14u-function-calling-ollama.py b/examples/foundational/14u-function-calling-ollama.py index a0c1ec33a..87491f3d6 100644 --- a/examples/foundational/14u-function-calling-ollama.py +++ b/examples/foundational/14u-function-calling-ollama.py @@ -76,7 +76,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OLLamaLLMService( settings=OLLamaLLMService.Settings( model="llama3.2", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # Update to the model you're running locally diff --git a/examples/foundational/14v-function-calling-openai.py b/examples/foundational/14v-function-calling-openai.py index db759b186..2b59d7072 100644 --- a/examples/foundational/14v-function-calling-openai.py +++ b/examples/foundational/14v-function-calling-openai.py @@ -82,7 +82,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/14w-function-calling-mistral.py b/examples/foundational/14w-function-calling-mistral.py index 421a63b74..c52d46c03 100644 --- a/examples/foundational/14w-function-calling-mistral.py +++ b/examples/foundational/14w-function-calling-mistral.py @@ -75,7 +75,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = MistralLLMService( api_key=os.getenv("MISTRAL_API_KEY"), settings=MistralLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/14x-function-calling-openpipe.py b/examples/foundational/14x-function-calling-openpipe.py index e82062c16..5074bda2f 100644 --- a/examples/foundational/14x-function-calling-openpipe.py +++ b/examples/foundational/14x-function-calling-openpipe.py @@ -79,7 +79,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): openpipe_api_key=os.getenv("OPENPIPE_API_KEY"), tags={"conversation_id": f"pipecat-{timestamp}"}, settings=OpenPipeLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py index 39918918b..daf77ecde 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -121,7 +121,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can do the following voices: 'News Lady', 'British Lady' and 'Barbershop Man'.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative and helpful way. You can do the following voices: 'News Lady', 'British Lady' and 'Barbershop Man'.", ), ) llm.register_function("switch_voice", tts.switch_voice) diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py index 67d943a0a..0ff13df1a 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -112,7 +112,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can speak the following languages: 'English' and 'Spanish'.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You can speak the following languages: 'English' and 'Spanish'.", ), ) llm.register_function("switch_language", tts.switch_language) diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py index 2b680eee1..b12d85dff 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -72,7 +72,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): # Or, to use a local vLLM (or similar) api server settings=OpenAILLMService.Settings( model="meta-llama/Meta-Llama-3-8B-Instruct", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), base_url="http://0.0.0.0:8000/v1", ) diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py index a1ac00501..1cd772af2 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -123,7 +123,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index d487133f5..f6a4dc937 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -95,7 +95,7 @@ async def load_conversation(params: FunctionCallParams): await params.result_callback({"success": False, "error": str(e)}) -system_instruction = "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way." +system_instruction = "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way." weather_function = FunctionSchema( name="get_current_weather", diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index e7a8335ba..1b74c87e1 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -94,7 +94,7 @@ async def load_conversation(params: FunctionCallParams): await params.result_callback({"success": False, "error": str(e)}) -system_instruction = "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a succinct, creative and helpful way. Prefer responses that are one sentence long unless you are asked for a longer or more detailed response." +system_instruction = "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a succinct, creative and helpful way. Prefer responses that are one sentence long unless you are asked for a longer or more detailed response." weather_function = FunctionSchema( name="get_current_weather", diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index 638728331..ae37e19ce 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -122,8 +122,8 @@ async def load_conversation(params: FunctionCallParams): await params.result_callback({"success": False, "error": str(e)}) -system_instruction = """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your -capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that +system_instruction = """You are a helpful assistant in a voice conversation. Your goal is to demonstrate your +capabilities in a succinct way. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Keep responses concise. Respond to what the user said in a creative can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. diff --git a/examples/foundational/21-tavus-transport.py b/examples/foundational/21-tavus-transport.py index 1ac54ff19..f09902fcd 100644 --- a/examples/foundational/21-tavus-transport.py +++ b/examples/foundational/21-tavus-transport.py @@ -59,7 +59,7 @@ async def main(): llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/21a-tavus-video-service.py b/examples/foundational/21a-tavus-video-service.py index f716d88d8..6e03a2418 100644 --- a/examples/foundational/21a-tavus-video-service.py +++ b/examples/foundational/21a-tavus-video-service.py @@ -69,7 +69,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/22-filter-incomplete-turns.py b/examples/foundational/22-filter-incomplete-turns.py index 454e22722..8a3001366 100644 --- a/examples/foundational/22-filter-incomplete-turns.py +++ b/examples/foundational/22-filter-incomplete-turns.py @@ -71,7 +71,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py index 6f6cf8eb7..0229db419 100644 --- a/examples/foundational/23-bot-background-sound.py +++ b/examples/foundational/23-bot-background-sound.py @@ -83,7 +83,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py index 6b549a7fe..7a6c910e7 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -47,7 +47,7 @@ load_dotenv(override=True) # The system prompt for the main conversation. # conversation_system_message = """ -You are a helpful LLM in a WebRTC call. Your goals are to be helpful and brief in your responses. Respond with one or two sentences at most, unless you are asked to +You are a helpful LLM in a voice call. Your goals are to be helpful and brief in your responses. Respond with one or two sentences at most, unless you are asked to respond at more length. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. """ diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index b2cbb6837..76c2dd99d 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -73,7 +73,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/28-user-assistant-turns.py b/examples/foundational/28-user-assistant-turns.py index b439794b5..d1c053710 100644 --- a/examples/foundational/28-user-assistant-turns.py +++ b/examples/foundational/28-user-assistant-turns.py @@ -130,7 +130,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative, helpful, and brief way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/29-turn-tracking-observer.py b/examples/foundational/29-turn-tracking-observer.py index f17dd0260..e4c33a379 100644 --- a/examples/foundational/29-turn-tracking-observer.py +++ b/examples/foundational/29-turn-tracking-observer.py @@ -81,7 +81,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/30-observer.py b/examples/foundational/30-observer.py index 227a8978b..523e09583 100644 --- a/examples/foundational/30-observer.py +++ b/examples/foundational/30-observer.py @@ -112,7 +112,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/34-audio-recording.py b/examples/foundational/34-audio-recording.py index 6ddfa6033..68354e0dd 100644 --- a/examples/foundational/34-audio-recording.py +++ b/examples/foundational/34-audio-recording.py @@ -120,7 +120,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful assistant demonstrating audio recording capabilities. Keep your responses brief and clear.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/38-smart-turn-fal.py b/examples/foundational/38-smart-turn-fal.py index b2de247d7..b8a62626a 100644 --- a/examples/foundational/38-smart-turn-fal.py +++ b/examples/foundational/38-smart-turn-fal.py @@ -69,7 +69,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/38a-smart-turn-local-coreml.py b/examples/foundational/38a-smart-turn-local-coreml.py index 3073ba0dd..fc2f1d14d 100644 --- a/examples/foundational/38a-smart-turn-local-coreml.py +++ b/examples/foundational/38a-smart-turn-local-coreml.py @@ -84,7 +84,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/38b-smart-turn-local.py b/examples/foundational/38b-smart-turn-local.py index de8b214ea..7e4a0abd8 100644 --- a/examples/foundational/38b-smart-turn-local.py +++ b/examples/foundational/38b-smart-turn-local.py @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/39-mcp-stdio.py b/examples/foundational/39-mcp-stdio.py index d46c034d1..48382046d 100644 --- a/examples/foundational/39-mcp-stdio.py +++ b/examples/foundational/39-mcp-stdio.py @@ -143,7 +143,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) system_prompt = f""" - You are a helpful LLM in a WebRTC call. + You are a helpful LLM in a voice call. Your goal is to demonstrate your capabilities in a succinct way. You have access to tools to search the Rijksmuseum collection. Offer, for example, to show a floral still life, use the `search_artwork` tool. diff --git a/examples/foundational/39a-mcp-streamable-http.py b/examples/foundational/39a-mcp-streamable-http.py index b71938e43..5ddf53264 100644 --- a/examples/foundational/39a-mcp-streamable-http.py +++ b/examples/foundational/39a-mcp-streamable-http.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) system_prompt = f""" - You are a helpful LLM in a WebRTC call. + You are a helpful LLM in a voice call. Your goal is to answer questions about the user's GitHub repositories and account. You have access to a number of tools provided by Github. Use any and all tools to help users. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. diff --git a/examples/foundational/39b-mcp-streamable-http-gemini-live.py b/examples/foundational/39b-mcp-streamable-http-gemini-live.py index 19a5aa1b6..eb276a91a 100644 --- a/examples/foundational/39b-mcp-streamable-http-gemini-live.py +++ b/examples/foundational/39b-mcp-streamable-http-gemini-live.py @@ -86,7 +86,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.exception("error trace:") system = f""" - You are a helpful LLM in a WebRTC call. + You are a helpful LLM in a voice call. Your goal is to answer questions about the user's GitHub repositories and account. You have access to a number of tools provided by Github. Use any and all tools to help users. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. diff --git a/examples/foundational/39c-multiple-mcp.py b/examples/foundational/39c-multiple-mcp.py index eabfeccab..9d449251d 100644 --- a/examples/foundational/39c-multiple-mcp.py +++ b/examples/foundational/39c-multiple-mcp.py @@ -126,7 +126,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ) system_prompt = f""" - You are a helpful LLM in a WebRTC call. + You are a helpful LLM in a voice call. Your goal is to demonstrate your capabilities in a succinct way. You have access to tools to search the Rijksmuseum collection and the user's GitHub repositories and account. Offer, for example, to show a floral still life, use the `search_artwork` tool. diff --git a/examples/foundational/42-interruption-config.py b/examples/foundational/42-interruption-config.py index d690f60f0..64ea2cee0 100644 --- a/examples/foundational/42-interruption-config.py +++ b/examples/foundational/42-interruption-config.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/44-voicemail-detection.py b/examples/foundational/44-voicemail-detection.py index df47bb945..854e546ec 100644 --- a/examples/foundational/44-voicemail-detection.py +++ b/examples/foundational/44-voicemail-detection.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) classifier_llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) diff --git a/examples/foundational/45-before-and-after-events.py b/examples/foundational/45-before-and-after-events.py index 3c583546f..3a36a626a 100644 --- a/examples/foundational/45-before-and-after-events.py +++ b/examples/foundational/45-before-and-after-events.py @@ -75,7 +75,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/47-sentry-metrics.py b/examples/foundational/47-sentry-metrics.py index 9fe35d4c4..3294727dc 100644 --- a/examples/foundational/47-sentry-metrics.py +++ b/examples/foundational/47-sentry-metrics.py @@ -76,7 +76,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("OPENAI_API_KEY"), metrics=SentryMetrics(), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/48-service-switcher.py b/examples/foundational/48-service-switcher.py index 0947bc6c2..9225963f7 100644 --- a/examples/foundational/48-service-switcher.py +++ b/examples/foundational/48-service-switcher.py @@ -114,7 +114,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): # Uses ServiceSwitcherStrategyManual by default tts_switcher = ServiceSwitcher(services=[tts_cartesia, tts_deepgram]) - system_prompt = "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way." + system_prompt = "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way." llm_openai = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), diff --git a/examples/foundational/49a-thinking-anthropic.py b/examples/foundational/49a-thinking-anthropic.py index 727e8e0a7..0495d5e97 100644 --- a/examples/foundational/49a-thinking-anthropic.py +++ b/examples/foundational/49a-thinking-anthropic.py @@ -68,7 +68,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): type="enabled", budget_tokens=2048, ), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/49b-thinking-google.py b/examples/foundational/49b-thinking-google.py index b2b44d59a..1cfeba456 100644 --- a/examples/foundational/49b-thinking-google.py +++ b/examples/foundational/49b-thinking-google.py @@ -69,7 +69,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): thinking_budget=-1, # Dynamic thinking include_thoughts=True, ), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/49c-thinking-functions-anthropic.py b/examples/foundational/49c-thinking-functions-anthropic.py index d6b2749e1..1ce5832fc 100644 --- a/examples/foundational/49c-thinking-functions-anthropic.py +++ b/examples/foundational/49c-thinking-functions-anthropic.py @@ -89,7 +89,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): type="enabled", budget_tokens=2048, ), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/49d-thinking-functions-google.py b/examples/foundational/49d-thinking-functions-google.py index f5b9a1d20..0b62fbf2e 100644 --- a/examples/foundational/49d-thinking-functions-google.py +++ b/examples/foundational/49d-thinking-functions-google.py @@ -90,7 +90,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): thinking_budget=-1, # Dynamic thinking include_thoughts=True, ), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/53-concurrent-llm-evaluation.py b/examples/foundational/53-concurrent-llm-evaluation.py index 46b2847e9..69cebd0ac 100644 --- a/examples/foundational/53-concurrent-llm-evaluation.py +++ b/examples/foundational/53-concurrent-llm-evaluation.py @@ -70,7 +70,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): openai_llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/53-concurrent-llm-rtvi-ignored-sources.py b/examples/foundational/53-concurrent-llm-rtvi-ignored-sources.py index d27bd86a1..481d865e1 100644 --- a/examples/foundational/53-concurrent-llm-rtvi-ignored-sources.py +++ b/examples/foundational/53-concurrent-llm-rtvi-ignored-sources.py @@ -76,7 +76,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): main_llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/54-context-summarization-openai.py b/examples/foundational/54-context-summarization-openai.py index a536fc813..060eda8f7 100644 --- a/examples/foundational/54-context-summarization-openai.py +++ b/examples/foundational/54-context-summarization-openai.py @@ -89,7 +89,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You have access to tools to get the current weather - use them when relevant.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You have access to tools to get the current weather - use them when relevant.", ), ) diff --git a/examples/foundational/54a-context-summarization-google.py b/examples/foundational/54a-context-summarization-google.py index 942567c83..2727d34a8 100644 --- a/examples/foundational/54a-context-summarization-google.py +++ b/examples/foundational/54a-context-summarization-google.py @@ -89,7 +89,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. You have access to tools to get the current weather - use them when relevant.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You have access to tools to get the current weather - use them when relevant.", ), ) diff --git a/examples/foundational/54b-context-summarization-manual-openai.py b/examples/foundational/54b-context-summarization-manual-openai.py index 520e1c996..e6bdcaf00 100644 --- a/examples/foundational/54b-context-summarization-manual-openai.py +++ b/examples/foundational/54b-context-summarization-manual-openai.py @@ -81,7 +81,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ), ) - system_prompt = """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your + system_prompt = """You are a helpful LLM in a voice call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. diff --git a/examples/foundational/54c-context-summarization-dedicated-llm.py b/examples/foundational/54c-context-summarization-dedicated-llm.py index 9f8f0b35d..ed56e343e 100644 --- a/examples/foundational/54c-context-summarization-dedicated-llm.py +++ b/examples/foundational/54c-context-summarization-dedicated-llm.py @@ -98,7 +98,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ), ) - system_prompt = """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your + system_prompt = """You are a helpful LLM in a voice call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. diff --git a/examples/foundational/55a-update-settings-deepgram-flux-stt.py b/examples/foundational/55a-update-settings-deepgram-flux-stt.py index 237e5c3f7..d710015bd 100644 --- a/examples/foundational/55a-update-settings-deepgram-flux-stt.py +++ b/examples/foundational/55a-update-settings-deepgram-flux-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py b/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py index 4b071d733..ebe3d4cce 100644 --- a/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py +++ b/examples/foundational/55a-update-settings-deepgram-sagemaker-stt.py @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55a-update-settings-deepgram-stt.py b/examples/foundational/55a-update-settings-deepgram-stt.py index 3c39c8cd5..46adaad6b 100644 --- a/examples/foundational/55a-update-settings-deepgram-stt.py +++ b/examples/foundational/55a-update-settings-deepgram-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55b-update-settings-azure-stt.py b/examples/foundational/55b-update-settings-azure-stt.py index 59a411caf..8e5d3bfe3 100644 --- a/examples/foundational/55b-update-settings-azure-stt.py +++ b/examples/foundational/55b-update-settings-azure-stt.py @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55c-update-settings-google-stt.py b/examples/foundational/55c-update-settings-google-stt.py index c6d44da12..be62d90ad 100644 --- a/examples/foundational/55c-update-settings-google-stt.py +++ b/examples/foundational/55c-update-settings-google-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55d-update-settings-assemblyai-stt.py b/examples/foundational/55d-update-settings-assemblyai-stt.py index f62a968eb..213873ab5 100644 --- a/examples/foundational/55d-update-settings-assemblyai-stt.py +++ b/examples/foundational/55d-update-settings-assemblyai-stt.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call demonstrating dynamic keyterms updates. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Try saying difficult names like 'Xiomara', 'Saoirse', or 'Krzystof' to test transcription accuracy.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Try saying difficult names like 'Xiomara', 'Saoirse', or 'Krzystof' to test transcription accuracy.", ), ) diff --git a/examples/foundational/55e-update-settings-gladia-stt.py b/examples/foundational/55e-update-settings-gladia-stt.py index 6ca8fa725..307ea2954 100644 --- a/examples/foundational/55e-update-settings-gladia-stt.py +++ b/examples/foundational/55e-update-settings-gladia-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py b/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py index ee3244ffe..4fc351de0 100644 --- a/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py +++ b/examples/foundational/55f-update-settings-elevenlabs-realtime-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55g-update-settings-elevenlabs-stt.py b/examples/foundational/55g-update-settings-elevenlabs-stt.py index 6307264fc..d610acea6 100644 --- a/examples/foundational/55g-update-settings-elevenlabs-stt.py +++ b/examples/foundational/55g-update-settings-elevenlabs-stt.py @@ -68,7 +68,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55h-update-settings-speechmatics-stt.py b/examples/foundational/55h-update-settings-speechmatics-stt.py index df6d9575b..eaa1874f6 100644 --- a/examples/foundational/55h-update-settings-speechmatics-stt.py +++ b/examples/foundational/55h-update-settings-speechmatics-stt.py @@ -70,7 +70,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55i-update-settings-whisper-api-stt.py b/examples/foundational/55i-update-settings-whisper-api-stt.py index e9ef61cce..179a65f83 100644 --- a/examples/foundational/55i-update-settings-whisper-api-stt.py +++ b/examples/foundational/55i-update-settings-whisper-api-stt.py @@ -69,7 +69,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55j-update-settings-sarvam-stt.py b/examples/foundational/55j-update-settings-sarvam-stt.py index 529cf461d..b65401dc5 100644 --- a/examples/foundational/55j-update-settings-sarvam-stt.py +++ b/examples/foundational/55j-update-settings-sarvam-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55k-update-settings-soniox-stt.py b/examples/foundational/55k-update-settings-soniox-stt.py index 07e33b4cd..2a2548888 100644 --- a/examples/foundational/55k-update-settings-soniox-stt.py +++ b/examples/foundational/55k-update-settings-soniox-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55l-update-settings-aws-transcribe-stt.py b/examples/foundational/55l-update-settings-aws-transcribe-stt.py index e18833d8d..cd9cfec11 100644 --- a/examples/foundational/55l-update-settings-aws-transcribe-stt.py +++ b/examples/foundational/55l-update-settings-aws-transcribe-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55m-update-settings-cartesia-stt.py b/examples/foundational/55m-update-settings-cartesia-stt.py index 0599c9b56..acd7fb972 100644 --- a/examples/foundational/55m-update-settings-cartesia-stt.py +++ b/examples/foundational/55m-update-settings-cartesia-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55n-update-settings-cartesia-http-tts.py b/examples/foundational/55n-update-settings-cartesia-http-tts.py index af4e8d5c1..328940bb5 100644 --- a/examples/foundational/55n-update-settings-cartesia-http-tts.py +++ b/examples/foundational/55n-update-settings-cartesia-http-tts.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55n-update-settings-cartesia-tts.py b/examples/foundational/55n-update-settings-cartesia-tts.py index 64afeffc2..67e0c599b 100644 --- a/examples/foundational/55n-update-settings-cartesia-tts.py +++ b/examples/foundational/55n-update-settings-cartesia-tts.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55o-update-settings-elevenlabs-http-tts.py b/examples/foundational/55o-update-settings-elevenlabs-http-tts.py index 8b6479edf..d819f2bdf 100644 --- a/examples/foundational/55o-update-settings-elevenlabs-http-tts.py +++ b/examples/foundational/55o-update-settings-elevenlabs-http-tts.py @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55o-update-settings-elevenlabs-tts.py b/examples/foundational/55o-update-settings-elevenlabs-tts.py index 03a692324..9afa33252 100644 --- a/examples/foundational/55o-update-settings-elevenlabs-tts.py +++ b/examples/foundational/55o-update-settings-elevenlabs-tts.py @@ -60,7 +60,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55p-update-settings-openai-tts.py b/examples/foundational/55p-update-settings-openai-tts.py index 110ee435b..a6f2b40c7 100644 --- a/examples/foundational/55p-update-settings-openai-tts.py +++ b/examples/foundational/55p-update-settings-openai-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55q-update-settings-deepgram-http-tts.py b/examples/foundational/55q-update-settings-deepgram-http-tts.py index 877ec3f65..706dcc357 100644 --- a/examples/foundational/55q-update-settings-deepgram-http-tts.py +++ b/examples/foundational/55q-update-settings-deepgram-http-tts.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py b/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py index 270e00747..76c67095f 100644 --- a/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py +++ b/examples/foundational/55q-update-settings-deepgram-sagemaker-tts.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55q-update-settings-deepgram-tts.py b/examples/foundational/55q-update-settings-deepgram-tts.py index 335526768..39a6ea7b7 100644 --- a/examples/foundational/55q-update-settings-deepgram-tts.py +++ b/examples/foundational/55q-update-settings-deepgram-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55r-update-settings-azure-http-tts.py b/examples/foundational/55r-update-settings-azure-http-tts.py index 56cb23854..c75ba5ff4 100644 --- a/examples/foundational/55r-update-settings-azure-http-tts.py +++ b/examples/foundational/55r-update-settings-azure-http-tts.py @@ -60,7 +60,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55r-update-settings-azure-tts.py b/examples/foundational/55r-update-settings-azure-tts.py index 5ccaa99bc..851e16de3 100644 --- a/examples/foundational/55r-update-settings-azure-tts.py +++ b/examples/foundational/55r-update-settings-azure-tts.py @@ -60,7 +60,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55s-update-settings-google-http-tts.py b/examples/foundational/55s-update-settings-google-http-tts.py index fbe44ff07..bb9b1862b 100644 --- a/examples/foundational/55s-update-settings-google-http-tts.py +++ b/examples/foundational/55s-update-settings-google-http-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55s-update-settings-google-stream-tts.py b/examples/foundational/55s-update-settings-google-stream-tts.py index b3310975a..68cfd2aaa 100644 --- a/examples/foundational/55s-update-settings-google-stream-tts.py +++ b/examples/foundational/55s-update-settings-google-stream-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55t-update-settings-piper-http-tts.py b/examples/foundational/55t-update-settings-piper-http-tts.py index 0b208ccbc..57770cc91 100644 --- a/examples/foundational/55t-update-settings-piper-http-tts.py +++ b/examples/foundational/55t-update-settings-piper-http-tts.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55t-update-settings-piper-tts.py b/examples/foundational/55t-update-settings-piper-tts.py index 6e9d79a72..5416b7e58 100644 --- a/examples/foundational/55t-update-settings-piper-tts.py +++ b/examples/foundational/55t-update-settings-piper-tts.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55u-update-settings-rime-http-tts.py b/examples/foundational/55u-update-settings-rime-http-tts.py index 07bb7e9a9..1d7154107 100644 --- a/examples/foundational/55u-update-settings-rime-http-tts.py +++ b/examples/foundational/55u-update-settings-rime-http-tts.py @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55u-update-settings-rime-tts.py b/examples/foundational/55u-update-settings-rime-tts.py index bbbdf534d..33a811228 100644 --- a/examples/foundational/55u-update-settings-rime-tts.py +++ b/examples/foundational/55u-update-settings-rime-tts.py @@ -60,7 +60,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55v-update-settings-lmnt-tts.py b/examples/foundational/55v-update-settings-lmnt-tts.py index 30f8135a0..1a6c9bd07 100644 --- a/examples/foundational/55v-update-settings-lmnt-tts.py +++ b/examples/foundational/55v-update-settings-lmnt-tts.py @@ -60,7 +60,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55w-update-settings-fish-tts.py b/examples/foundational/55w-update-settings-fish-tts.py index f54d97684..6ba018d0e 100644 --- a/examples/foundational/55w-update-settings-fish-tts.py +++ b/examples/foundational/55w-update-settings-fish-tts.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55x-update-settings-minimax-tts.py b/examples/foundational/55x-update-settings-minimax-tts.py index 53a120811..c91e728a4 100644 --- a/examples/foundational/55x-update-settings-minimax-tts.py +++ b/examples/foundational/55x-update-settings-minimax-tts.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55y-update-settings-groq-tts.py b/examples/foundational/55y-update-settings-groq-tts.py index c58b672c8..2e1a7a09d 100644 --- a/examples/foundational/55y-update-settings-groq-tts.py +++ b/examples/foundational/55y-update-settings-groq-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55z-update-settings-hume-tts.py b/examples/foundational/55z-update-settings-hume-tts.py index 3af8b5fe7..4a1fc061b 100644 --- a/examples/foundational/55z-update-settings-hume-tts.py +++ b/examples/foundational/55z-update-settings-hume-tts.py @@ -60,7 +60,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55za-update-settings-neuphonic-http-tts.py b/examples/foundational/55za-update-settings-neuphonic-http-tts.py index daaef4750..12d44aa02 100644 --- a/examples/foundational/55za-update-settings-neuphonic-http-tts.py +++ b/examples/foundational/55za-update-settings-neuphonic-http-tts.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55za-update-settings-neuphonic-tts.py b/examples/foundational/55za-update-settings-neuphonic-tts.py index dc9d3cb6f..f3b2970a6 100644 --- a/examples/foundational/55za-update-settings-neuphonic-tts.py +++ b/examples/foundational/55za-update-settings-neuphonic-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zb-update-settings-inworld-http-tts.py b/examples/foundational/55zb-update-settings-inworld-http-tts.py index bb031c231..fb96a8eeb 100644 --- a/examples/foundational/55zb-update-settings-inworld-http-tts.py +++ b/examples/foundational/55zb-update-settings-inworld-http-tts.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zb-update-settings-inworld-tts.py b/examples/foundational/55zb-update-settings-inworld-tts.py index f1581bd44..07ee4d674 100644 --- a/examples/foundational/55zb-update-settings-inworld-tts.py +++ b/examples/foundational/55zb-update-settings-inworld-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zc-update-settings-gemini-tts.py b/examples/foundational/55zc-update-settings-gemini-tts.py index 0bf027f20..fd7a8a5f7 100644 --- a/examples/foundational/55zc-update-settings-gemini-tts.py +++ b/examples/foundational/55zc-update-settings-gemini-tts.py @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zd-update-settings-aws-polly-tts.py b/examples/foundational/55zd-update-settings-aws-polly-tts.py index 38a2544f2..d293e12ed 100644 --- a/examples/foundational/55zd-update-settings-aws-polly-tts.py +++ b/examples/foundational/55zd-update-settings-aws-polly-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55ze-update-settings-sarvam-http-tts.py b/examples/foundational/55ze-update-settings-sarvam-http-tts.py index e00dfc621..5bc5da404 100644 --- a/examples/foundational/55ze-update-settings-sarvam-http-tts.py +++ b/examples/foundational/55ze-update-settings-sarvam-http-tts.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55ze-update-settings-sarvam-tts.py b/examples/foundational/55ze-update-settings-sarvam-tts.py index 6b6a0709a..8df7f781b 100644 --- a/examples/foundational/55ze-update-settings-sarvam-tts.py +++ b/examples/foundational/55ze-update-settings-sarvam-tts.py @@ -57,7 +57,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zf-update-settings-camb-tts.py b/examples/foundational/55zf-update-settings-camb-tts.py index 37b845ab4..66a90b6a8 100644 --- a/examples/foundational/55zf-update-settings-camb-tts.py +++ b/examples/foundational/55zf-update-settings-camb-tts.py @@ -58,7 +58,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zg-update-settings-kokoro-tts.py b/examples/foundational/55zg-update-settings-kokoro-tts.py index 69fb14512..2ef247f66 100644 --- a/examples/foundational/55zg-update-settings-kokoro-tts.py +++ b/examples/foundational/55zg-update-settings-kokoro-tts.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zh-update-settings-resembleai-tts.py b/examples/foundational/55zh-update-settings-resembleai-tts.py index befa0ce65..45f8785a0 100644 --- a/examples/foundational/55zh-update-settings-resembleai-tts.py +++ b/examples/foundational/55zh-update-settings-resembleai-tts.py @@ -60,7 +60,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zi-update-settings-azure-llm.py b/examples/foundational/55zi-update-settings-azure-llm.py index 5fbd796ca..20bae3aac 100644 --- a/examples/foundational/55zi-update-settings-azure-llm.py +++ b/examples/foundational/55zi-update-settings-azure-llm.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"), settings=AzureLLMService.Settings( model=os.getenv("AZURE_CHATGPT_MODEL"), - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zi-update-settings-openai-llm.py b/examples/foundational/55zi-update-settings-openai-llm.py index 76a14a3f0..5862e3cab 100644 --- a/examples/foundational/55zi-update-settings-openai-llm.py +++ b/examples/foundational/55zi-update-settings-openai-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zj-update-settings-anthropic-llm.py b/examples/foundational/55zj-update-settings-anthropic-llm.py index ba729bf4c..437769643 100644 --- a/examples/foundational/55zj-update-settings-anthropic-llm.py +++ b/examples/foundational/55zj-update-settings-anthropic-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = AnthropicLLMService( api_key=os.getenv("ANTHROPIC_API_KEY"), settings=AnthropicLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zk-update-settings-google-llm.py b/examples/foundational/55zk-update-settings-google-llm.py index dc3e5917a..a9fbfd093 100644 --- a/examples/foundational/55zk-update-settings-google-llm.py +++ b/examples/foundational/55zk-update-settings-google-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GoogleLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GoogleLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zk-update-settings-google-vertex-llm.py b/examples/foundational/55zk-update-settings-google-vertex-llm.py index 7f7a34cba..c2c8ea2bf 100644 --- a/examples/foundational/55zk-update-settings-google-vertex-llm.py +++ b/examples/foundational/55zk-update-settings-google-vertex-llm.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), location=os.getenv("GOOGLE_CLOUD_LOCATION"), settings=GoogleVertexLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zl-update-settings-azure-realtime.py b/examples/foundational/55zl-update-settings-azure-realtime.py index a977ea60c..fde633912 100644 --- a/examples/foundational/55zl-update-settings-azure-realtime.py +++ b/examples/foundational/55zl-update-settings-azure-realtime.py @@ -58,7 +58,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): messages = [ { "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + "content": "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", }, ] diff --git a/examples/foundational/55zl-update-settings-openai-realtime.py b/examples/foundational/55zl-update-settings-openai-realtime.py index 9f7281a72..83cdb9fa7 100644 --- a/examples/foundational/55zl-update-settings-openai-realtime.py +++ b/examples/foundational/55zl-update-settings-openai-realtime.py @@ -55,7 +55,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): messages = [ { "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + "content": "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", }, ] diff --git a/examples/foundational/55zm-update-settings-gemini-live-vertex.py b/examples/foundational/55zm-update-settings-gemini-live-vertex.py index c6a04347a..59b9a5a41 100644 --- a/examples/foundational/55zm-update-settings-gemini-live-vertex.py +++ b/examples/foundational/55zm-update-settings-gemini-live-vertex.py @@ -53,7 +53,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): project_id=os.getenv("GOOGLE_CLOUD_PROJECT_ID"), location=os.getenv("GOOGLE_CLOUD_LOCATION"), settings=GeminiLiveVertexLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zm-update-settings-gemini-live.py b/examples/foundational/55zm-update-settings-gemini-live.py index beb7f0c1c..fd5ef213a 100644 --- a/examples/foundational/55zm-update-settings-gemini-live.py +++ b/examples/foundational/55zm-update-settings-gemini-live.py @@ -51,7 +51,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GeminiLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), settings=GeminiLiveLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zn-update-settings-ultravox-realtime.py b/examples/foundational/55zn-update-settings-ultravox-realtime.py index ccf2d2003..f77aa4252 100644 --- a/examples/foundational/55zn-update-settings-ultravox-realtime.py +++ b/examples/foundational/55zn-update-settings-ultravox-realtime.py @@ -51,7 +51,7 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - system_prompt = "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way." + system_prompt = "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way." llm = UltravoxRealtimeLLMService( params=OneShotInputParams( diff --git a/examples/foundational/55zo-update-settings-grok-realtime.py b/examples/foundational/55zo-update-settings-grok-realtime.py index 60ba18755..0d44470e5 100644 --- a/examples/foundational/55zo-update-settings-grok-realtime.py +++ b/examples/foundational/55zo-update-settings-grok-realtime.py @@ -55,7 +55,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): messages = [ { "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + "content": "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", }, ] diff --git a/examples/foundational/55zp-update-settings-aws-bedrock-llm.py b/examples/foundational/55zp-update-settings-aws-bedrock-llm.py index 2452ceada..96f151463 100644 --- a/examples/foundational/55zp-update-settings-aws-bedrock-llm.py +++ b/examples/foundational/55zp-update-settings-aws-bedrock-llm.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): settings=AWSBedrockLLMService.Settings( model="us.anthropic.claude-sonnet-4-6", temperature=0.8, - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zq-update-settings-fal-stt.py b/examples/foundational/55zq-update-settings-fal-stt.py index 77d9bfd85..8047c04e3 100644 --- a/examples/foundational/55zq-update-settings-fal-stt.py +++ b/examples/foundational/55zq-update-settings-fal-stt.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zr-update-settings-gradium-stt.py b/examples/foundational/55zr-update-settings-gradium-stt.py index e621cc89b..b906306b4 100644 --- a/examples/foundational/55zr-update-settings-gradium-stt.py +++ b/examples/foundational/55zr-update-settings-gradium-stt.py @@ -65,7 +65,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zs-update-settings-whisper-mlx-stt.py b/examples/foundational/55zs-update-settings-whisper-mlx-stt.py index a3ca78f8c..c60cd072b 100644 --- a/examples/foundational/55zs-update-settings-whisper-mlx-stt.py +++ b/examples/foundational/55zs-update-settings-whisper-mlx-stt.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zs-update-settings-whisper-stt.py b/examples/foundational/55zs-update-settings-whisper-stt.py index b58e86dbf..5af0049ea 100644 --- a/examples/foundational/55zs-update-settings-whisper-stt.py +++ b/examples/foundational/55zs-update-settings-whisper-stt.py @@ -67,7 +67,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py b/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py index 62d10d455..28717e035 100644 --- a/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py +++ b/examples/foundational/55zt-update-settings-nvidia-segmented-stt.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zt-update-settings-nvidia-stt.py b/examples/foundational/55zt-update-settings-nvidia-stt.py index 612f12acf..c2ad7c813 100644 --- a/examples/foundational/55zt-update-settings-nvidia-stt.py +++ b/examples/foundational/55zt-update-settings-nvidia-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zu-update-settings-openai-realtime-stt.py b/examples/foundational/55zu-update-settings-openai-realtime-stt.py index 62cf902ed..5ddea6181 100644 --- a/examples/foundational/55zu-update-settings-openai-realtime-stt.py +++ b/examples/foundational/55zu-update-settings-openai-realtime-stt.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zv-update-settings-asyncai-http-tts.py b/examples/foundational/55zv-update-settings-asyncai-http-tts.py index 639a28d7f..caaba5ff2 100644 --- a/examples/foundational/55zv-update-settings-asyncai-http-tts.py +++ b/examples/foundational/55zv-update-settings-asyncai-http-tts.py @@ -68,7 +68,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zv-update-settings-asyncai-tts.py b/examples/foundational/55zv-update-settings-asyncai-tts.py index cac3fde19..6aa678df7 100644 --- a/examples/foundational/55zv-update-settings-asyncai-tts.py +++ b/examples/foundational/55zv-update-settings-asyncai-tts.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zw-update-settings-gradium-tts.py b/examples/foundational/55zw-update-settings-gradium-tts.py index d2b3535c4..71f19dc98 100644 --- a/examples/foundational/55zw-update-settings-gradium-tts.py +++ b/examples/foundational/55zw-update-settings-gradium-tts.py @@ -61,7 +61,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zx-update-settings-cerebras-llm.py b/examples/foundational/55zx-update-settings-cerebras-llm.py index 2d8e5a3d9..6e03a1d8b 100644 --- a/examples/foundational/55zx-update-settings-cerebras-llm.py +++ b/examples/foundational/55zx-update-settings-cerebras-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = CerebrasLLMService( api_key=os.getenv("CEREBRAS_API_KEY"), settings=CerebrasLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zy-update-settings-deepseek-llm.py b/examples/foundational/55zy-update-settings-deepseek-llm.py index 2a439f76d..4d34cf67b 100644 --- a/examples/foundational/55zy-update-settings-deepseek-llm.py +++ b/examples/foundational/55zy-update-settings-deepseek-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = DeepSeekLLMService( api_key=os.getenv("DEEPSEEK_API_KEY"), settings=DeepSeekLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zz-update-settings-fireworks-llm.py b/examples/foundational/55zz-update-settings-fireworks-llm.py index 0b184974d..85d83097e 100644 --- a/examples/foundational/55zz-update-settings-fireworks-llm.py +++ b/examples/foundational/55zz-update-settings-fireworks-llm.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("FIREWORKS_API_KEY"), settings=FireworksLLMService.Settings( model="accounts/fireworks/models/gpt-oss-20b", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zza-update-settings-grok-llm.py b/examples/foundational/55zza-update-settings-grok-llm.py index c5caef791..0a793d58e 100644 --- a/examples/foundational/55zza-update-settings-grok-llm.py +++ b/examples/foundational/55zza-update-settings-grok-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = GrokLLMService( api_key=os.getenv("GROK_API_KEY"), settings=GrokLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzb-update-settings-groq-llm.py b/examples/foundational/55zzb-update-settings-groq-llm.py index 25d1b4019..896f5d6ab 100644 --- a/examples/foundational/55zzb-update-settings-groq-llm.py +++ b/examples/foundational/55zzb-update-settings-groq-llm.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("GROQ_API_KEY"), settings=GroqLLMService.Settings( model="meta-llama/llama-4-maverick-17b-128e-instruct", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzc-update-settings-mistral-llm.py b/examples/foundational/55zzc-update-settings-mistral-llm.py index 94ee3f29a..6a03b9e8c 100644 --- a/examples/foundational/55zzc-update-settings-mistral-llm.py +++ b/examples/foundational/55zzc-update-settings-mistral-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = MistralLLMService( api_key=os.getenv("MISTRAL_API_KEY"), settings=MistralLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzd-update-settings-nvidia-llm.py b/examples/foundational/55zzd-update-settings-nvidia-llm.py index eb099e50c..c34d45a2a 100644 --- a/examples/foundational/55zzd-update-settings-nvidia-llm.py +++ b/examples/foundational/55zzd-update-settings-nvidia-llm.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("NVIDIA_API_KEY"), settings=NvidiaLLMService.Settings( model="meta/llama-3.1-405b-instruct", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zze-update-settings-ollama-llm.py b/examples/foundational/55zze-update-settings-ollama-llm.py index 5625e188c..666f96e6b 100644 --- a/examples/foundational/55zze-update-settings-ollama-llm.py +++ b/examples/foundational/55zze-update-settings-ollama-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OLLamaLLMService( settings=OLLamaLLMService.Settings( model="llama3.2", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) # Update to the model you're running locally diff --git a/examples/foundational/55zzf-update-settings-openrouter-llm.py b/examples/foundational/55zzf-update-settings-openrouter-llm.py index 4745ae537..2647848bc 100644 --- a/examples/foundational/55zzf-update-settings-openrouter-llm.py +++ b/examples/foundational/55zzf-update-settings-openrouter-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenRouterLLMService( api_key=os.getenv("OPENROUTER_API_KEY"), settings=OpenRouterLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzg-update-settings-perplexity-llm.py b/examples/foundational/55zzg-update-settings-perplexity-llm.py index b764e0ff7..c495830dd 100644 --- a/examples/foundational/55zzg-update-settings-perplexity-llm.py +++ b/examples/foundational/55zzg-update-settings-perplexity-llm.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): messages = [ { "role": "user", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way. Start by introducing yourself.", + "content": "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. Start by introducing yourself.", }, ] diff --git a/examples/foundational/55zzh-update-settings-qwen-llm.py b/examples/foundational/55zzh-update-settings-qwen-llm.py index 2f56886b9..6cfcfc97d 100644 --- a/examples/foundational/55zzh-update-settings-qwen-llm.py +++ b/examples/foundational/55zzh-update-settings-qwen-llm.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("QWEN_API_KEY"), settings=QwenLLMService.Settings( model="qwen2.5-72b-instruct", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzi-update-settings-sambanova-llm.py b/examples/foundational/55zzi-update-settings-sambanova-llm.py index 46db2099f..46a6561ae 100644 --- a/examples/foundational/55zzi-update-settings-sambanova-llm.py +++ b/examples/foundational/55zzi-update-settings-sambanova-llm.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = SambaNovaLLMService( api_key=os.getenv("SAMBANOVA_API_KEY"), settings=SambaNovaLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzj-update-settings-together-llm.py b/examples/foundational/55zzj-update-settings-together-llm.py index e293613b1..3ffbe9c3e 100644 --- a/examples/foundational/55zzj-update-settings-together-llm.py +++ b/examples/foundational/55zzj-update-settings-together-llm.py @@ -63,7 +63,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): api_key=os.getenv("TOGETHER_API_KEY"), settings=TogetherLLMService.Settings( model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py b/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py index bec1c4d86..0d03efe58 100644 --- a/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py +++ b/examples/foundational/55zzk-update-settings-aws-nova-sonic-llm.py @@ -53,7 +53,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), region=os.getenv("AWS_REGION"), settings=AWSNovaSonicLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzl-update-settings-nvidia-tts.py b/examples/foundational/55zzl-update-settings-nvidia-tts.py index 5caaad3b4..3282c981d 100644 --- a/examples/foundational/55zzl-update-settings-nvidia-tts.py +++ b/examples/foundational/55zzl-update-settings-nvidia-tts.py @@ -58,7 +58,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzm-update-settings-speechmatics-tts.py b/examples/foundational/55zzm-update-settings-speechmatics-tts.py index 80a6c694c..908ff9918 100644 --- a/examples/foundational/55zzm-update-settings-speechmatics-tts.py +++ b/examples/foundational/55zzm-update-settings-speechmatics-tts.py @@ -62,7 +62,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzn-update-settings-groq-stt.py b/examples/foundational/55zzn-update-settings-groq-stt.py index 1b4e9b8ed..e5c903753 100644 --- a/examples/foundational/55zzn-update-settings-groq-stt.py +++ b/examples/foundational/55zzn-update-settings-groq-stt.py @@ -64,7 +64,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzo-update-settings-openpipe-llm.py b/examples/foundational/55zzo-update-settings-openpipe-llm.py index 669a35cd5..89a94d61f 100644 --- a/examples/foundational/55zzo-update-settings-openpipe-llm.py +++ b/examples/foundational/55zzo-update-settings-openpipe-llm.py @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): openpipe_api_key=os.getenv("OPENPIPE_API_KEY"), tags={"conversation_id": f"pipecat-{timestamp}"}, settings=OpenPipeLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/55zzp-update-settings-xtts-tts.py b/examples/foundational/55zzp-update-settings-xtts-tts.py index 8a7667036..adb90247b 100644 --- a/examples/foundational/55zzp-update-settings-xtts-tts.py +++ b/examples/foundational/55zzp-update-settings-xtts-tts.py @@ -66,7 +66,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): llm = OpenAILLMService( api_key=os.getenv("OPENAI_API_KEY"), settings=OpenAILLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) diff --git a/examples/foundational/56-lemonslice-transport.py b/examples/foundational/56-lemonslice-transport.py index 67f4e8ad1..667b317f8 100644 --- a/examples/foundational/56-lemonslice-transport.py +++ b/examples/foundational/56-lemonslice-transport.py @@ -58,7 +58,7 @@ async def main(): llm = GroqLLMService( api_key=os.getenv("GROQ_API_KEY"), settings=GroqLLMService.Settings( - system_instruction="You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be spoken aloud, so avoid special characters that can't easily be spoken, such as emojis or bullet points. Respond to what the user said in a creative and helpful way.", + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", ), ) From 79b7a0f969e5c8946fcd51fa794c8690929e3301 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 13:53:06 -0400 Subject: [PATCH 063/159] Fix DeepgramSTTService base_url forcing HTTPS/WSS schemes The base_url parameter previously forced wss:// and https:// schemes, breaking air-gapped or private deployments that need ws:// or http://. Extract URL derivation into _derive_deepgram_urls() helper that respects the developers scheme choice while deriving the paired WebSocket and HTTP URLs the Deepgram SDK requires. Closes #4019 --- src/pipecat/services/deepgram/stt.py | 42 +++++++++++++++++++++-- tests/test_deepgram_stt.py | 51 ++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/test_deepgram_stt.py diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index 7d849a160..32710d00c 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -247,6 +247,45 @@ class DeepgramSTTSettings(STTSettings): del self.extra[key] +def _derive_deepgram_urls(base_url: str) -> tuple[str, str]: + """Derive paired WebSocket and HTTP URLs from a single base URL. + + The Deepgram SDK client requires both a WebSocket URL (for streaming) + and an HTTP URL (for REST calls). This helper lets developers provide + a single ``base_url`` and consistently derives both, preserving the + security level they chose. Useful for air-gapped or private deployments + where insecure schemes (ws:// / http://) are acceptable. + + Accepted inputs: + - ``wss://`` or ``https://`` — secure (paired as wss + https) + - ``ws://`` or ``http://`` — insecure (paired as ws + http) + - Bare hostname (no scheme) — defaults to secure + - Unrecognized scheme — logs a warning, defaults to secure + + Args: + base_url: Host with optional scheme, port, and path. + + Returns: + A (ws_url, http_url) tuple with consistent schemes. + """ + known_schemes = ("wss://", "https://", "ws://", "http://") + if "://" in base_url: + scheme, host = base_url.split("://", 1) + scheme += "://" + if scheme not in known_schemes: + logger.warning( + f"Unrecognized scheme in base_url '{base_url}', defaulting to wss:// / https://" + ) + else: + scheme = "" + host = base_url + + insecure = scheme in ("ws://", "http://") + ws_url = f"{'ws' if insecure else 'wss'}://{host}" + http_url = f"{'http' if insecure else 'https'}://{host}" + return ws_url, http_url + + class DeepgramSTTService(STTService): """Deepgram speech-to-text service. @@ -445,8 +484,7 @@ class DeepgramSTTService(STTService): try: from deepgram import DeepgramClientEnvironment - ws_url = base_url if base_url.startswith("wss://") else f"wss://{base_url}" - http_url = base_url if base_url.startswith("https://") else f"https://{base_url}" + ws_url, http_url = _derive_deepgram_urls(base_url) environment = DeepgramClientEnvironment( base=http_url, production=ws_url, diff --git a/tests/test_deepgram_stt.py b/tests/test_deepgram_stt.py new file mode 100644 index 000000000..eb8036237 --- /dev/null +++ b/tests/test_deepgram_stt.py @@ -0,0 +1,51 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import io + +import pytest +from loguru import logger + +from pipecat.services.deepgram.stt import _derive_deepgram_urls + + +@pytest.mark.parametrize( + "base_url, expected_ws, expected_http", + [ + # Secure schemes + ("wss://mydeepgram.com", "wss://mydeepgram.com", "https://mydeepgram.com"), + ("https://mydeepgram.com", "wss://mydeepgram.com", "https://mydeepgram.com"), + # Insecure schemes (air-gapped deployments) + ("ws://mydeepgram.com", "ws://mydeepgram.com", "http://mydeepgram.com"), + ("http://mydeepgram.com", "ws://mydeepgram.com", "http://mydeepgram.com"), + # Bare hostname defaults to secure + ("mydeepgram.com", "wss://mydeepgram.com", "https://mydeepgram.com"), + # With port + ("ws://localhost:8080", "ws://localhost:8080", "http://localhost:8080"), + ("wss://localhost:443", "wss://localhost:443", "https://localhost:443"), + ("localhost:8080", "wss://localhost:8080", "https://localhost:8080"), + # With path + ("wss://host/v1/listen", "wss://host/v1/listen", "https://host/v1/listen"), + ("http://host/v1/listen", "ws://host/v1/listen", "http://host/v1/listen"), + ], +) +def test_derive_deepgram_urls(base_url, expected_ws, expected_http): + ws_url, http_url = _derive_deepgram_urls(base_url) + assert ws_url == expected_ws + assert http_url == expected_http + + +def test_derive_deepgram_urls_unknown_scheme_warns(): + sink = io.StringIO() + handler_id = logger.add(sink, format="{message}") + try: + ws_url, http_url = _derive_deepgram_urls("ftp://mydeepgram.com") + # Falls back to secure + assert ws_url == "wss://mydeepgram.com" + assert http_url == "https://mydeepgram.com" + assert "Unrecognized scheme" in sink.getvalue() + finally: + logger.remove(handler_id) From 2f7c441c1ce94947e39ff3ede49b10d02b75318b Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 13 Mar 2026 13:55:27 -0400 Subject: [PATCH 064/159] Add changelog for #4026 --- changelog/4026.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4026.fixed.md diff --git a/changelog/4026.fixed.md b/changelog/4026.fixed.md new file mode 100644 index 000000000..a12321ab4 --- /dev/null +++ b/changelog/4026.fixed.md @@ -0,0 +1 @@ +- Fixed `DeepgramSTTService` ignoring the `base_url` scheme when using `ws://` or `http://`. Previously these were silently overwritten with `wss://` / `https://`, breaking air-gapped or private deployments that don't use TLS. All scheme choices (`wss://`, `https://`, `ws://`, `http://`, or bare hostname) are now respected. From 24c3d23229b3b78582a8597c0f6d21c56fa9a030 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sun, 15 Mar 2026 08:53:06 -0400 Subject: [PATCH 065/159] Bump PyJWT minimum version to 2.12.0 for CVE-2026-32597 Addresses Dependabot alert #165 (GHSA-752w-5fwx-jx9f) where PyJWT <= 2.11.0 accepts unknown `crit` header extensions. --- pyproject.toml | 2 +- uv.lock | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 48226b14f..6c47ea8b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,7 +83,7 @@ kokoro = [ "kokoro-onnx>=0.5.0,<1", "requests>=2.32.5,<3" ] krisp = [ "pipecat-ai-krisp~=0.4.0" ] langchain = [ "langchain~=0.3.20", "langchain-community~=0.3.20", "langchain-openai~=0.3.9" ] lemonslice = [ "pipecat-ai[daily]" ] -livekit = [ "livekit>=1.0.13,<2", "livekit-api>=1.0.5,<2", "tenacity>=8.2.3,<10.0.0", "pyjwt>=2.10.1,<3" ] +livekit = [ "livekit>=1.0.13,<2", "livekit-api>=1.0.5,<2", "tenacity>=8.2.3,<10.0.0", "pyjwt>=2.12.0,<3" ] lmnt = [ "pipecat-ai[websockets-base]" ] local = [ "pyaudio~=0.2.14" ] local-smart-turn = [ "coremltools>=8.0", "transformers>=4.48.0,<6", "torch>=2.5.0,<3", "torchaudio>=2.5.0,<3" ] diff --git a/uv.lock b/uv.lock index b68216261..b11a51bb2 100644 --- a/uv.lock +++ b/uv.lock @@ -4862,7 +4862,7 @@ requires-dist = [ { name = "pyaudio", marker = "extra == 'local'", specifier = "~=0.2.14" }, { name = "pydantic", specifier = ">=2.10.6,<3" }, { name = "pygobject", marker = "extra == 'gstreamer'", specifier = "~=3.50.0" }, - { name = "pyjwt", marker = "extra == 'livekit'", specifier = ">=2.10.1,<3" }, + { name = "pyjwt", marker = "extra == 'livekit'", specifier = ">=2.12.0,<3" }, { name = "pyloudnorm", specifier = "~=0.2.0" }, { name = "pyrnnoise", marker = "extra == 'rnnoise'", specifier = "~=0.4.1" }, { name = "python-dotenv", marker = "extra == 'runner'", specifier = ">=1.0.0,<2.0.0" }, @@ -5473,11 +5473,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/a7/5d/f2946cc6c1baf56de [[package]] name = "pyjwt" -version = "2.11.0" +version = "2.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, ] [package.optional-dependencies] From e8415b745162d269867d6c07c0b5298fa8175e16 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sun, 15 Mar 2026 08:56:54 -0400 Subject: [PATCH 066/159] Add changelog for #4035 --- changelog/4035.security.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4035.security.md diff --git a/changelog/4035.security.md b/changelog/4035.security.md new file mode 100644 index 000000000..9ffc17305 --- /dev/null +++ b/changelog/4035.security.md @@ -0,0 +1 @@ +- Bumped PyJWT minimum version from 2.10.1 to 2.12.0 in the `livekit` extra to address CVE-2026-32597 (GHSA-752w-5fwx-jx9f), where PyJWT <= 2.11.0 accepted unknown `crit` header extensions. From a6ad8a355b8878bdc47b5ada12eece476469bb9a Mon Sep 17 00:00:00 2001 From: Om Chauhan Date: Sun, 15 Mar 2026 19:10:32 +0530 Subject: [PATCH 067/159] forward timeout_secs in LLMSwitcher register methods --- src/pipecat/pipeline/llm_switcher.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pipecat/pipeline/llm_switcher.py b/src/pipecat/pipeline/llm_switcher.py index 47147dba2..cfee6b2b0 100644 --- a/src/pipecat/pipeline/llm_switcher.py +++ b/src/pipecat/pipeline/llm_switcher.py @@ -80,6 +80,7 @@ class LLMSwitcher(ServiceSwitcher[StrategyType]): start_callback=None, *, cancel_on_interruption: bool = True, + timeout_secs: Optional[float] = None, ): """Register a function handler for LLM function calls, on all LLMs, active or not. @@ -96,6 +97,7 @@ class LLMSwitcher(ServiceSwitcher[StrategyType]): cancel_on_interruption: Whether to cancel this function call when an interruption occurs. Defaults to True. + timeout_secs: Optional timeout in seconds for the function call. """ for llm in self.llms: llm.register_function( @@ -103,6 +105,7 @@ class LLMSwitcher(ServiceSwitcher[StrategyType]): handler=handler, start_callback=start_callback, cancel_on_interruption=cancel_on_interruption, + timeout_secs=timeout_secs, ) def register_direct_function( @@ -110,6 +113,7 @@ class LLMSwitcher(ServiceSwitcher[StrategyType]): handler: DirectFunction, *, cancel_on_interruption: bool = True, + timeout_secs: Optional[float] = None, ): """Register a direct function handler for LLM function calls, on all LLMs, active or not. @@ -117,9 +121,11 @@ class LLMSwitcher(ServiceSwitcher[StrategyType]): handler: The direct function to register. Must follow DirectFunction protocol. cancel_on_interruption: Whether to cancel this function call when an interruption occurs. Defaults to True. + timeout_secs: Optional timeout in seconds for the function call. """ for llm in self.llms: llm.register_direct_function( handler=handler, cancel_on_interruption=cancel_on_interruption, + timeout_secs=timeout_secs, ) From ed0f5ab09b6187fac7a2ed9b68de2f215464280c Mon Sep 17 00:00:00 2001 From: Om Chauhan Date: Sun, 15 Mar 2026 19:15:18 +0530 Subject: [PATCH 068/159] added changelog --- changelog/4037.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4037.fixed.md diff --git a/changelog/4037.fixed.md b/changelog/4037.fixed.md new file mode 100644 index 000000000..e55b6f998 --- /dev/null +++ b/changelog/4037.fixed.md @@ -0,0 +1 @@ +- Fixed `LLMSwitcher.register_function()` and `register_direct_function()` not accepting or forwarding the `timeout_secs` parameter. From 538b9fa2d928e52434a50fc282d2ea0064ed5b6d Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 16 Mar 2026 17:58:44 -0400 Subject: [PATCH 069/159] Bump pyopenssl in uv.lock to 26.0.0 --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index b68216261..b91380198 100644 --- a/uv.lock +++ b/uv.lock @@ -5524,15 +5524,15 @@ wheels = [ [[package]] name = "pyopenssl" -version = "25.3.0" +version = "26.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/be/97b83a464498a79103036bc74d1038df4a7ef0e402cfaf4d5e113fb14759/pyopenssl-25.3.0.tar.gz", hash = "sha256:c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329", size = 184073, upload-time = "2025-09-17T00:32:21.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/11/a62e1d33b373da2b2c2cd9eb508147871c80f12b1cacde3c5d314922afdd/pyopenssl-26.0.0.tar.gz", hash = "sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc", size = 185534, upload-time = "2026-03-15T14:28:26.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/81/ef2b1dfd1862567d573a4fdbc9f969067621764fbb74338496840a1d2977/pyopenssl-25.3.0-py3-none-any.whl", hash = "sha256:1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6", size = 57268, upload-time = "2025-09-17T00:32:19.474Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7d/d4f7d908fa8415571771b30669251d57c3cf313b36a856e6d7548ae01619/pyopenssl-26.0.0-py3-none-any.whl", hash = "sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81", size = 57969, upload-time = "2026-03-15T14:28:24.864Z" }, ] [[package]] From 3b8d040e41781a5bcfe8a9a569e2fe853cbadd20 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 16 Mar 2026 19:45:03 -0400 Subject: [PATCH 070/159] Fix SonioxSTTService crash when language_hints contains plain strings (#4045) Refactor language_to_soniox_language to use resolve_language + LANGUAGE_MAP pattern consistent with other services. Fix resolve_language fallback to use str(language) instead of language.value so plain strings don't crash. --- changelog/4045.fixed.md | 1 + src/pipecat/services/soniox/stt.py | 75 +++++++++++++++++++++++--- src/pipecat/transcriptions/language.py | 6 +-- 3 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 changelog/4045.fixed.md diff --git a/changelog/4045.fixed.md b/changelog/4045.fixed.md new file mode 100644 index 000000000..ecae78969 --- /dev/null +++ b/changelog/4045.fixed.md @@ -0,0 +1 @@ +Fixed `SonioxSTTService` crash when `language_hints` contains plain strings instead of `Language` enum values. diff --git a/src/pipecat/services/soniox/stt.py b/src/pipecat/services/soniox/stt.py index f123d850c..5163ef113 100644 --- a/src/pipecat/services/soniox/stt.py +++ b/src/pipecat/services/soniox/stt.py @@ -27,7 +27,7 @@ from pipecat.processors.frame_processor import FrameDirection from pipecat.services.settings import NOT_GIVEN, STTSettings, _NotGiven from pipecat.services.stt_latency import SONIOX_TTFS_P99 from pipecat.services.stt_service import WebsocketSTTService -from pipecat.transcriptions.language import Language +from pipecat.transcriptions.language import Language, resolve_language from pipecat.utils.time import time_now_iso8601 from pipecat.utils.tracing.service_decorators import traced_stt @@ -118,14 +118,75 @@ def is_end_token(token: dict) -> bool: def language_to_soniox_language(language: Language) -> str: - """Pipecat Language enum uses same ISO 2-letter codes as Soniox, except with added regional variants. + """Convert a Pipecat Language to a Soniox language code. - For a list of all supported languages, see: https://soniox.com/docs/speech-to-text/core-concepts/supported-languages + For a list of all supported languages, see: + https://soniox.com/docs/speech-to-text/core-concepts/supported-languages """ - lang_str = str(language.value).lower() - if "-" in lang_str: - return lang_str.split("-")[0] - return lang_str + LANGUAGE_MAP = { + Language.AF: "af", + Language.AR: "ar", + Language.AZ: "az", + Language.BE: "be", + Language.BG: "bg", + Language.BN: "bn", + Language.BS: "bs", + Language.CA: "ca", + Language.CS: "cs", + Language.CY: "cy", + Language.DA: "da", + Language.DE: "de", + Language.EL: "el", + Language.EN: "en", + Language.ES: "es", + Language.ET: "et", + Language.EU: "eu", + Language.FA: "fa", + Language.FI: "fi", + Language.FR: "fr", + Language.GL: "gl", + Language.GU: "gu", + Language.HE: "he", + Language.HI: "hi", + Language.HR: "hr", + Language.HU: "hu", + Language.ID: "id", + Language.IT: "it", + Language.JA: "ja", + Language.KA: "ka", + Language.KK: "kk", + Language.KN: "kn", + Language.KO: "ko", + Language.LT: "lt", + Language.LV: "lv", + Language.MK: "mk", + Language.ML: "ml", + Language.MR: "mr", + Language.MS: "ms", + Language.NL: "nl", + Language.NO: "no", + Language.PA: "pa", + Language.PL: "pl", + Language.PT: "pt", + Language.RO: "ro", + Language.RU: "ru", + Language.SK: "sk", + Language.SL: "sl", + Language.SQ: "sq", + Language.SR: "sr", + Language.SV: "sv", + Language.SW: "sw", + Language.TA: "ta", + Language.TE: "te", + Language.TH: "th", + Language.TL: "tl", + Language.TR: "tr", + Language.UK: "uk", + Language.UR: "ur", + Language.VI: "vi", + Language.ZH: "zh", + } + return resolve_language(language, LANGUAGE_MAP, use_base_code=True) def _prepare_language_hints( diff --git a/src/pipecat/transcriptions/language.py b/src/pipecat/transcriptions/language.py index a79a85166..1980590e3 100644 --- a/src/pipecat/transcriptions/language.py +++ b/src/pipecat/transcriptions/language.py @@ -631,13 +631,13 @@ def resolve_language( return result # Not in map - fall back with warning - lang_str = str(language.value) + lang_str = str(language) if use_base_code: # Extract base code (e.g., "en" from "en-US") base_code = lang_str.split("-")[0].lower() - logger.warning(f"Language {language.value} not verified. Using base code '{base_code}'.") + logger.warning(f"Language {language} not verified. Using base code '{base_code}'.") return base_code else: - logger.warning(f"Language {language.value} not verified. Using '{lang_str}'.") + logger.warning(f"Language {language} not verified. Using '{lang_str}'.") return lang_str From 2801439e489eb4511521758210ae465754df6f72 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 16 Mar 2026 19:48:49 -0400 Subject: [PATCH 071/159] Fix OpenAI STT crash when language is a plain string instead of Language enum --- src/pipecat/services/openai/stt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/openai/stt.py b/src/pipecat/services/openai/stt.py index 39ca60b25..35ec0350c 100644 --- a/src/pipecat/services/openai/stt.py +++ b/src/pipecat/services/openai/stt.py @@ -358,8 +358,8 @@ class OpenAIRealtimeSTTService(WebsocketSTTService): Returns: Two-letter ISO-639-1 language code. """ - # Language.value is e.g. "en", "en-US", "fr", "zh". - return language.value.split("-")[0].lower() + # Language value is e.g. "en", "en-US", "fr", "zh". + return str(language).split("-")[0].lower() def can_generate_metrics(self) -> bool: """Check if the service can generate processing metrics. From abb8bae6f7b4bd9085393d7e14da9a9979e6ae13 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 16 Mar 2026 19:49:23 -0400 Subject: [PATCH 072/159] Add changelog for #4046 --- changelog/4045.fixed.md | 1 - changelog/4046.fixed.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog/4045.fixed.md create mode 100644 changelog/4046.fixed.md diff --git a/changelog/4045.fixed.md b/changelog/4045.fixed.md deleted file mode 100644 index ecae78969..000000000 --- a/changelog/4045.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed `SonioxSTTService` crash when `language_hints` contains plain strings instead of `Language` enum values. diff --git a/changelog/4046.fixed.md b/changelog/4046.fixed.md new file mode 100644 index 000000000..0f147e04e --- /dev/null +++ b/changelog/4046.fixed.md @@ -0,0 +1 @@ +Fixed `SonioxSTTService` and `OpenAIRealtimeSTTService` crash when language parameters contain plain strings instead of `Language` enum values. From 5c685c35d77cb12c10135161670c1c9e6f8195e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 16 Mar 2026 17:41:44 -0700 Subject: [PATCH 073/159] pyproject: update daily-python to 0.25.0 --- pyproject.toml | 2 +- uv.lock | 594 +++++++++++++++++++++++++------------------------ 2 files changed, 299 insertions(+), 297 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 48226b14f..ec729cf49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ azure = [ "azure-cognitiveservices-speech>=1.47.0,<2"] cartesia = [ "pipecat-ai[websockets-base]" ] camb = [ "camb-sdk>=1.5.4,<2" ] cerebras = [] -daily = [ "daily-python~=0.24.0" ] +daily = [ "daily-python~=0.25.0" ] deepgram = [ "deepgram-sdk>=6.0.1,<7", "pipecat-ai[websockets-base]" ] deepseek = [] elevenlabs = [ "pipecat-ai[websockets-base]" ] diff --git a/uv.lock b/uv.lock index b68216261..38026dc19 100644 --- a/uv.lock +++ b/uv.lock @@ -376,19 +376,20 @@ wheels = [ [[package]] name = "audiolab" -version = "0.4.8" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "av" }, { name = "click" }, { name = "humanize" }, { name = "jinja2" }, + { name = "requests" }, { name = "smart-open" }, { name = "soundfile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/37/4321edad0813a7779472db47dacd9884c3ce00e381215e5a7fae8aad5ac6/audiolab-0.4.8.tar.gz", hash = "sha256:b6cd0e3e0bdee45e2df30b875f8577a3a101b2d0632a854465862e05602a0a6b", size = 32490, upload-time = "2026-01-15T14:14:04.095Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/ba/c0fab2cff574cfef0fb81071c1a0bc9f021b477a341adcf79e3220447b3f/audiolab-0.5.0.tar.gz", hash = "sha256:12f33c3cbbd09a9b6089f78fa8dd3f6472af345e8cdd6524009e5b2409b46c6a", size = 32970, upload-time = "2026-03-16T11:43:44.544Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/28/e9cd5a5b8838cff665f4e176d381d87d395641955615a94e8f19d7c0d361/audiolab-0.4.8-py3-none-any.whl", hash = "sha256:bddc5be8c7abbea292416fd67532c33112a677084086fa060bb7155d3fff9274", size = 51326, upload-time = "2026-01-15T14:14:02.813Z" }, + { url = "https://files.pythonhosted.org/packages/84/0f/c96512dedf6dcb06bf3647460d4bbd0f119853f9bb60115703cee1cc1711/audiolab-0.5.0-py3-none-any.whl", hash = "sha256:9670e6253cec87cca8e6f37c32abc93aadfe5e185b6743e05c83bdee2acd310e", size = 51682, upload-time = "2026-03-16T11:43:43.351Z" }, ] [[package]] @@ -588,15 +589,15 @@ wheels = [ [[package]] name = "azure-core" -version = "1.38.2" +version = "1.38.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/fe/5c7710bc611a4070d06ba801de9a935cc87c3d4b689c644958047bdf2cba/azure_core-1.38.2.tar.gz", hash = "sha256:67562857cb979217e48dc60980243b61ea115b77326fa93d83b729e7ff0482e7", size = 363734, upload-time = "2026-02-18T19:33:05.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/29/9641b73248745774a52c7ce7f965ed1febbdea787ec21caad3ae6891d18a/azure_core-1.38.3.tar.gz", hash = "sha256:a7931fd445cb4af8802c6f39c6a326bbd1e34b115846550a8245fa656ead6f8e", size = 367267, upload-time = "2026-03-12T20:28:21.122Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/23/6371a551800d3812d6019cd813acd985f9fac0fedc1290129211a73da4ae/azure_core-1.38.2-py3-none-any.whl", hash = "sha256:074806c75cf239ea284a33a66827695ef7aeddac0b4e19dda266a93e4665ead9", size = 217957, upload-time = "2026-02-18T19:33:07.696Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3d/ac86083efa45a439d0bbfb7947615227813d368b9e1e93d23fd30de6fec0/azure_core-1.38.3-py3-none-any.whl", hash = "sha256:bf59d29765bf4748ab9edf25f98a30b7ea9797f43e367c06d846a30b29c1f845", size = 218231, upload-time = "2026-03-12T20:28:22.462Z" }, ] [[package]] @@ -672,7 +673,7 @@ wheels = [ [[package]] name = "camb-sdk" -version = "1.5.9" +version = "1.5.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -681,9 +682,9 @@ dependencies = [ { name = "websocket-client" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/29/17527519a72ed1592f28a4d380fd50ed72978ac38148efc0f9e796504496/camb_sdk-1.5.9.tar.gz", hash = "sha256:c8daaa8eea20c94523ffddd2aa630a902932f78ea8af37e140603e52ff0025ad", size = 83521, upload-time = "2026-02-27T22:57:18.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/38/31fcc633963804a150175078c167f237bbd56dedba9903e4800a630ab944/camb_sdk-1.5.10.tar.gz", hash = "sha256:e1d23cbccea5aef5944612740b5c2e109a3c3ce778caa9664678a23b3a254419", size = 83643, upload-time = "2026-03-12T11:40:36.822Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/2a/b759c32c60c51f33ceb299b52f8f73348773cd75d3177a15eefc25b2dee9/camb_sdk-1.5.9-py3-none-any.whl", hash = "sha256:8c3fe9d05adee1d8de121eb6f1ee0a37e913f072d89c11ed3399746a9b69adbc", size = 152395, upload-time = "2026-02-27T22:57:14.137Z" }, + { url = "https://files.pythonhosted.org/packages/94/12/f294bf4f343b663dced6d8ea840985d58b0afd66cd40e221fc19dc6639f4/camb_sdk-1.5.10-py3-none-any.whl", hash = "sha256:5ec6014af18cf108041c921f743fbcabc17015df2fc4b7a17793ef17d0fe593a", size = 152463, upload-time = "2026-03-12T11:38:37.934Z" }, ] [[package]] @@ -802,91 +803,107 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.5" +version = "3.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/21/a2b1505639008ba2e6ef03733a81fc6cfd6a07ea6139a2b76421230b8dad/charset_normalizer-3.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4167a621a9a1a986c73777dbc15d4b5eac8ac5c10393374109a343d4013ec765", size = 283319, upload-time = "2026-03-06T06:00:26.433Z" }, - { url = "https://files.pythonhosted.org/packages/70/67/df234c29b68f4e1e095885c9db1cb4b69b8aba49cf94fac041db4aaf1267/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f64c6bf8f32f9133b668c7f7a7cbdbc453412bc95ecdbd157f3b1e377a92990", size = 189974, upload-time = "2026-03-06T06:00:28.222Z" }, - { url = "https://files.pythonhosted.org/packages/df/7f/fc66af802961c6be42e2c7b69c58f95cbd1f39b0e81b3365d8efe2a02a04/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:568e3c34b58422075a1b49575a6abc616d9751b4d61b23f712e12ebb78fe47b2", size = 207866, upload-time = "2026-03-06T06:00:29.769Z" }, - { url = "https://files.pythonhosted.org/packages/c9/23/404eb36fac4e95b833c50e305bba9a241086d427bb2167a42eac7c4f7da4/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:036c079aa08a6a592b82487f97c60b439428320ed1b2ea0b3912e99d30c77765", size = 203239, upload-time = "2026-03-06T06:00:31.086Z" }, - { url = "https://files.pythonhosted.org/packages/4b/2f/8a1d989bfadd120c90114ab33e0d2a0cbde05278c1fc15e83e62d570f50a/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340810d34ef83af92148e96e3e44cb2d3f910d2bf95e5618a5c467d9f102231d", size = 196529, upload-time = "2026-03-06T06:00:32.608Z" }, - { url = "https://files.pythonhosted.org/packages/a5/0c/c75f85ff7ca1f051958bb518cd43922d86f576c03947a050fbedfdfb4f15/charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:cd2d0f0ec9aa977a27731a3209ebbcacebebaf41f902bd453a928bfd281cf7f8", size = 184152, upload-time = "2026-03-06T06:00:33.93Z" }, - { url = "https://files.pythonhosted.org/packages/f9/20/4ed37f6199af5dde94d4aeaf577f3813a5ec6635834cda1d957013a09c76/charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b362bcd27819f9c07cbf23db4e0e8cd4b44c5ecd900c2ff907b2b92274a7412", size = 195226, upload-time = "2026-03-06T06:00:35.469Z" }, - { url = "https://files.pythonhosted.org/packages/28/31/7ba1102178cba7c34dcc050f43d427172f389729e356038f0726253dd914/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:77be992288f720306ab4108fe5c74797de327f3248368dfc7e1a916d6ed9e5a2", size = 192933, upload-time = "2026-03-06T06:00:36.83Z" }, - { url = "https://files.pythonhosted.org/packages/4b/23/f86443ab3921e6a60b33b93f4a1161222231f6c69bc24fb18f3bee7b8518/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8b78d8a609a4b82c273257ee9d631ded7fac0d875bdcdccc109f3ee8328cfcb1", size = 185647, upload-time = "2026-03-06T06:00:38.367Z" }, - { url = "https://files.pythonhosted.org/packages/82/44/08b8be891760f1f5a6d23ce11d6d50c92981603e6eb740b4f72eea9424e2/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ba20bdf69bd127f66d0174d6f2a93e69045e0b4036dc1ca78e091bcc765830c4", size = 209533, upload-time = "2026-03-06T06:00:41.931Z" }, - { url = "https://files.pythonhosted.org/packages/3b/5f/df114f23406199f8af711ddccfbf409ffbc5b7cdc18fa19644997ff0c9bb/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:76a9d0de4d0eab387822e7b35d8f89367dd237c72e82ab42b9f7bf5e15ada00f", size = 195901, upload-time = "2026-03-06T06:00:43.978Z" }, - { url = "https://files.pythonhosted.org/packages/07/83/71ef34a76fe8aa05ff8f840244bda2d61e043c2ef6f30d200450b9f6a1be/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8fff79bf5978c693c9b1a4d71e4a94fddfb5fe744eb062a318e15f4a2f63a550", size = 204950, upload-time = "2026-03-06T06:00:45.202Z" }, - { url = "https://files.pythonhosted.org/packages/58/40/0253be623995365137d7dc68e45245036207ab2227251e69a3d93ce43183/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c7e84e0c0005e3bdc1a9211cd4e62c78ba80bc37b2365ef4410cd2007a9047f2", size = 198546, upload-time = "2026-03-06T06:00:46.481Z" }, - { url = "https://files.pythonhosted.org/packages/ed/5c/5f3cb5b259a130895ef5ae16b38eaf141430fa3f7af50cd06c5d67e4f7b2/charset_normalizer-3.4.5-cp310-cp310-win32.whl", hash = "sha256:58ad8270cfa5d4bef1bc85bd387217e14ff154d6630e976c6f56f9a040757475", size = 132516, upload-time = "2026-03-06T06:00:47.924Z" }, - { url = "https://files.pythonhosted.org/packages/a5/c3/84fb174e7770f2df2e1a2115090771bfbc2227fb39a765c6d00568d1aab4/charset_normalizer-3.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:02a9d1b01c1e12c27883b0c9349e0bcd9ae92e727ff1a277207e1a262b1cbf05", size = 142906, upload-time = "2026-03-06T06:00:49.389Z" }, - { url = "https://files.pythonhosted.org/packages/d7/b2/6f852f8b969f2cbd0d4092d2e60139ab1af95af9bb651337cae89ec0f684/charset_normalizer-3.4.5-cp310-cp310-win_arm64.whl", hash = "sha256:039215608ac7b358c4da0191d10fc76868567fbf276d54c14721bdedeb6de064", size = 133258, upload-time = "2026-03-06T06:00:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9e/bcec3b22c64ecec47d39bf5167c2613efd41898c019dccd4183f6aa5d6a7/charset_normalizer-3.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:610f72c0ee565dfb8ae1241b666119582fdbfe7c0975c175be719f940e110694", size = 279531, upload-time = "2026-03-06T06:00:52.252Z" }, - { url = "https://files.pythonhosted.org/packages/58/12/81fd25f7e7078ab5d1eedbb0fac44be4904ae3370a3bf4533c8f2d159acd/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60d68e820af339df4ae8358c7a2e7596badeb61e544438e489035f9fbf3246a5", size = 188006, upload-time = "2026-03-06T06:00:53.8Z" }, - { url = "https://files.pythonhosted.org/packages/ae/6e/f2d30e8c27c1b0736a6520311982cf5286cfc7f6cac77d7bc1325e3a23f2/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b473fc8dca1c3ad8559985794815f06ca3fc71942c969129070f2c3cdf7281", size = 205085, upload-time = "2026-03-06T06:00:55.311Z" }, - { url = "https://files.pythonhosted.org/packages/d0/90/d12cefcb53b5931e2cf792a33718d7126efb116a320eaa0742c7059a95e4/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d4eb8ac7469b2a5d64b5b8c04f84d8bf3ad340f4514b98523805cbf46e3b3923", size = 200545, upload-time = "2026-03-06T06:00:56.532Z" }, - { url = "https://files.pythonhosted.org/packages/03/f4/44d3b830a20e89ff82a3134912d9a1cf6084d64f3b95dcad40f74449a654/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bcb3227c3d9aaf73eaaab1db7ccd80a8995c509ee9941e2aae060ca6e4e5d81", size = 193863, upload-time = "2026-03-06T06:00:57.823Z" }, - { url = "https://files.pythonhosted.org/packages/25/4b/f212119c18a6320a9d4a730d1b4057875cdeabf21b3614f76549042ef8a8/charset_normalizer-3.4.5-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:75ee9c1cce2911581a70a3c0919d8bccf5b1cbc9b0e5171400ec736b4b569497", size = 181827, upload-time = "2026-03-06T06:00:59.323Z" }, - { url = "https://files.pythonhosted.org/packages/74/00/b26158e48b425a202a92965f8069e8a63d9af1481dfa206825d7f74d2a3c/charset_normalizer-3.4.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d1401945cb77787dbd3af2446ff2d75912327c4c3a1526ab7955ecf8600687c", size = 191085, upload-time = "2026-03-06T06:01:00.546Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c2/1c1737bf6fd40335fe53d28fe49afd99ee4143cc57a845e99635ce0b9b6d/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a45e504f5e1be0bd385935a8e1507c442349ca36f511a47057a71c9d1d6ea9e", size = 190688, upload-time = "2026-03-06T06:01:02.479Z" }, - { url = "https://files.pythonhosted.org/packages/5a/3d/abb5c22dc2ef493cd56522f811246a63c5427c08f3e3e50ab663de27fcf4/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e09f671a54ce70b79a1fc1dc6da3072b7ef7251fadb894ed92d9aa8218465a5f", size = 183077, upload-time = "2026-03-06T06:01:04.231Z" }, - { url = "https://files.pythonhosted.org/packages/44/33/5298ad4d419a58e25b3508e87f2758d1442ff00c2471f8e0403dab8edad5/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d01de5e768328646e6a3fa9e562706f8f6641708c115c62588aef2b941a4f88e", size = 206706, upload-time = "2026-03-06T06:01:05.773Z" }, - { url = "https://files.pythonhosted.org/packages/7b/17/51e7895ac0f87c3b91d276a449ef09f5532a7529818f59646d7a55089432/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:131716d6786ad5e3dc542f5cc6f397ba3339dc0fb87f87ac30e550e8987756af", size = 191665, upload-time = "2026-03-06T06:01:07.473Z" }, - { url = "https://files.pythonhosted.org/packages/90/8f/cce9adf1883e98906dbae380d769b4852bb0fa0004bc7d7a2243418d3ea8/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a374cc0b88aa710e8865dc1bd6edb3743c59f27830f0293ab101e4cf3ce9f85", size = 201950, upload-time = "2026-03-06T06:01:08.973Z" }, - { url = "https://files.pythonhosted.org/packages/08/ca/bce99cd5c397a52919e2769d126723f27a4c037130374c051c00470bcd38/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d31f0d1671e1534e395f9eb84a68e0fb670e1edb1fe819a9d7f564ae3bc4e53f", size = 195830, upload-time = "2026-03-06T06:01:10.155Z" }, - { url = "https://files.pythonhosted.org/packages/87/4f/2e3d023a06911f1281f97b8f036edc9872167036ca6f55cc874a0be6c12c/charset_normalizer-3.4.5-cp311-cp311-win32.whl", hash = "sha256:cace89841c0599d736d3d74a27bc5821288bb47c5441923277afc6059d7fbcb4", size = 132029, upload-time = "2026-03-06T06:01:11.706Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1f/a853b73d386521fd44b7f67ded6b17b7b2367067d9106a5c4b44f9a34274/charset_normalizer-3.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:f8102ae93c0bc863b1d41ea0f4499c20a83229f52ed870850892df555187154a", size = 142404, upload-time = "2026-03-06T06:01:12.865Z" }, - { url = "https://files.pythonhosted.org/packages/b4/10/dba36f76b71c38e9d391abe0fd8a5b818790e053c431adecfc98c35cd2a9/charset_normalizer-3.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:ed98364e1c262cf5f9363c3eca8c2df37024f52a8fa1180a3610014f26eac51c", size = 132796, upload-time = "2026-03-06T06:01:14.106Z" }, - { url = "https://files.pythonhosted.org/packages/9c/b6/9ee9c1a608916ca5feae81a344dffbaa53b26b90be58cc2159e3332d44ec/charset_normalizer-3.4.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed97c282ee4f994ef814042423a529df9497e3c666dca19be1d4cd1129dc7ade", size = 280976, upload-time = "2026-03-06T06:01:15.276Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d8/a54f7c0b96f1df3563e9190f04daf981e365a9b397eedfdfb5dbef7e5c6c/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0294916d6ccf2d069727d65973c3a1ca477d68708db25fd758dd28b0827cff54", size = 189356, upload-time = "2026-03-06T06:01:16.511Z" }, - { url = "https://files.pythonhosted.org/packages/42/69/2bf7f76ce1446759a5787cb87d38f6a61eb47dbbdf035cfebf6347292a65/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc57a0baa3eeedd99fafaef7511b5a6ef4581494e8168ee086031744e2679467", size = 206369, upload-time = "2026-03-06T06:01:17.853Z" }, - { url = "https://files.pythonhosted.org/packages/10/9c/949d1a46dab56b959d9a87272482195f1840b515a3380e39986989a893ae/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ed1a9a204f317ef879b32f9af507d47e49cd5e7f8e8d5d96358c98373314fc60", size = 203285, upload-time = "2026-03-06T06:01:19.473Z" }, - { url = "https://files.pythonhosted.org/packages/67/5c/ae30362a88b4da237d71ea214a8c7eb915db3eec941adda511729ac25fa2/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad83b8f9379176c841f8865884f3514d905bcd2a9a3b210eaa446e7d2223e4d", size = 196274, upload-time = "2026-03-06T06:01:20.728Z" }, - { url = "https://files.pythonhosted.org/packages/b2/07/c9f2cb0e46cb6d64fdcc4f95953747b843bb2181bda678dc4e699b8f0f9a/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:a118e2e0b5ae6b0120d5efa5f866e58f2bb826067a646431da4d6a2bdae7950e", size = 184715, upload-time = "2026-03-06T06:01:22.194Z" }, - { url = "https://files.pythonhosted.org/packages/36/64/6b0ca95c44fddf692cd06d642b28f63009d0ce325fad6e9b2b4d0ef86a52/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:754f96058e61a5e22e91483f823e07df16416ce76afa4ebf306f8e1d1296d43f", size = 193426, upload-time = "2026-03-06T06:01:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/50/bc/a730690d726403743795ca3f5bb2baf67838c5fea78236098f324b965e40/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0c300cefd9b0970381a46394902cd18eaf2aa00163f999590ace991989dcd0fc", size = 191780, upload-time = "2026-03-06T06:01:25.053Z" }, - { url = "https://files.pythonhosted.org/packages/97/4f/6c0bc9af68222b22951552d73df4532b5be6447cee32d58e7e8c74ecbb7b/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c108f8619e504140569ee7de3f97d234f0fbae338a7f9f360455071ef9855a95", size = 185805, upload-time = "2026-03-06T06:01:26.294Z" }, - { url = "https://files.pythonhosted.org/packages/dd/b9/a523fb9b0ee90814b503452b2600e4cbc118cd68714d57041564886e7325/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d1028de43596a315e2720a9849ee79007ab742c06ad8b45a50db8cdb7ed4a82a", size = 208342, upload-time = "2026-03-06T06:01:27.55Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/c59e761dee4464050713e50e27b58266cc8e209e518c0b378c1580c959ba/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:19092dde50335accf365cce21998a1c6dd8eafd42c7b226eb54b2747cdce2fac", size = 193661, upload-time = "2026-03-06T06:01:29.051Z" }, - { url = "https://files.pythonhosted.org/packages/1c/43/729fa30aad69783f755c5ad8649da17ee095311ca42024742701e202dc59/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4354e401eb6dab9aed3c7b4030514328a6c748d05e1c3e19175008ca7de84fb1", size = 204819, upload-time = "2026-03-06T06:01:30.298Z" }, - { url = "https://files.pythonhosted.org/packages/87/33/d9b442ce5a91b96fc0840455a9e49a611bbadae6122778d0a6a79683dd31/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a68766a3c58fde7f9aaa22b3786276f62ab2f594efb02d0a1421b6282e852e98", size = 198080, upload-time = "2026-03-06T06:01:31.478Z" }, - { url = "https://files.pythonhosted.org/packages/56/5a/b8b5a23134978ee9885cee2d6995f4c27cc41f9baded0a9685eabc5338f0/charset_normalizer-3.4.5-cp312-cp312-win32.whl", hash = "sha256:1827734a5b308b65ac54e86a618de66f935a4f63a8a462ff1e19a6788d6c2262", size = 132630, upload-time = "2026-03-06T06:01:33.056Z" }, - { url = "https://files.pythonhosted.org/packages/70/53/e44a4c07e8904500aec95865dc3f6464dc3586a039ef0df606eb3ac38e35/charset_normalizer-3.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:728c6a963dfab66ef865f49286e45239384249672cd598576765acc2a640a636", size = 142856, upload-time = "2026-03-06T06:01:34.489Z" }, - { url = "https://files.pythonhosted.org/packages/ea/aa/c5628f7cad591b1cf45790b7a61483c3e36cf41349c98af7813c483fd6e8/charset_normalizer-3.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:75dfd1afe0b1647449e852f4fb428195a7ed0588947218f7ba929f6538487f02", size = 132982, upload-time = "2026-03-06T06:01:35.641Z" }, - { url = "https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac59c15e3f1465f722607800c68713f9fbc2f672b9eb649fe831da4019ae9b23", size = 280788, upload-time = "2026-03-06T06:01:37.126Z" }, - { url = "https://files.pythonhosted.org/packages/0e/09/6003e7ffeb90cc0560da893e3208396a44c210c5ee42efff539639def59b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:165c7b21d19365464e8f70e5ce5e12524c58b48c78c1f5a57524603c1ab003f8", size = 188890, upload-time = "2026-03-06T06:01:38.73Z" }, - { url = "https://files.pythonhosted.org/packages/42/1e/02706edf19e390680daa694d17e2b8eab4b5f7ac285e2a51168b4b22ee6b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:28269983f25a4da0425743d0d257a2d6921ea7d9b83599d4039486ec5b9f911d", size = 206136, upload-time = "2026-03-06T06:01:40.016Z" }, - { url = "https://files.pythonhosted.org/packages/c7/87/942c3def1b37baf3cf786bad01249190f3ca3d5e63a84f831e704977de1f/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d27ce22ec453564770d29d03a9506d449efbb9fa13c00842262b2f6801c48cce", size = 202551, upload-time = "2026-03-06T06:01:41.522Z" }, - { url = "https://files.pythonhosted.org/packages/94/0a/af49691938dfe175d71b8a929bd7e4ace2809c0c5134e28bc535660d5262/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0625665e4ebdddb553ab185de5db7054393af8879fb0c87bd5690d14379d6819", size = 195572, upload-time = "2026-03-06T06:01:43.208Z" }, - { url = "https://files.pythonhosted.org/packages/20/ea/dfb1792a8050a8e694cfbde1570ff97ff74e48afd874152d38163d1df9ae/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:c23eb3263356d94858655b3e63f85ac5d50970c6e8febcdde7830209139cc37d", size = 184438, upload-time = "2026-03-06T06:01:44.755Z" }, - { url = "https://files.pythonhosted.org/packages/72/12/c281e2067466e3ddd0595bfaea58a6946765ace5c72dfa3edc2f5f118026/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e6302ca4ae283deb0af68d2fbf467474b8b6aedcd3dab4db187e07f94c109763", size = 193035, upload-time = "2026-03-06T06:01:46.051Z" }, - { url = "https://files.pythonhosted.org/packages/ba/4f/3792c056e7708e10464bad0438a44708886fb8f92e3c3d29ec5e2d964d42/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e51ae7d81c825761d941962450f50d041db028b7278e7b08930b4541b3e45cb9", size = 191340, upload-time = "2026-03-06T06:01:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/e7/86/80ddba897127b5c7a9bccc481b0cd36c8fefa485d113262f0fe4332f0bf4/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:597d10dec876923e5c59e48dbd366e852eacb2b806029491d307daea6b917d7c", size = 185464, upload-time = "2026-03-06T06:01:48.764Z" }, - { url = "https://files.pythonhosted.org/packages/4d/00/b5eff85ba198faacab83e0e4b6f0648155f072278e3b392a82478f8b988b/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5cffde4032a197bd3b42fd0b9509ec60fb70918d6970e4cc773f20fc9180ca67", size = 208014, upload-time = "2026-03-06T06:01:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/c8/11/d36f70be01597fd30850dde8a1269ebc8efadd23ba5785808454f2389bde/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2da4eedcb6338e2321e831a0165759c0c620e37f8cd044a263ff67493be8ffb3", size = 193297, upload-time = "2026-03-06T06:01:51.933Z" }, - { url = "https://files.pythonhosted.org/packages/1a/1d/259eb0a53d4910536c7c2abb9cb25f4153548efb42800c6a9456764649c0/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:65a126fb4b070d05340a84fc709dd9e7c75d9b063b610ece8a60197a291d0adf", size = 204321, upload-time = "2026-03-06T06:01:53.887Z" }, - { url = "https://files.pythonhosted.org/packages/84/31/faa6c5b9d3688715e1ed1bb9d124c384fe2fc1633a409e503ffe1c6398c1/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7a80a9242963416bd81f99349d5f3fce1843c303bd404f204918b6d75a75fd6", size = 197509, upload-time = "2026-03-06T06:01:56.439Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a5/c7d9dd1503ffc08950b3260f5d39ec2366dd08254f0900ecbcf3a6197c7c/charset_normalizer-3.4.5-cp313-cp313-win32.whl", hash = "sha256:f1d725b754e967e648046f00c4facc42d414840f5ccc670c5670f59f83693e4f", size = 132284, upload-time = "2026-03-06T06:01:57.812Z" }, - { url = "https://files.pythonhosted.org/packages/b9/0f/57072b253af40c8aa6636e6de7d75985624c1eb392815b2f934199340a89/charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:e37bd100d2c5d3ba35db9c7c5ba5a9228cbcffe5c4778dc824b164e5257813d7", size = 142630, upload-time = "2026-03-06T06:01:59.062Z" }, - { url = "https://files.pythonhosted.org/packages/31/41/1c4b7cc9f13bd9d369ce3bc993e13d374ce25fa38a2663644283ecf422c1/charset_normalizer-3.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:93b3b2cc5cf1b8743660ce77a4f45f3f6d1172068207c1defc779a36eea6bb36", size = 133254, upload-time = "2026-03-06T06:02:00.281Z" }, - { url = "https://files.pythonhosted.org/packages/43/be/0f0fd9bb4a7fa4fb5067fb7d9ac693d4e928d306f80a0d02bde43a7c4aee/charset_normalizer-3.4.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8197abe5ca1ffb7d91e78360f915eef5addff270f8a71c1fc5be24a56f3e4873", size = 280232, upload-time = "2026-03-06T06:02:01.508Z" }, - { url = "https://files.pythonhosted.org/packages/28/02/983b5445e4bef49cd8c9da73a8e029f0825f39b74a06d201bfaa2e55142a/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2aecdb364b8a1802afdc7f9327d55dad5366bc97d8502d0f5854e50712dbc5f", size = 189688, upload-time = "2026-03-06T06:02:02.857Z" }, - { url = "https://files.pythonhosted.org/packages/d0/88/152745c5166437687028027dc080e2daed6fe11cfa95a22f4602591c42db/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a66aa5022bf81ab4b1bebfb009db4fd68e0c6d4307a1ce5ef6a26e5878dfc9e4", size = 206833, upload-time = "2026-03-06T06:02:05.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0f/ebc15c8b02af2f19be9678d6eed115feeeccc45ce1f4b098d986c13e8769/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d77f97e515688bd615c1d1f795d540f32542d514242067adcb8ef532504cb9ee", size = 202879, upload-time = "2026-03-06T06:02:06.446Z" }, - { url = "https://files.pythonhosted.org/packages/38/9c/71336bff6934418dc8d1e8a1644176ac9088068bc571da612767619c97b3/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01a1ed54b953303ca7e310fafe0fe347aab348bd81834a0bcd602eb538f89d66", size = 195764, upload-time = "2026-03-06T06:02:08.763Z" }, - { url = "https://files.pythonhosted.org/packages/b7/95/ce92fde4f98615661871bc282a856cf9b8a15f686ba0af012984660d480b/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:b2d37d78297b39a9eb9eb92c0f6df98c706467282055419df141389b23f93362", size = 183728, upload-time = "2026-03-06T06:02:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/1c/e7/f5b4588d94e747ce45ae680f0f242bc2d98dbd4eccfab73e6160b6893893/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e71bbb595973622b817c042bd943c3f3667e9c9983ce3d205f973f486fec98a7", size = 192937, upload-time = "2026-03-06T06:02:11.663Z" }, - { url = "https://files.pythonhosted.org/packages/f9/29/9d94ed6b929bf9f48bf6ede6e7474576499f07c4c5e878fb186083622716/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cd966c2559f501c6fd69294d082c2934c8dd4719deb32c22961a5ac6db0df1d", size = 192040, upload-time = "2026-03-06T06:02:13.489Z" }, - { url = "https://files.pythonhosted.org/packages/15/d2/1a093a1cf827957f9445f2fe7298bcc16f8fc5e05c1ed2ad1af0b239035e/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d5e52d127045d6ae01a1e821acfad2f3a1866c54d0e837828538fabe8d9d1bd6", size = 184107, upload-time = "2026-03-06T06:02:14.83Z" }, - { url = "https://files.pythonhosted.org/packages/0f/7d/82068ce16bd36135df7b97f6333c5d808b94e01d4599a682e2337ed5fd14/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:30a2b1a48478c3428d047ed9690d57c23038dac838a87ad624c85c0a78ebeb39", size = 208310, upload-time = "2026-03-06T06:02:16.165Z" }, - { url = "https://files.pythonhosted.org/packages/84/4e/4dfb52307bb6af4a5c9e73e482d171b81d36f522b21ccd28a49656baa680/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d8ed79b8f6372ca4254955005830fd61c1ccdd8c0fac6603e2c145c61dd95db6", size = 192918, upload-time = "2026-03-06T06:02:18.144Z" }, - { url = "https://files.pythonhosted.org/packages/08/a4/159ff7da662cf7201502ca89980b8f06acf3e887b278956646a8aeb178ab/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c5af897b45fa606b12464ccbe0014bbf8c09191e0a66aab6aa9d5cf6e77e0c94", size = 204615, upload-time = "2026-03-06T06:02:19.821Z" }, - { url = "https://files.pythonhosted.org/packages/d6/62/0dd6172203cb6b429ffffc9935001fde42e5250d57f07b0c28c6046deb6b/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1088345bcc93c58d8d8f3d783eca4a6e7a7752bbff26c3eee7e73c597c191c2e", size = 197784, upload-time = "2026-03-06T06:02:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/c7/5e/1aab5cb737039b9c59e63627dc8bbc0d02562a14f831cc450e5f91d84ce1/charset_normalizer-3.4.5-cp314-cp314-win32.whl", hash = "sha256:ee57b926940ba00bca7ba7041e665cc956e55ef482f851b9b65acb20d867e7a2", size = 133009, upload-time = "2026-03-06T06:02:23.289Z" }, - { url = "https://files.pythonhosted.org/packages/40/65/e7c6c77d7aaa4c0d7974f2e403e17f0ed2cb0fc135f77d686b916bf1eead/charset_normalizer-3.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4481e6da1830c8a1cc0b746b47f603b653dadb690bcd851d039ffaefe70533aa", size = 143511, upload-time = "2026-03-06T06:02:26.195Z" }, - { url = "https://files.pythonhosted.org/packages/ba/91/52b0841c71f152f563b8e072896c14e3d83b195c188b338d3cc2e582d1d4/charset_normalizer-3.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:97ab7787092eb9b50fb47fa04f24c75b768a606af1bcba1957f07f128a7219e4", size = 133775, upload-time = "2026-03-06T06:02:27.473Z" }, - { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/2c56124c6dc53a774d435f985b5973bc592f42d437be58c0c92d65ae7296/charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95", size = 298751, upload-time = "2026-03-15T18:50:00.003Z" }, + { url = "https://files.pythonhosted.org/packages/86/2a/2a7db6b314b966a3bcad8c731c0719c60b931b931de7ae9f34b2839289ee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd", size = 200027, upload-time = "2026-03-15T18:50:01.702Z" }, + { url = "https://files.pythonhosted.org/packages/68/f2/0fe775c74ae25e2a3b07b01538fc162737b3e3f795bada3bc26f4d4d495c/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4", size = 220741, upload-time = "2026-03-15T18:50:03.194Z" }, + { url = "https://files.pythonhosted.org/packages/10/98/8085596e41f00b27dd6aa1e68413d1ddda7e605f34dd546833c61fddd709/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db", size = 215802, upload-time = "2026-03-15T18:50:05.859Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ce/865e4e09b041bad659d682bbd98b47fb490b8e124f9398c9448065f64fee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89", size = 207908, upload-time = "2026-03-15T18:50:07.676Z" }, + { url = "https://files.pythonhosted.org/packages/a8/54/8c757f1f7349262898c2f169e0d562b39dcb977503f18fdf0814e923db78/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565", size = 194357, upload-time = "2026-03-15T18:50:09.327Z" }, + { url = "https://files.pythonhosted.org/packages/6f/29/e88f2fac9218907fc7a70722b393d1bbe8334c61fe9c46640dba349b6e66/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9", size = 205610, upload-time = "2026-03-15T18:50:10.732Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c5/21d7bb0cb415287178450171d130bed9d664211fdd59731ed2c34267b07d/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7", size = 203512, upload-time = "2026-03-15T18:50:12.535Z" }, + { url = "https://files.pythonhosted.org/packages/a4/be/ce52f3c7fdb35cc987ad38a53ebcef52eec498f4fb6c66ecfe62cfe57ba2/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550", size = 195398, upload-time = "2026-03-15T18:50:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/81/a0/3ab5dd39d4859a3555e5dadfc8a9fa7f8352f8c183d1a65c90264517da0e/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0", size = 221772, upload-time = "2026-03-15T18:50:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/6a4e41a97ba6b2fa87f849c41e4d229449a586be85053c4d90135fe82d26/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8", size = 205759, upload-time = "2026-03-15T18:50:17.047Z" }, + { url = "https://files.pythonhosted.org/packages/db/3b/34a712a5ee64a6957bf355b01dc17b12de457638d436fdb05d01e463cd1c/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0", size = 216938, upload-time = "2026-03-15T18:50:18.44Z" }, + { url = "https://files.pythonhosted.org/packages/cb/05/5bd1e12da9ab18790af05c61aafd01a60f489778179b621ac2a305243c62/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b", size = 210138, upload-time = "2026-03-15T18:50:19.852Z" }, + { url = "https://files.pythonhosted.org/packages/bd/8e/3cb9e2d998ff6b21c0a1860343cb7b83eba9cdb66b91410e18fc4969d6ab/charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557", size = 144137, upload-time = "2026-03-15T18:50:21.505Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8f/78f5489ffadb0db3eb7aff53d31c24531d33eb545f0c6f6567c25f49a5ff/charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6", size = 154244, upload-time = "2026-03-15T18:50:22.81Z" }, + { url = "https://files.pythonhosted.org/packages/e4/74/e472659dffb0cadb2f411282d2d76c60da1fc94076d7fffed4ae8a93ec01/charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058", size = 143312, upload-time = "2026-03-15T18:50:24.074Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, + { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, + { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, + { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, + { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, + { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, + { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, + { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, + { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, + { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, + { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, + { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, + { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, + { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, + { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, + { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, + { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, + { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, + { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, + { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, + { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, + { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, + { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, ] [[package]] @@ -1368,10 +1385,10 @@ wheels = [ [[package]] name = "cuda-pathfinder" -version = "1.4.1" +version = "1.4.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/02/59a5bc738a09def0b49aea0e460bdf97f65206d0d041246147cf6207e69c/cuda_pathfinder-1.4.1-py3-none-any.whl", hash = "sha256:40793006082de88e0950753655e55558a446bed9a7d9d0bcb48b2506d50ed82a", size = 43903, upload-time = "2026-03-06T21:05:24.372Z" }, + { url = "https://files.pythonhosted.org/packages/c0/59/911a1a597264f1fb7ac176995a0f0b6062e37f8c1b6e0f23071a76838507/cuda_pathfinder-1.4.3-py3-none-any.whl", hash = "sha256:4345d8ead1f701c4fb8a99be6bc1843a7348b6ba0ef3b031f5a2d66fb128ae4c", size = 47951, upload-time = "2026-03-16T21:31:25.526Z" }, ] [[package]] @@ -1385,13 +1402,13 @@ wheels = [ [[package]] name = "daily-python" -version = "0.24.0" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/ff/0e3d6c8bcc7ff2e7786d3cdf6e3e0e390bb9f4dda447b96e052cbd97c9e4/daily_python-0.24.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:73609918e80ffadb211b97acfbb8c6f64afaa10663d60c5ce1217b03fbbcccee", size = 13298408, upload-time = "2026-03-10T03:32:06.641Z" }, - { url = "https://files.pythonhosted.org/packages/0a/de/52ef2bec464c99ed4579fb475ccd4170655efe6cc2a6e4c5beda80240269/daily_python-0.24.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9e2c6cec5e4418ff097d47f0cccc7d2a6bd52ed94cb5b5ef3f7b34912dcb122a", size = 11815304, upload-time = "2026-03-10T03:32:09.465Z" }, - { url = "https://files.pythonhosted.org/packages/a3/11/c7c939522f36d7afd50dc1f70abd89487aaffdc49dcee0e3f404e89277d3/daily_python-0.24.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b661eb464d94b8eaa2a4b27ef75c2527d1229602dcb4d6d771ba52cea8e7882a", size = 13835649, upload-time = "2026-03-10T03:32:12.189Z" }, - { url = "https://files.pythonhosted.org/packages/39/0b/7d2ad07214b66e9e8c1366ff0217a7c2813223b190bc4ea95e172c02407c/daily_python-0.24.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:778c0c2c4c8dcd0e25af1d1179ef4f19cd8a5029c2c7b53b10d8b4a2bdcc402b", size = 14416021, upload-time = "2026-03-10T03:32:15.016Z" }, + { url = "https://files.pythonhosted.org/packages/1b/58/074c6fca866fa13006b880eab521985d39300ea0d1df75a60d6dac4b2d47/daily_python-0.25.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:bff92b598863201bdffeea17819b7418d5c03a7ffc665a02be0333237fb3e4be", size = 13312157, upload-time = "2026-03-17T00:10:03.273Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d1/0b623e8e6d06713e8d193335db1e5ec46760af276f08d8b378a0a8e4696b/daily_python-0.25.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:733c70e77bb1c8933a7fa779722592a109617b2d098c192399c2bfffcb210847", size = 11831685, upload-time = "2026-03-17T00:10:05.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/36/45c3a59e92a37e3a51dbe2603f9e855408ecdb48f1b4cd396de83839e6f9/daily_python-0.25.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a6da5f8c99ccbdb59042a4c5e48f9a85f68a32f65f16b3778da5f319fd6d7e17", size = 13865923, upload-time = "2026-03-17T00:10:07.391Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5e/4574dedb8faa6a578079c89825f17c07a0bd4e1b4c2d2161966e62a6c6aa/daily_python-0.25.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0258ce43b92682379ecfcee1fa276799ccf41db25410f86395ae9927c2661cba", size = 14439736, upload-time = "2026-03-17T00:10:09.51Z" }, ] [[package]] @@ -1604,7 +1621,7 @@ standard = [ [[package]] name = "fastapi-cloud-cli" -version = "0.14.1" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastar" }, @@ -1616,9 +1633,9 @@ dependencies = [ { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/30/1665ad6bd1c285d1c6947e6ab0eae168bc44a9b45d5fc11fa930603db1c7/fastapi_cloud_cli-0.14.1.tar.gz", hash = "sha256:5b086182570008f67d9ae989870102f595e4e216493cabcd270ef6cd0401339f", size = 39999, upload-time = "2026-03-08T01:40:24.166Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/e1/05c44e7bbc619e980fab0236cff9f5f323ac1aaa79434b4906febf98b1d3/fastapi_cloud_cli-0.15.0.tar.gz", hash = "sha256:d02515231f3f505f7669c20920343934570a88a08af9f9a6463ca2807f27ffe5", size = 45309, upload-time = "2026-03-11T22:31:32.455Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/c2/0117d2a1b93eb7c6d2084e6be320d34a404f621eb01a26c1471c0eb4ee82/fastapi_cloud_cli-0.14.1-py3-none-any.whl", hash = "sha256:99ab3a2fbd1880121a62fb9c4f584e15c42ef0fe762cb5f7380a548081e60d05", size = 28359, upload-time = "2026-03-08T01:40:24.949Z" }, + { url = "https://files.pythonhosted.org/packages/40/cc/1ccca747f5609be27186ea8c9219449142f40e3eded2c6089bba6a6ecc82/fastapi_cloud_cli-0.15.0-py3-none-any.whl", hash = "sha256:9ffcf90bd713747efa65447620d29cfbb7b3f7de38d97467952ca6346e418d70", size = 32267, upload-time = "2026-03-11T22:31:33.499Z" }, ] [[package]] @@ -1760,11 +1777,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.25.1" +version = "3.25.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8b/4c32ecde6bea6486a2a5d05340e695174351ff6b06cf651a74c005f9df00/filelock-3.25.1.tar.gz", hash = "sha256:b9a2e977f794ef94d77cdf7d27129ac648a61f585bff3ca24630c1629f701aa9", size = 40319, upload-time = "2026-03-09T19:38:47.309Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl", hash = "sha256:18972df45473c4aa2c7921b609ee9ca4925910cc3a0fb226c96b92fc224ef7bf", size = 26720, upload-time = "2026-03-09T19:38:45.718Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, ] [[package]] @@ -1777,59 +1794,59 @@ wheels = [ [[package]] name = "fonttools" -version = "4.62.0" +version = "4.62.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/96/686339e0fda8142b7ebed39af53f4a5694602a729662f42a6209e3be91d0/fonttools-4.62.0.tar.gz", hash = "sha256:0dc477c12b8076b4eb9af2e440421b0433ffa9e1dcb39e0640a6c94665ed1098", size = 3579521, upload-time = "2026-03-09T16:50:06.217Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/e0/9db48ec7f6b95bae7b20667ded54f18dba8e759ef66232c8683822ae26fc/fonttools-4.62.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:62b6a3d0028e458e9b59501cf7124a84cd69681c433570e4861aff4fb54a236c", size = 2873527, upload-time = "2026-03-09T16:48:12.416Z" }, - { url = "https://files.pythonhosted.org/packages/dd/45/86eccfdc922cb9fafc63189a9793fa9f6dd60e68a07be42e454ef2c0deae/fonttools-4.62.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:966557078b55e697f65300b18025c54e872d7908d1899b7314d7c16e64868cb2", size = 2417427, upload-time = "2026-03-09T16:48:15.122Z" }, - { url = "https://files.pythonhosted.org/packages/d3/98/f547a1fceeae81a9a5c6461bde2badac8bf50bda7122a8012b32b1e65396/fonttools-4.62.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf34861145b516cddd19b07ae6f4a61ea1c6326031b960ec9ddce8ee815e888", size = 4934993, upload-time = "2026-03-09T16:48:18.186Z" }, - { url = "https://files.pythonhosted.org/packages/5c/57/a23a051fcff998fdfabdd33c6721b5bad499da08b586d3676993410071f0/fonttools-4.62.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e2ff573de2775508c8a366351fb901c4ced5dc6cf2d87dd15c973bedcdd5216", size = 4892154, upload-time = "2026-03-09T16:48:20.736Z" }, - { url = "https://files.pythonhosted.org/packages/e2/62/e27644b433dc6db1d47bc6028a27d772eec5cc8338e24a9a1fce5d7120aa/fonttools-4.62.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:55b189a1b3033860a38e4e5bd0626c5aa25c7ce9caee7bc784a8caec7a675401", size = 4911635, upload-time = "2026-03-09T16:48:23.174Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e2/1bf141911a5616bacfe9cf237c80ccd69d0d92482c38c0f7f6a55d063ad9/fonttools-4.62.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:825f98cd14907c74a4d0a3f7db8570886ffce9c6369fed1385020febf919abf6", size = 5031492, upload-time = "2026-03-09T16:48:25.095Z" }, - { url = "https://files.pythonhosted.org/packages/2f/59/790c292f4347ecfa77d9c7e0d1d91e04ab227f6e4a337ed4fe37ca388048/fonttools-4.62.0-cp310-cp310-win32.whl", hash = "sha256:c858030560f92a054444c6e46745227bfd3bb4e55383c80d79462cd47289e4b5", size = 1507656, upload-time = "2026-03-09T16:48:26.973Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ee/08c0b7f8bac6e44638de6fe9a3e710a623932f60eccd58912c4d4743516d/fonttools-4.62.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bf75eb69330e34ad2a096fac67887102c8537991eb6cac1507fc835bbb70e0a", size = 1556540, upload-time = "2026-03-09T16:48:30.359Z" }, - { url = "https://files.pythonhosted.org/packages/e4/33/63d79ca41020dd460b51f1e0f58ad1ff0a36b7bcbdf8f3971d52836581e9/fonttools-4.62.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:196cafef9aeec5258425bd31a4e9a414b2ee0d1557bca184d7923d3d3bcd90f9", size = 2870816, upload-time = "2026-03-09T16:48:32.39Z" }, - { url = "https://files.pythonhosted.org/packages/c0/7a/9aeec114bc9fc00d757a41f092f7107863d372e684a5b5724c043654477c/fonttools-4.62.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:153afc3012ff8761b1733e8fbe5d98623409774c44ffd88fbcb780e240c11d13", size = 2416127, upload-time = "2026-03-09T16:48:34.627Z" }, - { url = "https://files.pythonhosted.org/packages/5a/71/12cfd8ae0478b7158ffa8850786781f67e73c00fd897ef9d053415c5f88b/fonttools-4.62.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13b663fb197334de84db790353d59da2a7288fd14e9be329f5debc63ec0500a5", size = 5100678, upload-time = "2026-03-09T16:48:36.454Z" }, - { url = "https://files.pythonhosted.org/packages/8a/d7/8e4845993ee233c2023d11babe9b3dae7d30333da1d792eeccebcb77baab/fonttools-4.62.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:591220d5333264b1df0d3285adbdfe2af4f6a45bbf9ca2b485f97c9f577c49ff", size = 5070859, upload-time = "2026-03-09T16:48:38.786Z" }, - { url = "https://files.pythonhosted.org/packages/ae/a0/287ae04cd883a52e7bb1d92dfc4997dcffb54173761c751106845fa9e316/fonttools-4.62.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:579f35c121528a50c96bf6fcb6a393e81e7f896d4326bf40e379f1c971603db9", size = 5076689, upload-time = "2026-03-09T16:48:41.886Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4e/a2377ad26c36fcd3e671a1c316ea5ed83107de1588e2d897a98349363bc7/fonttools-4.62.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:44956b003151d5a289eba6c71fe590d63509267c37e26de1766ba15d9c589582", size = 5202053, upload-time = "2026-03-09T16:48:43.867Z" }, - { url = "https://files.pythonhosted.org/packages/44/2e/ad0472e69b02f83dc88983a9910d122178461606404be5b4838af6d1744a/fonttools-4.62.0-cp311-cp311-win32.whl", hash = "sha256:42c7848fa8836ab92c23b1617c407a905642521ff2d7897fe2bf8381530172f1", size = 2292852, upload-time = "2026-03-09T16:48:46.962Z" }, - { url = "https://files.pythonhosted.org/packages/77/ce/f5a4c42c117f8113ce04048053c128d17426751a508f26398110c993a074/fonttools-4.62.0-cp311-cp311-win_amd64.whl", hash = "sha256:4da779e8f342a32856075ddb193b2a024ad900bc04ecb744014c32409ae871ed", size = 2344367, upload-time = "2026-03-09T16:48:48.818Z" }, - { url = "https://files.pythonhosted.org/packages/ab/9d/7ad1ffc080619f67d0b1e0fa6a0578f0be077404f13fd8e448d1616a94a3/fonttools-4.62.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:22bde4dc12a9e09b5ced77f3b5053d96cf10c4976c6ac0dee293418ef289d221", size = 2870004, upload-time = "2026-03-09T16:48:50.837Z" }, - { url = "https://files.pythonhosted.org/packages/4d/8b/ba59069a490f61b737e064c3129453dbd28ee38e81d56af0d04d7e6b4de4/fonttools-4.62.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7199c73b326bad892f1cb53ffdd002128bfd58a89b8f662204fbf1daf8d62e85", size = 2414662, upload-time = "2026-03-09T16:48:53.295Z" }, - { url = "https://files.pythonhosted.org/packages/8c/8c/c52a4310de58deeac7e9ea800892aec09b00bb3eb0c53265b31ec02be115/fonttools-4.62.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d732938633681d6e2324e601b79e93f7f72395ec8681f9cdae5a8c08bc167e72", size = 5032975, upload-time = "2026-03-09T16:48:55.718Z" }, - { url = "https://files.pythonhosted.org/packages/0b/a1/d16318232964d786907b9b3613b8409f74cf0be2da400854509d3a864e43/fonttools-4.62.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:31a804c16d76038cc4e3826e07678efb0a02dc4f15396ea8e07088adbfb2578e", size = 4988544, upload-time = "2026-03-09T16:48:57.715Z" }, - { url = "https://files.pythonhosted.org/packages/b2/8d/7e745ca3e65852adc5e52a83dc213fe1b07d61cb5b394970fcd4b1199d1e/fonttools-4.62.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:090e74ac86e68c20150e665ef8e7e0c20cb9f8b395302c9419fa2e4d332c3b51", size = 4971296, upload-time = "2026-03-09T16:48:59.678Z" }, - { url = "https://files.pythonhosted.org/packages/e6/d4/b717a4874175146029ca1517e85474b1af80c9d9a306fc3161e71485eea5/fonttools-4.62.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8f086120e8be9e99ca1288aa5ce519833f93fe0ec6ebad2380c1dee18781f0b5", size = 5122503, upload-time = "2026-03-09T16:49:02.464Z" }, - { url = "https://files.pythonhosted.org/packages/cb/4b/92cfcba4bf8373f51c49c5ae4b512ead6fbda7d61a0e8c35a369d0db40a0/fonttools-4.62.0-cp312-cp312-win32.whl", hash = "sha256:37a73e5e38fd05c637daede6ffed5f3496096be7df6e4a3198d32af038f87527", size = 2281060, upload-time = "2026-03-09T16:49:04.385Z" }, - { url = "https://files.pythonhosted.org/packages/cd/06/cc96468781a4dc8ae2f14f16f32b32f69bde18cb9384aad27ccc7adf76f7/fonttools-4.62.0-cp312-cp312-win_amd64.whl", hash = "sha256:658ab837c878c4d2a652fcbb319547ea41693890e6434cf619e66f79387af3b8", size = 2331193, upload-time = "2026-03-09T16:49:06.598Z" }, - { url = "https://files.pythonhosted.org/packages/82/c7/985c1670aa6d82ef270f04cde11394c168f2002700353bd2bde405e59b8f/fonttools-4.62.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:274c8b8a87e439faf565d3bcd3f9f9e31bca7740755776a4a90a4bfeaa722efa", size = 2864929, upload-time = "2026-03-09T16:49:09.331Z" }, - { url = "https://files.pythonhosted.org/packages/c1/dc/c409c8ceec0d3119e9ab0b7b1a2e3c76d1f4d66e4a9db5c59e6b7652e7df/fonttools-4.62.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93e27131a5a0ae82aaadcffe309b1bae195f6711689722af026862bede05c07c", size = 2412586, upload-time = "2026-03-09T16:49:11.378Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ac/8e300dbf7b4d135287c261ffd92ede02d9f48f0d2db14665fbc8b059588a/fonttools-4.62.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83c6524c5b93bad9c2939d88e619fedc62e913c19e673f25d5ab74e7a5d074e5", size = 5013708, upload-time = "2026-03-09T16:49:14.063Z" }, - { url = "https://files.pythonhosted.org/packages/fb/bc/60d93477b653eeb1ddf5f9ec34be689b79234d82dbdded269ac0252715b8/fonttools-4.62.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:106aec9226f9498fc5345125ff7200842c01eda273ae038f5049b0916907acee", size = 4964355, upload-time = "2026-03-09T16:49:16.515Z" }, - { url = "https://files.pythonhosted.org/packages/cb/eb/6dc62bcc3c3598c28a3ecb77e69018869c3e109bd83031d4973c059d318b/fonttools-4.62.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15d86b96c79013320f13bc1b15f94789edb376c0a2d22fb6088f33637e8dfcbc", size = 4953472, upload-time = "2026-03-09T16:49:18.494Z" }, - { url = "https://files.pythonhosted.org/packages/82/b3/3af7592d9b254b7b7fec018135f8776bfa0d1ad335476c2791b1334dc5e4/fonttools-4.62.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f16c07e5250d5d71d0f990a59460bc5620c3cc456121f2cfb5b60475699905f", size = 5094701, upload-time = "2026-03-09T16:49:21.67Z" }, - { url = "https://files.pythonhosted.org/packages/31/3d/976645583ab567d3ee75ff87b33aa1330fa2baeeeae5fc46210b4274dd45/fonttools-4.62.0-cp313-cp313-win32.whl", hash = "sha256:d31558890f3fa00d4f937d12708f90c7c142c803c23eaeb395a71f987a77ebe3", size = 2279710, upload-time = "2026-03-09T16:49:23.812Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7a/e25245a30457595740041dba9d0ea8ec1b2517f2f1a6a741f15eba1a4edc/fonttools-4.62.0-cp313-cp313-win_amd64.whl", hash = "sha256:6826a5aa53fb6def8a66bf423939745f415546c4e92478a7c531b8b6282b6c3b", size = 2330291, upload-time = "2026-03-09T16:49:26.237Z" }, - { url = "https://files.pythonhosted.org/packages/1a/64/61f69298aa6e7c363dcf00dd6371a654676900abe27d1effd1a74b43e5d0/fonttools-4.62.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:4fa5a9c716e2f75ef34b5a5c2ca0ee4848d795daa7e6792bf30fd4abf8993449", size = 2864222, upload-time = "2026-03-09T16:49:28.285Z" }, - { url = "https://files.pythonhosted.org/packages/c6/57/6b08756fe4455336b1fe160ab3c11fccc90768ccb6ee03fb0b45851aace4/fonttools-4.62.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:625f5cbeb0b8f4e42343eaeb4bc2786718ddd84760a2f5e55fdd3db049047c00", size = 2410674, upload-time = "2026-03-09T16:49:30.504Z" }, - { url = "https://files.pythonhosted.org/packages/6f/86/db65b63bb1b824b63e602e9be21b18741ddc99bcf5a7850f9181159ae107/fonttools-4.62.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6247e58b96b982709cd569a91a2ba935d406dccf17b6aa615afaed37ac3856aa", size = 4999387, upload-time = "2026-03-09T16:49:32.593Z" }, - { url = "https://files.pythonhosted.org/packages/86/c8/c6669e42d2f4efd60d38a3252cebbb28851f968890efb2b9b15f9d1092b0/fonttools-4.62.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:840632ea9c1eab7b7f01c369e408c0721c287dfd7500ab937398430689852fd1", size = 4912506, upload-time = "2026-03-09T16:49:34.927Z" }, - { url = "https://files.pythonhosted.org/packages/2e/49/0ae552aa098edd0ec548413fbf818f52ceb70535016215094a5ce9bf8f70/fonttools-4.62.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:28a9ea2a7467a816d1bec22658b0cce4443ac60abac3e293bdee78beb74588f3", size = 4951202, upload-time = "2026-03-09T16:49:37.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/65/ae38fc8a4cea6f162d74cf11f58e9aeef1baa7d0e3d1376dabd336c129e5/fonttools-4.62.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5ae611294f768d413949fd12693a8cba0e6332fbc1e07aba60121be35eac68d0", size = 5060758, upload-time = "2026-03-09T16:49:39.464Z" }, - { url = "https://files.pythonhosted.org/packages/db/3d/bb797496f35c60544cd5af71ffa5aad62df14ef7286908d204cb5c5096fe/fonttools-4.62.0-cp314-cp314-win32.whl", hash = "sha256:273acb61f316d07570a80ed5ff0a14a23700eedbec0ad968b949abaa4d3f6bb5", size = 2283496, upload-time = "2026-03-09T16:49:42.448Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9f/91081ffe5881253177c175749cce5841f5ec6e931f5d52f4a817207b7429/fonttools-4.62.0-cp314-cp314-win_amd64.whl", hash = "sha256:a5f974006d14f735c6c878fc4b117ad031dc93638ddcc450ca69f8fd64d5e104", size = 2335426, upload-time = "2026-03-09T16:49:44.228Z" }, - { url = "https://files.pythonhosted.org/packages/f8/65/f47f9b3db1ec156a1f222f1089ba076b2cc9ee1d024a8b0a60c54258517e/fonttools-4.62.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0361a7d41d86937f1f752717c19f719d0fde064d3011038f9f19bdf5fc2f5c95", size = 2947079, upload-time = "2026-03-09T16:49:46.471Z" }, - { url = "https://files.pythonhosted.org/packages/52/73/bc62e5058a0c22cf02b1e0169ef0c3ca6c3247216d719f95bead3c05a991/fonttools-4.62.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4108c12773b3c97aa592311557c405d5b4fc03db2b969ed928fcf68e7b3c887", size = 2448802, upload-time = "2026-03-09T16:49:48.328Z" }, - { url = "https://files.pythonhosted.org/packages/2b/df/bfaa0e845884935355670e6e68f137185ab87295f8bc838db575e4a66064/fonttools-4.62.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b448075f32708e8fb377fe7687f769a5f51a027172c591ba9a58693631b077a8", size = 5137378, upload-time = "2026-03-09T16:49:50.223Z" }, - { url = "https://files.pythonhosted.org/packages/32/32/04f616979a18b48b52e634988b93d847b6346260faf85ecccaf7e2e9057f/fonttools-4.62.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5f1fa8cc9f1a56a3e33ee6b954d6d9235e6b9d11eb7a6c9dfe2c2f829dc24db", size = 4920714, upload-time = "2026-03-09T16:49:53.172Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2e/274e16689c1dfee5c68302cd7c444213cfddd23cf4620374419625037ec6/fonttools-4.62.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f8c8ea812f82db1e884b9cdb663080453e28f0f9a1f5027a5adb59c4cc8d38d1", size = 5016012, upload-time = "2026-03-09T16:49:55.762Z" }, - { url = "https://files.pythonhosted.org/packages/7f/0c/b08117270626e7117ac2f89d732fdd4386ec37d2ab3a944462d29e6f89a1/fonttools-4.62.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:03c6068adfdc67c565d217e92386b1cdd951abd4240d65180cec62fa74ba31b2", size = 5042766, upload-time = "2026-03-09T16:49:57.726Z" }, - { url = "https://files.pythonhosted.org/packages/11/83/a48b73e54efa272ee65315a6331b30a9b3a98733310bc11402606809c50e/fonttools-4.62.0-cp314-cp314t-win32.whl", hash = "sha256:d28d5baacb0017d384df14722a63abe6e0230d8ce642b1615a27d78ffe3bc983", size = 2347785, upload-time = "2026-03-09T16:49:59.698Z" }, - { url = "https://files.pythonhosted.org/packages/f8/27/c67eab6dc3525bdc39586511b1b3d7161e972dacc0f17476dbaf932e708b/fonttools-4.62.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3f9e20c4618f1e04190c802acae6dc337cb6db9fa61e492fd97cd5c5a9ff6d07", size = 2413914, upload-time = "2026-03-09T16:50:02.251Z" }, - { url = "https://files.pythonhosted.org/packages/9c/57/c2487c281dde03abb2dec244fd67059b8d118bd30a653cbf69e94084cb23/fonttools-4.62.0-py3-none-any.whl", hash = "sha256:75064f19a10c50c74b336aa5ebe7b1f89fd0fb5255807bfd4b0c6317098f4af3", size = 1152427, upload-time = "2026-03-09T16:50:04.074Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ff/532ed43808b469c807e8cb6b21358da3fe6fd51486b3a8c93db0bb5d957f/fonttools-4.62.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c", size = 2873740, upload-time = "2026-03-13T13:52:11.822Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/2318d2b430562da7227010fb2bb029d2fa54d7b46443ae8942bab224e2a0/fonttools-4.62.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a", size = 2417649, upload-time = "2026-03-13T13:52:14.605Z" }, + { url = "https://files.pythonhosted.org/packages/4c/28/40f15523b5188598018e7956899fed94eb7debec89e2dd70cb4a8df90492/fonttools-4.62.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3", size = 4935213, upload-time = "2026-03-13T13:52:17.399Z" }, + { url = "https://files.pythonhosted.org/packages/42/09/7dbe3d7023f57d9b580cfa832109d521988112fd59dddfda3fddda8218f9/fonttools-4.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23", size = 4892374, upload-time = "2026-03-13T13:52:20.175Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2d/84509a2e32cb925371560ef5431365d8da2183c11d98e5b4b8b4e42426a5/fonttools-4.62.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d", size = 4911856, upload-time = "2026-03-13T13:52:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/a5/80/df28131379eed93d9e6e6fccd3bf6e3d077bebbfe98cc83f21bbcd83ed02/fonttools-4.62.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae", size = 5031712, upload-time = "2026-03-13T13:52:25.14Z" }, + { url = "https://files.pythonhosted.org/packages/3d/03/3c8f09aad64230cd6d921ae7a19f9603c36f70930b00459f112706f6769a/fonttools-4.62.1-cp310-cp310-win32.whl", hash = "sha256:486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed", size = 1507878, upload-time = "2026-03-13T13:52:28.149Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ec/f53f626f8f3e89f4cadd8fc08f3452c8fd182c951ad5caa35efac22b29ab/fonttools-4.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9", size = 1556766, upload-time = "2026-03-13T13:52:30.814Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7", size = 2871039, upload-time = "2026-03-13T13:52:33.127Z" }, + { url = "https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14", size = 2416346, upload-time = "2026-03-13T13:52:35.676Z" }, + { url = "https://files.pythonhosted.org/packages/aa/53/5276ceba7bff95da7793a07c5284e1da901cf00341ce5e2f3273056c0cca/fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7", size = 5100897, upload-time = "2026-03-13T13:52:38.102Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b", size = 5071078, upload-time = "2026-03-13T13:52:41.305Z" }, + { url = "https://files.pythonhosted.org/packages/e3/be/d378fca4c65ea1956fee6d90ace6e861776809cbbc5af22388a090c3c092/fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1", size = 5076908, upload-time = "2026-03-13T13:52:44.122Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d9/ae6a1d0693a4185a84605679c8a1f719a55df87b9c6e8e817bfdd9ef5936/fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416", size = 5202275, upload-time = "2026-03-13T13:52:46.591Z" }, + { url = "https://files.pythonhosted.org/packages/54/6c/af95d9c4efb15cabff22642b608342f2bd67137eea6107202d91b5b03184/fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53", size = 2293075, upload-time = "2026-03-13T13:52:48.711Z" }, + { url = "https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2", size = 2344593, upload-time = "2026-03-13T13:52:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", size = 2870219, upload-time = "2026-03-13T13:52:53.664Z" }, + { url = "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", size = 2414891, upload-time = "2026-03-13T13:52:56.493Z" }, + { url = "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", size = 5033197, upload-time = "2026-03-13T13:52:59.179Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", size = 4988768, upload-time = "2026-03-13T13:53:02.761Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", size = 4971512, upload-time = "2026-03-13T13:53:05.678Z" }, + { url = "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", size = 5122723, upload-time = "2026-03-13T13:53:08.662Z" }, + { url = "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", size = 2281278, upload-time = "2026-03-13T13:53:10.998Z" }, + { url = "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", size = 2331414, upload-time = "2026-03-13T13:53:13.992Z" }, + { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" }, + { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" }, + { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" }, + { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" }, + { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" }, + { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" }, + { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" }, + { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" }, + { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" }, + { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" }, + { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" }, + { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" }, + { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" }, + { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, ] [[package]] @@ -1986,16 +2003,15 @@ grpc = [ [[package]] name = "google-auth" -version = "2.49.0" +version = "2.49.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, - { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/59/7371175bfd949abfb1170aa076352131d7281bd9449c0f978604fc4431c3/google_auth-2.49.0.tar.gz", hash = "sha256:9cc2d9259d3700d7a257681f81052db6737495a1a46b610597f4b8bafe5286ae", size = 333444, upload-time = "2026-03-06T21:53:06.07Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/80/6a696a07d3d3b0a92488933532f03dbefa4a24ab80fb231395b9a2a1be77/google_auth-2.49.1.tar.gz", hash = "sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64", size = 333825, upload-time = "2026-03-12T19:30:58.135Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/45/de64b823b639103de4b63dd193480dce99526bd36be6530c2dba85bf7817/google_auth-2.49.0-py3-none-any.whl", hash = "sha256:f893ef7307f19cf53700b7e2f61b5a6affe3aa0edf9943b13788920ab92d8d87", size = 240676, upload-time = "2026-03-06T21:52:38.304Z" }, + { url = "https://files.pythonhosted.org/packages/e9/eb/c6c2478d8a8d633460be40e2a8a6f8f429171997a35a96f81d3b680dec83/google_auth-2.49.1-py3-none-any.whl", hash = "sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7", size = 240737, upload-time = "2026-03-12T19:30:53.159Z" }, ] [package.optional-dependencies] @@ -2070,7 +2086,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.66.0" +version = "1.67.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2084,9 +2100,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/ba/0b343b0770d4710ad2979fd9301d7caa56c940174d5361ed4a7cc4979241/google_genai-1.66.0.tar.gz", hash = "sha256:ffc01647b65046bca6387320057aa51db0ad64bcc72c8e3e914062acfa5f7c49", size = 504386, upload-time = "2026-03-04T22:15:28.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/07/59a498f81f2c7b0649eacda2ea470b7fd8bd7149f20caba22962081bdd51/google_genai-1.67.0.tar.gz", hash = "sha256:897195a6a9742deb6de240b99227189ada8b2d901d61bdfba836c3092021eab6", size = 506972, upload-time = "2026-03-12T20:39:16.241Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/dd/403949d922d4e261b08b64aaa132af4e456c3b15c8e2a2d9e6ef693f66e2/google_genai-1.66.0-py3-none-any.whl", hash = "sha256:7f127a39cf695277104ce4091bb26e417c59bb46e952ff3699c3a982d9c474ee", size = 732174, upload-time = "2026-03-04T22:15:26.63Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/562aa1f086e53529ffbeb5b43d5d8bc42c1b968102b5e2163fad005ce298/google_genai-1.67.0-py3-none-any.whl", hash = "sha256:58b0484ff2d4335fa53c724b489e9f807fcca8115d9cdbd8fdf341121fbd6d2d", size = 733542, upload-time = "2026-03-12T20:39:14.615Z" }, ] [[package]] @@ -2110,7 +2126,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747, upload-time = "2026-02-20T20:16:21.325Z" }, { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202, upload-time = "2026-02-20T20:47:28.955Z" }, { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620, upload-time = "2026-02-20T20:55:55.581Z" }, - { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132, upload-time = "2026-02-20T21:02:43.261Z" }, { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729, upload-time = "2026-02-20T20:20:58.395Z" }, { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946, upload-time = "2026-02-20T20:49:31.102Z" }, { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494, upload-time = "2026-02-20T20:21:06.541Z" }, @@ -2118,7 +2133,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, @@ -2127,7 +2141,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, @@ -2136,7 +2149,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, - { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, @@ -2145,7 +2157,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, @@ -2154,7 +2165,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, - { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, @@ -2163,7 +2173,7 @@ wheels = [ [[package]] name = "groq" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2173,9 +2183,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/28/61ce4b51d090c0fd4c9218514c91256b4406b993fe9637c6aa5606390116/groq-1.1.0.tar.gz", hash = "sha256:342005ff9d5586c24cbee9247c83ee7c7103ab7dc1185c55624dbe00627c8f4d", size = 150111, upload-time = "2026-03-08T23:11:55.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/bc/7ad1d9967c58b21cdec0c94f26f40fc37b07ba60715d6cbc7c7ef775d927/groq-1.1.1.tar.gz", hash = "sha256:ea971eca72d88e875a78567904bfb46a2f2e43907bfe400fc36a81150a4066d8", size = 150783, upload-time = "2026-03-11T09:11:32.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/8b/d397030c49d8d8bf2b332c4d4ee2ee3452edfc1d99fa5bbd72e615531e7a/groq-1.1.0-py3-none-any.whl", hash = "sha256:29fcef1d4ed9130c500ed54dd4aa06ca74bb7635d36d17c1abec7b9fc605c06d", size = 139583, upload-time = "2026-03-08T23:11:54.439Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1d/0749c5f0ed76693f6a3a40e2b0c40201fa23e1ccb00e69d5aa63e3f5b0ff/groq-1.1.1-py3-none-any.whl", hash = "sha256:6b7932c0fd3189ad1842fbc294f57fbf014713e01f72037451cb60a138c4b846", size = 139650, upload-time = "2026-03-11T09:11:29.87Z" }, ] [[package]] @@ -2309,34 +2319,34 @@ wheels = [ [[package]] name = "hf-xet" -version = "1.3.2" +version = "1.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/cb/9bb543bd987ffa1ee48202cc96a756951b734b79a542335c566148ade36c/hf_xet-1.3.2.tar.gz", hash = "sha256:e130ee08984783d12717444e538587fa2119385e5bd8fc2bb9f930419b73a7af", size = 643646, upload-time = "2026-02-27T17:26:08.051Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/08/23c84a26716382c89151b5b447b4beb19e3345f3a93d3b73009a71a57ad3/hf_xet-1.4.2.tar.gz", hash = "sha256:b7457b6b482d9e0743bd116363239b1fa904a5e65deede350fbc0c4ea67c71ea", size = 672357, upload-time = "2026-03-13T06:58:51.077Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/75/462285971954269432aad2e7938c5c7ff9ec7d60129cec542ab37121e3d6/hf_xet-1.3.2-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:335a8f36c55fd35a92d0062f4e9201b4015057e62747b7e7001ffb203c0ee1d2", size = 3761019, upload-time = "2026-02-27T17:25:49.441Z" }, - { url = "https://files.pythonhosted.org/packages/35/56/987b0537ddaf88e17192ea09afa8eca853e55f39a4721578be436f8409df/hf_xet-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c1ae4d3a716afc774e66922f3cac8206bfa707db13f6a7e62dfff74bfc95c9a8", size = 3521565, upload-time = "2026-02-27T17:25:47.469Z" }, - { url = "https://files.pythonhosted.org/packages/a8/5c/7e4a33a3d689f77761156cc34558047569e54af92e4d15a8f493229f6767/hf_xet-1.3.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6dbdf231efac0b9b39adcf12a07f0c030498f9212a18e8c50224d0e84ab803d", size = 4176494, upload-time = "2026-02-27T17:25:40.247Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b3/71e856bf9d9a69b3931837e8bf22e095775f268c8edcd4a9e8c355f92484/hf_xet-1.3.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c1980abfb68ecf6c1c7983379ed7b1e2b49a1aaf1a5aca9acc7d48e5e2e0a961", size = 3955601, upload-time = "2026-02-27T17:25:38.376Z" }, - { url = "https://files.pythonhosted.org/packages/63/d7/aecf97b3f0a981600a67ff4db15e2d433389d698a284bb0ea5d8fcdd6f7f/hf_xet-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1c88fbd90ad0d27c46b77a445f0a436ebaa94e14965c581123b68b1c52f5fd30", size = 4154770, upload-time = "2026-02-27T17:25:56.756Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e1/3af961f71a40e09bf5ee909842127b6b00f5ab4ee3817599dc0771b79893/hf_xet-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:35b855024ca37f2dd113ac1c08993e997fbe167b9d61f9ef66d3d4f84015e508", size = 4394161, upload-time = "2026-02-27T17:25:58.111Z" }, - { url = "https://files.pythonhosted.org/packages/a1/c3/859509bade9178e21b8b1db867b8e10e9f817ab9ac1de77cb9f461ced765/hf_xet-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:31612ba0629046e425ba50375685a2586e11fb9144270ebabd75878c3eaf6378", size = 3637377, upload-time = "2026-02-27T17:26:10.611Z" }, - { url = "https://files.pythonhosted.org/packages/05/7f/724cfbef4da92d577b71f68bf832961c8919f36c60d28d289a9fc9d024d4/hf_xet-1.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:433c77c9f4e132b562f37d66c9b22c05b5479f243a1f06a120c1c06ce8b1502a", size = 3497875, upload-time = "2026-02-27T17:26:09.034Z" }, - { url = "https://files.pythonhosted.org/packages/ba/75/9d54c1ae1d05fb704f977eca1671747babf1957f19f38ae75c5933bc2dc1/hf_xet-1.3.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:c34e2c7aefad15792d57067c1c89b2b02c1bbaeabd7f8456ae3d07b4bbaf4094", size = 3761076, upload-time = "2026-02-27T17:25:55.42Z" }, - { url = "https://files.pythonhosted.org/packages/f2/8a/08a24b6c6f52b5d26848c16e4b6d790bb810d1bf62c3505bed179f7032d3/hf_xet-1.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4bc995d6c41992831f762096020dc14a65fdf3963f86ffed580b596d04de32e3", size = 3521745, upload-time = "2026-02-27T17:25:54.217Z" }, - { url = "https://files.pythonhosted.org/packages/b5/db/a75cf400dd8a1a8acf226a12955ff6ee999f272dfc0505bafd8079a61267/hf_xet-1.3.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:959083c89dee30f7d6f890b36cdadda823386c4de63b1a30384a75bfd2ae995d", size = 4176301, upload-time = "2026-02-27T17:25:46.044Z" }, - { url = "https://files.pythonhosted.org/packages/01/40/6c4c798ffdd83e740dd3925c4e47793b07442a9efa3bc3866ba141a82365/hf_xet-1.3.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:cfa760888633b08c01b398d212ce7e8c0d7adac6c86e4b20dfb2397d8acd78ee", size = 3955437, upload-time = "2026-02-27T17:25:44.703Z" }, - { url = "https://files.pythonhosted.org/packages/0c/09/9a3aa7c5f07d3e5cc57bb750d12a124ffa72c273a87164bd848f9ac5cc14/hf_xet-1.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3155a02e083aa21fd733a7485c7c36025e49d5975c8d6bda0453d224dd0b0ac4", size = 4154535, upload-time = "2026-02-27T17:26:05.207Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e0/831f7fa6d90cb47a230bc23284b502c700e1483bbe459437b3844cdc0776/hf_xet-1.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:91b1dc03c31cbf733d35dc03df7c5353686233d86af045e716f1e0ea4a2673cf", size = 4393891, upload-time = "2026-02-27T17:26:06.607Z" }, - { url = "https://files.pythonhosted.org/packages/ab/96/6ed472fdce7f8b70f5da6e3f05be76816a610063003bfd6d9cea0bbb58a3/hf_xet-1.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:211f30098512d95e85ad03ae63bd7dd2c4df476558a5095d09f9e38e78cbf674", size = 3637583, upload-time = "2026-02-27T17:26:17.349Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/a069edc4570b3f8e123c0b80fadc94530f3d7b01394e1fc1bb223339366c/hf_xet-1.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4a6817c41de7c48ed9270da0b02849347e089c5ece9a0e72ae4f4b3a57617f82", size = 3497977, upload-time = "2026-02-27T17:26:14.966Z" }, - { url = "https://files.pythonhosted.org/packages/d8/28/dbb024e2e3907f6f3052847ca7d1a2f7a3972fafcd53ff79018977fcb3e4/hf_xet-1.3.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f93b7595f1d8fefddfede775c18b5c9256757824f7f6832930b49858483cd56f", size = 3763961, upload-time = "2026-02-27T17:25:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/e4/71/b99aed3823c9d1795e4865cf437d651097356a3f38c7d5877e4ac544b8e4/hf_xet-1.3.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a85d3d43743174393afe27835bde0cd146e652b5fcfdbcd624602daef2ef3259", size = 3526171, upload-time = "2026-02-27T17:25:50.968Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ca/907890ce6ef5598b5920514f255ed0a65f558f820515b18db75a51b2f878/hf_xet-1.3.2-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7c2a054a97c44e136b1f7f5a78f12b3efffdf2eed3abc6746fc5ea4b39511633", size = 4180750, upload-time = "2026-02-27T17:25:43.125Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ad/bc7f41f87173d51d0bce497b171c4ee0cbde1eed2d7b4216db5d0ada9f50/hf_xet-1.3.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:06b724a361f670ae557836e57801b82c75b534812e351a87a2c739f77d1e0635", size = 3961035, upload-time = "2026-02-27T17:25:41.837Z" }, - { url = "https://files.pythonhosted.org/packages/73/38/600f4dda40c4a33133404d9fe644f1d35ff2d9babb4d0435c646c63dd107/hf_xet-1.3.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:305f5489d7241a47e0458ef49334be02411d1d0f480846363c1c8084ed9916f7", size = 4161378, upload-time = "2026-02-27T17:26:00.365Z" }, - { url = "https://files.pythonhosted.org/packages/00/b3/7bc1ff91d1ac18420b7ad1e169b618b27c00001b96310a89f8a9294fe509/hf_xet-1.3.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:06cdbde243c85f39a63b28e9034321399c507bcd5e7befdd17ed2ccc06dfe14e", size = 4398020, upload-time = "2026-02-27T17:26:03.977Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0b/99bfd948a3ed3620ab709276df3ad3710dcea61976918cce8706502927af/hf_xet-1.3.2-cp37-abi3-win_amd64.whl", hash = "sha256:9298b47cce6037b7045ae41482e703c471ce36b52e73e49f71226d2e8e5685a1", size = 3641624, upload-time = "2026-02-27T17:26:13.542Z" }, - { url = "https://files.pythonhosted.org/packages/cc/02/9a6e4ca1f3f73a164c0cd48e41b3cc56585dcc37e809250de443d673266f/hf_xet-1.3.2-cp37-abi3-win_arm64.whl", hash = "sha256:83d8ec273136171431833a6957e8f3af496bee227a0fe47c7b8b39c106d1749a", size = 3503976, upload-time = "2026-02-27T17:26:12.123Z" }, + { url = "https://files.pythonhosted.org/packages/18/06/e8cf74c3c48e5485c7acc5a990d0d8516cdfb5fdf80f799174f1287cc1b5/hf_xet-1.4.2-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ac8202ae1e664b2c15cdfc7298cbb25e80301ae596d602ef7870099a126fcad4", size = 3796125, upload-time = "2026-03-13T06:58:33.177Z" }, + { url = "https://files.pythonhosted.org/packages/66/d4/b73ebab01cbf60777323b7de9ef05550790451eb5172a220d6b9845385ec/hf_xet-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6d2f8ee39fa9fba9af929f8c0d0482f8ee6e209179ad14a909b6ad78ffcb7c81", size = 3555985, upload-time = "2026-03-13T06:58:31.797Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e7/ded6d1bd041c3f2bca9e913a0091adfe32371988e047dd3a68a2463c15a2/hf_xet-1.4.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4642a6cf249c09da8c1f87fe50b24b2a3450b235bf8adb55700b52f0ea6e2eb6", size = 4212085, upload-time = "2026-03-13T06:58:24.323Z" }, + { url = "https://files.pythonhosted.org/packages/97/c1/a0a44d1f98934f7bdf17f7a915b934f9fca44bb826628c553589900f6df8/hf_xet-1.4.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:769431385e746c92dc05492dde6f687d304584b89c33d79def8367ace06cb555", size = 3988266, upload-time = "2026-03-13T06:58:22.887Z" }, + { url = "https://files.pythonhosted.org/packages/7a/82/be713b439060e7d1f1d93543c8053d4ef2fe7e6922c5b31642eaa26f3c4b/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c9dd1c1bc4cc56168f81939b0e05b4c36dd2d28c13dc1364b17af89aa0082496", size = 4188513, upload-time = "2026-03-13T06:58:40.858Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/cbd4188b22abd80ebd0edbb2b3e87f2633e958983519980815fb8314eae5/hf_xet-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fca58a2ae4e6f6755cc971ac6fcdf777ea9284d7e540e350bb000813b9a3008d", size = 4428287, upload-time = "2026-03-13T06:58:42.601Z" }, + { url = "https://files.pythonhosted.org/packages/b2/4e/84e45b25e2e3e903ed3db68d7eafa96dae9a1d1f6d0e7fc85120347a852f/hf_xet-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:163aab46854ccae0ab6a786f8edecbbfbaa38fcaa0184db6feceebf7000c93c0", size = 3665574, upload-time = "2026-03-13T06:58:53.881Z" }, + { url = "https://files.pythonhosted.org/packages/ee/71/c5ac2b9a7ae39c14e91973035286e73911c31980fe44e7b1d03730c00adc/hf_xet-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:09b138422ecbe50fd0c84d4da5ff537d27d487d3607183cd10e3e53f05188e82", size = 3528760, upload-time = "2026-03-13T06:58:52.187Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0f/fcd2504015eab26358d8f0f232a1aed6b8d363a011adef83fe130bff88f7/hf_xet-1.4.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:949dcf88b484bb9d9276ca83f6599e4aa03d493c08fc168c124ad10b2e6f75d7", size = 3796493, upload-time = "2026-03-13T06:58:39.267Z" }, + { url = "https://files.pythonhosted.org/packages/82/56/19c25105ff81731ca6d55a188b5de2aa99d7a2644c7aa9de1810d5d3b726/hf_xet-1.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:41659966020d59eb9559c57de2cde8128b706a26a64c60f0531fa2318f409418", size = 3555797, upload-time = "2026-03-13T06:58:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/8933c073186849b5e06762aa89847991d913d10a95d1603eb7f2c3834086/hf_xet-1.4.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c588e21d80010119458dd5d02a69093f0d115d84e3467efe71ffb2c67c19146", size = 4212127, upload-time = "2026-03-13T06:58:30.539Z" }, + { url = "https://files.pythonhosted.org/packages/eb/01/f89ebba4e369b4ed699dcb60d3152753870996f41c6d22d3d7cac01310e1/hf_xet-1.4.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a296744d771a8621ad1d50c098d7ab975d599800dae6d48528ba3944e5001ba0", size = 3987788, upload-time = "2026-03-13T06:58:29.139Z" }, + { url = "https://files.pythonhosted.org/packages/84/4d/8a53e5ffbc2cc33bbf755382ac1552c6d9af13f623ed125fe67cc3e6772f/hf_xet-1.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f563f7efe49588b7d0629d18d36f46d1658fe7e08dce3fa3d6526e1c98315e2d", size = 4188315, upload-time = "2026-03-13T06:58:48.017Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b8/b7a1c1b5592254bd67050632ebbc1b42cc48588bf4757cb03c2ef87e704a/hf_xet-1.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5b2e0132c56d7ee1bf55bdb638c4b62e7106f6ac74f0b786fed499d5548c5570", size = 4428306, upload-time = "2026-03-13T06:58:49.502Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/40779e45b20e11c7c5821a94135e0207080d6b3d76e7b78ccb413c6f839b/hf_xet-1.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:2f45c712c2fa1215713db10df6ac84b49d0e1c393465440e9cb1de73ecf7bbf6", size = 3665826, upload-time = "2026-03-13T06:58:59.88Z" }, + { url = "https://files.pythonhosted.org/packages/51/4c/e2688c8ad1760d7c30f7c429c79f35f825932581bc7c9ec811436d2f21a0/hf_xet-1.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6d53df40616f7168abfccff100d232e9d460583b9d86fa4912c24845f192f2b8", size = 3529113, upload-time = "2026-03-13T06:58:58.491Z" }, + { url = "https://files.pythonhosted.org/packages/b4/86/b40b83a2ff03ef05c4478d2672b1fc2b9683ff870e2b25f4f3af240f2e7b/hf_xet-1.4.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:71f02d6e4cdd07f344f6844845d78518cc7186bd2bc52d37c3b73dc26a3b0bc5", size = 3800339, upload-time = "2026-03-13T06:58:36.245Z" }, + { url = "https://files.pythonhosted.org/packages/64/2e/af4475c32b4378b0e92a587adb1aa3ec53e3450fd3e5fe0372a874531c00/hf_xet-1.4.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e9b38d876e94d4bdcf650778d6ebbaa791dd28de08db9736c43faff06ede1b5a", size = 3559664, upload-time = "2026-03-13T06:58:34.787Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4c/781267da3188db679e601de18112021a5cb16506fe86b246e22c5401a9c4/hf_xet-1.4.2-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:77e8c180b7ef12d8a96739a4e1e558847002afe9ea63b6f6358b2271a8bdda1c", size = 4217422, upload-time = "2026-03-13T06:58:27.472Z" }, + { url = "https://files.pythonhosted.org/packages/68/47/d6cf4a39ecf6c7705f887a46f6ef5c8455b44ad9eb0d391aa7e8a2ff7fea/hf_xet-1.4.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c3b3c6a882016b94b6c210957502ff7877802d0dbda8ad142c8595db8b944271", size = 3992847, upload-time = "2026-03-13T06:58:25.989Z" }, + { url = "https://files.pythonhosted.org/packages/2d/ef/e80815061abff54697239803948abc665c6b1d237102c174f4f7a9a5ffc5/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9d9a634cc929cfbaf2e1a50c0e532ae8c78fa98618426769480c58501e8c8ac2", size = 4193843, upload-time = "2026-03-13T06:58:44.59Z" }, + { url = "https://files.pythonhosted.org/packages/54/75/07f6aa680575d9646c4167db6407c41340cbe2357f5654c4e72a1b01ca14/hf_xet-1.4.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b0932eb8b10317ea78b7da6bab172b17be03bbcd7809383d8d5abd6a2233e04", size = 4432751, upload-time = "2026-03-13T06:58:46.533Z" }, + { url = "https://files.pythonhosted.org/packages/cd/71/193eabd7e7d4b903c4aa983a215509c6114915a5a237525ec562baddb868/hf_xet-1.4.2-cp37-abi3-win_amd64.whl", hash = "sha256:ad185719fb2e8ac26f88c8100562dbf9dbdcc3d9d2add00faa94b5f106aea53f", size = 3671149, upload-time = "2026-03-13T06:58:57.07Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7e/ccf239da366b37ba7f0b36095450efae4a64980bdc7ec2f51354205fdf39/hf_xet-1.4.2-cp37-abi3-win_arm64.whl", hash = "sha256:32c012286b581f783653e718c1862aea5b9eb140631685bb0c5e7012c8719a87", size = 3533426, upload-time = "2026-03-13T06:58:55.46Z" }, ] [[package]] @@ -2503,11 +2513,11 @@ wheels = [ [[package]] name = "identify" -version = "2.6.17" +version = "2.6.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/84/376a3b96e5a8d33a7aa2c5b3b31a4b3c364117184bf0b17418055f6ace66/identify-2.6.17.tar.gz", hash = "sha256:f816b0b596b204c9fdf076ded172322f2723cf958d02f9c3587504834c8ff04d", size = 99579, upload-time = "2026-03-01T20:04:12.702Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl", hash = "sha256:be5f8412d5ed4b20f2bd41a65f920990bdccaa6a4a18a08f1eefdcd0bdd885f0", size = 99382, upload-time = "2026-03-01T20:04:11.439Z" }, + { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, ] [[package]] @@ -3072,7 +3082,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.7.16" +version = "0.7.18" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -3085,9 +3095,9 @@ dependencies = [ { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/18/b240d33e32d3f71a3c3375781cb11f3be6b27c275acdcf18c08a65a560cc/langsmith-0.7.16.tar.gz", hash = "sha256:87267d32c1220ec34bd0074d3d04b57c7394328a39a02182b62ab4ae09d28144", size = 1115428, upload-time = "2026-03-09T21:11:16.985Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/a8/31e9e7b42089cf194a0d873cbfc1ecec6d6dd0cc693808f1cb64494cbd0c/langsmith-0.7.18.tar.gz", hash = "sha256:d7e6e1f9c9300ee83b9f201c9254b4a32799218de102a5b1d2b217e00be2dfa2", size = 1134635, upload-time = "2026-03-16T18:54:19.131Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/a8/4202ca65561213ec84ca3800b1d4e5d37a1441cddeec533367ecbca7f408/langsmith-0.7.16-py3-none-any.whl", hash = "sha256:c84a7a06938025fe0aad992acc546dd75ce3f757ba8ee5b00ad914911d4fc02e", size = 347538, upload-time = "2026-03-09T21:11:15.02Z" }, + { url = "https://files.pythonhosted.org/packages/29/58/244a14e29c7feccf06ed3929c9ab65a747a9ee94d5ac43d40862053b2f54/langsmith-0.7.18-py3-none-any.whl", hash = "sha256:3253c171fe2f6506056a42f9077983a34749b7a1629e41d8fb8e2005d8960886", size = 359268, upload-time = "2026-03-16T18:54:17.397Z" }, ] [[package]] @@ -3442,47 +3452,47 @@ wheels = [ [[package]] name = "mlx" -version = "0.31.0" +version = "0.31.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mlx-metal", marker = "sys_platform == 'darwin'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/73/54/269d13847b04b07523d44cf903e1d3c6d48f56e6e89dda7e16418b411629/mlx-0.31.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:38680838e0dd9a621ed4adc5a9ed8b94aeb6a4798142fbe215b821b8c6b8fc36", size = 575395, upload-time = "2026-02-27T23:49:11.886Z" }, - { url = "https://files.pythonhosted.org/packages/3d/86/1fbe1f8f3a23c92c821c235ab7a28395c86c900b0a2b2425f3c8862bbeb6/mlx-0.31.0-cp310-cp310-macosx_15_0_arm64.whl", hash = "sha256:7aded590bcf6839307c3acc899e196936991f97b499ddbdd0cd3b228bf10792f", size = 575394, upload-time = "2026-02-27T23:49:13.738Z" }, - { url = "https://files.pythonhosted.org/packages/20/01/02b79132e91182c779bb6c4f586c5fb86d49c32e8f07f307d2d4ca64cca6/mlx-0.31.0-cp310-cp310-macosx_26_0_arm64.whl", hash = "sha256:6e3ae83607b798b44cb3e44437095cfd26886fecc15f90f29f9eafd206d4d170", size = 575411, upload-time = "2026-02-27T23:49:15.374Z" }, - { url = "https://files.pythonhosted.org/packages/13/86/c501ddb496a185b69f3181d77276907f43a847eaa4d9fff86bc0616d1dcc/mlx-0.31.0-cp310-cp310-manylinux_2_35_aarch64.whl", hash = "sha256:b25f785c94eb47d8104604a5de0e7d749b801e7a40073cbf457aa94c372e5593", size = 639542, upload-time = "2026-02-27T23:49:16.822Z" }, - { url = "https://files.pythonhosted.org/packages/86/7c/508bfc140cf777dbe61fc2be0fbfca56e3f0ceed233cd7a8ef4add84262e/mlx-0.31.0-cp310-cp310-manylinux_2_35_x86_64.whl", hash = "sha256:6a4342027e6608ce69807a8f079c750a7c6161f543ebb49e55654edd03c178d6", size = 672721, upload-time = "2026-02-27T23:49:17.978Z" }, - { url = "https://files.pythonhosted.org/packages/1e/d3/fcb8b9f645ae70b3295a353999c3c6c7a66fd43ed8aa716b13da12bf40d4/mlx-0.31.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:285313eaeba425e58cbb3238c2d1a3894e6252d58f243ce56681d5419a568d6c", size = 575602, upload-time = "2026-02-27T23:49:19.314Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2a/d35072e8dc31d9550f8218cfc388c1cd12c7fd89e8246540a9c7b873d958/mlx-0.31.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:acf4f04ff33a80784a0f15c492166dc889e65659b41c410ca5a7c2d78bee2a3a", size = 575603, upload-time = "2026-02-27T23:49:20.651Z" }, - { url = "https://files.pythonhosted.org/packages/43/fa/eca64a514cd50a4a38cc9b8827db85d9e554c3fe407ede043d061055b1ab/mlx-0.31.0-cp311-cp311-macosx_26_0_arm64.whl", hash = "sha256:f624571e23a86654496c42a507b4bb42ded0edb91f33161fabafdbf6b81ba024", size = 575637, upload-time = "2026-02-27T23:49:22.02Z" }, - { url = "https://files.pythonhosted.org/packages/72/cd/0ee01b646010c7a22872d2b849b766941f813c4fd777602306d01af3915f/mlx-0.31.0-cp311-cp311-manylinux_2_35_aarch64.whl", hash = "sha256:5b5306a0934b15c4e3a1088a10066bdde3966c21b95006c63ecc38ca8e3891e0", size = 639267, upload-time = "2026-02-27T23:49:23.265Z" }, - { url = "https://files.pythonhosted.org/packages/73/50/c72e2cabdeefc2bf51ae5c1111bdaa9055a0c2d18bc87314ef965ffff422/mlx-0.31.0-cp311-cp311-manylinux_2_35_x86_64.whl", hash = "sha256:18078bc67dfb7ed602fca233d00ce93e23d590d9347da5009472455a92831066", size = 672858, upload-time = "2026-02-27T23:49:24.627Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7d/87fb0daa006dbbbd8894c3d496c7d9dfc52e4ade260482276d3eca137a15/mlx-0.31.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:de6c0a3e8aa0e7d1365d46634fdbb3f835c164fbdb6ba8a239e039a4efa07fe2", size = 575834, upload-time = "2026-02-27T23:49:26.61Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e3/aa0fac5a9d52b1a4686c7097e56775c1a96dee3084f9c587b74e4c2cd284/mlx-0.31.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:d6af01b15177da995336a6fd9878e7c5994720a9f1614d8f4d1dbe9293167c30", size = 575836, upload-time = "2026-02-27T23:49:28.505Z" }, - { url = "https://files.pythonhosted.org/packages/8d/15/6aa3edaa34aeef370634756b7d131b8dc1cdb0002ddecdd3d876b5f9fa0c/mlx-0.31.0-cp312-cp312-macosx_26_0_arm64.whl", hash = "sha256:1ad14ddc3a15818f5bba0de35e88559ed8dcb93ccff2ef879ff604d02d663b25", size = 575828, upload-time = "2026-02-27T23:49:29.684Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d3/53ac650a569f5f5111c0280611acf0dcbdfa5fd0da2d433bad0f5575de73/mlx-0.31.0-cp312-cp312-manylinux_2_35_aarch64.whl", hash = "sha256:a80754ecf64191f71da1946dc5de6cf903344cc90dd286c589792ee9d3fc62f9", size = 624405, upload-time = "2026-02-27T23:49:31.687Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fe/a0c0b73c04f7673a50c505e155dd0088cc7a116d7b8d4eb4d1d9fdcd2c8f/mlx-0.31.0-cp312-cp312-manylinux_2_35_x86_64.whl", hash = "sha256:363282eb094785f6aba27810ff89331c0f7829c6961f571cd0feaad09d2c809f", size = 666952, upload-time = "2026-02-27T23:49:33.262Z" }, - { url = "https://files.pythonhosted.org/packages/4a/09/35d1192cf1f655438213d8baa2264a8bc2426b44d93802dabfc177fd8e81/mlx-0.31.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4f33e9aafc6d3ad29e72743dfb786c4ce67397414f0a091469058626381fc1bc", size = 575815, upload-time = "2026-02-27T23:49:34.607Z" }, - { url = "https://files.pythonhosted.org/packages/59/9d/29e0cb154a31ed05c9d24c776513bf1ec506b8570e214b4563b55bb19ef6/mlx-0.31.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:242806b8ad6a4d3ce86cdff513f86520552de7592786712770b2e1ebd178816a", size = 575821, upload-time = "2026-02-27T23:49:35.947Z" }, - { url = "https://files.pythonhosted.org/packages/5f/6c/437aefdca17216aab02d0fb7528cd63e2c3d8d9c1b079c07d579a770645f/mlx-0.31.0-cp313-cp313-macosx_26_0_arm64.whl", hash = "sha256:7f0bdbac084017820ce513a12318771a06c7ec10fad159839e27c998bc5dad89", size = 575810, upload-time = "2026-02-27T23:49:37.165Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d5/986777b53e2c3eff709ee5a275b41ed84a9c04f60071e97f9d3b60dec845/mlx-0.31.0-cp313-cp313-manylinux_2_35_aarch64.whl", hash = "sha256:8642dda2b23195d9921973749ae9bf764e2c7d70bfc0e60b23b6335e660cc610", size = 624713, upload-time = "2026-02-27T23:49:38.672Z" }, - { url = "https://files.pythonhosted.org/packages/2d/29/da0875739d08760461a5b21207c34d959bc7572b27e46ccc0f48badae078/mlx-0.31.0-cp313-cp313-manylinux_2_35_x86_64.whl", hash = "sha256:c6daa671cfa3c194951d742aa09030c5008d9d9657034b2903389fa090b3ba92", size = 666888, upload-time = "2026-02-27T23:49:40.222Z" }, - { url = "https://files.pythonhosted.org/packages/66/60/0152a44ed737c3b16e9044909d01212b99e216c6ab4b2f76faa054ae8172/mlx-0.31.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:cce3e15cf11c608c9e721502fe56e54f9f48b897e9b80f1204a48643d68710c0", size = 577579, upload-time = "2026-02-27T23:49:41.723Z" }, - { url = "https://files.pythonhosted.org/packages/e3/6b/70f0a254d7ace58a030547a99219f1342c3cf383029e1af90eee3efaeb85/mlx-0.31.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:ba330fe40d73b202880bbb5cac62de0b639cf4c44a12853bcadb34a9e3ffe880", size = 577582, upload-time = "2026-02-27T23:49:42.998Z" }, - { url = "https://files.pythonhosted.org/packages/63/5a/81cf057dbc005a43d27b7dfaff88198c61bbfe76cb8da3499821083c3fca/mlx-0.31.0-cp314-cp314-macosx_26_0_arm64.whl", hash = "sha256:d2014d113070846c6cdee980653f561c92a4a663a449f64e70c15bbf74d637e1", size = 577535, upload-time = "2026-02-27T23:49:44.475Z" }, - { url = "https://files.pythonhosted.org/packages/75/22/1b2bddb2774c7951aa620d286157439f288186215ff6ce18d9a9a45e608e/mlx-0.31.0-cp314-cp314-manylinux_2_35_aarch64.whl", hash = "sha256:994fab25ff521621e03001177a8f0f1a7bf8294ff340f89910ec074f9f681ed9", size = 627410, upload-time = "2026-02-27T23:49:45.654Z" }, - { url = "https://files.pythonhosted.org/packages/46/f4/e9256326912ac21a9853b3a9856da19292b908270ff96cb27abb8421c8c6/mlx-0.31.0-cp314-cp314-manylinux_2_35_x86_64.whl", hash = "sha256:c3bb9961f40d098659326b0edb96e2a16adecfaf3c1f2518cad5a0b7e55a3a5d", size = 667351, upload-time = "2026-02-27T23:49:46.868Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f9/f1663dafd45af02467f4f41777c13ec34b9104b2b0450d870c3f906285cd/mlx-0.31.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:bc46c911cc060d2eaf21b9e24a1712dc56763b660b53631b9057a32ab1c0271a", size = 574137, upload-time = "2026-03-12T02:15:54.996Z" }, + { url = "https://files.pythonhosted.org/packages/c6/26/1fd632f537a5160a21475a70aaef252090c62f9629f45ad20f5acfe810f3/mlx-0.31.1-cp310-cp310-macosx_15_0_arm64.whl", hash = "sha256:fa132def5b3d959362077521c80f1fc80f64c45060d2940dc1d66a1aa19ce5f6", size = 574140, upload-time = "2026-03-12T02:15:56.709Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c9/e790fa8ddc1b27fea7ba749699883f31c65e166b18e4598beab4574e4686/mlx-0.31.1-cp310-cp310-macosx_26_0_arm64.whl", hash = "sha256:877ff2f98debd035b922825a0d7e7e1be0959fc5ca1d24cb5020a23e510ff16d", size = 574124, upload-time = "2026-03-12T02:15:58.323Z" }, + { url = "https://files.pythonhosted.org/packages/b4/da/f7375fc2be05d026640c5ced085a9e71066a33100638e5762347dae5d680/mlx-0.31.1-cp310-cp310-manylinux_2_35_aarch64.whl", hash = "sha256:931c9316ec47b45ec0e737519f4f4c90eb69cbbdaaecadd6dd2ccdf1a85d4e61", size = 641428, upload-time = "2026-03-12T02:15:59.743Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3f/ab060661d966d435e41212d4f6d6e9d1202da8b9043b1c18c343ab7d1b08/mlx-0.31.1-cp310-cp310-manylinux_2_35_x86_64.whl", hash = "sha256:dec00ce7b094d6bc2876996291fd76c9e28326bc1a9853440903f2a06946ce1f", size = 674521, upload-time = "2026-03-12T02:16:01.057Z" }, + { url = "https://files.pythonhosted.org/packages/75/32/25dc2eae1d6f867224ef2bca2c644e3e913fe8067991f8394c090b720e3e/mlx-0.31.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:8863835fb36c7c4f65008b1426ddb9ff7931a13c975e0ef58a40002ae8048922", size = 574311, upload-time = "2026-03-12T02:16:02.651Z" }, + { url = "https://files.pythonhosted.org/packages/9b/bf/c5aa1d1154f5a216139c8162cd3e6568b7eb427390d655f7f5ae3a1a61e7/mlx-0.31.1-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:0de504c1f1fe73b32fc3cf457b8eac30d1f7ce22440ef075c1970f96712e6fff", size = 574312, upload-time = "2026-03-12T02:16:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/3a/88/ef57747552c9e9da0c28465d9266c05a0009b698d90fb0bc63eb81840b8d/mlx-0.31.1-cp311-cp311-macosx_26_0_arm64.whl", hash = "sha256:10715b895e1f3e984c2c54257b7db956ff8af1fa93255412794a3724fe2dd3b1", size = 574385, upload-time = "2026-03-12T02:16:05.528Z" }, + { url = "https://files.pythonhosted.org/packages/ac/51/dbea4bbe7a2e4cd05226965b34198d49459cfaef8b9b37b72f006a9811ab/mlx-0.31.1-cp311-cp311-manylinux_2_35_aarch64.whl", hash = "sha256:d065625ab3101adcd7f5824297243fe40a0615099a06f5597ab67284483aa2f8", size = 641347, upload-time = "2026-03-12T02:16:07.013Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/3db98e8805637fb56f078311d622e9500f5c9088f6d79a6e304ec8235b47/mlx-0.31.1-cp311-cp311-manylinux_2_35_x86_64.whl", hash = "sha256:b2cf8502d9d64dc6851034fcd4a656cbb26be20c36f190f2971f4ac0caed89cb", size = 674769, upload-time = "2026-03-12T02:16:08.51Z" }, + { url = "https://files.pythonhosted.org/packages/38/29/71fe1f68756f515856e6930973c23245810d4aa3cd22fddd719d86a709dc/mlx-0.31.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8a63b31a398c9519f2bb0c81cf3865d9baca4ff573ffc31ead465d18286184e8", size = 574308, upload-time = "2026-03-12T02:16:10.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/be/70654a2cee0d71fd10bd237a50a79d06ae51679a194db6a3b16c0c84e6a5/mlx-0.31.1-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:a7a9347df4dcc41f0d16ff70b65650820af4879f686534b233b16826a22afa00", size = 574309, upload-time = "2026-03-12T02:16:11.577Z" }, + { url = "https://files.pythonhosted.org/packages/ad/69/c7bc7b04f76b0cbd678f328011d1634bd0bcfc2da45aba06e084cb031127/mlx-0.31.1-cp312-cp312-macosx_26_0_arm64.whl", hash = "sha256:6cdb797ea31787d1ce9e5be77991c4bd5cbf129ab15f7253b78e09737f535fce", size = 574289, upload-time = "2026-03-12T02:16:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/55/f7/dcc129228faab4d406041d91413c5999250ab79da6fe5417ac84f1616ff1/mlx-0.31.1-cp312-cp312-manylinux_2_35_aarch64.whl", hash = "sha256:1ed1991c8e39f841d5756c0c543beb819763a2f80fba3f4b150bc6cad4d973de", size = 626439, upload-time = "2026-03-12T02:16:14.741Z" }, + { url = "https://files.pythonhosted.org/packages/90/1d/8b32e46ea98ab5c1c15cf1b37ac97af651977f84e72e1800412a700c51d9/mlx-0.31.1-cp312-cp312-manylinux_2_35_x86_64.whl", hash = "sha256:195c5cb27328380287c0ffe9ef48f860ab75ec5d3dfce153d475dc2c99369708", size = 668679, upload-time = "2026-03-12T02:16:16.012Z" }, + { url = "https://files.pythonhosted.org/packages/44/45/04465da443634b23fb11670bbd2f7538b1ed43ffc5e0de44a95b3c29e9c1/mlx-0.31.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9a6d3410fc951bd28508fed9c1ab5d9903f6f6bb101c3a5d63d4191d49a384a1", size = 574268, upload-time = "2026-03-12T02:16:17.27Z" }, + { url = "https://files.pythonhosted.org/packages/85/7b/84956960356ff36e8c1bbed68fac96709e98e6a1adbc8e3d0ff71022d84e/mlx-0.31.1-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:20bd7ba19882603ac22711092d0e799f1ff7b5183c2c641d417dab4d2423d99e", size = 574265, upload-time = "2026-03-12T02:16:18.479Z" }, + { url = "https://files.pythonhosted.org/packages/86/01/d6f0ef5b8c0b390af08246d1301e9717dfb076b3920012b53105a888ed8c/mlx-0.31.1-cp313-cp313-macosx_26_0_arm64.whl", hash = "sha256:4c4565d6f4f8ce295613ee342d313ee5ab0b0eab9a6272954450f8343f7876bc", size = 574172, upload-time = "2026-03-12T02:16:19.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/05/eb29e9eb0cff9c7dfd872e26663e6e9512629730740e1db629086c80ac5a/mlx-0.31.1-cp313-cp313-manylinux_2_35_aarch64.whl", hash = "sha256:9dc564a8b38b9aec279a1c7d34551068b1cc1f8e43b5ac044b56b2a9a4205195", size = 626558, upload-time = "2026-03-12T02:16:21.652Z" }, + { url = "https://files.pythonhosted.org/packages/25/45/ecb746fbb6acb75d03760e41cc7bd21c2e2b544528b3033f7d70402334ac/mlx-0.31.1-cp313-cp313-manylinux_2_35_x86_64.whl", hash = "sha256:78f51ab929278366006ee7793dbb5c942b121542c793c33eb9b894a2ce8e27e1", size = 668625, upload-time = "2026-03-12T02:16:23.103Z" }, + { url = "https://files.pythonhosted.org/packages/99/65/208f511acd5fb1ed0b08f047bd6229583845cc6f4b5aa6547a3219332dbb/mlx-0.31.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bba9d471ba20e050676292b1089a355c8042d3fc9462e4c1738a9735d7d40cfa", size = 576300, upload-time = "2026-03-12T02:16:24.545Z" }, + { url = "https://files.pythonhosted.org/packages/98/58/2d925cb3fa3cd28d279ed6f44508ab7fbbf7359b17359914aa3652a7d734/mlx-0.31.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:d90b0529b22553eb1353b113b7233aa391ca55e24b1ba69024c732fcc21c5c49", size = 576303, upload-time = "2026-03-12T02:16:26.283Z" }, + { url = "https://files.pythonhosted.org/packages/e1/17/abec0bd0f9347dae13e60b33325cb199312798842901953495e19f3bb3c8/mlx-0.31.1-cp314-cp314-macosx_26_0_arm64.whl", hash = "sha256:69bc88b41ddd61b44cd6a4d417790f9971ba3fdf58d824934cea95a95b9b4031", size = 576275, upload-time = "2026-03-12T02:16:27.57Z" }, + { url = "https://files.pythonhosted.org/packages/a2/91/85c73f7cc3a661416d05315623458c719eda7de958b05f4e10ba40c52d07/mlx-0.31.1-cp314-cp314-manylinux_2_35_aarch64.whl", hash = "sha256:b973506fd49ba39df6dc4ff655b77bd35ea193cee878e71d6ee3d1a951d2b3a6", size = 628701, upload-time = "2026-03-12T02:16:28.949Z" }, + { url = "https://files.pythonhosted.org/packages/7d/e9/d87638e00a44dcf346fe838caaf1e2dae96a88d5779edbd66ce27d4bbdcc/mlx-0.31.1-cp314-cp314-manylinux_2_35_x86_64.whl", hash = "sha256:3987282a1e63252bdd7c636138812c67316c3f7c7a7acad08e76c8843648a056", size = 668959, upload-time = "2026-03-12T02:16:30.41Z" }, ] [[package]] name = "mlx-metal" -version = "0.31.0" +version = "0.31.1" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/4f/0a0671dfa62b59bf429edab0e2c9c7f9bc77865aa4218cd46f2f41d7d11a/mlx_metal-0.31.0-py3-none-macosx_14_0_arm64.whl", hash = "sha256:1c572a6e3634a63060c103b0c38ac309e2d217be15519e3d8f0d6b452bb015f5", size = 38596752, upload-time = "2026-02-27T23:29:39.52Z" }, - { url = "https://files.pythonhosted.org/packages/8d/42/c6d7bfd097b777f932d6cf8c79e41b565070b63cc452a069b8804e505140/mlx_metal-0.31.0-py3-none-macosx_15_0_arm64.whl", hash = "sha256:554dc7cb29e0ea5fb6941df42f11a1de385b095848e6183c7a99d7c1f1a11f5d", size = 38595434, upload-time = "2026-02-27T23:29:43.285Z" }, - { url = "https://files.pythonhosted.org/packages/ed/8f/cdaffd759b4c71e74c294e773daacad8aafabac103b93e0aa56d4468d279/mlx_metal-0.31.0-py3-none-macosx_26_0_arm64.whl", hash = "sha256:7fd412f55ddf9f1d90c2cd86ce281d19e8eb93d093c6dbd784a49f8bd7d0a22c", size = 47879607, upload-time = "2026-02-27T23:29:46.571Z" }, + { url = "https://files.pythonhosted.org/packages/39/66/2313497fdbc7fbadf8e026c09366e3f049f9114e65ca4edc23cdb8699186/mlx_metal-0.31.1-py3-none-macosx_14_0_arm64.whl", hash = "sha256:70741174131dbf7fdd479cb730e06e08c358eac3bf7905d9e884e7960cfdd5b8", size = 38624074, upload-time = "2026-03-12T02:15:48.036Z" }, + { url = "https://files.pythonhosted.org/packages/c7/34/4c3c6890ce6095b2ab2ba2f5f15c9a7ba17208d47f8cacb572885a2dc0eb/mlx_metal-0.31.1-py3-none-macosx_15_0_arm64.whl", hash = "sha256:6c56bd8cd27743e635f5a90a22535af7c31bd22b4b126d46b6da2da52d72e413", size = 38618950, upload-time = "2026-03-12T02:15:51.908Z" }, + { url = "https://files.pythonhosted.org/packages/51/bc/987cb99e3aafb296aa11ce5133838a10eae8447edd53168d0804d4fb3a14/mlx_metal-0.31.1-py3-none-macosx_26_0_arm64.whl", hash = "sha256:e7324b7c56b519ae67c025d3ced07e5d35bc3a9f19d4c45fe4927f385148c59e", size = 49256543, upload-time = "2026-03-12T02:15:54.851Z" }, ] [[package]] @@ -4794,7 +4804,7 @@ requires-dist = [ { name = "azure-cognitiveservices-speech", marker = "extra == 'azure'", specifier = ">=1.47.0,<2" }, { name = "camb-sdk", marker = "extra == 'camb'", specifier = ">=1.5.4,<2" }, { name = "coremltools", marker = "extra == 'local-smart-turn'", specifier = ">=8.0" }, - { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.24.0" }, + { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.25.0" }, { name = "deepgram-sdk", marker = "extra == 'deepgram'", specifier = ">=6.0.1,<7" }, { name = "docstring-parser", specifier = ">=0.16,<1" }, { name = "einops", marker = "extra == 'moondream'", specifier = "~=0.8.0" }, @@ -4927,14 +4937,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/1d/37/0f1d11d1dc33234a3 [[package]] name = "pipecat-ai-small-webrtc-prebuilt" -version = "2.3.0" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastapi", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/5f/b0f73bbc6997c22655f0495ce21a4cb176e192df1b5407f66fad8101c697/pipecat_ai_small_webrtc_prebuilt-2.3.0.tar.gz", hash = "sha256:10dc31db9978d68001ae941066fe460c533412a8984df71e5416d4ebeb9c0371", size = 469001, upload-time = "2026-02-25T17:18:43.316Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/02/1e6e90f084ebb1fc954f37661c4614219e4c9fec3d305c8abe5141707b0c/pipecat_ai_small_webrtc_prebuilt-2.4.0.tar.gz", hash = "sha256:c5eddca4e061afb7c5f98cf52ccb85511978a8c834447f6c6d662029e02950c4", size = 472449, upload-time = "2026-03-13T14:17:08.164Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/bc/6193b639a53f4bac1c0fe29b1f8e0d49085c60e457b02a01e725eb7c093f/pipecat_ai_small_webrtc_prebuilt-2.3.0-py3-none-any.whl", hash = "sha256:b3ddaff8bbd56746fe3c58a2d721d3ccc94d17a33c16d78dcbce73d7526c1a05", size = 468881, upload-time = "2026-02-25T17:18:41.869Z" }, + { url = "https://files.pythonhosted.org/packages/25/77/8f6f67142a153943fff31530d51dcf7a2374c39dfa9aba6ef163bf0c622f/pipecat_ai_small_webrtc_prebuilt-2.4.0-py3-none-any.whl", hash = "sha256:9e9a3aa24231b1bf4101a6a2b42c4164a186c0c3d3e49bd51f77280eaa402d12", size = 472792, upload-time = "2026-03-13T14:17:06.556Z" }, ] [[package]] @@ -4985,7 +4995,7 @@ wheels = [ [[package]] name = "posthog" -version = "7.9.8" +version = "7.9.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backoff" }, @@ -4995,9 +5005,9 @@ dependencies = [ { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/f5/490fbe0cd357bf5efaa026200d2a29aaa5e39cd8272cfe0e2d449f46f2db/posthog-7.9.8.tar.gz", hash = "sha256:52b1fa5f3d3faf2ee2fb7f5eb375332905887f7c1e386ef45103448413bd3e57", size = 176688, upload-time = "2026-03-09T14:34:07.822Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/a7/2865487853061fbd62383492237b546d2d8f7c1846272350d2b9e14138cd/posthog-7.9.12.tar.gz", hash = "sha256:ebabf2eb2e1c1fbf22b0759df4644623fa43cc6c9dcbe9fd429b7937d14251ec", size = 176828, upload-time = "2026-03-12T09:01:15.184Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/aa/8b3de1650e0c39223c7f9b7c0f4961f7d39bfa690fa800a9521565381ecb/posthog-7.9.8-py3-none-any.whl", hash = "sha256:2735bcc3232e22c88034454e820c1739f4b29e606d55f31e56b52202650e4330", size = 202361, upload-time = "2026-03-09T14:34:06.031Z" }, + { url = "https://files.pythonhosted.org/packages/65/a9/7a803aed5a5649cf78ea7b31e90d0080181ba21f739243e1741a1e607f1f/posthog-7.9.12-py3-none-any.whl", hash = "sha256:7175bd1698a566bfea98a016c64e3456399f8046aeeca8f1d04ae5bf6c5a38d0", size = 202469, upload-time = "2026-03-12T09:01:13.38Z" }, ] [[package]] @@ -5416,15 +5426,15 @@ wheels = [ [[package]] name = "pydantic-extra-types" -version = "2.11.0" +version = "2.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/71/dba38ee2651f84f7842206adbd2233d8bbdb59fb85e9fa14232486a8c471/pydantic_extra_types-2.11.1.tar.gz", hash = "sha256:46792d2307383859e923d8fcefa82108b1a141f8a9c0198982b3832ab5ef1049", size = 172002, upload-time = "2026-03-16T08:08:03.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" }, + { url = "https://files.pythonhosted.org/packages/17/c1/3226e6d7f5a4f736f38ac11a6fbb262d701889802595cdb0f53a885ac2e0/pydantic_extra_types-2.11.1-py3-none-any.whl", hash = "sha256:1722ea2bddae5628ace25f2aa685b69978ef533123e5638cfbddb999e0100ec1", size = 79526, upload-time = "2026-03-16T08:08:02.533Z" }, ] [[package]] @@ -5473,11 +5483,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/a7/5d/f2946cc6c1baf56de [[package]] name = "pyjwt" -version = "2.11.0" +version = "2.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, ] [package.optional-dependencies] @@ -5524,15 +5537,15 @@ wheels = [ [[package]] name = "pyopenssl" -version = "25.3.0" +version = "26.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/be/97b83a464498a79103036bc74d1038df4a7ef0e402cfaf4d5e113fb14759/pyopenssl-25.3.0.tar.gz", hash = "sha256:c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329", size = 184073, upload-time = "2025-09-17T00:32:21.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/11/a62e1d33b373da2b2c2cd9eb508147871c80f12b1cacde3c5d314922afdd/pyopenssl-26.0.0.tar.gz", hash = "sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc", size = 185534, upload-time = "2026-03-15T14:28:26.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/81/ef2b1dfd1862567d573a4fdbc9f969067621764fbb74338496840a1d2977/pyopenssl-25.3.0-py3-none-any.whl", hash = "sha256:1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6", size = 57268, upload-time = "2025-09-17T00:32:19.474Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7d/d4f7d908fa8415571771b30669251d57c3cf313b36a856e6d7548ae01619/pyopenssl-26.0.0-py3-none-any.whl", hash = "sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81", size = 57969, upload-time = "2026-03-15T14:28:24.864Z" }, ] [[package]] @@ -5654,15 +5667,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.1.2" +version = "1.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/16/6f3f5e9258f0733aaca19aa18e298cb3a629ae49363573e78d241abeef59/python_discovery-1.1.2.tar.gz", hash = "sha256:c500bd2153e3afc5f48a61d33ff570b6f3e710d36ceaaf882fa9bbe5cc2cec49", size = 56928, upload-time = "2026-03-09T20:02:28.402Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/7e/9f3b0dd3a074a6c3e1e79f35e465b1f2ee4b262d619de00cfce523cc9b24/python_discovery-1.1.3.tar.gz", hash = "sha256:7acca36e818cd88e9b2ba03e045ad7e93e1713e29c6bbfba5d90202310b7baa5", size = 56945, upload-time = "2026-03-10T15:08:15.038Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl", hash = "sha256:d18edd61b382d62f8bcd004a71ebaabc87df31dbefb30aeed59f4fc6afa005be", size = 31486, upload-time = "2026-03-09T20:02:27.277Z" }, + { url = "https://files.pythonhosted.org/packages/e7/80/73211fc5bfbfc562369b4aa61dc1e4bf07dc7b34df7b317e4539316b809c/python_discovery-1.1.3-py3-none-any.whl", hash = "sha256:90e795f0121bc84572e737c9aa9966311b9fde44ffb88a5953b3ec9b31c6945e", size = 31485, upload-time = "2026-03-10T15:08:13.06Z" }, ] [[package]] @@ -5815,7 +5828,7 @@ wheels = [ [[package]] name = "qdrant-client" -version = "1.17.0" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "grpcio" }, @@ -5827,9 +5840,9 @@ dependencies = [ { name = "pydantic" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/fb/c9c4cecf6e7fdff2dbaeee0de40e93fe495379eb5fe2775b184ea45315da/qdrant_client-1.17.0.tar.gz", hash = "sha256:47eb033edb9be33a4babb4d87b0d8d5eaf03d52112dca0218db7f2030bf41ba9", size = 344839, upload-time = "2026-02-19T16:03:17.069Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/dd/f8a8261b83946af3cd65943c93c4f83e044f01184e8525404989d22a81a5/qdrant_client-1.17.1.tar.gz", hash = "sha256:22f990bbd63485ed97ba551a4c498181fcb723f71dcab5d6e4e43fe1050a2bc0", size = 344979, upload-time = "2026-03-13T17:13:44.678Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/15/dfadbc9d8c9872e8ac45fa96f5099bb2855f23426bfea1bbcdc85e64ef6e/qdrant_client-1.17.0-py3-none-any.whl", hash = "sha256:f5b452c68c42b3580d3d266446fb00d3c6e3aae89c916e16585b3c704e108438", size = 390381, upload-time = "2026-02-19T16:03:15.486Z" }, + { url = "https://files.pythonhosted.org/packages/68/69/77d1a971c4b933e8c79403e99bcbb790463da5e48333cc4fd5d412c63c98/qdrant_client-1.17.1-py3-none-any.whl", hash = "sha256:6cda4064adfeaf211c751f3fbc00edbbdb499850918c7aff4855a9a759d56cbd", size = 389947, upload-time = "2026-03-13T17:13:43.156Z" }, ] [[package]] @@ -6309,41 +6322,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] -[[package]] -name = "rsa" -version = "4.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - [[package]] name = "ruff" -version = "0.15.5" +version = "0.15.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214, upload-time = "2026-03-05T20:06:34.946Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185, upload-time = "2026-03-05T20:06:29.093Z" }, - { url = "https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080", size = 10859201, upload-time = "2026-03-05T20:06:32.632Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010", size = 10184752, upload-time = "2026-03-05T20:06:40.312Z" }, - { url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857, upload-time = "2026-03-05T20:06:19.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120, upload-time = "2026-03-05T20:06:00.435Z" }, - { url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428, upload-time = "2026-03-05T20:05:51.867Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251, upload-time = "2026-03-05T20:06:22.887Z" }, - { url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801, upload-time = "2026-03-05T20:05:57.168Z" }, - { url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821, upload-time = "2026-03-05T20:06:03.441Z" }, - { url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326, upload-time = "2026-03-05T20:06:25.655Z" }, - { url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820, upload-time = "2026-03-05T20:06:37.79Z" }, - { url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395, upload-time = "2026-03-05T20:05:54.504Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069, upload-time = "2026-03-05T20:06:06.422Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315, upload-time = "2026-03-05T20:06:10.867Z" }, - { url = "https://files.pythonhosted.org/packages/28/3a/950367aee7c69027f4f422059227b290ed780366b6aecee5de5039d50fa8/ruff-0.15.5-py3-none-win32.whl", hash = "sha256:732e5ee1f98ba5b3679029989a06ca39a950cced52143a0ea82a2102cb592b74", size = 10551676, upload-time = "2026-03-05T20:06:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl", hash = "sha256:821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe", size = 11678972, upload-time = "2026-03-05T20:06:45.379Z" }, - { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, + { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, + { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, + { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, + { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, + { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, + { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, + { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, + { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, ] [[package]] @@ -6953,7 +6954,7 @@ wheels = [ [[package]] name = "sphinx-markdown-builder" -version = "0.6.9" +version = "0.6.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -6963,9 +6964,9 @@ dependencies = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d2/f6/7566ba54c8b9744192bdf19ba01e62e1bb6cb1e8526447cdb29feb7cac7c/sphinx_markdown_builder-0.6.9.tar.gz", hash = "sha256:e89dc1b9eb837da430c2c230011fad95a3dfab0345ad503a32e35a31d284a722", size = 22707, upload-time = "2025-12-07T14:36:14.088Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/58/0b7b9a7d071140b3705885d51932e8b62f520388c2772e4952189971727b/sphinx_markdown_builder-0.6.10.tar.gz", hash = "sha256:cd5acf88d52ea0146a712fd557404f10326dff3428a78ba928e59b1727fd4a86", size = 22688, upload-time = "2026-03-11T10:56:57.639Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/ee/02f9986d7818be2ccc5bce76d388e73f5a163f604f682d1ad69e6bc0df7c/sphinx_markdown_builder-0.6.9-py3-none-any.whl", hash = "sha256:35b555760c48d4a38fe4b27813cb5ca636bbd22d8ef0742ac6959043f8000840", size = 16717, upload-time = "2025-12-07T14:36:12.646Z" }, + { url = "https://files.pythonhosted.org/packages/c2/8f/9fecf3d081d5cd49eff83a17b9fef50ed741e6223ab3bb906de4ab0068f9/sphinx_markdown_builder-0.6.10-py3-none-any.whl", hash = "sha256:16d86738b9ac69fcbc86e373c31c6402c30af1fa8d98d0f62cc5f38bfe5fc26e", size = 16700, upload-time = "2026-03-11T10:56:56.135Z" }, ] [[package]] @@ -7141,7 +7142,7 @@ wheels = [ [[package]] name = "strands-agents" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, @@ -7153,12 +7154,13 @@ dependencies = [ { name = "opentelemetry-instrumentation-threading" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, + { name = "pyyaml" }, { name = "typing-extensions" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/51/16241b671a7b8c1970775ee24a50ca8a1a3a319f0652a3b7336989baa245/strands_agents-1.29.0.tar.gz", hash = "sha256:2d07dbbd5af552460f43c764c5a34cc34d90638578ec420999b5dce683e431d9", size = 737805, upload-time = "2026-03-04T21:24:48.911Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/82/6c193a8ea19ed91a368a4cf7d20c87457793e1286dac5811a5c2a60a5cc2/strands_agents-1.30.0.tar.gz", hash = "sha256:358db9d78304fc1fe324763be545243e3f9cb030ed0f6f51d0c91d37caff7746", size = 773031, upload-time = "2026-03-11T18:38:32.257Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/19/468605511e596f838455e6a16ccf380c2d57c4b21f5658cf740b01917753/strands_agents-1.29.0-py3-none-any.whl", hash = "sha256:9cab6ce14292c450d2f0996a06f647754d772c9f1431170963921fe8f5eaaff8", size = 366508, upload-time = "2026-03-04T21:24:47.184Z" }, + { url = "https://files.pythonhosted.org/packages/6e/94/ecc2df8100fdf745d41d10ac2de4c9cb0325384d0e28b4bb90c82a6ec63b/strands_agents-1.30.0-py3-none-any.whl", hash = "sha256:457ba7b063df61d00f122c913b6b85ba6431d17741b9e34484a7e16fb7e00430", size = 386493, upload-time = "2026-03-11T18:38:30.503Z" }, ] [[package]] @@ -7691,16 +7693,16 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.41.0" +version = "0.42.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/ce/eeb58ae4ac36fe09e3842eb02e0eb676bf2c53ae062b98f1b2531673efdd/uvicorn-0.41.0.tar.gz", hash = "sha256:09d11cf7008da33113824ee5a1c6422d89fbc2ff476540d69a34c87fab8b571a", size = 82633, upload-time = "2026-02-16T23:07:24.1Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/ad/4a96c425be6fb67e0621e62d86c402b4a17ab2be7f7c055d9bd2f638b9e2/uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775", size = 85393, upload-time = "2026-03-16T06:19:50.077Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, + { url = "https://files.pythonhosted.org/packages/0a/89/f8827ccff89c1586027a105e5630ff6139a64da2515e24dafe860bd9ae4d/uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", size = 68830, upload-time = "2026-03-16T06:19:48.325Z" }, ] [package.optional-dependencies] From 5cb6aecc9fc226bca3f2a22b69a3dc4b22c326a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 16 Mar 2026 17:57:39 -0700 Subject: [PATCH 074/159] Add DTMF input event support to Daily transport Handle Daily's on_dtmf_event callback, convert it to an InputDTMFFrame pushed into the input transport. Also add __str__ methods to InputDTMFFrame and OutputDTMFFrame for better logging. --- src/pipecat/frames/frames.py | 6 ++++-- src/pipecat/transports/daily/transport.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 1ef7bdb4a..fbc01c488 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -1001,7 +1001,8 @@ class OutputDTMFFrame(DTMFFrame, DataFrame): specify where the DTMF keypress should be sent. """ - pass + def __str__(self): + return f"{self.name}(tone: {self.button})" # @@ -1658,7 +1659,8 @@ class AssistantImageRawFrame(OutputImageRawFrame): class InputDTMFFrame(DTMFFrame, SystemFrame): """DTMF keypress input frame from transport.""" - pass + def __str__(self): + return f"{self.name}(tone: {self.button.value})" @dataclass diff --git a/src/pipecat/transports/daily/transport.py b/src/pipecat/transports/daily/transport.py index 4626f9f53..2798f6d5e 100644 --- a/src/pipecat/transports/daily/transport.py +++ b/src/pipecat/transports/daily/transport.py @@ -22,6 +22,7 @@ import aiohttp from loguru import logger from pydantic import BaseModel +from pipecat.audio.dtmf.types import KeypadEntry from pipecat.audio.vad.vad_analyzer import VADAnalyzer, VADParams from pipecat.frames.frames import ( BotConnectedFrame, @@ -31,6 +32,7 @@ from pipecat.frames.frames import ( EndFrame, Frame, InputAudioRawFrame, + InputDTMFFrame, InputTransportMessageFrame, InterimTranscriptionFrame, OutputAudioRawFrame, @@ -394,6 +396,7 @@ class DailyCallbacks(BaseModel): on_dialout_stopped: Called when dial-out is stopped. on_dialout_error: Called when dial-out encounters an error. on_dialout_warning: Called when dial-out has a warning. + on_dtmf_event: Called when a DTMF tone happens. on_participant_joined: Called when a participant joins. on_participant_left: Called when a participant leaves. on_participant_updated: Called when participant info is updated. @@ -424,6 +427,7 @@ class DailyCallbacks(BaseModel): on_dialout_stopped: Callable[[Any], Awaitable[None]] on_dialout_error: Callable[[Any], Awaitable[None]] on_dialout_warning: Callable[[Any], Awaitable[None]] + on_dtmf_event: Callable[[Any], Awaitable[None]] on_participant_joined: Callable[[Mapping[str, Any]], Awaitable[None]] on_participant_left: Callable[[Mapping[str, Any], str], Awaitable[None]] on_participant_updated: Callable[[Mapping[str, Any]], Awaitable[None]] @@ -1560,6 +1564,14 @@ class DailyTransportClient(EventHandler): """ self._call_event_callback(self._callbacks.on_dialout_warning, data) + def on_dtmf_event(self, data: Any): + """Handle incoming DTMF events. + + Args: + data: DTMF data. + """ + self._call_event_callback(self._callbacks.on_dtmf_event, data) + def on_participant_joined(self, participant): """Handle participant joined events. @@ -2313,6 +2325,7 @@ class DailyTransport(BaseTransport): on_dialout_stopped=self._on_dialout_stopped, on_dialout_error=self._on_dialout_error, on_dialout_warning=self._on_dialout_warning, + on_dtmf_event=self._on_dtmf_event, on_participant_joined=self._on_participant_joined, on_participant_left=self._on_participant_left, on_participant_updated=self._on_participant_updated, @@ -2354,6 +2367,7 @@ class DailyTransport(BaseTransport): self._register_event_handler("on_dialout_stopped") self._register_event_handler("on_dialout_error") self._register_event_handler("on_dialout_warning") + self._register_event_handler("on_dtmf_event") self._register_event_handler("on_first_participant_joined") self._register_event_handler("on_participant_joined") self._register_event_handler("on_participant_left") @@ -2864,6 +2878,15 @@ class DailyTransport(BaseTransport): logger.warning(f"{self} dial-out warning: {data}") await self._call_event_handler("on_dialout_warning", data) + async def _on_dtmf_event(self, data): + """Handle incoming DTMF events.""" + logger.debug(f"{self} DTMF event: {data}") + await self._call_event_handler("on_dtmf_event", data) + + if self._input: + frame = InputDTMFFrame(button=KeypadEntry(data["tone"])) + await self._input.push_frame(frame) + async def _on_participant_joined(self, participant): """Handle participant joined events.""" id = participant["id"] From 59486d5abfbb2b70ff1748612b3e31898c4d8c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 16 Mar 2026 17:58:12 -0700 Subject: [PATCH 075/159] Add changelog entries for PR #4047 --- changelog/4047.added.md | 1 + changelog/4047.changed.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog/4047.added.md create mode 100644 changelog/4047.changed.md diff --git a/changelog/4047.added.md b/changelog/4047.added.md new file mode 100644 index 000000000..b0bda2ed4 --- /dev/null +++ b/changelog/4047.added.md @@ -0,0 +1 @@ +- Added DTMF input event support to the Daily transport. Incoming DTMF tones are now received via Daily's `on_dtmf_event` callback and pushed into the pipeline as `InputDTMFFrame`, enabling bots to react to keypad presses from phone callers. diff --git a/changelog/4047.changed.md b/changelog/4047.changed.md new file mode 100644 index 000000000..c93e95f76 --- /dev/null +++ b/changelog/4047.changed.md @@ -0,0 +1 @@ +- Updated `daily-python` dependency to 0.25.0. From e5b4403ed4f6c5234bec080e7febf54db7c9797a Mon Sep 17 00:00:00 2001 From: Julien Vantyghem Date: Mon, 16 Mar 2026 19:54:04 -0600 Subject: [PATCH 076/159] update docstring following https://github.com/pipecat-ai/pipecat/pull/3916 --- src/pipecat/transports/daily/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipecat/transports/daily/utils.py b/src/pipecat/transports/daily/utils.py index 8719cfe7d..8c7526357 100644 --- a/src/pipecat/transports/daily/utils.py +++ b/src/pipecat/transports/daily/utils.py @@ -89,7 +89,7 @@ class DailyRoomProperties(BaseModel): enable_emoji_reactions: Whether emoji reactions are enabled. eject_at_room_exp: Whether to remove participants when room expires. enable_dialout: Whether SIP dial-out is enabled. - enable_recording: Recording settings ('cloud', 'local', 'raw-tracks'). + enable_recording: Recording settings ('cloud', 'cloud-audio-only', 'local', 'raw-tracks'). enable_transcription_storage: Whether transcription storage is enabled. geo: Geographic region for room. max_participants: Maximum number of participants allowed in the room. @@ -185,7 +185,7 @@ class DailyMeetingTokenProperties(BaseModel): enable_screenshare: If True, the user will be able to share their screen. start_video_off: If True, the user's video will be turned off when they join the room. start_audio_off: If True, the user's audio will be turned off when they join the room. - enable_recording: Recording settings for the token. Must be one of 'cloud', 'local' or 'raw-tracks'. + enable_recording: Recording settings for the token. Must be one of 'cloud', 'cloud-audio-only', 'local' or 'raw-tracks'. enable_prejoin_ui: If True, the user will see the prejoin UI before joining the room. start_cloud_recording: Start cloud recording when the user joins the room. permissions: Specifies the initial default permissions for a non-meeting-owner participant. From 89cb0f089e128a6192d0de90b751e3537da0b1c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:01:00 +0000 Subject: [PATCH 077/159] Initial plan From 7e60320a746184391233630489c53ffa69ce0637 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 04:04:11 +0000 Subject: [PATCH 078/159] fix: set enable_dialout to False in PSTN runner to prevent room creation failures Co-authored-by: jamsea <614910+jamsea@users.noreply.github.com> --- src/pipecat/runner/daily.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/runner/daily.py b/src/pipecat/runner/daily.py index cfb754dfd..d61f14381 100644 --- a/src/pipecat/runner/daily.py +++ b/src/pipecat/runner/daily.py @@ -229,7 +229,7 @@ async def configure( provider=sip_provider, ) room_properties.sip = sip_params - room_properties.enable_dialout = True # Enable outbound calls if needed + room_properties.enable_dialout = False # Requires dialout entitlement on Daily plan room_properties.start_video_off = not sip_enable_video # Voice-only by default # Create room parameters From e11b48631230fe5affd0dbaf9aaf6598f3c6e4aa Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 08:54:07 -0400 Subject: [PATCH 079/159] fix: clean up configure() type hints, deduplicate token expiry, and improve comment Narrow misleading Optional type hints on parameters that never accept None, extract the duplicated token_exp_duration * 60 * 60 calculation, remove unnecessary forward-reference quotes on DailyMeetingTokenProperties, and clarify why enable_dialout is explicitly set to False. --- changelog/4048.changed.md | 1 + src/pipecat/runner/daily.py | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 changelog/4048.changed.md diff --git a/changelog/4048.changed.md b/changelog/4048.changed.md new file mode 100644 index 000000000..e5b9aab06 --- /dev/null +++ b/changelog/4048.changed.md @@ -0,0 +1 @@ +- Narrowed misleading `Optional` type hints and deduplicated token expiry calculation in `configure()` (`pipecat.runner.daily`). diff --git a/src/pipecat/runner/daily.py b/src/pipecat/runner/daily.py index d61f14381..099d931a4 100644 --- a/src/pipecat/runner/daily.py +++ b/src/pipecat/runner/daily.py @@ -79,16 +79,16 @@ async def configure( aiohttp_session: aiohttp.ClientSession, *, api_key: Optional[str] = None, - room_exp_duration: Optional[float] = 2.0, - token_exp_duration: Optional[float] = 2.0, + room_exp_duration: float = 2.0, + token_exp_duration: float = 2.0, sip_caller_phone: Optional[str] = None, - sip_enable_video: Optional[bool] = False, - sip_num_endpoints: Optional[int] = 1, + sip_enable_video: bool = False, + sip_num_endpoints: int = 1, sip_codecs: Optional[Dict[str, List[str]]] = None, sip_provider: Optional[str] = None, room_geo: Optional[str] = None, room_properties: Optional[DailyRoomProperties] = None, - token_properties: Optional["DailyMeetingTokenProperties"] = None, + token_properties: Optional[DailyMeetingTokenProperties] = None, ) -> DailyRoomConfig: """Configure Daily room URL and token with optional SIP capabilities. @@ -184,6 +184,8 @@ async def configure( aiohttp_session=aiohttp_session, ) + token_expiry_seconds: float = token_exp_duration * 60 * 60 + # Check for existing room URL (only in standard mode) existing_room_url = os.getenv("DAILY_ROOM_URL") if existing_room_url and not sip_enabled: @@ -192,11 +194,12 @@ async def configure( room_url = existing_room_url # Create token and return standard format - expiry_time: float = token_exp_duration * 60 * 60 token_params = None if token_properties: token_params = DailyMeetingTokenParams(properties=token_properties) - token = await daily_rest_helper.get_token(room_url, expiry_time, params=token_params) + token = await daily_rest_helper.get_token( + room_url, token_expiry_seconds, params=token_params + ) return DailyRoomConfig(room_url=room_url, token=token) # Create a new room @@ -229,7 +232,10 @@ async def configure( provider=sip_provider, ) room_properties.sip = sip_params - room_properties.enable_dialout = False # Requires dialout entitlement on Daily plan + # Explicitly disable dialout to prevent room creation failures on + # accounts where dialout defaults to enabled but the plan lacks the + # required dialout entitlement. + room_properties.enable_dialout = False room_properties.start_video_off = not sip_enable_video # Voice-only by default # Create room parameters @@ -241,7 +247,6 @@ async def configure( logger.info(f"Created Daily room: {room_url}") # Create meeting token - token_expiry_seconds = token_exp_duration * 60 * 60 token_params = None if token_properties: token_params = DailyMeetingTokenParams(properties=token_properties) From 091f88e42e4b8456dde672acfbcc4a3677fb4ef5 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 09:03:50 -0400 Subject: [PATCH 080/159] feat: add enable_dialout parameter to configure() for dial-out rooms Expose enable_dialout as a configure() parameter (default False) so dial-out examples can opt in without needing to build DailyRoomProperties manually. --- changelog/4048.changed.md | 2 +- src/pipecat/runner/daily.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/changelog/4048.changed.md b/changelog/4048.changed.md index e5b9aab06..edef83283 100644 --- a/changelog/4048.changed.md +++ b/changelog/4048.changed.md @@ -1 +1 @@ -- Narrowed misleading `Optional` type hints and deduplicated token expiry calculation in `configure()` (`pipecat.runner.daily`). +- Added `enable_dialout` parameter to `configure()` in `pipecat.runner.daily` to support dial-out rooms. Also narrowed misleading `Optional` type hints and deduplicated token expiry calculation. diff --git a/src/pipecat/runner/daily.py b/src/pipecat/runner/daily.py index 099d931a4..b9bcb6e3d 100644 --- a/src/pipecat/runner/daily.py +++ b/src/pipecat/runner/daily.py @@ -84,6 +84,7 @@ async def configure( sip_caller_phone: Optional[str] = None, sip_enable_video: bool = False, sip_num_endpoints: int = 1, + enable_dialout: bool = False, sip_codecs: Optional[Dict[str, List[str]]] = None, sip_provider: Optional[str] = None, room_geo: Optional[str] = None, @@ -105,6 +106,8 @@ async def configure( When provided, enables SIP functionality and returns SipRoomConfig. sip_enable_video: Whether video is enabled for SIP. sip_num_endpoints: Number of allowed SIP endpoints. + enable_dialout: Whether to enable outbound dialing (PSTN or SIP) on the room. + Requires dial-out entitlement on your Daily account. sip_codecs: Codecs to support for audio and video. If None, uses Daily defaults. Example: {"audio": ["OPUS"], "video": ["H264"]} sip_provider: SIP provider name (e.g., "daily"). Only used when @@ -159,6 +162,7 @@ async def configure( sip_caller_phone is not None, sip_enable_video is not False, sip_num_endpoints != 1, + enable_dialout is not False, sip_codecs is not None, sip_provider is not None, room_geo is not None, @@ -232,10 +236,7 @@ async def configure( provider=sip_provider, ) room_properties.sip = sip_params - # Explicitly disable dialout to prevent room creation failures on - # accounts where dialout defaults to enabled but the plan lacks the - # required dialout entitlement. - room_properties.enable_dialout = False + room_properties.enable_dialout = enable_dialout room_properties.start_video_off = not sip_enable_video # Voice-only by default # Create room parameters From 024e2ebd4e63f0efb8334a4ff3a27b86b6aba1e7 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 10:51:01 -0400 Subject: [PATCH 081/159] Fix deprecation warning when using filter_incomplete_user_turns --- .../processors/aggregators/llm_response_universal.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pipecat/processors/aggregators/llm_response_universal.py b/src/pipecat/processors/aggregators/llm_response_universal.py index e1e356007..b8a7061d8 100644 --- a/src/pipecat/processors/aggregators/llm_response_universal.py +++ b/src/pipecat/processors/aggregators/llm_response_universal.py @@ -75,6 +75,7 @@ from pipecat.processors.aggregators.llm_context_summarizer import ( SummaryAppliedEvent, ) from pipecat.processors.frame_processor import FrameCallback, FrameDirection, FrameProcessor +from pipecat.services.settings import LLMSettings from pipecat.turns.user_idle_controller import UserIdleController from pipecat.turns.user_mute import BaseUserMuteStrategy from pipecat.turns.user_start import BaseUserTurnStartStrategy, UserTurnStartedParams @@ -561,10 +562,10 @@ class LLMUserAggregator(LLMContextAggregator): # Enable the feature on the LLM with config await self.push_frame( LLMUpdateSettingsFrame( - settings={ - "filter_incomplete_user_turns": True, - "user_turn_completion_config": config, - } + delta=LLMSettings( + filter_incomplete_user_turns=True, + user_turn_completion_config=config, + ) ) ) From 5000b040ddd5aa10eac3239b87fbbded0362075e Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 11:31:08 -0400 Subject: [PATCH 082/159] Fix stale state in user turn stop strategies between turns Reset stop strategies at turn start (not just turn stop) so that late transcriptions arriving between turns do not leave stale _text that causes premature stops on the next turn. Also cancel pending timeout tasks in reset() for both SpeechTimeout and TurnAnalyzer strategies. --- .../speech_timeout_user_turn_stop_strategy.py | 3 + .../turn_analyzer_user_turn_stop_strategy.py | 3 + src/pipecat/turns/user_turn_controller.py | 4 ++ tests/test_user_turn_controller.py | 68 +++++++++++++++++++ tests/test_user_turn_stop_strategy.py | 44 ++++++++++++ 5 files changed, 122 insertions(+) diff --git a/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py index 66d6fa703..ec94d9176 100644 --- a/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py @@ -64,6 +64,9 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy): self._vad_user_speaking = False self._transcript_finalized = False self._vad_stopped_time = None + if self._timeout_task: + await self.task_manager.cancel_task(self._timeout_task) + self._timeout_task = None async def setup(self, task_manager: BaseTaskManager): """Initialize the strategy with the given task manager. diff --git a/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py index f141a75b7..232bde223 100644 --- a/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py @@ -68,6 +68,9 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy): self._vad_user_speaking = False self._vad_stopped_time = None self._transcript_finalized = False + if self._timeout_task: + await self.task_manager.cancel_task(self._timeout_task) + self._timeout_task = None async def setup(self, task_manager: BaseTaskManager): """Initialize the strategy with the given task manager. diff --git a/src/pipecat/turns/user_turn_controller.py b/src/pipecat/turns/user_turn_controller.py index adaafd298..a064fc47b 100644 --- a/src/pipecat/turns/user_turn_controller.py +++ b/src/pipecat/turns/user_turn_controller.py @@ -256,6 +256,10 @@ class UserTurnController(BaseObject): for s in self._user_turn_strategies.start or []: await s.reset() + # Reset all user turn stop strategies to start fresh for the new turn. + for s in self._user_turn_strategies.stop or []: + await s.reset() + await self._call_event_handler("on_user_turn_started", strategy, params) async def _trigger_user_turn_stop( diff --git a/tests/test_user_turn_controller.py b/tests/test_user_turn_controller.py index 72a04a519..2883d39bd 100644 --- a/tests/test_user_turn_controller.py +++ b/tests/test_user_turn_controller.py @@ -15,6 +15,7 @@ from pipecat.frames.frames import ( VADUserStartedSpeakingFrame, VADUserStoppedSpeakingFrame, ) +from pipecat.turns.user_start import VADUserTurnStartStrategy from pipecat.turns.user_start.min_words_user_turn_start_strategy import ( MinWordsUserTurnStartStrategy, ) @@ -199,6 +200,73 @@ class TestUserTurnController(unittest.IsolatedAsyncioTestCase): self.assertTrue(should_stop) self.assertTrue(timeout) + async def test_late_transcription_between_turns_no_premature_stop(self): + """Test that a late transcription arriving between turns does not cause a premature stop. + + Reproduces the bug from issue #4053: after turn 1 completes and reset() + clears state, a late TranscriptionFrame sets _text to stale content. On + the next turn, that stale _text gates a premature turn stop via timeout(0) + before the current turn's transcript arrives. + + Uses only VADUserTurnStartStrategy (no TranscriptionUserTurnStartStrategy) + so the late transcription doesn't trigger a spurious turn start. + """ + controller = UserTurnController( + user_turn_strategies=UserTurnStrategies( + start=[VADUserTurnStartStrategy()], + stop=[SpeechTimeoutUserTurnStopStrategy(user_speech_timeout=TRANSCRIPTION_TIMEOUT)], + ), + user_turn_stop_timeout=USER_TURN_STOP_TIMEOUT, + ) + + await controller.setup(self.task_manager) + + start_count = 0 + stop_count = 0 + + @controller.event_handler("on_user_turn_started") + async def on_user_turn_started(controller, strategy, params): + nonlocal start_count + start_count += 1 + + @controller.event_handler("on_user_turn_stopped") + async def on_user_turn_stopped(controller, strategy, params): + nonlocal stop_count + stop_count += 1 + + # === Turn 1: S-T-E === + await controller.process_frame(VADUserStartedSpeakingFrame()) + self.assertEqual(start_count, 1) + + await controller.process_frame( + TranscriptionFrame(text="Hello!", user_id="", timestamp="now") + ) + + await controller.process_frame(VADUserStoppedSpeakingFrame()) + await asyncio.sleep(TRANSCRIPTION_TIMEOUT + 0.1) + self.assertEqual(stop_count, 1) + + # === Between turns: late transcription arrives === + # This sets _text on the stop strategy while _user_turn is False. + await controller.process_frame( + TranscriptionFrame(text="Hello!", user_id="", timestamp="now") + ) + + # === Turn 2: S-T-E (transcription arrives during turn) === + # The fix resets stop strategies at turn start, clearing stale _text. + await controller.process_frame(VADUserStartedSpeakingFrame()) + self.assertEqual(start_count, 2) + + await controller.process_frame( + TranscriptionFrame(text="How are you?", user_id="", timestamp="now") + ) + + await controller.process_frame(VADUserStoppedSpeakingFrame()) + + # Wait for user_speech_timeout to elapse — should get turn 2 stop + await asyncio.sleep(TRANSCRIPTION_TIMEOUT + 0.1) + self.assertEqual(stop_count, 2) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_user_turn_stop_strategy.py b/tests/test_user_turn_stop_strategy.py index 80fb98efc..85f9f2752 100644 --- a/tests/test_user_turn_stop_strategy.py +++ b/tests/test_user_turn_stop_strategy.py @@ -493,6 +493,50 @@ class TestSpeechTimeoutUserTurnStopStrategy(unittest.IsolatedAsyncioTestCase): # Finalized transcript received after timeout, triggers immediately self.assertTrue(should_start) + async def test_reset_clears_stale_text_no_premature_stop(self): + """Test that reset() clears stale text and cancels timeout, preventing premature stop. + + Reproduces the bug from issue #4053: after turn 1 completes and + reset() is called, a late transcription sets _text. If reset() is + called again at turn 2 start, the stale _text should be cleared + so no premature stop occurs on VAD stop. + """ + strategy = await self._create_strategy() + + stop_count = 0 + + @strategy.event_handler("on_user_turn_stopped") + async def on_user_turn_stopped(strategy, params): + nonlocal stop_count + stop_count += 1 + + # === Turn 1: S-T-E === + await strategy.process_frame(VADUserStartedSpeakingFrame()) + await strategy.process_frame(TranscriptionFrame(text="Hello!", user_id="cat", timestamp="")) + await strategy.process_frame(VADUserStoppedSpeakingFrame()) + await asyncio.sleep(AGGREGATION_TIMEOUT + 0.1) + self.assertEqual(stop_count, 1) + + # Reset after turn 1 (as controller would do at turn stop) + await strategy.reset() + + # === Late transcription arrives between turns === + await strategy.process_frame(TranscriptionFrame(text="Hello!", user_id="cat", timestamp="")) + + # Reset at turn 2 start (the fix: controller now resets stop strategies at turn start) + await strategy.reset() + + # === Turn 2: S-T-E (transcription arrives during turn) === + await strategy.process_frame(VADUserStartedSpeakingFrame()) + await strategy.process_frame( + TranscriptionFrame(text="How are you?", user_id="cat", timestamp="") + ) + await strategy.process_frame(VADUserStoppedSpeakingFrame()) + + # Wait for timeout — should get turn 2 stop with the real transcription + await asyncio.sleep(AGGREGATION_TIMEOUT + 0.1) + self.assertEqual(stop_count, 2) + class TestExternalUserTurnStopStrategy(unittest.IsolatedAsyncioTestCase): async def test_external_strategy(self): From d70df1d8b0cd1a11ce524617dc2a25a9266c06e3 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 11:35:38 -0400 Subject: [PATCH 083/159] Add changelog for #4057 --- changelog/4057.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4057.fixed.md diff --git a/changelog/4057.fixed.md b/changelog/4057.fixed.md new file mode 100644 index 000000000..b63b8540e --- /dev/null +++ b/changelog/4057.fixed.md @@ -0,0 +1 @@ +- Fixed premature user turn stops caused by late transcriptions arriving between turns. A stale transcript from the previous turn could persist into the next turn and trigger a stop before the current turn's real transcript arrived. Stop strategies are now reset at both turn start and turn stop to prevent state from leaking across turn boundaries. From 790a23d2e569957923f49bc790f82be26477900c Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 12:00:28 -0400 Subject: [PATCH 084/159] fix: resolve raw language strings through Language enum for proper service conversion Raw strings like "de-DE" passed as the language parameter to TTS/STT services were bypassing the Language enum resolution logic, causing silent failures (e.g. ElevenLabs expects "de" not "de-DE"). Now raw strings are first converted to Language enums so they go through the same resolve_language() path, with a warning logged for unrecognized strings. --- src/pipecat/services/stt_service.py | 27 ++- src/pipecat/services/tts_service.py | 27 ++- tests/test_service_language.py | 251 ++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 tests/test_service_language.py diff --git a/src/pipecat/services/stt_service.py b/src/pipecat/services/stt_service.py index c442c41eb..ecfec7b9b 100644 --- a/src/pipecat/services/stt_service.py +++ b/src/pipecat/services/stt_service.py @@ -124,6 +124,18 @@ class STTService(AIService): # Convert Language enum to service-specific format at init time. # Runtime updates are handled by _update_settings(), but init-time # settings bypass that path and need explicit conversion. + # Raw strings (e.g. "de-DE") are first converted to Language enums + # so they go through the same resolution logic. + if isinstance(self._settings.language, str) and not isinstance( + self._settings.language, Language + ): + try: + self._settings.language = Language(self._settings.language) + except ValueError: + logger.warning( + f"Language string '{self._settings.language}' is not a recognized " + f"Language code. It will be passed to the service as-is." + ) if isinstance(self._settings.language, Language): converted = self.language_to_service_language(self._settings.language) if converted is not None: @@ -294,7 +306,20 @@ class STTService(AIService): Returns: Dict mapping changed field names to their previous values. """ - # Translate language *before* applying so the stored value is canonical + # Translate language *before* applying so the stored value is canonical. + # Raw strings are first converted to Language enums for proper resolution. + if ( + is_given(delta.language) + and isinstance(delta.language, str) + and not isinstance(delta.language, Language) + ): + try: + delta.language = Language(delta.language) + except ValueError: + logger.warning( + f"Language string '{delta.language}' is not a recognized " + f"Language code. It will be passed to the service as-is." + ) if is_given(delta.language) and isinstance(delta.language, Language): converted = self.language_to_service_language(delta.language) if converted is not None: diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index e2a9190ab..7583339c8 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -248,6 +248,18 @@ class TTSService(AIService): # Convert Language enum to service-specific format at init time. # Runtime updates are handled by _update_settings(), but init-time # settings bypass that path and need explicit conversion. + # Raw strings (e.g. "de-DE") are first converted to Language enums + # so they go through the same resolution logic. + if isinstance(self._settings.language, str) and not isinstance( + self._settings.language, Language + ): + try: + self._settings.language = Language(self._settings.language) + except ValueError: + logger.warning( + f"Language string '{self._settings.language}' is not a recognized " + f"Language code. It will be passed to the service as-is." + ) if isinstance(self._settings.language, Language): converted = self.language_to_service_language(self._settings.language) if converted is not None: @@ -610,7 +622,20 @@ class TTSService(AIService): Returns: Dict mapping changed field names to their previous values. """ - # Translate language *before* applying so the stored value is canonical + # Translate language *before* applying so the stored value is canonical. + # Raw strings are first converted to Language enums for proper resolution. + if ( + is_given(delta.language) + and isinstance(delta.language, str) + and not isinstance(delta.language, Language) + ): + try: + delta.language = Language(delta.language) + except ValueError: + logger.warning( + f"Language string '{delta.language}' is not a recognized " + f"Language code. It will be passed to the service as-is." + ) if is_given(delta.language) and isinstance(delta.language, Language): converted = self.language_to_service_language(delta.language) if converted is not None: diff --git a/tests/test_service_language.py b/tests/test_service_language.py new file mode 100644 index 000000000..6ac23c823 --- /dev/null +++ b/tests/test_service_language.py @@ -0,0 +1,251 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Tests for language parameter handling in TTS and STT services. + +Verifies that Language enums, raw strings (e.g. "de-DE"), and unrecognized +strings are all resolved correctly at both init time and runtime update time. +""" + +from typing import AsyncGenerator, Optional +from unittest.mock import patch + +import pytest + +from pipecat.frames.frames import Frame +from pipecat.services.settings import STTSettings, TTSSettings +from pipecat.services.stt_service import STTService +from pipecat.services.tts_service import TTSService +from pipecat.transcriptions.language import Language, resolve_language + +# --------------------------------------------------------------------------- +# Minimal concrete subclasses for testing +# --------------------------------------------------------------------------- + +# A simple language map using only base codes (like ElevenLabs does). +_LANGUAGE_MAP = { + Language.DE: "de", + Language.EN: "en", + Language.FR: "fr", +} + + +class _TestTTSService(TTSService): + """Minimal concrete TTS service for testing language resolution.""" + + class Settings(TTSSettings): + pass + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: + yield # pragma: no cover + + def language_to_service_language(self, language: Language) -> Optional[str]: + return resolve_language(language, _LANGUAGE_MAP, use_base_code=True) + + +class _TestSTTService(STTService): + """Minimal concrete STT service for testing language resolution.""" + + class Settings(STTSettings): + pass + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + async def run_stt(self, audio: bytes) -> AsyncGenerator[Frame, None]: + yield # pragma: no cover + + async def process_audio_frame(self, frame, direction): + pass # pragma: no cover + + def language_to_service_language(self, language: Language) -> Optional[str]: + return resolve_language(language, _LANGUAGE_MAP, use_base_code=True) + + +# --------------------------------------------------------------------------- +# TTS init tests +# --------------------------------------------------------------------------- + + +class TestTTSLanguageInit: + """Test language resolution at TTS service init time.""" + + def test_language_enum_base_code(self): + """Language.DE (base code in map) resolves to 'de'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=Language.DE)) + assert svc._settings.language == "de" + + def test_language_enum_regional_code(self): + """Language.DE_DE (regional, not in map) falls back to base code 'de'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=Language.DE_DE)) + assert svc._settings.language == "de" + + def test_raw_string_base_code(self): + """Raw string 'de' is converted to Language.DE then resolved to 'de'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language="de")) + assert svc._settings.language == "de" + + def test_raw_string_regional_code(self): + """Raw string 'de-DE' is converted to Language.DE_DE then resolved to 'de'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language="de-DE")) + assert svc._settings.language == "de" + + def test_raw_string_other_regional(self): + """Raw string 'en-US' is converted to Language.EN_US then resolved to 'en'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language="en-US")) + assert svc._settings.language == "en" + + def test_raw_string_unrecognized(self): + """Unrecognized raw string logs a warning and is passed through as-is.""" + with patch("pipecat.services.tts_service.logger") as mock_logger: + svc = _TestTTSService(settings=_TestTTSService.Settings(language="klingon")) + assert svc._settings.language == "klingon" + mock_logger.warning.assert_called_once() + assert "klingon" in mock_logger.warning.call_args[0][0] + + def test_language_none(self): + """None language is left as None.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=None)) + assert svc._settings.language is None + + +# --------------------------------------------------------------------------- +# STT init tests +# --------------------------------------------------------------------------- + + +class TestSTTLanguageInit: + """Test language resolution at STT service init time.""" + + def test_language_enum_base_code(self): + """Language.FR (base code in map) resolves to 'fr'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=Language.FR)) + assert svc._settings.language == "fr" + + def test_language_enum_regional_code(self): + """Language.FR_FR (regional, not in map) falls back to base code 'fr'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=Language.FR_FR)) + assert svc._settings.language == "fr" + + def test_raw_string_base_code(self): + """Raw string 'fr' is converted to Language.FR then resolved to 'fr'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language="fr")) + assert svc._settings.language == "fr" + + def test_raw_string_regional_code(self): + """Raw string 'de-DE' is converted to Language.DE_DE then resolved to 'de'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language="de-DE")) + assert svc._settings.language == "de" + + def test_raw_string_unrecognized(self): + """Unrecognized raw string logs a warning and is passed through as-is.""" + with patch("pipecat.services.stt_service.logger") as mock_logger: + svc = _TestSTTService(settings=_TestSTTService.Settings(language="klingon")) + assert svc._settings.language == "klingon" + mock_logger.warning.assert_called_once() + assert "klingon" in mock_logger.warning.call_args[0][0] + + def test_language_none(self): + """None language is left as None.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=None)) + assert svc._settings.language is None + + +# --------------------------------------------------------------------------- +# TTS runtime update tests +# --------------------------------------------------------------------------- + + +class TestTTSLanguageUpdate: + """Test language resolution during runtime settings updates.""" + + @pytest.mark.asyncio + async def test_update_language_enum_base_code(self): + """Updating with Language.EN resolves to 'en'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=None)) + await svc._update_settings(_TestTTSService.Settings(language=Language.EN)) + assert svc._settings.language == "en" + + @pytest.mark.asyncio + async def test_update_language_enum_regional_code(self): + """Updating with Language.DE_DE falls back to base code 'de'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=None)) + await svc._update_settings(_TestTTSService.Settings(language=Language.DE_DE)) + assert svc._settings.language == "de" + + @pytest.mark.asyncio + async def test_update_raw_string_base_code(self): + """Updating with raw string 'de' resolves to 'de'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=None)) + await svc._update_settings(_TestTTSService.Settings(language="de")) + assert svc._settings.language == "de" + + @pytest.mark.asyncio + async def test_update_raw_string_regional_code(self): + """Updating with raw string 'de-DE' resolves to 'de'.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=None)) + await svc._update_settings(_TestTTSService.Settings(language="de-DE")) + assert svc._settings.language == "de" + + @pytest.mark.asyncio + async def test_update_raw_string_unrecognized(self): + """Updating with unrecognized string logs warning and passes through.""" + svc = _TestTTSService(settings=_TestTTSService.Settings(language=None)) + with patch("pipecat.services.tts_service.logger") as mock_logger: + await svc._update_settings(_TestTTSService.Settings(language="klingon")) + assert svc._settings.language == "klingon" + mock_logger.warning.assert_called_once() + assert "klingon" in mock_logger.warning.call_args[0][0] + + +# --------------------------------------------------------------------------- +# STT runtime update tests +# --------------------------------------------------------------------------- + + +class TestSTTLanguageUpdate: + """Test language resolution during runtime settings updates.""" + + @pytest.mark.asyncio + async def test_update_language_enum_base_code(self): + """Updating with Language.EN resolves to 'en'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=None)) + await svc._update_settings(_TestSTTService.Settings(language=Language.EN)) + assert svc._settings.language == "en" + + @pytest.mark.asyncio + async def test_update_language_enum_regional_code(self): + """Updating with Language.FR_FR falls back to base code 'fr'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=None)) + await svc._update_settings(_TestSTTService.Settings(language=Language.FR_FR)) + assert svc._settings.language == "fr" + + @pytest.mark.asyncio + async def test_update_raw_string_base_code(self): + """Updating with raw string 'fr' resolves to 'fr'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=None)) + await svc._update_settings(_TestSTTService.Settings(language="fr")) + assert svc._settings.language == "fr" + + @pytest.mark.asyncio + async def test_update_raw_string_regional_code(self): + """Updating with raw string 'fr-FR' resolves to 'fr'.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=None)) + await svc._update_settings(_TestSTTService.Settings(language="fr-FR")) + assert svc._settings.language == "fr" + + @pytest.mark.asyncio + async def test_update_raw_string_unrecognized(self): + """Updating with unrecognized string logs warning and passes through.""" + svc = _TestSTTService(settings=_TestSTTService.Settings(language=None)) + with patch("pipecat.services.stt_service.logger") as mock_logger: + await svc._update_settings(_TestSTTService.Settings(language="klingon")) + assert svc._settings.language == "klingon" + mock_logger.warning.assert_called_once() + assert "klingon" in mock_logger.warning.call_args[0][0] From 18e654b3f03e294d6c6b4ca5d813530c4dffbc96 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 12:01:50 -0400 Subject: [PATCH 085/159] docs: add changelog for #4058 --- changelog/4058.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4058.fixed.md diff --git a/changelog/4058.fixed.md b/changelog/4058.fixed.md new file mode 100644 index 000000000..4b5baed82 --- /dev/null +++ b/changelog/4058.fixed.md @@ -0,0 +1 @@ +- Fixed raw language strings like `"de-DE"` silently failing when passed to TTS/STT services (e.g. ElevenLabs producing no audio). Raw strings now go through the same `Language` enum resolution as enum values, so regional codes like `"de-DE"` are properly converted to service-expected formats like `"de"`. Unrecognized strings log a warning instead of failing silently. From 05abc95b5fbee9df621ac3c65e115c3a43d1bb42 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 16:10:35 -0400 Subject: [PATCH 086/159] Update uv.lock with pyasn1 v0.6.3 --- uv.lock | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 144b30e66..1263097a8 100644 --- a/uv.lock +++ b/uv.lock @@ -2126,6 +2126,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747, upload-time = "2026-02-20T20:16:21.325Z" }, { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202, upload-time = "2026-02-20T20:47:28.955Z" }, { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620, upload-time = "2026-02-20T20:55:55.581Z" }, + { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132, upload-time = "2026-02-20T21:02:43.261Z" }, { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729, upload-time = "2026-02-20T20:20:58.395Z" }, { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946, upload-time = "2026-02-20T20:49:31.102Z" }, { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494, upload-time = "2026-02-20T20:21:06.541Z" }, @@ -2133,6 +2134,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, @@ -2141,6 +2143,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, @@ -2149,6 +2152,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, + { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, @@ -2157,6 +2161,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, @@ -2165,6 +2170,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, @@ -5217,11 +5223,11 @@ wheels = [ [[package]] name = "pyasn1" -version = "0.6.2" +version = "0.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, ] [[package]] From edf16c55338d4ced0931cfa3303a2de0ca8b07f9 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 17 Mar 2026 18:16:58 -0400 Subject: [PATCH 087/159] fix: pass list-type Deepgram settings as lists instead of stringifying List-valued settings like keyterm, keywords, search, redact, and replace were being converted to strings before being passed to the SDK connect() method. The SDK expects lists so its encode_query can produce repeated query params (keyterm=a&keyterm=b). --- changelog/4063.fixed.md | 1 + src/pipecat/services/deepgram/stt.py | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 changelog/4063.fixed.md diff --git a/changelog/4063.fixed.md b/changelog/4063.fixed.md new file mode 100644 index 000000000..ea703078a --- /dev/null +++ b/changelog/4063.fixed.md @@ -0,0 +1 @@ +- Fixed Deepgram STT list-type settings (`keyterm`, `keywords`, `search`, `redact`, `replace`) being stringified instead of passed as lists to the SDK, which caused them to be sent as literal strings (e.g. `"['pipecat']"`) in the WebSocket query params. diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index 7d849a160..85dfc059c 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -554,7 +554,15 @@ class DeepgramSTTService(STTService): value = getattr(s, f.name) if not is_given(value) or value is None: continue - kwargs[f.name] = str(value).lower() if isinstance(value, bool) else str(value) + # Lists (e.g. keyterm, keywords, search, redact, replace) must be + # passed through as-is so the SDK's encode_query produces repeated + # query params (keyterm=a&keyterm=b) instead of a stringified list. + if isinstance(value, list): + kwargs[f.name] = value + elif isinstance(value, bool): + kwargs[f.name] = str(value).lower() + else: + kwargs[f.name] = str(value) # model and language if is_given(s.model) and s.model is not None: @@ -580,7 +588,12 @@ class DeepgramSTTService(STTService): # Any remaining values in extra (that didn't map to declared fields) for key, value in s.extra.items(): if value is not None: - kwargs[key] = str(value).lower() if isinstance(value, bool) else str(value) + if isinstance(value, list): + kwargs[key] = value + elif isinstance(value, bool): + kwargs[key] = str(value).lower() + else: + kwargs[key] = str(value) if self._addons: for key, value in self._addons.items(): From 0378fb0d9194e0a6206671d283a965e899f2258e Mon Sep 17 00:00:00 2001 From: joachimchauvet Date: Wed, 18 Mar 2026 16:04:42 +0200 Subject: [PATCH 088/159] fix(livekit): suppress InvalidState log spam from audio mixer during interruptions --- src/pipecat/transports/livekit/transport.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/pipecat/transports/livekit/transport.py b/src/pipecat/transports/livekit/transport.py index 3fb3d1694..18163cf8e 100644 --- a/src/pipecat/transports/livekit/transport.py +++ b/src/pipecat/transports/livekit/transport.py @@ -388,7 +388,16 @@ class LiveKitTransportClient: await self._audio_source.capture_frame(audio_frame) return True except Exception as e: - logger.error(f"Error publishing audio: {e}") + # When using an audio mixer, the base output transport's + # with_mixer() generator continuously yields frames (mixed with + # background audio) even when no TTS audio is queued. During + # interruptions, the audio task is cancelled and recreated, but + # there is a brief window where the native LiveKit AudioSource + # rejects capture_frame() with an InvalidState error. This is a + # transient condition — the mixer will produce a new frame within + # milliseconds, so we silently drop these frames. + if "InvalidState" not in str(e): + logger.error(f"Error publishing audio: {e}") return False def get_participants(self) -> List[str]: From 45186cc4ce0024c414216d79ce8f5681f1a5f7b5 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 11:45:23 -0400 Subject: [PATCH 089/159] feat: add OpenAI Responses API LLM service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add OpenAIResponsesLLMService using the Responses API, with a dedicated adapter that converts LLMContext messages to Responses API input items (system→developer, tool_calls→function_call, tool→function_call_output, multimodal content conversion, and tools schema flattening). - New adapter: open_ai_responses_adapter.py - New service: openai/responses/llm.py - Examples: 07-interruptible and 14-function-calling variants - 19 unit tests for adapter conversion logic - Eval entries for both examples --- .../07-interruptible-openai-responses.py | 125 ++++++ .../14-function-calling-openai-responses.py | 175 ++++++++ scripts/evals/run-release-evals.py | 3 + .../services/open_ai_responses_adapter.py | 240 +++++++++++ src/pipecat/services/openai/__init__.py | 1 + .../services/openai/responses/__init__.py | 5 + src/pipecat/services/openai/responses/llm.py | 393 ++++++++++++++++++ tests/test_openai_responses_adapter.py | 349 ++++++++++++++++ 8 files changed, 1291 insertions(+) create mode 100644 examples/foundational/07-interruptible-openai-responses.py create mode 100644 examples/foundational/14-function-calling-openai-responses.py create mode 100644 src/pipecat/adapters/services/open_ai_responses_adapter.py create mode 100644 src/pipecat/services/openai/responses/__init__.py create mode 100644 src/pipecat/services/openai/responses/llm.py create mode 100644 tests/test_openai_responses_adapter.py diff --git a/examples/foundational/07-interruptible-openai-responses.py b/examples/foundational/07-interruptible-openai-responses.py new file mode 100644 index 000000000..baae3754a --- /dev/null +++ b/examples/foundational/07-interruptible-openai-responses.py @@ -0,0 +1,125 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import LLMRunFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + +# We use lambdas to defer transport parameter creation until the transport +# type is selected at runtime. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + settings=CartesiaTTSService.Settings( + voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ), + ) + + llm = OpenAIResponsesLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAIResponsesLLMService.Settings( + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", + ), + ) + + context = LLMContext() + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, + user_aggregator, # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + assistant_aggregator, # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + context.add_message( + {"role": "developer", "content": "Please introduce yourself to the user."} + ) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/examples/foundational/14-function-calling-openai-responses.py b/examples/foundational/14-function-calling-openai-responses.py new file mode 100644 index 000000000..58cac774a --- /dev/null +++ b/examples/foundational/14-function-calling-openai-responses.py @@ -0,0 +1,175 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import LLMRunFrame, TTSSpeakFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.llm_service import FunctionCallParams +from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + + +async def fetch_weather_from_api(params: FunctionCallParams): + await params.result_callback({"conditions": "nice", "temperature": "75"}) + + +async def fetch_restaurant_recommendation(params: FunctionCallParams): + await params.result_callback({"name": "The Golden Dragon"}) + + +# We use lambdas to defer transport parameter creation until the transport +# type is selected at runtime. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + settings=CartesiaTTSService.Settings( + voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ), + ) + + llm = OpenAIResponsesLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAIResponsesLLMService.Settings( + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", + ), + ) + + # You can also register a function_name of None to get all functions + # sent to the same callback with an additional function_name parameter. + llm.register_function("get_current_weather", fetch_weather_from_api) + llm.register_function("get_restaurant_recommendation", fetch_restaurant_recommendation) + + @llm.event_handler("on_function_calls_started") + async def on_function_calls_started(service, function_calls): + await tts.queue_frame(TTSSpeakFrame("Let me check on that.")) + + weather_function = FunctionSchema( + name="get_current_weather", + description="Get the current weather", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "format": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "The temperature unit to use. Infer this from the user's location.", + }, + }, + required=["location", "format"], + ) + restaurant_function = FunctionSchema( + name="get_restaurant_recommendation", + description="Get a restaurant recommendation", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + }, + required=["location"], + ) + tools = ToolsSchema(standard_tools=[weather_function, restaurant_function]) + + context = LLMContext(tools=tools) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) + + pipeline = Pipeline( + [ + transport.input(), + stt, + user_aggregator, + llm, + tts, + transport.output(), + assistant_aggregator, + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + context.add_message( + {"role": "developer", "content": "Please introduce yourself to the user."} + ) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/scripts/evals/run-release-evals.py b/scripts/evals/run-release-evals.py index 625f33564..9671703ba 100644 --- a/scripts/evals/run-release-evals.py +++ b/scripts/evals/run-release-evals.py @@ -147,6 +147,7 @@ TESTS_07 = [ ("07zi-interruptible-piper.py", EVAL_SIMPLE_MATH), ("07zj-interruptible-kokoro.py", EVAL_SIMPLE_MATH), ("07zk-interruptible-resembleai.py", EVAL_SIMPLE_MATH), + ("07-interruptible-openai-responses.py", EVAL_SIMPLE_MATH), # Needs a local XTTS docker instance running. # ("07i-interruptible-xtts.py", EVAL_SIMPLE_MATH), ] @@ -184,6 +185,8 @@ TESTS_14 = [ ("14v-function-calling-openai.py", EVAL_WEATHER), ("14w-function-calling-mistral.py", EVAL_WEATHER), ("14x-function-calling-openpipe.py", EVAL_WEATHER), + ("14-function-calling-openai-responses.py", EVAL_WEATHER), + ("14-function-calling-openai-responses.py", EVAL_WEATHER_AND_RESTAURANT), # Video ("14d-function-calling-anthropic-video.py", EVAL_VISION_CAMERA), ("14d-function-calling-aws-video.py", EVAL_VISION_CAMERA), diff --git a/src/pipecat/adapters/services/open_ai_responses_adapter.py b/src/pipecat/adapters/services/open_ai_responses_adapter.py new file mode 100644 index 000000000..f55ba6435 --- /dev/null +++ b/src/pipecat/adapters/services/open_ai_responses_adapter.py @@ -0,0 +1,240 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""OpenAI Responses API adapter for Pipecat.""" + +import copy +from typing import Any, Dict, List, Optional, TypedDict + +from loguru import logger +from openai._types import NotGiven as OpenAINotGiven +from openai.types.responses import FunctionToolParam, ResponseInputItemParam + +from pipecat.adapters.base_llm_adapter import BaseLLMAdapter +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.processors.aggregators.llm_context import ( + LLMContext, + LLMContextMessage, + LLMSpecificMessage, + NotGiven, +) + + +class OpenAIResponsesLLMInvocationParams(TypedDict, total=False): + """Context-based parameters for invoking OpenAI Responses API.""" + + input: List[ResponseInputItemParam] + tools: List[FunctionToolParam] | OpenAINotGiven + instructions: str + + +class OpenAIResponsesLLMAdapter(BaseLLMAdapter[OpenAIResponsesLLMInvocationParams]): + """OpenAI Responses API adapter for Pipecat. + + Handles: + + - Converting LLMContext messages to Responses API input items + - Converting Pipecat's standardized tools schema to Responses API function tool format + - Extracting and sanitizing messages from the LLM context for logging + """ + + def __init__(self): + """Initialize the adapter.""" + super().__init__() + self._warned_system_instruction = False + + @property + def id_for_llm_specific_messages(self) -> str: + """Get the identifier used in LLMSpecificMessage instances.""" + return "openai_responses" + + def get_llm_invocation_params( + self, + context: LLMContext, + *, + system_instruction: Optional[str] = None, + ) -> OpenAIResponsesLLMInvocationParams: + """Get Responses API invocation parameters from a universal LLM context. + + Args: + context: The LLM context containing messages, tools, etc. + system_instruction: Optional system instruction from service settings. + + Returns: + Dictionary of parameters for the Responses API. + """ + messages = self.get_messages(context) + input_items = self._convert_messages_to_input(messages) + + params: OpenAIResponsesLLMInvocationParams = { + "input": input_items, + "tools": self.from_standard_tools(context.tools), + } + + if system_instruction: + # Compatibility: The Responses API requires at least one input + # message when instructions are provided. Contexts that worked with + # OpenAILLMService (system_instruction + empty messages) need the + # instructions converted to an initial developer message. + if not input_items: + params["input"] = [{"role": "developer", "content": system_instruction}] + else: + params["instructions"] = system_instruction + + return params + + def to_provider_tools_format(self, tools_schema: ToolsSchema) -> List[FunctionToolParam]: + """Convert function schemas to Responses API function tool format. + + Args: + tools_schema: The Pipecat tools schema to convert. + + Returns: + List of Responses API function tool definitions. + """ + functions_schema = tools_schema.standard_tools + result = [] + for func in functions_schema: + d = func.to_default_dict() + tool: FunctionToolParam = { + "type": "function", + "name": d["name"], + "parameters": d.get("parameters", {}), + "strict": d.get("strict", None), + } + if "description" in d: + tool["description"] = d["description"] + result.append(tool) + return result + + def get_messages_for_logging(self, context: LLMContext) -> List[Dict[str, Any]]: + """Get messages from context in a format ready for logging. + + Removes or truncates sensitive data like image content for safe logging. + + Args: + context: The LLM context containing messages. + + Returns: + List of messages in a format ready for logging. + """ + msgs = [] + for message in self.get_messages(context): + msg = copy.deepcopy(message) + if "content" in msg: + if isinstance(msg["content"], list): + for item in msg["content"]: + if item.get("type") == "image_url": + if item["image_url"]["url"].startswith("data:image/"): + item["image_url"]["url"] = "data:image/..." + if item.get("type") == "input_audio": + item["input_audio"]["data"] = "..." + msgs.append(msg) + return msgs + + def _convert_messages_to_input( + self, messages: List[LLMContextMessage] + ) -> List[ResponseInputItemParam]: + """Convert LLMContext messages to Responses API input items. + + Args: + messages: Messages from the LLMContext. + + Returns: + List of Responses API input items. + """ + result: List[ResponseInputItemParam] = [] + is_first = True + + for message in messages: + if isinstance(message, LLMSpecificMessage): + result.append(message.message) + is_first = False + continue + + role = message.get("role") + + if role == "system": + if is_first and not self._warned_system_instruction: + logger.warning( + "System messages in LLMContext are converted to 'developer' role for the " + "Responses API. Consider using settings.system_instruction instead, which " + "maps to the 'instructions' parameter." + ) + self._warned_system_instruction = True + content = message.get("content", "") + if isinstance(content, list): + content = self._convert_multimodal_content(content) + result.append({"role": "developer", "content": content}) + + elif role == "user": + content = message.get("content", "") + if isinstance(content, list): + content = self._convert_multimodal_content(content) + result.append({"role": "user", "content": content}) + + elif role == "assistant": + tool_calls = message.get("tool_calls") + if tool_calls: + for tc in tool_calls: + func = tc.get("function", {}) + result.append( + { + "type": "function_call", + "call_id": tc.get("id", ""), + "name": func.get("name", ""), + "arguments": func.get("arguments", ""), + } + ) + else: + content = message.get("content", "") + if isinstance(content, list): + content = self._convert_multimodal_content(content) + result.append({"role": "assistant", "content": content}) + + elif role == "tool": + content = message.get("content", "") + if not isinstance(content, str): + content = str(content) + result.append( + { + "type": "function_call_output", + "call_id": message.get("tool_call_id", ""), + "output": content, + } + ) + + is_first = False + + return result + + def _convert_multimodal_content(self, content: list) -> list: + """Convert multimodal content parts to Responses API format. + + Args: + content: List of content parts from the LLMContext message. + + Returns: + List of content parts in Responses API format. + """ + result = [] + for part in content: + part_type = part.get("type") + if part_type == "text": + result.append({"type": "input_text", "text": part.get("text", "")}) + elif part_type == "image_url": + image_url_obj = part.get("image_url", {}) + result.append( + { + "type": "input_image", + "image_url": image_url_obj.get("url", ""), + "detail": image_url_obj.get("detail", "auto"), + } + ) + else: + # Pass through unknown types as-is + result.append(part) + return result diff --git a/src/pipecat/services/openai/__init__.py b/src/pipecat/services/openai/__init__.py index e182264b1..3caa3c3cb 100644 --- a/src/pipecat/services/openai/__init__.py +++ b/src/pipecat/services/openai/__init__.py @@ -11,6 +11,7 @@ from pipecat.services import DeprecatedModuleProxy from .image import * from .llm import * from .realtime import * +from .responses.llm import * from .stt import * from .tts import * diff --git a/src/pipecat/services/openai/responses/__init__.py b/src/pipecat/services/openai/responses/__init__.py new file mode 100644 index 000000000..c4d243b97 --- /dev/null +++ b/src/pipecat/services/openai/responses/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py new file mode 100644 index 000000000..e72b8acdb --- /dev/null +++ b/src/pipecat/services/openai/responses/llm.py @@ -0,0 +1,393 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""OpenAI Responses API LLM service implementation.""" + +import json +from contextlib import asynccontextmanager +from dataclasses import dataclass, field +from typing import Any, Dict, List, Mapping, Optional + +import httpx +from loguru import logger +from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream, DefaultAsyncHttpxClient +from openai.types.responses import ( + ResponseCompletedEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseStreamEvent, + ResponseTextDeltaEvent, +) + +from pipecat.adapters.services.open_ai_responses_adapter import ( + OpenAIResponsesLLMAdapter, + OpenAIResponsesLLMInvocationParams, +) +from pipecat.frames.frames import ( + Frame, + LLMContextFrame, + LLMFullResponseEndFrame, + LLMFullResponseStartFrame, +) +from pipecat.metrics.metrics import LLMTokenUsage +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.frame_processor import FrameDirection +from pipecat.services.llm_service import FunctionCallFromLLM, LLMService +from pipecat.services.settings import NOT_GIVEN as _NOT_GIVEN +from pipecat.services.settings import LLMSettings, _NotGiven +from pipecat.utils.tracing.service_decorators import traced_llm + + +@dataclass +class OpenAIResponsesLLMSettings(LLMSettings): + """Settings for OpenAIResponsesLLMService. + + Parameters: + max_completion_tokens: Maximum completion tokens to generate. + """ + + max_completion_tokens: int | _NotGiven = field(default_factory=lambda: _NOT_GIVEN) + + +class OpenAIResponsesLLMService(LLMService): + """OpenAI Responses API LLM service. + + This service works with the universal LLMContext and LLMContextAggregatorPair. + + Example:: + + llm = OpenAIResponsesLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAIResponsesLLMService.Settings( + model="gpt-4.1", + system_instruction="You are a helpful assistant.", + ), + ) + """ + + Settings = OpenAIResponsesLLMSettings + _settings: Settings + + adapter_class = OpenAIResponsesLLMAdapter + + def __init__( + self, + *, + model: Optional[str] = None, + api_key=None, + base_url=None, + organization=None, + project=None, + default_headers: Optional[Mapping[str, str]] = None, + settings: Optional[Settings] = None, + **kwargs, + ): + """Initialize the OpenAI Responses API LLM service. + + Args: + model: The OpenAI model name to use. Defaults to "gpt-4.1". + api_key: OpenAI API key. If None, uses environment variable. + base_url: Custom base URL for OpenAI API. If None, uses default. + organization: OpenAI organization ID. + project: OpenAI project ID. + default_headers: Additional HTTP headers to include in requests. + settings: Runtime-updatable settings. When provided alongside + other parameters, ``settings`` values take precedence. + **kwargs: Additional arguments passed to the parent LLMService. + """ + default_settings = self.Settings( + model="gpt-4.1", + system_instruction=None, + frequency_penalty=None, + presence_penalty=None, + seed=None, + temperature=NOT_GIVEN, + top_p=NOT_GIVEN, + top_k=None, + max_tokens=None, + max_completion_tokens=NOT_GIVEN, + filter_incomplete_user_turns=False, + user_turn_completion_config=None, + extra={}, + ) + + if model is not None: + default_settings.model = model + + if settings is not None: + default_settings.apply_update(settings) + + super().__init__( + settings=default_settings, + **kwargs, + ) + + self._client = self._create_client( + api_key=api_key, + base_url=base_url, + organization=organization, + project=project, + default_headers=default_headers, + ) + + if self._settings.system_instruction: + logger.debug(f"{self}: Using system instruction: {self._settings.system_instruction}") + + def _create_client( + self, + api_key=None, + base_url=None, + organization=None, + project=None, + default_headers=None, + ) -> AsyncOpenAI: + """Create an AsyncOpenAI client instance. + + Args: + api_key: OpenAI API key. + base_url: Custom base URL for the API. + organization: OpenAI organization ID. + project: OpenAI project ID. + default_headers: Additional HTTP headers. + + Returns: + Configured AsyncOpenAI client instance. + """ + return AsyncOpenAI( + api_key=api_key, + base_url=base_url, + organization=organization, + project=project, + http_client=DefaultAsyncHttpxClient( + limits=httpx.Limits( + max_keepalive_connections=100, max_connections=1000, keepalive_expiry=None + ) + ), + default_headers=default_headers, + ) + + def can_generate_metrics(self) -> bool: + """Check if this service can generate processing metrics.""" + return True + + async def process_frame(self, frame: Frame, direction: FrameDirection): + """Process frames for LLM completion requests. + + Args: + frame: The frame to process. + direction: The direction of frame processing. + """ + await super().process_frame(frame, direction) + + context = None + if isinstance(frame, LLMContextFrame): + context = frame.context + else: + await self.push_frame(frame, direction) + + if context: + try: + await self.push_frame(LLMFullResponseStartFrame()) + await self.start_processing_metrics() + await self._process_context(context) + except httpx.TimeoutException as e: + await self._call_event_handler("on_completion_timeout") + await self.push_error(error_msg="LLM completion timeout", exception=e) + except Exception as e: + await self.push_error(error_msg=f"Error during completion: {e}", exception=e) + finally: + await self.stop_processing_metrics() + await self.push_frame(LLMFullResponseEndFrame()) + + @traced_llm + async def _process_context(self, context: LLMContext): + adapter = self.get_llm_adapter() + logger.debug( + f"{self}: Generating response from universal context " + f"{adapter.get_messages_for_logging(context)}" + ) + + invocation_params: OpenAIResponsesLLMInvocationParams = adapter.get_llm_invocation_params( + context, system_instruction=self._settings.system_instruction + ) + + params = self._build_response_params(invocation_params) + + await self.start_ttfb_metrics() + + stream: AsyncStream[ResponseStreamEvent] = await self._client.responses.create(**params) + + # Track function calls across stream events + function_calls: Dict[str, Dict[str, str]] = {} # item_id -> {name, call_id, arguments} + current_arguments: Dict[str, str] = {} # item_id -> accumulated arguments + + @asynccontextmanager + async def _closing(stream): + chunk_iter = stream.__aiter__() + try: + yield chunk_iter + finally: + if hasattr(chunk_iter, "aclose"): + await chunk_iter.aclose() + if hasattr(stream, "close"): + await stream.close() + elif hasattr(stream, "aclose"): + await stream.aclose() + + async with _closing(stream) as event_iter: + async for event in event_iter: + if isinstance(event, ResponseTextDeltaEvent): + await self.stop_ttfb_metrics() + await self._push_llm_text(event.delta) + + elif isinstance(event, ResponseOutputItemAddedEvent): + await self.stop_ttfb_metrics() + item = event.item + if getattr(item, "type", None) == "function_call": + item_id = getattr(item, "id", "") or "" + function_calls[item_id] = { + "name": getattr(item, "name", ""), + "call_id": getattr(item, "call_id", ""), + "arguments": "", + } + current_arguments[item_id] = "" + + elif isinstance(event, ResponseFunctionCallArgumentsDeltaEvent): + item_id = event.item_id + if item_id in current_arguments: + current_arguments[item_id] += event.delta + + elif isinstance(event, ResponseFunctionCallArgumentsDoneEvent): + item_id = event.item_id + if item_id in function_calls: + function_calls[item_id]["arguments"] = event.arguments + + elif isinstance(event, ResponseOutputItemDoneEvent): + item = event.item + if getattr(item, "type", None) == "function_call": + item_id = getattr(item, "id", "") or "" + if item_id in function_calls: + function_calls[item_id]["name"] = getattr(item, "name", "") + function_calls[item_id]["call_id"] = getattr(item, "call_id", "") + function_calls[item_id]["arguments"] = getattr(item, "arguments", "") + + elif isinstance(event, ResponseCompletedEvent): + response = event.response + usage = getattr(response, "usage", None) + if usage: + tokens = LLMTokenUsage( + prompt_tokens=getattr(usage, "input_tokens", 0), + completion_tokens=getattr(usage, "output_tokens", 0), + total_tokens=getattr(usage, "total_tokens", 0), + ) + await self.start_llm_usage_metrics(tokens) + + model = getattr(response, "model", None) + if model: + self._full_model_name = model + + # Process any function calls + if function_calls: + fc_list: List[FunctionCallFromLLM] = [] + for item_id, fc in function_calls.items(): + try: + arguments = json.loads(fc["arguments"]) if fc["arguments"] else {} + except json.JSONDecodeError: + logger.warning( + f"{self}: Failed to parse function call arguments: {fc['arguments']}" + ) + arguments = {} + fc_list.append( + FunctionCallFromLLM( + context=context, + tool_call_id=fc["call_id"], + function_name=fc["name"], + arguments=arguments, + ) + ) + await self.run_function_calls(fc_list) + + def _build_response_params(self, invocation_params: OpenAIResponsesLLMInvocationParams) -> dict: + """Build parameters for the responses.create() call. + + Args: + invocation_params: Parameters derived from the LLM context. + + Returns: + Dictionary of parameters for the Responses API call. + """ + params: Dict[str, Any] = { + "model": self._settings.model, + "stream": True, + "input": invocation_params["input"], + } + + # instructions (set by the adapter when input is non-empty) + if "instructions" in invocation_params: + params["instructions"] = invocation_params["instructions"] + + # Optional parameters - only include if given + if isinstance(self._settings.temperature, (int, float)): + params["temperature"] = self._settings.temperature + + if isinstance(self._settings.top_p, (int, float)): + params["top_p"] = self._settings.top_p + + if isinstance(self._settings.max_completion_tokens, int): + params["max_output_tokens"] = self._settings.max_completion_tokens + + # Tools + tools = invocation_params.get("tools") + if tools is not None and not isinstance(tools, type(NOT_GIVEN)): + params["tools"] = tools + + # Extra settings + params.update(self._settings.extra) + + return params + + async def run_inference( + self, + context: LLMContext, + max_tokens: Optional[int] = None, + system_instruction: Optional[str] = None, + ) -> Optional[str]: + """Run a one-shot, out-of-band inference with the given LLM context. + + Args: + context: The LLM context containing conversation history. + max_tokens: Optional maximum number of tokens to generate. + system_instruction: Optional system instruction for this inference. + + Returns: + The LLM's response as a string, or None if no response is generated. + """ + adapter = self.get_llm_adapter() + effective_instruction = system_instruction or self._settings.system_instruction + invocation_params = adapter.get_llm_invocation_params( + context, system_instruction=effective_instruction + ) + + params = self._build_response_params(invocation_params) + + # Override for non-streaming + params["stream"] = False + + # Override instructions if caller provided one explicitly + if system_instruction is not None: + params["instructions"] = system_instruction + + if max_tokens is not None: + params["max_output_tokens"] = max_tokens + + response = await self._client.responses.create(**params) + + return response.output_text + + +__all__ = ["OpenAIResponsesLLMService", "OpenAIResponsesLLMSettings"] diff --git a/tests/test_openai_responses_adapter.py b/tests/test_openai_responses_adapter.py new file mode 100644 index 000000000..973c05c8c --- /dev/null +++ b/tests/test_openai_responses_adapter.py @@ -0,0 +1,349 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Unit tests for the OpenAI Responses API adapter. + +Tests the conversion from LLMContext messages to Responses API input items, including: + +1. Simple user/assistant text messages pass through (with correct role) +2. System role converted to developer role +3. First-message system role triggers a warning +4. Assistant messages with tool_calls produce function_call input items +5. Tool messages produce function_call_output input items +6. Mixed conversations with text + function calls convert correctly +7. Multimodal content conversion (text -> input_text, image_url -> input_image) +8. Tools schema flattening (nested function dict -> flat format) +9. Empty messages list +10. LLMSpecificMessage with llm="openai_responses" passes through +""" + +import unittest +from unittest.mock import patch + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.adapters.services.open_ai_responses_adapter import OpenAIResponsesLLMAdapter +from pipecat.processors.aggregators.llm_context import LLMContext, LLMStandardMessage + + +class TestOpenAIResponsesAdapter(unittest.TestCase): + def setUp(self): + self.adapter = OpenAIResponsesLLMAdapter() + + def test_simple_user_assistant_messages(self): + """Simple user/assistant text messages are converted correctly.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there!"}, + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["input"]), 2) + self.assertEqual(params["input"][0], {"role": "user", "content": "Hello"}) + self.assertEqual(params["input"][1], {"role": "assistant", "content": "Hi there!"}) + + def test_system_role_converted_to_developer(self): + """System role messages are converted to developer role.""" + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are helpful."}, + {"role": "user", "content": "Hello"}, + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(params["input"][0]["role"], "developer") + self.assertEqual(params["input"][0]["content"], "You are helpful.") + + def test_first_system_message_triggers_warning(self): + """First system message triggers a warning about using system_instruction.""" + # Use a fresh adapter so the warning hasn't been emitted yet + adapter = OpenAIResponsesLLMAdapter() + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are helpful."}, + {"role": "user", "content": "Hello"}, + ] + context = LLMContext(messages=messages) + + with patch("pipecat.adapters.services.open_ai_responses_adapter.logger") as mock_logger: + adapter.get_llm_invocation_params(context) + mock_logger.warning.assert_called_once() + warning_msg = mock_logger.warning.call_args[0][0] + self.assertIn("system_instruction", warning_msg) + + def test_non_initial_system_message_no_warning(self): + """Non-initial system messages are converted without a warning.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "Hello"}, + {"role": "system", "content": "New instruction"}, + ] + context = LLMContext(messages=messages) + + adapter = OpenAIResponsesLLMAdapter() + with patch("pipecat.adapters.services.open_ai_responses_adapter.logger") as mock_logger: + params = adapter.get_llm_invocation_params(context) + mock_logger.warning.assert_not_called() + + self.assertEqual(params["input"][1]["role"], "developer") + self.assertEqual(params["input"][1]["content"], "New instruction") + + def test_first_system_message_warning_fires_only_once(self): + """The first-system-message warning fires only once per adapter instance.""" + messages: list[LLMStandardMessage] = [ + {"role": "system", "content": "You are helpful."}, + {"role": "user", "content": "Hello"}, + ] + context = LLMContext(messages=messages) + + adapter = OpenAIResponsesLLMAdapter() + with patch("pipecat.adapters.services.open_ai_responses_adapter.logger") as mock_logger: + adapter.get_llm_invocation_params(context) + adapter.get_llm_invocation_params(context) + # Warning should have been emitted exactly once, not twice + mock_logger.warning.assert_called_once() + + def test_assistant_tool_calls_to_function_call(self): + """Assistant messages with tool_calls produce function_call input items.""" + messages = [ + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_123", + "function": { + "name": "get_weather", + "arguments": '{"location": "SF"}', + }, + "type": "function", + } + ], + } + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["input"]), 1) + fc = params["input"][0] + self.assertEqual(fc["type"], "function_call") + self.assertEqual(fc["call_id"], "call_123") + self.assertEqual(fc["name"], "get_weather") + self.assertEqual(fc["arguments"], '{"location": "SF"}') + + def test_multiple_tool_calls(self): + """Multiple tool calls in one assistant message produce multiple function_call items.""" + messages = [ + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "function": {"name": "get_weather", "arguments": '{"location": "SF"}'}, + "type": "function", + }, + { + "id": "call_2", + "function": {"name": "get_restaurant", "arguments": '{"location": "SF"}'}, + "type": "function", + }, + ], + } + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["input"]), 2) + self.assertEqual(params["input"][0]["name"], "get_weather") + self.assertEqual(params["input"][1]["name"], "get_restaurant") + + def test_tool_message_to_function_call_output(self): + """Tool role messages produce function_call_output input items.""" + messages = [ + { + "role": "tool", + "content": '{"temperature": "72"}', + "tool_call_id": "call_123", + } + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["input"]), 1) + fco = params["input"][0] + self.assertEqual(fco["type"], "function_call_output") + self.assertEqual(fco["call_id"], "call_123") + self.assertEqual(fco["output"], '{"temperature": "72"}') + + def test_mixed_conversation(self): + """Mixed conversation with text + function calls converts correctly.""" + messages = [ + {"role": "user", "content": "What's the weather in SF?"}, + { + "role": "assistant", + "tool_calls": [ + { + "id": "call_abc", + "function": {"name": "get_weather", "arguments": '{"location": "SF"}'}, + "type": "function", + } + ], + }, + { + "role": "tool", + "content": '{"temp": "72"}', + "tool_call_id": "call_abc", + }, + {"role": "assistant", "content": "It's 72 degrees in SF."}, + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["input"]), 4) + self.assertEqual(params["input"][0]["role"], "user") + self.assertEqual(params["input"][1]["type"], "function_call") + self.assertEqual(params["input"][2]["type"], "function_call_output") + self.assertEqual(params["input"][3]["role"], "assistant") + + def test_multimodal_text_conversion(self): + """Multimodal text content parts are converted to input_text.""" + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "What's in this image?"}, + ], + } + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + content = params["input"][0]["content"] + self.assertEqual(len(content), 1) + self.assertEqual(content[0]["type"], "input_text") + self.assertEqual(content[0]["text"], "What's in this image?") + + def test_multimodal_image_conversion(self): + """Multimodal image_url content parts are converted to input_image.""" + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "Describe this:"}, + { + "type": "image_url", + "image_url": {"url": "data:image/jpeg;base64,abc123"}, + }, + ], + } + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + content = params["input"][0]["content"] + self.assertEqual(len(content), 2) + self.assertEqual(content[0]["type"], "input_text") + self.assertEqual(content[1]["type"], "input_image") + self.assertEqual(content[1]["image_url"], "data:image/jpeg;base64,abc123") + self.assertEqual(content[1]["detail"], "auto") + + def test_multimodal_image_with_detail(self): + """Image content parts preserve the detail setting when provided.""" + messages = [ + { + "role": "user", + "content": [ + { + "type": "image_url", + "image_url": {"url": "https://example.com/img.png", "detail": "high"}, + }, + ], + } + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + content = params["input"][0]["content"] + self.assertEqual(content[0]["detail"], "high") + + def test_tools_schema_flattening(self): + """Tools schema with nested function dict is flattened to Responses API format.""" + weather_fn = FunctionSchema( + name="get_weather", + description="Get the current weather", + properties={ + "location": {"type": "string", "description": "The city"}, + }, + required=["location"], + ) + tools = ToolsSchema(standard_tools=[weather_fn]) + context = LLMContext(tools=tools) + params = self.adapter.get_llm_invocation_params(context) + + tool_list = params["tools"] + self.assertEqual(len(tool_list), 1) + tool = tool_list[0] + self.assertEqual(tool["type"], "function") + self.assertEqual(tool["name"], "get_weather") + self.assertEqual(tool["description"], "Get the current weather") + self.assertIn("properties", tool["parameters"]) + + def test_empty_messages(self): + """Empty messages list produces empty input list.""" + context = LLMContext(messages=[]) + params = self.adapter.get_llm_invocation_params(context) + self.assertEqual(params["input"], []) + + def test_llm_specific_message_passthrough(self): + """LLMSpecificMessage with llm='openai_responses' passes through.""" + specific_msg = self.adapter.create_llm_specific_message( + {"type": "function_call", "call_id": "x", "name": "foo", "arguments": "{}"} + ) + messages = [ + {"role": "user", "content": "Hello"}, + specific_msg, + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context) + + self.assertEqual(len(params["input"]), 2) + self.assertEqual(params["input"][0]["role"], "user") + self.assertEqual(params["input"][1]["type"], "function_call") + + def test_id_for_llm_specific_messages(self): + """Adapter identifier is 'openai_responses'.""" + self.assertEqual(self.adapter.id_for_llm_specific_messages, "openai_responses") + + def test_system_instruction_with_messages_sets_instructions(self): + """When system_instruction is provided and input is non-empty, sets instructions.""" + messages: list[LLMStandardMessage] = [ + {"role": "user", "content": "Hello"}, + ] + context = LLMContext(messages=messages) + params = self.adapter.get_llm_invocation_params(context, system_instruction="Be helpful.") + + self.assertEqual(params["instructions"], "Be helpful.") + self.assertEqual(len(params["input"]), 1) + self.assertEqual(params["input"][0]["role"], "user") + + def test_system_instruction_with_empty_input_becomes_developer_message(self): + """When system_instruction is provided but input is empty, it becomes a developer message.""" + context = LLMContext(messages=[]) + params = self.adapter.get_llm_invocation_params(context, system_instruction="Be helpful.") + + self.assertNotIn("instructions", params) + self.assertEqual(len(params["input"]), 1) + self.assertEqual(params["input"][0]["role"], "developer") + self.assertEqual(params["input"][0]["content"], "Be helpful.") + + def test_no_system_instruction_omits_instructions(self): + """When no system_instruction is provided, instructions key is absent.""" + context = LLMContext(messages=[{"role": "user", "content": "Hi"}]) + params = self.adapter.get_llm_invocation_params(context) + + self.assertNotIn("instructions", params) + + +if __name__ == "__main__": + unittest.main() From eaccb964541d19496970557b732461a51667c706 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 11:46:49 -0400 Subject: [PATCH 090/159] docs: add changelog for OpenAI Responses API service --- changelog/4074.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4074.added.md diff --git a/changelog/4074.added.md b/changelog/4074.added.md new file mode 100644 index 000000000..c27a8e3cf --- /dev/null +++ b/changelog/4074.added.md @@ -0,0 +1 @@ +- Added `OpenAIResponsesLLMService`, a new LLM service that uses the OpenAI Responses API. Supports streaming text, function calling, usage metrics, and out-of-band inference. Works with the universal `LLMContext` and `LLMContextAggregatorPair`. See `examples/foundational/07-interruptible-openai-responses.py` and `14-function-calling-openai-responses.py`. From a7167ad121f206ebb97f1ae120015d6254ab85b7 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 14:09:17 -0400 Subject: [PATCH 091/159] test: add run_inference tests for OpenAIResponsesLLMService Tests cover basic inference, client exception propagation, system_instruction override, and max_tokens override. --- src/pipecat/services/openai/responses/llm.py | 6 +- tests/test_run_inference.py | 148 +++++++++++++++++++ 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index e72b8acdb..e0a3e4428 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -206,13 +206,13 @@ class OpenAIResponsesLLMService(LLMService): @traced_llm async def _process_context(self, context: LLMContext): - adapter = self.get_llm_adapter() + adapter: OpenAIResponsesLLMAdapter = self.get_llm_adapter() logger.debug( f"{self}: Generating response from universal context " f"{adapter.get_messages_for_logging(context)}" ) - invocation_params: OpenAIResponsesLLMInvocationParams = adapter.get_llm_invocation_params( + invocation_params = adapter.get_llm_invocation_params( context, system_instruction=self._settings.system_instruction ) @@ -367,7 +367,7 @@ class OpenAIResponsesLLMService(LLMService): Returns: The LLM's response as a string, or None if no response is generated. """ - adapter = self.get_llm_adapter() + adapter: OpenAIResponsesLLMAdapter = self.get_llm_adapter() effective_instruction = system_instruction or self._settings.system_instruction invocation_params = adapter.get_llm_invocation_params( context, system_instruction=effective_instruction diff --git a/tests/test_run_inference.py b/tests/test_run_inference.py index 4f4021b5d..4b35aee2b 100644 --- a/tests/test_run_inference.py +++ b/tests/test_run_inference.py @@ -15,11 +15,13 @@ from pipecat.adapters.services.anthropic_adapter import AnthropicLLMInvocationPa from pipecat.adapters.services.bedrock_adapter import AWSBedrockLLMInvocationParams from pipecat.adapters.services.gemini_adapter import GeminiLLMInvocationParams from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams +from pipecat.adapters.services.open_ai_responses_adapter import OpenAIResponsesLLMInvocationParams from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.aws.llm import AWSBedrockLLMService from pipecat.services.google.llm import GoogleLLMService from pipecat.services.openai.llm import OpenAILLMService +from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService @pytest.mark.asyncio @@ -765,3 +767,149 @@ async def test_aws_bedrock_run_inference_system_instruction_none_unchanged(): assert result == "Response" call_kwargs = mock_client.converse.call_args.kwargs assert call_kwargs["system"] == [{"text": "Original system"}] + + +# --- OpenAI Responses API tests --- + + +@pytest.mark.asyncio +async def test_openai_responses_run_inference_with_llm_context(): + """Test run_inference with LLMContext returns expected response.""" + with patch.object(OpenAIResponsesLLMService, "_create_client"): + service = OpenAIResponsesLLMService( + settings=OpenAIResponsesLLMService.Settings( + model="gpt-4.1", + system_instruction="You are a helpful assistant", + temperature=0.7, + max_completion_tokens=100, + ), + ) + service._client = AsyncMock() + + # Setup mocks + mock_context = MagicMock(spec=LLMContext) + mock_adapter = MagicMock() + test_input = [ + {"role": "developer", "content": "You are a helpful assistant"}, + {"role": "user", "content": "Hello, world!"}, + ] + mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( + input=test_input, + tools=OPENAI_NOT_GIVEN, + instructions="You are a helpful assistant", + ) + service.get_llm_adapter = MagicMock(return_value=mock_adapter) + + # Mock response + mock_response = MagicMock() + mock_response.output_text = "Hello! How can I help you today?" + service._client.responses.create = AsyncMock(return_value=mock_response) + + # Execute + result = await service.run_inference(mock_context) + + # Verify + assert result == "Hello! How can I help you today?" + service.get_llm_adapter.assert_called_once() + mock_adapter.get_llm_invocation_params.assert_called_once_with( + mock_context, system_instruction="You are a helpful assistant" + ) + service._client.responses.create.assert_called_once_with( + model="gpt-4.1", + stream=False, + input=test_input, + instructions="You are a helpful assistant", + temperature=0.7, + max_output_tokens=100, + ) + + +@pytest.mark.asyncio +async def test_openai_responses_run_inference_client_exception(): + """Test that exceptions from the client are propagated.""" + with patch.object(OpenAIResponsesLLMService, "_create_client"): + service = OpenAIResponsesLLMService() + service._client = AsyncMock() + + mock_context = MagicMock(spec=LLMContext) + mock_adapter = MagicMock() + mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( + input=[], tools=OPENAI_NOT_GIVEN + ) + service.get_llm_adapter = MagicMock(return_value=mock_adapter) + service._client.responses.create = AsyncMock(side_effect=Exception("API Error")) + + with pytest.raises(Exception, match="API Error"): + await service.run_inference(mock_context) + + +@pytest.mark.asyncio +async def test_openai_responses_run_inference_system_instruction_overrides(): + """Test that system_instruction parameter overrides the settings instruction.""" + with patch.object(OpenAIResponsesLLMService, "_create_client"): + service = OpenAIResponsesLLMService( + settings=OpenAIResponsesLLMService.Settings( + model="gpt-4.1", + system_instruction="Original instruction", + ), + ) + service._client = AsyncMock() + + mock_context = MagicMock(spec=LLMContext) + mock_adapter = MagicMock() + test_input = [{"role": "user", "content": "Hello"}] + mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( + input=test_input, + tools=OPENAI_NOT_GIVEN, + instructions="New system instruction", + ) + service.get_llm_adapter = MagicMock(return_value=mock_adapter) + + mock_response = MagicMock() + mock_response.output_text = "Response" + service._client.responses.create = AsyncMock(return_value=mock_response) + + result = await service.run_inference( + mock_context, system_instruction="New system instruction" + ) + + assert result == "Response" + # The adapter should have been called with the override instruction + mock_adapter.get_llm_invocation_params.assert_called_once_with( + mock_context, system_instruction="New system instruction" + ) + # The final API call should have the override instruction + call_kwargs = service._client.responses.create.call_args.kwargs + assert call_kwargs["instructions"] == "New system instruction" + + +@pytest.mark.asyncio +async def test_openai_responses_run_inference_max_tokens_override(): + """Test that max_tokens parameter overrides max_output_tokens.""" + with patch.object(OpenAIResponsesLLMService, "_create_client"): + service = OpenAIResponsesLLMService( + settings=OpenAIResponsesLLMService.Settings( + model="gpt-4.1", + max_completion_tokens=500, + ), + ) + service._client = AsyncMock() + + mock_context = MagicMock(spec=LLMContext) + mock_adapter = MagicMock() + test_input = [{"role": "user", "content": "Summarize this"}] + mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( + input=test_input, tools=OPENAI_NOT_GIVEN + ) + service.get_llm_adapter = MagicMock(return_value=mock_adapter) + + mock_response = MagicMock() + mock_response.output_text = "Summary" + service._client.responses.create = AsyncMock(return_value=mock_response) + + result = await service.run_inference(mock_context, max_tokens=200) + + assert result == "Summary" + call_kwargs = service._client.responses.create.call_args.kwargs + # max_tokens override should take precedence + assert call_kwargs["max_output_tokens"] == 200 From c4f21ef76ba9d15b6fae045f6870cf1d789b6b2a Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 14:17:21 -0400 Subject: [PATCH 092/159] test: add run_inference tests for OpenAIResponsesLLMService Uses real LLMContext and adapter (only HTTP client is mocked) to test basic inference, client exception propagation, system_instruction override, empty context fallback, and max_tokens override. --- tests/test_run_inference.py | 108 ++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 60 deletions(-) diff --git a/tests/test_run_inference.py b/tests/test_run_inference.py index 4b35aee2b..e01d7b36d 100644 --- a/tests/test_run_inference.py +++ b/tests/test_run_inference.py @@ -15,7 +15,6 @@ from pipecat.adapters.services.anthropic_adapter import AnthropicLLMInvocationPa from pipecat.adapters.services.bedrock_adapter import AWSBedrockLLMInvocationParams from pipecat.adapters.services.gemini_adapter import GeminiLLMInvocationParams from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams -from pipecat.adapters.services.open_ai_responses_adapter import OpenAIResponsesLLMInvocationParams from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.aws.llm import AWSBedrockLLMService @@ -786,42 +785,26 @@ async def test_openai_responses_run_inference_with_llm_context(): ) service._client = AsyncMock() - # Setup mocks - mock_context = MagicMock(spec=LLMContext) - mock_adapter = MagicMock() - test_input = [ - {"role": "developer", "content": "You are a helpful assistant"}, - {"role": "user", "content": "Hello, world!"}, - ] - mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( - input=test_input, - tools=OPENAI_NOT_GIVEN, - instructions="You are a helpful assistant", + context = LLMContext( + messages=[ + {"role": "user", "content": "Hello, world!"}, + ] ) - service.get_llm_adapter = MagicMock(return_value=mock_adapter) - # Mock response mock_response = MagicMock() mock_response.output_text = "Hello! How can I help you today?" service._client.responses.create = AsyncMock(return_value=mock_response) - # Execute - result = await service.run_inference(mock_context) + result = await service.run_inference(context) - # Verify assert result == "Hello! How can I help you today?" - service.get_llm_adapter.assert_called_once() - mock_adapter.get_llm_invocation_params.assert_called_once_with( - mock_context, system_instruction="You are a helpful assistant" - ) - service._client.responses.create.assert_called_once_with( - model="gpt-4.1", - stream=False, - input=test_input, - instructions="You are a helpful assistant", - temperature=0.7, - max_output_tokens=100, - ) + call_kwargs = service._client.responses.create.call_args.kwargs + assert call_kwargs["model"] == "gpt-4.1" + assert call_kwargs["stream"] is False + assert call_kwargs["input"] == [{"role": "user", "content": "Hello, world!"}] + assert call_kwargs["instructions"] == "You are a helpful assistant" + assert call_kwargs["temperature"] == 0.7 + assert call_kwargs["max_output_tokens"] == 100 @pytest.mark.asyncio @@ -831,16 +814,11 @@ async def test_openai_responses_run_inference_client_exception(): service = OpenAIResponsesLLMService() service._client = AsyncMock() - mock_context = MagicMock(spec=LLMContext) - mock_adapter = MagicMock() - mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( - input=[], tools=OPENAI_NOT_GIVEN - ) - service.get_llm_adapter = MagicMock(return_value=mock_adapter) + context = LLMContext(messages=[{"role": "user", "content": "Hello"}]) service._client.responses.create = AsyncMock(side_effect=Exception("API Error")) with pytest.raises(Exception, match="API Error"): - await service.run_inference(mock_context) + await service.run_inference(context) @pytest.mark.asyncio @@ -855,32 +833,47 @@ async def test_openai_responses_run_inference_system_instruction_overrides(): ) service._client = AsyncMock() - mock_context = MagicMock(spec=LLMContext) - mock_adapter = MagicMock() - test_input = [{"role": "user", "content": "Hello"}] - mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( - input=test_input, - tools=OPENAI_NOT_GIVEN, - instructions="New system instruction", + context = LLMContext( + messages=[{"role": "user", "content": "Hello"}], ) - service.get_llm_adapter = MagicMock(return_value=mock_adapter) mock_response = MagicMock() mock_response.output_text = "Response" service._client.responses.create = AsyncMock(return_value=mock_response) - result = await service.run_inference( - mock_context, system_instruction="New system instruction" - ) + result = await service.run_inference(context, system_instruction="New system instruction") assert result == "Response" - # The adapter should have been called with the override instruction - mock_adapter.get_llm_invocation_params.assert_called_once_with( - mock_context, system_instruction="New system instruction" - ) - # The final API call should have the override instruction call_kwargs = service._client.responses.create.call_args.kwargs assert call_kwargs["instructions"] == "New system instruction" + assert call_kwargs["input"] == [{"role": "user", "content": "Hello"}] + + +@pytest.mark.asyncio +async def test_openai_responses_run_inference_empty_context_with_instruction(): + """Test that system_instruction becomes a developer message when context is empty.""" + with patch.object(OpenAIResponsesLLMService, "_create_client"): + service = OpenAIResponsesLLMService( + settings=OpenAIResponsesLLMService.Settings( + model="gpt-4.1", + system_instruction="You are helpful", + ), + ) + service._client = AsyncMock() + + context = LLMContext(messages=[]) + + mock_response = MagicMock() + mock_response.output_text = "Response" + service._client.responses.create = AsyncMock(return_value=mock_response) + + result = await service.run_inference(context) + + assert result == "Response" + call_kwargs = service._client.responses.create.call_args.kwargs + # With empty context, instruction should become a developer message + assert call_kwargs["input"] == [{"role": "developer", "content": "You are helpful"}] + assert "instructions" not in call_kwargs @pytest.mark.asyncio @@ -895,21 +888,16 @@ async def test_openai_responses_run_inference_max_tokens_override(): ) service._client = AsyncMock() - mock_context = MagicMock(spec=LLMContext) - mock_adapter = MagicMock() - test_input = [{"role": "user", "content": "Summarize this"}] - mock_adapter.get_llm_invocation_params.return_value = OpenAIResponsesLLMInvocationParams( - input=test_input, tools=OPENAI_NOT_GIVEN + context = LLMContext( + messages=[{"role": "user", "content": "Summarize this"}], ) - service.get_llm_adapter = MagicMock(return_value=mock_adapter) mock_response = MagicMock() mock_response.output_text = "Summary" service._client.responses.create = AsyncMock(return_value=mock_response) - result = await service.run_inference(mock_context, max_tokens=200) + result = await service.run_inference(context, max_tokens=200) assert result == "Summary" call_kwargs = service._client.responses.create.call_args.kwargs - # max_tokens override should take precedence assert call_kwargs["max_output_tokens"] == 200 From 21b1812c71413cabdb7f2a836024d16e0e345d24 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 14:26:51 -0400 Subject: [PATCH 093/159] chore: add note about previous_response_id and empty input handling --- src/pipecat/adapters/services/open_ai_responses_adapter.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pipecat/adapters/services/open_ai_responses_adapter.py b/src/pipecat/adapters/services/open_ai_responses_adapter.py index f55ba6435..01e6a7503 100644 --- a/src/pipecat/adapters/services/open_ai_responses_adapter.py +++ b/src/pipecat/adapters/services/open_ai_responses_adapter.py @@ -79,6 +79,9 @@ class OpenAIResponsesLLMAdapter(BaseLLMAdapter[OpenAIResponsesLLMInvocationParam # message when instructions are provided. Contexts that worked with # OpenAILLMService (system_instruction + empty messages) need the # instructions converted to an initial developer message. + # NOTE: once we support `previous_response_id`, we need to revisit + # this logic, as it'll be legit to provide instructions without input + # items if `previous_response_id` is provided. if not input_items: params["input"] = [{"role": "developer", "content": system_instruction}] else: From 951bb0c1a724712e03757f2315f76571bb8d378a Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 14:47:12 -0400 Subject: [PATCH 094/159] feat: set store=False and add run_inference tests Set store=False in Responses API calls since we send full conversation history as input items and don't use previous_response_id. Add 5 run_inference tests for OpenAIResponsesLLMService using real LLMContext and adapter (only HTTP client mocked). --- .../adapters/services/open_ai_responses_adapter.py | 14 ++++++++++++-- src/pipecat/services/openai/responses/llm.py | 1 + tests/test_run_inference.py | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pipecat/adapters/services/open_ai_responses_adapter.py b/src/pipecat/adapters/services/open_ai_responses_adapter.py index 01e6a7503..4f88e028d 100644 --- a/src/pipecat/adapters/services/open_ai_responses_adapter.py +++ b/src/pipecat/adapters/services/open_ai_responses_adapter.py @@ -79,9 +79,19 @@ class OpenAIResponsesLLMAdapter(BaseLLMAdapter[OpenAIResponsesLLMInvocationParam # message when instructions are provided. Contexts that worked with # OpenAILLMService (system_instruction + empty messages) need the # instructions converted to an initial developer message. - # NOTE: once we support `previous_response_id`, we need to revisit + # + # NOTE: if/when we support `previous_response_id`, we'll need to revisit # this logic, as it'll be legit to provide instructions without input - # items if `previous_response_id` is provided. + # items if `previous_response_id` is provided. Though...OpenAI's docs + + # ChatGPT suggests that `previous_response_id` is primarily for + # development convenience, not performance (other than minor bandwidth + # savings from not transferring the full context), as the model still + # processes the full context from the previous response. The tradeoff + # of using `previous_response_id` is that it requires enabling OpenAI-side + # 30-day conversation storage (meaning we couldn't do `store=False` + # in the API call), which may not be desirable for all users. So, + # my guess is we won't need to support `previous_response_id` in the + # immediate future. if not input_items: params["input"] = [{"role": "developer", "content": system_instruction}] else: diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index e0a3e4428..fd1d711cc 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -324,6 +324,7 @@ class OpenAIResponsesLLMService(LLMService): params: Dict[str, Any] = { "model": self._settings.model, "stream": True, + "store": False, "input": invocation_params["input"], } diff --git a/tests/test_run_inference.py b/tests/test_run_inference.py index e01d7b36d..f67c725ae 100644 --- a/tests/test_run_inference.py +++ b/tests/test_run_inference.py @@ -801,6 +801,7 @@ async def test_openai_responses_run_inference_with_llm_context(): call_kwargs = service._client.responses.create.call_args.kwargs assert call_kwargs["model"] == "gpt-4.1" assert call_kwargs["stream"] is False + assert call_kwargs["store"] is False assert call_kwargs["input"] == [{"role": "user", "content": "Hello, world!"}] assert call_kwargs["instructions"] == "You are a helpful assistant" assert call_kwargs["temperature"] == 0.7 From 0449df828cf6ae0959e36cf2e3374fe527fa81d0 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 15:06:54 -0400 Subject: [PATCH 095/159] chore: update previous_response_id comment --- .../services/open_ai_responses_adapter.py | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/pipecat/adapters/services/open_ai_responses_adapter.py b/src/pipecat/adapters/services/open_ai_responses_adapter.py index 4f88e028d..0d586fb12 100644 --- a/src/pipecat/adapters/services/open_ai_responses_adapter.py +++ b/src/pipecat/adapters/services/open_ai_responses_adapter.py @@ -80,18 +80,16 @@ class OpenAIResponsesLLMAdapter(BaseLLMAdapter[OpenAIResponsesLLMInvocationParam # OpenAILLMService (system_instruction + empty messages) need the # instructions converted to an initial developer message. # - # NOTE: if/when we support `previous_response_id`, we'll need to revisit - # this logic, as it'll be legit to provide instructions without input - # items if `previous_response_id` is provided. Though...OpenAI's docs + - # ChatGPT suggests that `previous_response_id` is primarily for - # development convenience, not performance (other than minor bandwidth - # savings from not transferring the full context), as the model still - # processes the full context from the previous response. The tradeoff - # of using `previous_response_id` is that it requires enabling OpenAI-side - # 30-day conversation storage (meaning we couldn't do `store=False` - # in the API call), which may not be desirable for all users. So, - # my guess is we won't need to support `previous_response_id` in the - # immediate future. + # NOTE: if/when we support `previous_response_id` and/or + # `conversation_id`, we'll need to revisit this logic, as it'll + # be legit to provide instructions without input items. Worth + # noting that OpenAI's docs suggest these parameters are primarily + # for development convenience rather than performance (the model + # still processes the full context), and come with the tradeoff + # of requiring OpenAI-side 30-day conversation storage, which may + # not be desirable for many users. But it could give folks an easy + # way to store/switch between conversations without needing to + # manage that storage themselves. if not input_items: params["input"] = [{"role": "developer", "content": system_instruction}] else: From 2001ab4577df414bf26128678b5980a8b0976e56 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 15:14:28 -0400 Subject: [PATCH 096/159] feat: add 20a persistent context example for OpenAI Responses --- ...20a-persistent-context-openai-responses.py | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 examples/foundational/20a-persistent-context-openai-responses.py diff --git a/examples/foundational/20a-persistent-context-openai-responses.py b/examples/foundational/20a-persistent-context-openai-responses.py new file mode 100644 index 000000000..730e90197 --- /dev/null +++ b/examples/foundational/20a-persistent-context-openai-responses.py @@ -0,0 +1,249 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import glob +import json +import os +from datetime import datetime + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import LLMRunFrame, TTSSpeakFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.llm_service import FunctionCallParams +from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + + +BASE_FILENAME = "/tmp/pipecat_conversation_" + + +async def fetch_weather_from_api(params: FunctionCallParams): + temperature = 75 if params.arguments["format"] == "fahrenheit" else 24 + await params.result_callback( + { + "conditions": "nice", + "temperature": temperature, + "format": params.arguments["format"], + "timestamp": datetime.now().strftime("%Y%m%d_%H%M%S"), + } + ) + + +async def get_saved_conversation_filenames(params: FunctionCallParams): + # Construct the full pattern including the BASE_FILENAME + full_pattern = f"{BASE_FILENAME}*.json" + + # Use glob to find all matching files + matching_files = glob.glob(full_pattern) + logger.debug(f"matching files: {matching_files}") + + await params.result_callback({"filenames": matching_files}) + + +async def save_conversation(params: FunctionCallParams): + timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") + filename = f"{BASE_FILENAME}{timestamp}.json" + logger.debug( + f"writing conversation to {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" + ) + try: + with open(filename, "w") as file: + messages = params.context.get_messages() + # remove the last message, which is the instruction we just gave to save the conversation + messages.pop() + json.dump(messages, file, indent=2) + await params.result_callback({"success": True}) + except Exception as e: + await params.result_callback({"success": False, "error": str(e)}) + + +async def load_conversation(params: FunctionCallParams): + global tts + filename = params.arguments["filename"] + logger.debug(f"loading conversation from {filename}") + try: + with open(filename, "r") as file: + params.context.set_messages(json.load(file)) + logger.debug( + f"loaded conversation from {filename}\n{json.dumps(params.context.get_messages(), indent=4)}" + ) + await params.llm.queue_frame(TTSSpeakFrame("Ok, I've loaded that conversation.")) + except Exception as e: + await params.result_callback({"success": False, "error": str(e)}) + + +system_instruction = "You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way." + +weather_function = FunctionSchema( + name="get_current_weather", + description="Get the current weather", + properties={ + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "format": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "The temperature unit to use. Infer this from the users location.", + }, + }, + required=["location", "format"], +) + +save_conversation_function = FunctionSchema( + name="save_conversation", + description="Save the current conversatione. Use this function to persist the current conversation to external storage.", + properties={}, + required=[], +) + +get_filenames_function = FunctionSchema( + name="get_saved_conversation_filenames", + description="Get a list of saved conversation histories. Returns a list of filenames. Each filename includes a date and timestamp. Each file is conversation history that can be loaded into this session.", + properties={}, + required=[], +) + +load_conversation_function = FunctionSchema( + name="load_conversation", + description="Load a conversation history. Use this function to load a conversation history into the current session.", + properties={ + "filename": { + "type": "string", + "description": "The filename of the conversation history to load.", + } + }, + required=["filename"], +) + +tools = ToolsSchema( + standard_tools=[ + weather_function, + save_conversation_function, + get_filenames_function, + load_conversation_function, + ] +) + + +# We use lambdas to defer transport parameter creation until the transport +# type is selected at runtime. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + settings=CartesiaTTSService.Settings( + voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ), + ) + + llm = OpenAIResponsesLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAIResponsesLLMService.Settings( + system_instruction=system_instruction, + ), + ) + + # you can either register a single function for all function calls, or specific functions + # llm.register_function(None, fetch_weather_from_api) + llm.register_function("get_current_weather", fetch_weather_from_api) + llm.register_function("save_conversation", save_conversation) + llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames) + llm.register_function("load_conversation", load_conversation) + + context = LLMContext(tools=tools) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, # STT + user_aggregator, + llm, # LLM + tts, + transport.output(), # Transport bot output + assistant_aggregator, + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() From 891966346ca88443f33b61921dd2a466d78f897b Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 15:17:16 -0400 Subject: [PATCH 097/159] feat: add 55zi update-settings example for OpenAI Responses --- ...zi-update-settings-openai-responses-llm.py | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 examples/foundational/55zi-update-settings-openai-responses-llm.py diff --git a/examples/foundational/55zi-update-settings-openai-responses-llm.py b/examples/foundational/55zi-update-settings-openai-responses-llm.py new file mode 100644 index 000000000..61bd8329e --- /dev/null +++ b/examples/foundational/55zi-update-settings-openai-responses-llm.py @@ -0,0 +1,127 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import LLMRunFrame, LLMUpdateSettingsFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams +from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams + +load_dotenv(override=True) + +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + settings=CartesiaTTSService.Settings( + voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ), + ) + + llm = OpenAIResponsesLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAIResponsesLLMService.Settings( + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way.", + ), + ) + + context = LLMContext() + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) + + pipeline = Pipeline( + [ + transport.input(), + stt, + user_aggregator, + llm, + tts, + transport.output(), + assistant_aggregator, + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + context.add_message({"role": "user", "content": "Please introduce yourself to the user."}) + await task.queue_frames([LLMRunFrame()]) + + await asyncio.sleep(10) + logger.info("Updating OpenAI LLM settings: temperature=0.1") + await task.queue_frame( + LLMUpdateSettingsFrame(delta=OpenAIResponsesLLMService.Settings(temperature=0.1)) + ) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() From 5de794e1da5389005d2d155c8311a1191b93479e Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 15:29:04 -0400 Subject: [PATCH 098/159] feat: add service_tier support to OpenAIResponsesLLMService --- src/pipecat/services/openai/responses/llm.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index fd1d711cc..f62c6e758 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -84,6 +84,7 @@ class OpenAIResponsesLLMService(LLMService): organization=None, project=None, default_headers: Optional[Mapping[str, str]] = None, + service_tier: Optional[str] = None, settings: Optional[Settings] = None, **kwargs, ): @@ -96,6 +97,7 @@ class OpenAIResponsesLLMService(LLMService): organization: OpenAI organization ID. project: OpenAI project ID. default_headers: Additional HTTP headers to include in requests. + service_tier: Service tier to use (e.g., "auto", "flex", "priority"). settings: Runtime-updatable settings. When provided alongside other parameters, ``settings`` values take precedence. **kwargs: Additional arguments passed to the parent LLMService. @@ -127,6 +129,7 @@ class OpenAIResponsesLLMService(LLMService): **kwargs, ) + self._service_tier = service_tier self._client = self._create_client( api_key=api_key, base_url=base_url, @@ -342,6 +345,9 @@ class OpenAIResponsesLLMService(LLMService): if isinstance(self._settings.max_completion_tokens, int): params["max_output_tokens"] = self._settings.max_completion_tokens + if self._service_tier is not None: + params["service_tier"] = self._service_tier + # Tools tools = invocation_params.get("tools") if tools is not None and not isinstance(tools, type(NOT_GIVEN)): From b1a8588209d407feebbffd1b7c40998bcfe8a33b Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 15:39:06 -0400 Subject: [PATCH 099/159] feat: add 12- and 14d- image/video examples for OpenAI Responses --- .../12-describe-image-openai-responses.py | 139 +++++++++++++ ...function-calling-openai-responses-video.py | 195 ++++++++++++++++++ scripts/evals/run-release-evals.py | 2 + 3 files changed, 336 insertions(+) create mode 100644 examples/foundational/12-describe-image-openai-responses.py create mode 100644 examples/foundational/14d-function-calling-openai-responses-video.py diff --git a/examples/foundational/12-describe-image-openai-responses.py b/examples/foundational/12-describe-image-openai-responses.py new file mode 100644 index 000000000..a3c113cb2 --- /dev/null +++ b/examples/foundational/12-describe-image-openai-responses.py @@ -0,0 +1,139 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + + +import os + +from dotenv import load_dotenv +from loguru import logger +from PIL import Image + +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import LLMRunFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import create_transport +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams + +load_dotenv(override=True) + + +# We use lambdas to defer transport parameter creation until the transport +# type is selected at runtime. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + settings=CartesiaTTSService.Settings( + voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ), + ) + + llm = OpenAIResponsesLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAIResponsesLLMService.Settings( + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are also able to describe images.", + ), + ) + + context = LLMContext() + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, # STT + user_aggregator, # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + assistant_aggregator, # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + + if not runner_args.body: + script_dir = os.path.dirname(__file__) + runner_args.body = { + "image_path": os.path.join(script_dir, "assets", "cat.jpg"), + "question": "Describe this image", + } + + image_path = runner_args.body["image_path"] + question = runner_args.body["question"] + + # Kick off the conversation. + image = Image.open(image_path) + message = await LLMContext.create_image_message( + image=image.tobytes(), + format="RGB", + size=image.size, + text=question, + ) + context.add_message(message) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/examples/foundational/14d-function-calling-openai-responses-video.py b/examples/foundational/14d-function-calling-openai-responses-video.py new file mode 100644 index 000000000..440c51cc1 --- /dev/null +++ b/examples/foundational/14d-function-calling-openai-responses-video.py @@ -0,0 +1,195 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + + +import os + +from dotenv import load_dotenv +from loguru import logger + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import LLMRunFrame, TTSSpeakFrame, UserImageRequestFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.llm_context import LLMContext +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) +from pipecat.processors.frame_processor import FrameDirection +from pipecat.runner.types import RunnerArguments +from pipecat.runner.utils import ( + create_transport, + get_transport_client_id, + maybe_capture_participant_camera, +) +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.llm_service import FunctionCallParams +from pipecat.services.openai.responses.llm import OpenAIResponsesLLMService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.daily.transport import DailyParams + +load_dotenv(override=True) + + +async def fetch_user_image(params: FunctionCallParams): + """Fetch the user image and push it to the LLM. + + When called, this function pushes a UserImageRequestFrame upstream to the + transport. As a result, the transport will request the user image and push a + UserImageRawFrame downstream which will be added to the context by the LLM + assistant aggregator. The result_callback will be invoked once the image is + retrieved and processed. + """ + user_id = params.arguments["user_id"] + question = params.arguments["question"] + logger.debug(f"Requesting image with user_id={user_id}, question={question}") + + # Request a user image frame and indicate that it should be added to the + # context. Also associate it to the function call. Pass the result_callback + # so it can be invoked when the image is actually retrieved. + await params.llm.push_frame( + UserImageRequestFrame( + user_id=user_id, + text=question, + append_to_context=True, + function_name=params.function_name, + tool_call_id=params.tool_call_id, + result_callback=params.result_callback, + ), + FrameDirection.UPSTREAM, + ) + + +# We use lambdas to defer transport parameter creation until the transport +# type is selected at runtime. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + ), +} + + +async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): + logger.info(f"Starting bot") + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + settings=CartesiaTTSService.Settings( + voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ), + ) + + llm = OpenAIResponsesLLMService( + api_key=os.getenv("OPENAI_API_KEY"), + settings=OpenAIResponsesLLMService.Settings( + system_instruction="You are a helpful assistant in a voice conversation. Your responses will be spoken aloud, so avoid emojis, bullet points, or other formatting that can't be spoken. Respond to what the user said in a creative, helpful, and brief way. You are able to describe images from the user camera.", + ), + ) + llm.register_function("fetch_user_image", fetch_user_image) + + @llm.event_handler("on_function_calls_started") + async def on_function_calls_started(service, function_calls): + await tts.queue_frame(TTSSpeakFrame("Let me check on that.", append_to_context=False)) + + fetch_image_function = FunctionSchema( + name="fetch_user_image", + description="Called when the user requests a description of their camera feed", + properties={ + "user_id": { + "type": "string", + "description": "The ID of the user to grab the image from", + }, + "question": { + "type": "string", + "description": "The question that the user is asking about the image", + }, + }, + required=["user_id", "question"], + ) + tools = ToolsSchema(standard_tools=[fetch_image_function]) + + context = LLMContext(tools=tools) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + ) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, # STT + user_aggregator, # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + assistant_aggregator, # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + enable_metrics=True, + enable_usage_metrics=True, + ), + idle_timeout_secs=runner_args.pipeline_idle_timeout_secs, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + + await maybe_capture_participant_camera(transport, client) + + client_id = get_transport_client_id(transport, client) + + # Kick off the conversation. + context.add_message( + { + "role": "user", + "content": f"Please introduce yourself to the user. Use '{client_id}' as the user ID during function calls.", + } + ) + await task.queue_frames([LLMRunFrame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + @tts.event_handler("on_tts_request") + async def on_tts_request(tts, context_id: str, text: str): + logger.debug(f"On TTS request: {context_id}: {text}") + + runner = PipelineRunner(handle_sigint=runner_args.handle_sigint) + + await runner.run(task) + + +async def bot(runner_args: RunnerArguments): + """Main bot entry point compatible with Pipecat Cloud.""" + transport = await create_transport(runner_args, transport_params) + await run_bot(transport, runner_args) + + +if __name__ == "__main__": + from pipecat.runner.run import main + + main() diff --git a/scripts/evals/run-release-evals.py b/scripts/evals/run-release-evals.py index 9671703ba..19492ba62 100644 --- a/scripts/evals/run-release-evals.py +++ b/scripts/evals/run-release-evals.py @@ -154,6 +154,7 @@ TESTS_07 = [ TESTS_12 = [ ("12-describe-image-openai.py", EVAL_VISION_IMAGE(eval_speaks_first=True)), + ("12-describe-image-openai-responses.py", EVAL_VISION_IMAGE(eval_speaks_first=True)), ("12a-describe-image-anthropic.py", EVAL_VISION_IMAGE(eval_speaks_first=True)), ("12b-describe-image-aws.py", EVAL_VISION_IMAGE(eval_speaks_first=True)), ("12c-describe-image-gemini-flash.py", EVAL_VISION_IMAGE(eval_speaks_first=True)), @@ -193,6 +194,7 @@ TESTS_14 = [ ("14d-function-calling-gemini-flash-video.py", EVAL_VISION_CAMERA), ("14d-function-calling-moondream-video.py", EVAL_VISION_CAMERA), ("14d-function-calling-openai-video.py", EVAL_VISION_CAMERA), + ("14d-function-calling-openai-responses-video.py", EVAL_VISION_CAMERA), # Currently not working. # ("14c-function-calling-together.py", EVAL_WEATHER), # ("14l-function-calling-deepseek.py", EVAL_WEATHER), From 4b704e6d3a5bbe509f1b435df04da4aaf08f04ad Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 18 Mar 2026 15:57:34 -0400 Subject: [PATCH 100/159] GradiumSTTService improvements (#4066) * Remove duplicate reconnection logic from Gradium STT The _receive_messages method had its own while-True reconnect loop, duplicating the reconnection handling already provided by WebsocketService._receive_task_handler (exponential backoff, max retries, error reporting). Flatten to just the inner message loop and let the base class handle reconnection. * Align Gradium STT VAD handling with base class patterns Replace the process_frame override with a _handle_vad_user_stopped_speaking override, which is the proper hook provided by STTService. Move start_processing_metrics() into run_stt (matching Gladia's pattern). Remove unused FrameDirection and VADUserStartedSpeakingFrame imports. * Add transcript aggregation delay after flushed to capture trailing tokens Gradium flushed response can arrive before all text tokens have been delivered. Instead of finalizing immediately on flushed, start a short timer (100ms) that allows trailing tokens to accumulate before pushing the final TranscriptionFrame. * Add changelog for PR #4066 * Change default encoding to pcm_16000 * Decouple encoding from sample_rate in Gradium STT The encoding parameter now takes just the base type (pcm, wav, opus) and the sample rate is derived from the pipeline audio_in_sample_rate, assembled dynamically via input_format_from_encoding(). This fixes the mismatch where SAMPLE_RATE=24000 was passed to the base class while encoding defaulted to pcm_16000. --- changelog/4066.changed.2.md | 1 + changelog/4066.changed.md | 1 + src/pipecat/services/gradium/stt.py | 216 ++++++++++++++++++---------- 3 files changed, 144 insertions(+), 74 deletions(-) create mode 100644 changelog/4066.changed.2.md create mode 100644 changelog/4066.changed.md diff --git a/changelog/4066.changed.2.md b/changelog/4066.changed.2.md new file mode 100644 index 000000000..751961f10 --- /dev/null +++ b/changelog/4066.changed.2.md @@ -0,0 +1 @@ +- `GradiumSTTService` now takes both an `encoding` and `sample_rate` constructor argument which is assmebled in the class to form the `input_format`. PCM accepts `8000`, `16000`, and `24000` Hz sample rates. diff --git a/changelog/4066.changed.md b/changelog/4066.changed.md new file mode 100644 index 000000000..65e95ff2c --- /dev/null +++ b/changelog/4066.changed.md @@ -0,0 +1 @@ +- Improved `GradiumSTTService` transcription accuracy by reworking how text fragments are accumulated and finalized. Previously, trailing words could be dropped when the server's `flushed` response arrived before all text tokens were delivered. The service now uses a short aggregation delay after flush to capture trailing tokens, producing complete utterances. diff --git a/src/pipecat/services/gradium/stt.py b/src/pipecat/services/gradium/stt.py index c328dceab..5dea2c824 100644 --- a/src/pipecat/services/gradium/stt.py +++ b/src/pipecat/services/gradium/stt.py @@ -10,6 +10,7 @@ This module provides integration with Gradium's real-time speech-to-text WebSocket API for streaming audio transcription. """ +import asyncio import base64 import json from dataclasses import dataclass, field @@ -22,6 +23,7 @@ from pipecat.frames.frames import ( CancelFrame, EndFrame, Frame, + InterimTranscriptionFrame, StartFrame, TranscriptionFrame, VADUserStartedSpeakingFrame, @@ -43,7 +45,37 @@ except ModuleNotFoundError as e: logger.error('In order to use Gradium, you need to `pip install "pipecat-ai[gradium]"`.') raise Exception(f"Missing module: {e}") -SAMPLE_RATE = 24000 +# Seconds to wait after a "flushed" message for trailing text tokens to arrive +# before finalizing the transcription. +TRANSCRIPT_AGGREGATION_DELAY = 0.1 + + +def _input_format_from_encoding(encoding: str, sample_rate: int) -> str: + """Build Gradium input_format from encoding type and sample rate. + + For PCM encoding, appends the sample rate (e.g., "pcm_16000"). + For other encodings (wav, opus), returns the encoding as-is. + + Args: + encoding: Base encoding type ("pcm", "wav", or "opus"). + sample_rate: Audio sample rate in Hz. + + Returns: + The full input_format string for the Gradium API. + """ + if encoding == "pcm": + match sample_rate: + case 8000: + return "pcm_8000" + case 16000: + return "pcm_16000" + case 24000: + return "pcm_24000" + logger.warning( + f"GradiumSTTService: unsupported sample rate {sample_rate} for PCM encoding, using pcm_16000" + ) + return "pcm_16000" + return encoding def language_to_gradium_language(language: Language) -> Optional[str]: @@ -115,6 +147,8 @@ class GradiumSTTService(WebsocketSTTService): *, api_key: str, api_endpoint_base_url: str = "wss://eu.api.gradium.ai/api/speech/asr", + encoding: str = "pcm", + sample_rate: Optional[int] = None, params: Optional[InputParams] = None, json_config: Optional[str] = None, settings: Optional[Settings] = None, @@ -126,6 +160,12 @@ class GradiumSTTService(WebsocketSTTService): Args: api_key: Gradium API key for authentication. api_endpoint_base_url: WebSocket endpoint URL. Defaults to Gradium's streaming endpoint. + encoding: Base audio encoding type. One of "pcm", "wav", or "opus". + For PCM, the sample rate is appended automatically from the + pipeline's audio_in_sample_rate (e.g., "pcm" becomes "pcm_16000"). + Defaults to "pcm". + sample_rate: Audio sample rate in Hz. If None, uses the pipeline + sample rate. params: Configuration parameters for language and delay settings. .. deprecated:: 0.0.105 @@ -153,7 +193,7 @@ class GradiumSTTService(WebsocketSTTService): # 1. Initialize default_settings with hardcoded defaults default_settings = self.Settings( - model=None, + model="default", language=None, delay_in_frames=None, ) @@ -173,7 +213,7 @@ class GradiumSTTService(WebsocketSTTService): default_settings.apply_update(settings) super().__init__( - sample_rate=SAMPLE_RATE, + sample_rate=sample_rate, ttfs_p99_latency=ttfs_p99_latency, settings=default_settings, **kwargs, @@ -181,19 +221,25 @@ class GradiumSTTService(WebsocketSTTService): self._api_key = api_key self._api_endpoint_base_url = api_endpoint_base_url + self._encoding = encoding self._websocket = None self._json_config = json_config self._receive_task = None + self._input_format = "" + self._audio_buffer = bytearray() self._chunk_size_ms = 80 self._chunk_size_bytes = 0 - # Set from the ready message when connecting to the service. - # These values are used for flushing transcription. - self._delay_in_frames = 0 - self._frame_size = 0 + # Accumulates text fragments within a turn. Each "text" message + # appends to this list. On "flushed" a short aggregation delay + # allows trailing tokens to arrive before the full text is joined + # and pushed as a TranscriptionFrame. + self._accumulated_text: list[str] = [] + self._flush_counter = 0 + self._transcript_aggregation_task: Optional[asyncio.Task] = None def can_generate_metrics(self) -> bool: """Check if the service can generate metrics. @@ -228,6 +274,7 @@ class GradiumSTTService(WebsocketSTTService): frame: Start frame to begin processing. """ await super().start(frame) + self._input_format = _input_format_from_encoding(self._encoding, self.sample_rate) self._chunk_size_bytes = int(self._chunk_size_ms * self.sample_rate * 2 / 1000) await self._connect() @@ -249,56 +296,41 @@ class GradiumSTTService(WebsocketSTTService): await super().cancel(frame) await self._disconnect() - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process frames with VAD-specific handling. + async def _start_metrics(self): + """Start performance metrics collection for transcription processing.""" + await self.start_processing_metrics() - When VAD detects the user has stopped speaking, we flush the transcription - by sending silence frames. This makes the system more reactive by getting - the final transcription faster without closing the connection. + async def process_frame(self, frame: Frame, direction: FrameDirection): + """Process incoming frames and handle speech events. Args: frame: The frame to process. - direction: The direction of frame processing. + direction: Direction of frame flow in the pipeline. """ await super().process_frame(frame, direction) if isinstance(frame, VADUserStartedSpeakingFrame): - await self.start_processing_metrics() + await self._start_metrics() elif isinstance(frame, VADUserStoppedSpeakingFrame): - await self._flush_transcription() + await self._send_flush() - async def _flush_transcription(self): - """Flush the transcription by sending silence frames. + async def _send_flush(self): + """Send a flush request to process any buffered audio immediately. - When VAD detects the user stopped speaking, we send delay_in_frames - chunks of silence (zeros) to flush the remaining audio from the model's - buffer. This allows for faster turn-around without closing the connection. - - From Gradium docs: "feed in delay_in_frames chunks of silence (vectors - of zeros). If those are fed in faster than realtime, the API also has - a possibility to process them faster." + Sends a flush message to tell the server to process buffered audio. + The server responds with text fragments followed by a "flushed" + acknowledgment, which triggers finalization. """ if not self._websocket or self._websocket.state is not State.OPEN: return - if self._delay_in_frames <= 0: - logger.debug("No delay_in_frames set, skipping flush") - return - - # Create a silence chunk (zeros) of frame_size samples - # Each sample is 2 bytes (16-bit PCM) - silence_bytes = bytes(self._frame_size * 2) - silence_b64 = base64.b64encode(silence_bytes).decode("utf-8") - - logger.debug(f"Flushing Gradium STT with {self._delay_in_frames} silence frames") - - for _ in range(self._delay_in_frames): - msg = {"type": "audio", "audio": silence_b64} - try: - await self._websocket.send(json.dumps(msg)) - except Exception as e: - logger.warning(f"Failed to send silence frame: {e}") - break + self._flush_counter += 1 + flush_id = str(self._flush_counter) + msg = {"type": "flush", "flush_id": flush_id} + try: + await self._websocket.send(json.dumps(msg)) + except Exception as e: + logger.warning(f"Failed to send flush: {e}") async def run_stt(self, audio: bytes) -> AsyncGenerator[Frame, None]: """Process audio data for speech-to-text conversion. @@ -353,7 +385,8 @@ class GradiumSTTService(WebsocketSTTService): await self._call_event_handler("on_connected") setup_msg = { "type": "setup", - "input_format": "pcm", + "model_name": self._settings.model, + "input_format": self._input_format, } # Build json_config: start with deprecated json_config, then override with params json_config = {} @@ -375,13 +408,7 @@ class GradiumSTTService(WebsocketSTTService): if ready_msg["type"] != "ready": raise Exception(f"unexpected first message type {ready_msg['type']}") - # Store delay_in_frames and frame_size for silence flushing - self._delay_in_frames = ready_msg.get("delay_in_frames", 0) - self._frame_size = ready_msg.get("frame_size", 1920) - logger.debug( - f"Connected to Gradium STT (delay_in_frames={self._delay_in_frames}, " - f"frame_size={self._frame_size})" - ) + logger.debug("Connected to Gradium STT") except Exception as e: await self.push_error(error_msg=f"Unknown error occurred: {e}", exception=e) @@ -390,6 +417,13 @@ class GradiumSTTService(WebsocketSTTService): async def _disconnect(self): await super()._disconnect() + if self._transcript_aggregation_task: + await self.cancel_task(self._transcript_aggregation_task) + self._transcript_aggregation_task = None + + self._accumulated_text.clear() + self._flush_counter = 0 + if self._receive_task: await self.cancel_task(self._receive_task) self._receive_task = None @@ -412,41 +446,75 @@ class GradiumSTTService(WebsocketSTTService): return self._websocket raise Exception("Websocket not connected") - async def _process_messages(self): + async def _receive_messages(self): async for message in self._get_websocket(): try: - data = json.loads(message) - await self._process_response(data) + msg = json.loads(message) except json.JSONDecodeError: logger.warning(f"Received non-JSON message: {message}") + continue - async def _receive_messages(self): - while True: - await self._process_messages() - logger.debug(f"{self} Gradium connection was disconnected (timeout?), reconnecting") - await self._connect_websocket() - - async def _process_response(self, msg): - type_ = msg.get("type", "") - if type_ == "text": - await self._handle_text(msg["text"]) - elif type_ == "end_of_stream": - await self._handle_end_of_stream() - elif type_ == "error": - await self.push_error(error_msg=f"Error: {msg}") - - async def _handle_end_of_stream(self): - """Handle termination message.""" - logger.debug("Received end_of_stream message from server") + type_ = msg.get("type", "") + if type_ == "text": + await self._handle_text(msg["text"]) + elif type_ == "flushed": + await self._handle_flushed() + elif type_ == "end_of_stream": + logger.debug("Received end_of_stream message from server") + elif type_ == "error": + await self.push_error(error_msg=f"Error: {msg}") async def _handle_text(self, text: str): - """Handle transcription results.""" + """Handle streaming transcription fragment. + + Accumulates text and pushes an InterimTranscriptionFrame with the + full accumulated text so far. + """ + self._accumulated_text.append(text) + accumulated = " ".join(self._accumulated_text) + await self.push_frame( + InterimTranscriptionFrame( + text=accumulated, + user_id=self._user_id, + timestamp=time_now_iso8601(), + language=self._settings.language, + ) + ) + await self.stop_processing_metrics() + + async def _handle_flushed(self): + """Handle flush completion by starting a transcript aggregation timer. + + The "flushed" message confirms that buffered audio has been processed, + but text tokens may still arrive after this point. A short timer allows + trailing tokens to accumulate before finalizing the transcription. + """ + if self._transcript_aggregation_task: + await self.cancel_task(self._transcript_aggregation_task) + self._transcript_aggregation_task = self.create_task( + self._transcript_aggregation_handler(), "transcript_aggregation" + ) + + async def _transcript_aggregation_handler(self): + """Wait for trailing tokens then finalize the accumulated transcription.""" + await asyncio.sleep(TRANSCRIPT_AGGREGATION_DELAY) + await self._finalize_accumulated_text() + + async def _finalize_accumulated_text(self): + """Join accumulated text, push TranscriptionFrame, and clear state.""" + if not self._accumulated_text: + return + self._transcript_aggregation_task = None + + text = " ".join(self._accumulated_text) + self._accumulated_text.clear() + logger.debug(f"Final transcription: [{text}]") await self.push_frame( TranscriptionFrame( text, self._user_id, time_now_iso8601(), + self._settings.language, ) ) - await self._trace_transcription(text, is_final=True, language=None) - await self.stop_processing_metrics() + await self._trace_transcription(text, is_final=True, language=self._settings.language) From c4be51304437d5958156f4f62e0261299eed49cf Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 18 Mar 2026 16:04:12 -0400 Subject: [PATCH 101/159] Improvements for Nova Sonic LLM and TTS output frames (#4042) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix empty user transcription causing spurious interruption in Nova Sonic Skip _report_user_transcription_ended() when _user_text_buffer is empty, which happens when the initial prompt is text-only. Previously, an empty TranscriptionFrame was pushed upstream, triggering a chain reaction: on_user_turn_stopped → UserStartedSpeakingFrame → interruption → premature BotStoppedSpeaking → multiple response start/stop cycles. * Improve TextFrame and assistant end of turn logic Now, SPECULATIVE text results are used to push the LLMTextFrame, AggregatedTextFrame, and TTSTextFrame. Additionally, the TTSTextFrames are push at the end of the corresponding audio segment. * Remove BotStoppedSpeakingFrame fallback from Nova Sonic Now that assistant response end is detected directly from Nova Sonic contentEnd events (END_TURN and INTERRUPTED), the BotStoppedSpeakingFrame handler is no longer needed. Inline the cleanup logic in reset_conversation. --- changelog/4042.changed.md | 1 + changelog/4042.fixed.md | 1 + src/pipecat/services/aws/nova_sonic/llm.py | 185 +++++++-------------- 3 files changed, 66 insertions(+), 121 deletions(-) create mode 100644 changelog/4042.changed.md create mode 100644 changelog/4042.fixed.md diff --git a/changelog/4042.changed.md b/changelog/4042.changed.md new file mode 100644 index 000000000..f83c32f59 --- /dev/null +++ b/changelog/4042.changed.md @@ -0,0 +1 @@ +- Nova Sonic assistant text transcripts are now delivered in real-time using speculative text events instead of delayed final text events. Previously, assistant text only arrived after all audio had finished playing, causing laggy transcripts in client UIs. Speculative text arrives before each audio chunk, providing text synchronized with what the bot is saying. This also simplifies the internal text handling by removing the interruption re-push hack and assistant text buffer. diff --git a/changelog/4042.fixed.md b/changelog/4042.fixed.md new file mode 100644 index 000000000..a48996208 --- /dev/null +++ b/changelog/4042.fixed.md @@ -0,0 +1 @@ +- Fixed empty user transcriptions in Nova Sonic causing spurious interruptions. Previously, an empty transcription could trigger an interruption of the assistant's response even though the user hadn't actually spoken. diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 0947ba1d7..ffcbb5e5d 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -27,8 +27,8 @@ from pydantic import BaseModel, Field from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.adapters.services.aws_nova_sonic_adapter import AWSNovaSonicLLMAdapter, Role from pipecat.frames.frames import ( + AggregatedTextFrame, AggregationType, - BotStoppedSpeakingFrame, CancelFrame, EndFrame, Frame, @@ -424,18 +424,16 @@ class AWSNovaSonicLLMService(LLMService): self._input_audio_content_name: Optional[str] = None self._content_being_received: Optional[CurrentContent] = None self._assistant_is_responding = False - self._may_need_repush_assistant_text = False self._ready_to_send_context = False - self._handling_bot_stopped_speaking = False self._triggering_assistant_response = False self._waiting_for_trigger_transcription = False self._disconnecting = False self._connected_time: Optional[float] = None self._wants_connection = False self._user_text_buffer = "" - self._assistant_text_buffer = "" self._completed_tool_calls = set() self._audio_input_started = False + self._pending_speculative_text: Optional[str] = None file_path = files("pipecat.services.aws.nova_sonic").joinpath("ready.wav") with wave.open(file_path.open("rb"), "rb") as wav_file: @@ -505,11 +503,13 @@ class AWSNovaSonicLLMService(LLMService): async def reset_conversation(self): """Reset the conversation state while preserving context. - Handles bot stopped speaking event, disconnects from the service, - and reconnects with the preserved context. + Cleans up any in-progress assistant response, disconnects from the + service, and reconnects with the preserved context. """ logger.debug("Resetting conversation") - await self._handle_bot_stopped_speaking(delay_to_catch_trailing_assistant_text=False) + if self._assistant_is_responding: + self._assistant_is_responding = False + await self._report_assistant_response_ended() # Grab context to carry through disconnect/reconnect context = self._context @@ -540,8 +540,6 @@ class AWSNovaSonicLLMService(LLMService): await self._handle_context(context) elif isinstance(frame, InputAudioRawFrame): await self._handle_input_audio_frame(frame) - elif isinstance(frame, BotStoppedSpeakingFrame): - await self._handle_bot_stopped_speaking(delay_to_catch_trailing_assistant_text=True) elif isinstance(frame, InterruptionFrame): await self._handle_interruption_frame() @@ -569,49 +567,8 @@ class AWSNovaSonicLLMService(LLMService): await self._send_user_audio_event(frame.audio) - async def _handle_bot_stopped_speaking(self, delay_to_catch_trailing_assistant_text: bool): - # Protect against back-to-back BotStoppedSpeaking calls, which I've observed - if self._handling_bot_stopped_speaking: - return - self._handling_bot_stopped_speaking = True - - async def finalize_assistant_response(): - if self._assistant_is_responding: - # Consider the assistant finished with their response (possibly after a short delay, - # to allow for any trailing FINAL assistant text block to come in that need to make - # it into context). - # - # TODO: ideally we could base this solely on the LLM output events, but I couldn't - # figure out a reliable way to determine when we've gotten our last FINAL text block - # after the LLM is done talking. - # - # First I looked at stopReason, but it doesn't seem like the last FINAL text block - # is reliably marked END_TURN (sometimes the *first* one is, but not the last... - # bug?) - # - # Then I considered schemes where we tally or match up SPECULATIVE text blocks with - # FINAL text blocks to know how many or which FINAL blocks to expect, but user - # interruptions throw a wrench in these schemes: depending on the exact timing of - # the interruption, we should or shouldn't expect some FINAL blocks. - if delay_to_catch_trailing_assistant_text: - # This delay length is a balancing act between "catching" trailing assistant - # text that is quite delayed but not waiting so long that user text comes in - # first and results in a bit of context message order scrambling. - await asyncio.sleep(1.25) - self._assistant_is_responding = False - await self._report_assistant_response_ended() - - self._handling_bot_stopped_speaking = False - - # Finalize the assistant response, either now or after a delay - if delay_to_catch_trailing_assistant_text: - self.create_task(finalize_assistant_response()) - else: - await finalize_assistant_response() - async def _handle_interruption_frame(self): - if self._assistant_is_responding: - self._may_need_repush_assistant_text = True + pass # # LLM communication: lifecycle @@ -771,17 +728,15 @@ class AWSNovaSonicLLMService(LLMService): self._input_audio_content_name = None self._content_being_received = None self._assistant_is_responding = False - self._may_need_repush_assistant_text = False self._ready_to_send_context = False - self._handling_bot_stopped_speaking = False self._triggering_assistant_response = False self._waiting_for_trigger_transcription = False self._disconnecting = False self._connected_time = None self._user_text_buffer = "" - self._assistant_text_buffer = "" self._completed_tool_calls = set() self._audio_input_started = False + self._pending_speculative_text = None logger.info("Finished disconnecting") except Exception as e: @@ -1153,10 +1108,11 @@ class AWSNovaSonicLLMService(LLMService): self._content_being_received = content if content.role == Role.ASSISTANT: - if content.type == ContentType.AUDIO: - # Note that an assistant response can comprise of multiple audio blocks - if not self._assistant_is_responding: - # The assistant has started responding. + if content.type == ContentType.TEXT: + if ( + content.text_stage == TextStage.SPECULATIVE + and not self._assistant_is_responding + ): self._assistant_is_responding = True await self._report_user_transcription_ended() # Consider user turn over await self._report_assistant_response_started() @@ -1232,18 +1188,30 @@ class AWSNovaSonicLLMService(LLMService): if content.role == Role.ASSISTANT: if content.type == ContentType.TEXT: - # Ignore non-final text, and the "interrupted" message (which isn't meaningful text) - if content.text_stage == TextStage.FINAL and stop_reason != "INTERRUPTED": - if self._assistant_is_responding: - # Text added to the ongoing assistant response - await self._report_assistant_response_text_added(content.text_content) + if stop_reason != "INTERRUPTED": + if content.text_stage == TextStage.SPECULATIVE: + await self._report_llm_text(content.text_content) + elif self._assistant_is_responding: + # TEXT INTERRUPTED with no audio means the user interrupted + # before audio started. End the response here since no AUDIO + # contentEnd will arrive. + self._assistant_is_responding = False + await self._report_assistant_response_ended() + elif content.type == ContentType.AUDIO: + # Emit deferred TTSTextFrame after all audio chunks have been sent + await self._report_tts_text() + if stop_reason in ("END_TURN", "INTERRUPTED"): + # END_TURN: normal completion. INTERRUPTED: user interrupted + # mid-audio. Both mean no more audio for this turn. + self._assistant_is_responding = False + await self._report_assistant_response_ended() elif content.role == Role.USER: if content.type == ContentType.TEXT: if content.text_stage == TextStage.FINAL: # User transcription text added await self._report_user_transcription_text_added(content.text_content) - async def _handle_completion_end_event(self, event_json): + async def _handle_completion_end_event(self, _): pass # @@ -1256,29 +1224,40 @@ class AWSNovaSonicLLMService(LLMService): async def _report_assistant_response_started(self): logger.debug("Assistant response started") - - # Report the start of the assistant response. await self.push_frame(LLMFullResponseStartFrame()) # Report that equivalent of TTS (this is a speech-to-speech model) started await self.push_frame(TTSStartedFrame()) - async def _report_assistant_response_text_added(self, text): - if not self._context: # should never happen - return + async def _report_llm_text(self, text): + """Push speculative assistant text and defer TTSTextFrame. - logger.debug(f"Assistant response text added: {text}") + Speculative text arrives before each audio chunk, providing real-time + text that is synchronized with what the bot is saying. LLMTextFrame and + AggregatedTextFrame are pushed immediately for real-time text display. + TTSTextFrame emission is deferred to audio contentEnd so it aligns with + audio playout timing. + """ + logger.debug(f"Assistant speculative text: {text}") - # Report the text of the assistant response. - await self._push_assistant_response_text_frames(text) + llm_text_frame = LLMTextFrame(text) + llm_text_frame.append_to_context = False + await self.push_frame(llm_text_frame) - # HACK: here we're also buffering the assistant text ourselves as a - # backup rather than relying solely on the assistant context aggregator - # to do it, because the text arrives from Nova Sonic only after all the - # assistant audio frames have been pushed, meaning that if an - # interruption frame were to arrive we would lose all of it (the text - # frames sitting in the queue would be wiped). - self._assistant_text_buffer += text + aggregated_text_frame = AggregatedTextFrame(text, aggregated_by=AggregationType.SENTENCE) + aggregated_text_frame.append_to_context = False + await self.push_frame(aggregated_text_frame) + + self._pending_speculative_text = text + + async def _report_tts_text(self): + if self._pending_speculative_text: + tts_text_frame = TTSTextFrame( + self._pending_speculative_text, aggregated_by=AggregationType.SENTENCE + ) + tts_text_frame.includes_inter_frame_spaces = True + await self.push_frame(tts_text_frame) + self._pending_speculative_text = None async def _report_assistant_response_ended(self): if not self._context: # should never happen @@ -1286,54 +1265,12 @@ class AWSNovaSonicLLMService(LLMService): logger.debug("Assistant response ended") - # If an interruption frame arrived while the assistant was responding - # we may have lost all of the assistant text (see HACK, above), so - # re-push it downstream to the aggregator now. - if self._may_need_repush_assistant_text: - # Just in case, check that assistant text hasn't already made it - # into the context (sometimes it does, despite the interruption). - messages = self._context.get_messages() - last_message = messages[-1] if messages else None - if ( - not last_message - or last_message.get("role") != "assistant" - or last_message.get("content") != self._assistant_text_buffer - ): - # We also need to re-push the LLMFullResponseStartFrame since the - # TTSTextFrame would be ignored otherwise (the interruption frame - # would have cleared the assistant aggregator state). - await self.push_frame(LLMFullResponseStartFrame()) - await self._push_assistant_response_text_frames(self._assistant_text_buffer) - self._may_need_repush_assistant_text = False - # Report the end of the assistant response. await self.push_frame(LLMFullResponseEndFrame()) # Report that equivalent of TTS (this is a speech-to-speech model) stopped. await self.push_frame(TTSStoppedFrame()) - # Clear out the buffered assistant text - self._assistant_text_buffer = "" - - async def _push_assistant_response_text_frames(self, text: str): - # In a typical "cascade" LLM + TTS setup, LLMTextFrames would not - # proceed beyond the TTS service. Therefore, since a speech-to-speech - # service like Nova Sonic combines both LLM and TTS functionality, you - # would think we wouldn't need to push LLMTextFrames at all. However, - # RTVI relies on LLMTextFrames being pushed to trigger its - # "bot-llm-text" event. So here we push an LLMTextFrame, too, but avoid - # appending it to context to avoid context message duplication. - - # Push LLMTextFrame - llm_text_frame = LLMTextFrame(text) - llm_text_frame.append_to_context = False - await self.push_frame(llm_text_frame) - - # Push TTSTextFrame - tts_text_frame = TTSTextFrame(text, aggregated_by=AggregationType.SENTENCE) - tts_text_frame.includes_inter_frame_spaces = True - await self.push_frame(tts_text_frame) - # # user transcription reporting # @@ -1363,6 +1300,12 @@ class AWSNovaSonicLLMService(LLMService): if not self._context: # should never happen return + # Nothing to report if no user speech was transcribed (e.g. the prompt + # was text-only, which is the case on the first user turn when the bot + # starts the conversation). + if not self._user_text_buffer: + return + logger.debug(f"User transcription ended") # Report to the upstream user context aggregator that some new user From bad10177d426c99c07c7fa4d3a5d6ddf6fbab457 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 18 Mar 2026 16:47:17 -0400 Subject: [PATCH 102/159] Add WakePhraseUserTurnStartStrategy (#4064) - Add WakePhraseUserTurnStartStrategy for gating interaction behind wake phrase detection, with timeout and single_activation modes - Add default_user_turn_start_strategies() and default_user_turn_stop_strategies() helper functions - Deprecate WakeCheckFilter in favor of the new strategy - Extend ProcessFrameResult to stop strategies for short-circuit evaluation - Fix MinWordsUserTurnStartStrategy including filtered text in output --- changelog/4064.added.2.md | 1 + changelog/4064.added.md | 1 + changelog/4064.changed.md | 1 + changelog/4064.deprecated.md | 1 + changelog/4064.fixed.md | 1 + examples/foundational/10-wake-phrase.py | 39 +- .../aggregators/llm_response_universal.py | 9 + .../processors/filters/wake_check_filter.py | 18 + src/pipecat/turns/types.py | 24 ++ src/pipecat/turns/user_start/__init__.py | 2 + .../base_user_turn_start_strategy.py | 12 +- .../external_user_turn_start_strategy.py | 11 +- .../min_words_user_turn_start_strategy.py | 27 +- .../transcription_user_turn_start_strategy.py | 12 +- .../vad_user_turn_start_strategy.py | 11 +- .../wake_phrase_user_turn_start_strategy.py | 281 ++++++++++++++ .../user_stop/base_user_turn_stop_strategy.py | 7 +- .../external_user_turn_stop_strategy.py | 7 +- .../speech_timeout_user_turn_stop_strategy.py | 7 +- .../turn_analyzer_user_turn_stop_strategy.py | 8 +- src/pipecat/turns/user_turn_controller.py | 19 +- src/pipecat/turns/user_turn_strategies.py | 29 +- ...st_wake_phrase_user_turn_start_strategy.py | 346 ++++++++++++++++++ 23 files changed, 835 insertions(+), 39 deletions(-) create mode 100644 changelog/4064.added.2.md create mode 100644 changelog/4064.added.md create mode 100644 changelog/4064.changed.md create mode 100644 changelog/4064.deprecated.md create mode 100644 changelog/4064.fixed.md create mode 100644 src/pipecat/turns/types.py create mode 100644 src/pipecat/turns/user_start/wake_phrase_user_turn_start_strategy.py create mode 100644 tests/test_wake_phrase_user_turn_start_strategy.py diff --git a/changelog/4064.added.2.md b/changelog/4064.added.2.md new file mode 100644 index 000000000..f2dc79a16 --- /dev/null +++ b/changelog/4064.added.2.md @@ -0,0 +1 @@ +- Added `default_user_turn_start_strategies()` and `default_user_turn_stop_strategies()` helper functions for composing custom strategy lists. diff --git a/changelog/4064.added.md b/changelog/4064.added.md new file mode 100644 index 000000000..5c28bde94 --- /dev/null +++ b/changelog/4064.added.md @@ -0,0 +1 @@ +- Added `WakePhraseUserTurnStartStrategy` for triggering user turns based on wake phrases, with support for `single_activation` mode. Deprecates `WakeCheckFilter`. diff --git a/changelog/4064.changed.md b/changelog/4064.changed.md new file mode 100644 index 000000000..c66263fbe --- /dev/null +++ b/changelog/4064.changed.md @@ -0,0 +1 @@ +- Extended `ProcessFrameResult` to stop strategies, allowing a stop strategy to short-circuit evaluation of subsequent strategies by returning `STOP`. diff --git a/changelog/4064.deprecated.md b/changelog/4064.deprecated.md new file mode 100644 index 000000000..b5f42c215 --- /dev/null +++ b/changelog/4064.deprecated.md @@ -0,0 +1 @@ +- Deprecated `WakeCheckFilter` in favor of `WakePhraseUserTurnStartStrategy`. diff --git a/changelog/4064.fixed.md b/changelog/4064.fixed.md new file mode 100644 index 000000000..6824ce26f --- /dev/null +++ b/changelog/4064.fixed.md @@ -0,0 +1 @@ +- Fixed `MinWordsUserTurnStartStrategy` including text below the word threshold in the output by resetting aggregation when the minimum word count is not met. diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py index 0a4244c33..5b93efb0e 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -19,7 +19,6 @@ from pipecat.processors.aggregators.llm_response_universal import ( LLMContextAggregatorPair, LLMUserAggregatorParams, ) -from pipecat.processors.filters.wake_check_filter import WakeCheckFilter from pipecat.runner.types import RunnerArguments from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaTTSService @@ -28,6 +27,11 @@ from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams +from pipecat.turns.user_start import WakePhraseUserTurnStartStrategy +from pipecat.turns.user_turn_strategies import ( + UserTurnStrategies, + default_user_turn_start_strategies, +) load_dotenv(override=True) @@ -52,7 +56,12 @@ transport_params = { async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): logger.info(f"Starting bot") - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + stt = DeepgramSTTService( + api_key=os.getenv("DEEPGRAM_API_KEY"), + settings=DeepgramSTTService.Settings( + keyterm=["pipecat"], + ), + ) tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), @@ -68,19 +77,28 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): ), ) - hey_robot_filter = WakeCheckFilter(["hey robot", "hey, robot"]) - context = LLMContext() user_aggregator, assistant_aggregator = LLMContextAggregatorPair( context, - user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()), + user_params=LLMUserAggregatorParams( + user_turn_strategies=UserTurnStrategies( + start=[ + WakePhraseUserTurnStartStrategy( + phrases=["pipecat"], + # Timeout before wake phrase must be spoken again + timeout=5.0, + ), + *default_user_turn_start_strategies(), + ] + ), + vad_analyzer=SileroVADAnalyzer(), + ), ) pipeline = Pipeline( [ transport.input(), # Transport user input - stt, # STT - hey_robot_filter, # Filter out speech not directed at the robot + stt, user_aggregator, # User responses llm, # LLM tts, # TTS @@ -102,12 +120,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): async def on_client_connected(transport, client): logger.info(f"Client connected") # Kick off the conversation. - context.add_message( - { - "role": "user", - "content": "Please introduce yourself. Tell the user they should say 'Hey Robot' before talking to you.", - } - ) + context.add_message({"role": "user", "content": "Please introduce yourself to the user."}) await task.queue_frames([LLMRunFrame()]) @transport.event_handler("on_client_disconnected") diff --git a/src/pipecat/processors/aggregators/llm_response_universal.py b/src/pipecat/processors/aggregators/llm_response_universal.py index b8a7061d8..605db31f6 100644 --- a/src/pipecat/processors/aggregators/llm_response_universal.py +++ b/src/pipecat/processors/aggregators/llm_response_universal.py @@ -447,6 +447,9 @@ class LLMUserAggregator(LLMContextAggregator): self._user_turn_controller.add_event_handler( "on_user_turn_stop_timeout", self._on_user_turn_stop_timeout ) + self._user_turn_controller.add_event_handler( + "on_reset_aggregation", self._on_reset_aggregation + ) self._user_idle_controller = UserIdleController( user_idle_timeout=self._params.user_idle_timeout @@ -748,6 +751,12 @@ class LLMUserAggregator(LLMContextAggregator): await self._maybe_emit_user_turn_stopped(strategy) + async def _on_reset_aggregation( + self, controller: UserTurnController, strategy: BaseUserTurnStartStrategy + ): + logger.debug(f"{self}: Resetting aggregation (strategy: {strategy})") + await self.reset() + async def _on_user_turn_stop_timeout(self, controller): await self._call_event_handler("on_user_turn_stop_timeout") diff --git a/src/pipecat/processors/filters/wake_check_filter.py b/src/pipecat/processors/filters/wake_check_filter.py index ec8f31f53..6a9e524e6 100644 --- a/src/pipecat/processors/filters/wake_check_filter.py +++ b/src/pipecat/processors/filters/wake_check_filter.py @@ -6,6 +6,9 @@ """Wake phrase detection filter for Pipecat transcription processing. +.. deprecated:: 0.0.106 + Use :class:`~pipecat.turns.user_start.WakePhraseUserTurnStartStrategy` instead. + This module provides a frame processor that filters transcription frames, only allowing them through after wake phrases have been detected. Includes keepalive functionality to maintain conversation flow after wake detection. @@ -13,6 +16,7 @@ keepalive functionality to maintain conversation flow after wake detection. import re import time +import warnings from enum import Enum from typing import List @@ -25,6 +29,11 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor class WakeCheckFilter(FrameProcessor): """Frame processor that filters transcription frames based on wake phrase detection. + .. deprecated:: 0.0.106 + Use :class:`~pipecat.turns.user_start.WakePhraseUserTurnStartStrategy` instead, + which integrates with the user turn strategy system and supports configurable + timeouts and single-activation mode. + This filter monitors transcription frames for configured wake phrases and only passes frames through after a wake phrase has been detected. Maintains a keepalive timeout to allow continued conversation after wake detection. @@ -65,12 +74,21 @@ class WakeCheckFilter(FrameProcessor): def __init__(self, wake_phrases: List[str], keepalive_timeout: float = 3): """Initialize the wake phrase filter. + .. deprecated:: 0.0.106 + Use :class:`~pipecat.turns.user_start.WakePhraseUserTurnStartStrategy` instead. + Args: wake_phrases: List of wake phrases to detect in transcriptions. keepalive_timeout: Duration in seconds to keep passing frames after wake detection. Defaults to 3 seconds. """ super().__init__() + warnings.warn( + "WakeCheckFilter is deprecated since v0.0.106. " + "Use WakePhraseUserTurnStartStrategy instead.", + DeprecationWarning, + stacklevel=2, + ) self._participant_states = {} self._keepalive_timeout = keepalive_timeout self._wake_patterns = [] diff --git a/src/pipecat/turns/types.py b/src/pipecat/turns/types.py new file mode 100644 index 000000000..5ebdf20a3 --- /dev/null +++ b/src/pipecat/turns/types.py @@ -0,0 +1,24 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Shared result type for user turn strategy frame processing.""" + +from enum import Enum + + +class ProcessFrameResult(Enum): + """Result of processing a frame in a user turn strategy. + + Controls whether the strategy loop in the controller continues to the + next strategy or stops early. + + Attributes: + CONTINUE: Continue to the next strategy in the loop. + STOP: Stop evaluating further strategies for this frame. + """ + + CONTINUE = "continue" + STOP = "stop" diff --git a/src/pipecat/turns/user_start/__init__.py b/src/pipecat/turns/user_start/__init__.py index fb84f62ed..94d12708d 100644 --- a/src/pipecat/turns/user_start/__init__.py +++ b/src/pipecat/turns/user_start/__init__.py @@ -9,6 +9,7 @@ from .external_user_turn_start_strategy import ExternalUserTurnStartStrategy from .min_words_user_turn_start_strategy import MinWordsUserTurnStartStrategy from .transcription_user_turn_start_strategy import TranscriptionUserTurnStartStrategy from .vad_user_turn_start_strategy import VADUserTurnStartStrategy +from .wake_phrase_user_turn_start_strategy import WakePhraseUserTurnStartStrategy __all__ = [ "BaseUserTurnStartStrategy", @@ -17,4 +18,5 @@ __all__ = [ "TranscriptionUserTurnStartStrategy", "UserTurnStartedParams", "VADUserTurnStartStrategy", + "WakePhraseUserTurnStartStrategy", ] diff --git a/src/pipecat/turns/user_start/base_user_turn_start_strategy.py b/src/pipecat/turns/user_start/base_user_turn_start_strategy.py index 25f5c8303..fd928a8a7 100644 --- a/src/pipecat/turns/user_start/base_user_turn_start_strategy.py +++ b/src/pipecat/turns/user_start/base_user_turn_start_strategy.py @@ -11,6 +11,7 @@ from typing import Optional, Type from pipecat.frames.frames import Frame from pipecat.processors.frame_processor import FrameDirection +from pipecat.turns.types import ProcessFrameResult from pipecat.utils.asyncio.task_manager import BaseTaskManager from pipecat.utils.base_object import BaseObject @@ -76,6 +77,7 @@ class BaseUserTurnStartStrategy(BaseObject): self._register_event_handler("on_push_frame", sync=True) self._register_event_handler("on_broadcast_frame", sync=True) self._register_event_handler("on_user_turn_started", sync=True) + self._register_event_handler("on_reset_aggregation", sync=True) @property def task_manager(self) -> BaseTaskManager: @@ -100,7 +102,7 @@ class BaseUserTurnStartStrategy(BaseObject): """Reset the strategy to its initial state.""" pass - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame. Subclasses should override this to implement logic that decides whether @@ -108,6 +110,10 @@ class BaseUserTurnStartStrategy(BaseObject): Args: frame: The frame to be processed. + + Returns: + A ProcessFrameResult indicating the outcome. Subclasses that return + None are treated as CONTINUE for backward compatibility. """ pass @@ -138,3 +144,7 @@ class BaseUserTurnStartStrategy(BaseObject): enable_user_speaking_frames=self._enable_user_speaking_frames, ), ) + + async def trigger_reset_aggregation(self): + """Trigger the `on_reset_aggregation` event.""" + await self._call_event_handler("on_reset_aggregation") diff --git a/src/pipecat/turns/user_start/external_user_turn_start_strategy.py b/src/pipecat/turns/user_start/external_user_turn_start_strategy.py index 9a85cad6d..1031a6b46 100644 --- a/src/pipecat/turns/user_start/external_user_turn_start_strategy.py +++ b/src/pipecat/turns/user_start/external_user_turn_start_strategy.py @@ -7,6 +7,7 @@ """User turn start strategy triggered by externally emitted frames.""" from pipecat.frames.frames import Frame, UserStartedSpeakingFrame +from pipecat.turns.types import ProcessFrameResult from pipecat.turns.user_start.base_user_turn_start_strategy import BaseUserTurnStartStrategy @@ -27,13 +28,17 @@ class ExternalUserTurnStartStrategy(BaseUserTurnStartStrategy): """ super().__init__(enable_interruptions=False, enable_user_speaking_frames=False, **kwargs) - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to detect user turn start. Args: frame: The frame to be analyzed. - """ - await super().process_frame(frame) + Returns: + STOP if a user started speaking frame was received, CONTINUE otherwise. + """ if isinstance(frame, UserStartedSpeakingFrame): await self.trigger_user_turn_started() + return ProcessFrameResult.STOP + + return ProcessFrameResult.CONTINUE diff --git a/src/pipecat/turns/user_start/min_words_user_turn_start_strategy.py b/src/pipecat/turns/user_start/min_words_user_turn_start_strategy.py index 57a0ca0d8..10bcfcdad 100644 --- a/src/pipecat/turns/user_start/min_words_user_turn_start_strategy.py +++ b/src/pipecat/turns/user_start/min_words_user_turn_start_strategy.py @@ -15,6 +15,7 @@ from pipecat.frames.frames import ( InterimTranscriptionFrame, TranscriptionFrame, ) +from pipecat.turns.types import ProcessFrameResult from pipecat.turns.user_start.base_user_turn_start_strategy import BaseUserTurnStartStrategy @@ -47,7 +48,7 @@ class MinWordsUserTurnStartStrategy(BaseUserTurnStartStrategy): await super().reset() self._bot_speaking = False - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to detect the start of a user turn. This method updates internal state based on transcription frames and @@ -55,17 +56,20 @@ class MinWordsUserTurnStartStrategy(BaseUserTurnStartStrategy): Args: frame: The frame to be analyzed. - """ - await super().process_frame(frame) + Returns: + STOP if the minimum word count was reached, CONTINUE otherwise. + """ if isinstance(frame, BotStartedSpeakingFrame): await self._handle_bot_started_speaking(frame) elif isinstance(frame, BotStoppedSpeakingFrame): await self._handle_bot_stopped_speaking(frame) elif isinstance(frame, TranscriptionFrame): - await self._handle_transcription(frame) + return await self._handle_transcription(frame) elif isinstance(frame, InterimTranscriptionFrame) and self._use_interim: - await self._handle_transcription(frame) + return await self._handle_transcription(frame) + + return ProcessFrameResult.CONTINUE async def _handle_bot_started_speaking(self, frame: BotStartedSpeakingFrame): """Handle bot started speaking frame. @@ -87,11 +91,16 @@ class MinWordsUserTurnStartStrategy(BaseUserTurnStartStrategy): """ self._bot_speaking = False - async def _handle_transcription(self, frame: TranscriptionFrame | InterimTranscriptionFrame): - """Handle a completed transcription frame and check word count. + async def _handle_transcription( + self, frame: TranscriptionFrame | InterimTranscriptionFrame + ) -> ProcessFrameResult: + """Handle a transcription frame and check word count. Args: frame: The transcription frame to be processed. + + Returns: + STOP if the minimum word count was reached, CONTINUE otherwise. """ min_words = self._min_words if self._bot_speaking else 1 @@ -106,3 +115,7 @@ class MinWordsUserTurnStartStrategy(BaseUserTurnStartStrategy): if should_trigger: await self.trigger_user_turn_started() + return ProcessFrameResult.STOP + await self.trigger_reset_aggregation() + + return ProcessFrameResult.CONTINUE diff --git a/src/pipecat/turns/user_start/transcription_user_turn_start_strategy.py b/src/pipecat/turns/user_start/transcription_user_turn_start_strategy.py index b69b127ea..34a3e83e6 100644 --- a/src/pipecat/turns/user_start/transcription_user_turn_start_strategy.py +++ b/src/pipecat/turns/user_start/transcription_user_turn_start_strategy.py @@ -7,6 +7,7 @@ """User turn start strategy based on transcriptions.""" from pipecat.frames.frames import Frame, InterimTranscriptionFrame, TranscriptionFrame +from pipecat.turns.types import ProcessFrameResult from pipecat.turns.user_start.base_user_turn_start_strategy import BaseUserTurnStartStrategy @@ -25,15 +26,20 @@ class TranscriptionUserTurnStartStrategy(BaseUserTurnStartStrategy): super().__init__(**kwargs) self._use_interim = use_interim - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to detect the start of a user turn. Args: frame: The frame to be processed. - """ - await super().process_frame(frame) + Returns: + STOP if a transcription was received, CONTINUE otherwise. + """ if isinstance(frame, InterimTranscriptionFrame) and self._use_interim: await self.trigger_user_turn_started() + return ProcessFrameResult.STOP elif isinstance(frame, TranscriptionFrame): await self.trigger_user_turn_started() + return ProcessFrameResult.STOP + + return ProcessFrameResult.CONTINUE diff --git a/src/pipecat/turns/user_start/vad_user_turn_start_strategy.py b/src/pipecat/turns/user_start/vad_user_turn_start_strategy.py index 4bdf48594..2bc3875e0 100644 --- a/src/pipecat/turns/user_start/vad_user_turn_start_strategy.py +++ b/src/pipecat/turns/user_start/vad_user_turn_start_strategy.py @@ -7,6 +7,7 @@ """User turn start strategy based on VAD events.""" from pipecat.frames.frames import Frame, VADUserStartedSpeakingFrame +from pipecat.turns.types import ProcessFrameResult from pipecat.turns.user_start.base_user_turn_start_strategy import BaseUserTurnStartStrategy @@ -18,13 +19,17 @@ class VADUserTurnStartStrategy(BaseUserTurnStartStrategy): """ - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to detect user turn start. Args: frame: The frame to be analyzed. - """ - await super().process_frame(frame) + Returns: + STOP if the user started speaking, CONTINUE otherwise. + """ if isinstance(frame, VADUserStartedSpeakingFrame): await self.trigger_user_turn_started() + return ProcessFrameResult.STOP + + return ProcessFrameResult.CONTINUE diff --git a/src/pipecat/turns/user_start/wake_phrase_user_turn_start_strategy.py b/src/pipecat/turns/user_start/wake_phrase_user_turn_start_strategy.py new file mode 100644 index 000000000..bcc069ad7 --- /dev/null +++ b/src/pipecat/turns/user_start/wake_phrase_user_turn_start_strategy.py @@ -0,0 +1,281 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""User turn start strategy that gates interaction behind wake phrase detection.""" + +import asyncio +import enum +import re +from typing import List, Optional + +from loguru import logger + +from pipecat.frames.frames import ( + BotSpeakingFrame, + Frame, + TranscriptionFrame, + UserSpeakingFrame, + VADUserStartedSpeakingFrame, +) +from pipecat.turns.types import ProcessFrameResult +from pipecat.turns.user_start.base_user_turn_start_strategy import BaseUserTurnStartStrategy +from pipecat.utils.asyncio.task_manager import BaseTaskManager + + +class _WakeState(enum.Enum): + """Internal state for wake phrase detection.""" + + IDLE = "idle" + AWAKE = "awake" + + +class WakePhraseUserTurnStartStrategy(BaseUserTurnStartStrategy): + """User turn start strategy that requires a wake phrase before interaction. + + Blocks subsequent strategies until a wake phrase is detected in a final + transcription. After detection, allows interaction for a configurable + timeout period before requiring the wake phrase again. Use + ``single_activation=True`` to require the wake phrase before every turn. + + This strategy should be placed first in the start strategies list. + + Event handlers available: + + - on_wake_phrase_detected: Called when a wake phrase is matched. + - on_wake_phrase_timeout: Called when the inactivity timeout expires + (timeout mode only). + + Example:: + + # Timeout mode (default): wake phrase unlocks interaction for 10s + strategy = WakePhraseUserTurnStartStrategy( + phrases=["hey pipecat", "ok pipecat"], + timeout=10.0, + ) + + # Single activation: wake phrase required before every turn + strategy = WakePhraseUserTurnStartStrategy( + phrases=["hey pipecat"], + single_activation=True, + ) + + @strategy.event_handler("on_wake_phrase_detected") + async def on_wake_phrase_detected(strategy, phrase): + ... + + @strategy.event_handler("on_wake_phrase_timeout") + async def on_wake_phrase_timeout(strategy): + ... + + Args: + phrases: List of wake phrases to detect. + timeout: Inactivity timeout in seconds before returning to IDLE. + In timeout mode, the timer resets on activity (user, bot speech). + In single activation mode, acts as a keepalive window — the strategy + stays AWAKE for this duration after wake phrase detection, allowing + the current turn to complete before returning to IDLE. + single_activation: If True, the wake phrase is required before every + turn. The strategy returns to IDLE after each turn completes. + **kwargs: Additional keyword arguments passed to parent. + """ + + def __init__( + self, + *, + phrases: List[str], + timeout: float = 10.0, + single_activation: bool = False, + **kwargs, + ): + """Initialize the wake phrase user turn start strategy. + + Args: + phrases: List of wake phrases to detect. + timeout: Inactivity timeout in seconds before returning to IDLE. + In timeout mode, the timer resets on activity. In single activation + mode, acts as a keepalive window after wake phrase detection. + single_activation: If True, the wake phrase is required before every + turn. The strategy returns to IDLE after each turn completes. + **kwargs: Additional keyword arguments passed to parent. + """ + super().__init__(**kwargs) + self._phrases = phrases + self._timeout = timeout + self._single_activation = single_activation + + self._patterns: List[re.Pattern] = [] + for phrase in phrases: + pattern = re.compile( + r"\b" + r"\s*".join(re.escape(word) for word in phrase.split()) + r"\b", + re.IGNORECASE, + ) + self._patterns.append(pattern) + + self._state = _WakeState.IDLE + self._accumulated_text = "" + + self._timeout_event = asyncio.Event() + self._timeout_task: Optional[asyncio.Task] = None + + self._register_event_handler("on_wake_phrase_detected") + self._register_event_handler("on_wake_phrase_timeout") + + @property + def state(self) -> _WakeState: + """Returns the current wake state.""" + return self._state + + async def setup(self, task_manager: BaseTaskManager): + """Initialize the strategy with the given task manager. + + Args: + task_manager: The task manager to be associated with this instance. + """ + await super().setup(task_manager) + if not self._timeout_task: + self._timeout_task = self.task_manager.create_task( + self._timeout_task_handler(), + f"{self}::_timeout_task_handler", + ) + + async def cleanup(self): + """Cleanup the strategy.""" + await super().cleanup() + if self._timeout_task: + await self.task_manager.cancel_task(self._timeout_task) + self._timeout_task = None + + async def reset(self): + """Reset the strategy. + + In timeout mode, preserves state and refreshes timeout since reset + means a turn started (activity). In single activation mode, does + nothing — the keepalive timeout (started when the wake phrase was + detected) handles the transition back to IDLE. + """ + await super().reset() + if self._state == _WakeState.AWAKE: + if not self._single_activation: + self._refresh_timeout() + + async def process_frame(self, frame: Frame) -> ProcessFrameResult: + """Process an incoming frame for wake phrase detection or passthrough. + + Args: + frame: The frame to be processed. + + Returns: + STOP when the wake phrase is detected or when in IDLE state + (blocks subsequent strategies), CONTINUE when in AWAKE state + (allows subsequent strategies to proceed). + """ + await super().process_frame(frame) + + if self._state == _WakeState.IDLE: + return await self._process_idle(frame) + else: + return await self._process_awake(frame) + + async def _process_idle(self, frame: Frame) -> ProcessFrameResult: + """Process a frame while in IDLE state. + + Only final ``TranscriptionFrame`` instances are checked for wake phrase + matches. When a match is found, a user turn start is triggered. + Transcription frames that don't match have their text cleared so that + pre-wake-phrase speech is not added to the LLM context. All frames + return STOP to block subsequent strategies. + """ + if isinstance(frame, TranscriptionFrame): + if self._check_wake_phrase(frame.text): + await self.trigger_user_turn_started() + return ProcessFrameResult.STOP + await self.trigger_reset_aggregation() + + return ProcessFrameResult.STOP + + async def _process_awake(self, frame: Frame) -> ProcessFrameResult: + """Process a frame while in AWAKE state. + + Refreshes the timeout on activity frames (timeout mode only). Returns + CONTINUE so subsequent strategies can process the frame. + """ + if not self._single_activation: + if isinstance(frame, (UserSpeakingFrame, BotSpeakingFrame)): + self._refresh_timeout() + elif isinstance(frame, TranscriptionFrame): + self._refresh_timeout() + elif isinstance(frame, VADUserStartedSpeakingFrame): + self._refresh_timeout() + + return ProcessFrameResult.CONTINUE + + @staticmethod + def _strip_punctuation(text: str) -> str: + """Strip punctuation from text, keeping only letters, digits, and whitespace.""" + return re.sub(r"[^\w\s]", "", text) + + def _check_wake_phrase(self, text: str) -> bool: + """Check if the accumulated text contains a wake phrase. + + Punctuation is stripped before matching so that STT output like + "Hey, Pipecat!" still matches the phrase "hey pipecat". + + Args: + text: New transcription text to append and check. + + Returns: + True if a wake phrase was found, False otherwise. + """ + self._accumulated_text += " " + self._strip_punctuation(text) + # Cap accumulated text to prevent unbounded growth. + if len(self._accumulated_text) > 250: + self._accumulated_text = self._accumulated_text[-250:] + + for i, pattern in enumerate(self._patterns): + if pattern.search(self._accumulated_text): + phrase = self._phrases[i] + logger.debug(f"{self} wake phrase detected: {phrase!r}") + self._transition_to_awake(phrase) + return True + + return False + + def _transition_to_awake(self, phrase: str): + """Transition from IDLE to AWAKE state.""" + self._state = _WakeState.AWAKE + self._accumulated_text = "" + self._refresh_timeout() + self.task_manager.create_task( + self._call_event_handler("on_wake_phrase_detected", phrase), + f"{self}::on_wake_phrase_detected", + ) + + def _transition_to_idle(self): + """Transition from AWAKE to IDLE state.""" + logger.debug(f"{self} wake phrase timeout, returning to IDLE") + self._state = _WakeState.IDLE + self._accumulated_text = "" + self.task_manager.create_task( + self._call_event_handler("on_wake_phrase_timeout"), + f"{self}::on_wake_phrase_timeout", + ) + + def _refresh_timeout(self): + """Refresh the inactivity timeout.""" + self._timeout_event.set() + + async def _timeout_task_handler(self): + """Background task that monitors inactivity timeout.""" + while True: + try: + await asyncio.wait_for( + self._timeout_event.wait(), + timeout=self._timeout, + ) + self._timeout_event.clear() + except asyncio.TimeoutError: + if self._state == _WakeState.AWAKE: + self._transition_to_idle() diff --git a/src/pipecat/turns/user_stop/base_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/base_user_turn_stop_strategy.py index c0042f902..7c493475e 100644 --- a/src/pipecat/turns/user_stop/base_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/base_user_turn_stop_strategy.py @@ -11,6 +11,7 @@ from typing import Optional, Type from pipecat.frames.frames import Frame from pipecat.processors.frame_processor import FrameDirection +from pipecat.turns.types import ProcessFrameResult from pipecat.utils.asyncio.task_manager import BaseTaskManager from pipecat.utils.base_object import BaseObject @@ -89,7 +90,7 @@ class BaseUserTurnStopStrategy(BaseObject): """Reset the strategy to its initial state.""" pass - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to decide whether the user stopped speaking. Subclasses should override this to implement logic that decides whether @@ -97,6 +98,10 @@ class BaseUserTurnStopStrategy(BaseObject): Args: frame: The frame to be analyzed. + + Returns: + A ProcessFrameResult indicating the outcome. Subclasses that return + None are treated as CONTINUE for backward compatibility. """ pass diff --git a/src/pipecat/turns/user_stop/external_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/external_user_turn_stop_strategy.py index 58e037fbf..4ffa3360b 100644 --- a/src/pipecat/turns/user_stop/external_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/external_user_turn_stop_strategy.py @@ -16,6 +16,7 @@ from pipecat.frames.frames import ( UserStartedSpeakingFrame, UserStoppedSpeakingFrame, ) +from pipecat.turns.types import ProcessFrameResult from pipecat.turns.user_stop.base_user_turn_stop_strategy import BaseUserTurnStopStrategy from pipecat.utils.asyncio.task_manager import BaseTaskManager @@ -69,7 +70,7 @@ class ExternalUserTurnStopStrategy(BaseUserTurnStopStrategy): await self.task_manager.cancel_task(self._task) self._task = None - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to update strategy state. Updates internal transcription text and VAD state. The user end turn @@ -78,6 +79,8 @@ class ExternalUserTurnStopStrategy(BaseUserTurnStopStrategy): Args: frame: The frame to be analyzed. + Returns: + Always returns CONTINUE so subsequent stop strategies are evaluated. """ if isinstance(frame, UserStartedSpeakingFrame): await self._handle_user_started_speaking(frame) @@ -88,6 +91,8 @@ class ExternalUserTurnStopStrategy(BaseUserTurnStopStrategy): elif isinstance(frame, TranscriptionFrame): await self._handle_transcription(frame) + return ProcessFrameResult.CONTINUE + async def _handle_user_started_speaking(self, _: UserStartedSpeakingFrame): """Handle when the external service indicates the user is speaking.""" self._user_speaking = True diff --git a/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py index ec94d9176..341a4c1e3 100644 --- a/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/speech_timeout_user_turn_stop_strategy.py @@ -17,6 +17,7 @@ from pipecat.frames.frames import ( VADUserStartedSpeakingFrame, VADUserStoppedSpeakingFrame, ) +from pipecat.turns.types import ProcessFrameResult from pipecat.turns.user_stop.base_user_turn_stop_strategy import BaseUserTurnStopStrategy from pipecat.utils.asyncio.task_manager import BaseTaskManager @@ -83,7 +84,7 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy): await self.task_manager.cancel_task(self._timeout_task) self._timeout_task = None - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to update strategy state. Updates internal transcription text and VAD state. The user end turn @@ -92,6 +93,8 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy): Args: frame: The frame to be analyzed. + Returns: + Always returns CONTINUE so subsequent stop strategies are evaluated. """ if isinstance(frame, STTMetadataFrame): self._stt_timeout = frame.ttfs_p99_latency @@ -102,6 +105,8 @@ class SpeechTimeoutUserTurnStopStrategy(BaseUserTurnStopStrategy): elif isinstance(frame, TranscriptionFrame): await self._handle_transcription(frame) + return ProcessFrameResult.CONTINUE + async def _handle_vad_user_started_speaking(self, _: VADUserStartedSpeakingFrame): """Handle when the VAD indicates the user is speaking.""" self._vad_user_speaking = True diff --git a/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py b/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py index 232bde223..a0df2efbb 100644 --- a/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py +++ b/src/pipecat/turns/user_stop/turn_analyzer_user_turn_stop_strategy.py @@ -22,6 +22,7 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.metrics.metrics import MetricsData +from pipecat.turns.types import ProcessFrameResult from pipecat.turns.user_stop.base_user_turn_stop_strategy import BaseUserTurnStopStrategy from pipecat.utils.asyncio.task_manager import BaseTaskManager @@ -88,11 +89,14 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy): await self.task_manager.cancel_task(self._timeout_task) self._timeout_task = None - async def process_frame(self, frame: Frame): + async def process_frame(self, frame: Frame) -> ProcessFrameResult: """Process an incoming frame to update the turn analyzer and strategy state. Args: frame: The frame to be analyzed. + + Returns: + Always returns CONTINUE so subsequent stop strategies are evaluated. """ await super().process_frame(frame) @@ -109,6 +113,8 @@ class TurnAnalyzerUserTurnStopStrategy(BaseUserTurnStopStrategy): elif isinstance(frame, TranscriptionFrame): await self._handle_transcription(frame) + return ProcessFrameResult.CONTINUE + async def _start(self, frame: StartFrame): """Process the start frame to configure the turn analyzer.""" self._turn_analyzer.set_sample_rate(frame.audio_in_sample_rate) diff --git a/src/pipecat/turns/user_turn_controller.py b/src/pipecat/turns/user_turn_controller.py index a064fc47b..9abed3932 100644 --- a/src/pipecat/turns/user_turn_controller.py +++ b/src/pipecat/turns/user_turn_controller.py @@ -19,7 +19,11 @@ from pipecat.frames.frames import ( VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection -from pipecat.turns.user_start import BaseUserTurnStartStrategy, UserTurnStartedParams +from pipecat.turns.types import ProcessFrameResult +from pipecat.turns.user_start import ( + BaseUserTurnStartStrategy, + UserTurnStartedParams, +) from pipecat.turns.user_stop import BaseUserTurnStopStrategy, UserTurnStoppedParams from pipecat.turns.user_turn_strategies import UserTurnStrategies from pipecat.utils.asyncio.task_manager import BaseTaskManager @@ -94,6 +98,7 @@ class UserTurnController(BaseObject): self._register_event_handler("on_user_turn_started", sync=True) self._register_event_handler("on_user_turn_stopped", sync=True) self._register_event_handler("on_user_turn_stop_timeout", sync=True) + self._register_event_handler("on_reset_aggregation", sync=True) @property def task_manager(self) -> BaseTaskManager: @@ -161,10 +166,14 @@ class UserTurnController(BaseObject): await self._handle_transcription(frame) for strategy in self._user_turn_strategies.start or []: - await strategy.process_frame(frame) + result = await strategy.process_frame(frame) + if result == ProcessFrameResult.STOP: + break for strategy in self._user_turn_strategies.stop or []: - await strategy.process_frame(frame) + result = await strategy.process_frame(frame) + if result == ProcessFrameResult.STOP: + break async def _setup_strategies(self): for s in self._user_turn_strategies.start or []: @@ -172,6 +181,7 @@ class UserTurnController(BaseObject): s.add_event_handler("on_push_frame", self._on_push_frame) s.add_event_handler("on_broadcast_frame", self._on_broadcast_frame) s.add_event_handler("on_user_turn_started", self._on_user_turn_started) + s.add_event_handler("on_reset_aggregation", self._on_reset_aggregation) for s in self._user_turn_strategies.stop or []: await s.setup(self.task_manager) @@ -242,6 +252,9 @@ class UserTurnController(BaseObject): ): await self._trigger_user_turn_stop(strategy, params) + async def _on_reset_aggregation(self, strategy: BaseUserTurnStartStrategy): + await self._call_event_handler("on_reset_aggregation", strategy) + async def _trigger_user_turn_start( self, strategy: Optional[BaseUserTurnStartStrategy], params: UserTurnStartedParams ): diff --git a/src/pipecat/turns/user_turn_strategies.py b/src/pipecat/turns/user_turn_strategies.py index 0435f141c..5789c6328 100644 --- a/src/pipecat/turns/user_turn_strategies.py +++ b/src/pipecat/turns/user_turn_strategies.py @@ -23,6 +23,31 @@ from pipecat.turns.user_stop import ( ) +def default_user_turn_start_strategies() -> List[BaseUserTurnStartStrategy]: + """Return the default user turn start strategies. + + Returns ``[VADUserTurnStartStrategy, TranscriptionUserTurnStartStrategy]``. + Useful when building a custom strategy list that extends the defaults. + + Example:: + + start_strategies = [ + WakePhraseUserTurnStartStrategy(phrases=["hey pipecat"]), + *default_user_turn_start_strategies(), + ] + """ + return [VADUserTurnStartStrategy(), TranscriptionUserTurnStartStrategy()] + + +def default_user_turn_stop_strategies() -> List[BaseUserTurnStopStrategy]: + """Return the default user turn stop strategies. + + Returns ``[TurnAnalyzerUserTurnStopStrategy(LocalSmartTurnAnalyzerV3)]``. + Useful when building a custom strategy list that extends the defaults. + """ + return [TurnAnalyzerUserTurnStopStrategy(turn_analyzer=LocalSmartTurnAnalyzerV3())] + + @dataclass class UserTurnStrategies: """Container for user turn start and stop strategies. @@ -45,9 +70,9 @@ class UserTurnStrategies: def __post_init__(self): if not self.start: - self.start = [VADUserTurnStartStrategy(), TranscriptionUserTurnStartStrategy()] + self.start = default_user_turn_start_strategies() if not self.stop: - self.stop = [TurnAnalyzerUserTurnStopStrategy(turn_analyzer=LocalSmartTurnAnalyzerV3())] + self.stop = default_user_turn_stop_strategies() @dataclass diff --git a/tests/test_wake_phrase_user_turn_start_strategy.py b/tests/test_wake_phrase_user_turn_start_strategy.py new file mode 100644 index 000000000..e79b9ab99 --- /dev/null +++ b/tests/test_wake_phrase_user_turn_start_strategy.py @@ -0,0 +1,346 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +import unittest + +from pipecat.frames.frames import ( + BotSpeakingFrame, + InterimTranscriptionFrame, + TranscriptionFrame, + UserSpeakingFrame, + VADUserStartedSpeakingFrame, +) +from pipecat.turns.types import ProcessFrameResult +from pipecat.turns.user_start.wake_phrase_user_turn_start_strategy import ( + WakePhraseUserTurnStartStrategy, + _WakeState, +) +from pipecat.utils.asyncio.task_manager import TaskManager, TaskManagerParams + + +class TestWakePhraseUserTurnStartStrategy(unittest.IsolatedAsyncioTestCase): + def _create_strategy(self, **kwargs) -> WakePhraseUserTurnStartStrategy: + kwargs.setdefault("phrases", ["hey pipecat"]) + kwargs.setdefault("timeout", 10.0) + return WakePhraseUserTurnStartStrategy(**kwargs) + + async def _setup_strategy(self, strategy: WakePhraseUserTurnStartStrategy): + task_manager = TaskManager() + loop = asyncio.get_running_loop() + task_manager.setup(TaskManagerParams(loop=loop)) + await strategy.setup(task_manager) + return task_manager + + async def test_wake_phrase_in_final_transcription(self): + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + result = await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + await strategy.cleanup() + + async def test_interim_transcription_ignored(self): + """Interim transcriptions are never used for wake phrase matching.""" + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + result = await strategy.process_frame( + InterimTranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.IDLE) + + await strategy.cleanup() + + async def test_no_wake_phrase_returns_stop(self): + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + result = await strategy.process_frame( + TranscriptionFrame(text="hello world", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.IDLE) + + await strategy.cleanup() + + async def test_non_matching_text_resets_aggregation(self): + """Non-matching transcription triggers aggregation reset to prevent LLM context pollution.""" + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + reset_called = False + + @strategy.event_handler("on_reset_aggregation") + async def on_reset_aggregation(strategy): + nonlocal reset_called + reset_called = True + + await strategy.process_frame( + TranscriptionFrame(text="hello world", user_id="user1", timestamp="") + ) + self.assertTrue(reset_called) + + await strategy.cleanup() + + async def test_vad_frame_returns_stop_in_listening(self): + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + result = await strategy.process_frame(VADUserStartedSpeakingFrame()) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.IDLE) + + await strategy.cleanup() + + async def test_inactive_returns_continue(self): + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + # Trigger wake phrase first. + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Subsequent frames should return CONTINUE. + result = await strategy.process_frame(VADUserStartedSpeakingFrame()) + self.assertEqual(result, ProcessFrameResult.CONTINUE) + + result = await strategy.process_frame( + TranscriptionFrame(text="what is the weather", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.CONTINUE) + + await strategy.cleanup() + + async def test_accumulation_across_frames(self): + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + result = await strategy.process_frame( + TranscriptionFrame(text="hey", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.IDLE) + + result = await strategy.process_frame( + TranscriptionFrame(text="pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + await strategy.cleanup() + + async def test_multiple_phrases(self): + strategy = self._create_strategy(phrases=["hey pipecat", "ok computer"]) + await self._setup_strategy(strategy) + + result = await strategy.process_frame( + TranscriptionFrame(text="ok computer", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + await strategy.cleanup() + + async def test_punctuation_stripped(self): + """STT punctuation like 'Hey, Pipecat!' should still match.""" + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + result = await strategy.process_frame( + TranscriptionFrame(text="Hey, Pipecat!", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + await strategy.cleanup() + + async def test_reset_preserves_inactive_state(self): + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + await strategy.reset() + self.assertEqual(strategy.state, _WakeState.AWAKE) + + await strategy.cleanup() + + async def test_timeout_returns_to_listening(self): + strategy = self._create_strategy(timeout=0.1) + await self._setup_strategy(strategy) + + # Trigger wake phrase. + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Wait for timeout to expire. + await asyncio.sleep(0.3) + + self.assertEqual(strategy.state, _WakeState.IDLE) + + await strategy.cleanup() + + async def test_activity_refreshes_timeout(self): + strategy = self._create_strategy(timeout=0.2) + await self._setup_strategy(strategy) + + # Trigger wake phrase. + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Send activity before timeout. + await asyncio.sleep(0.1) + await strategy.process_frame(UserSpeakingFrame()) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Send more activity. + await asyncio.sleep(0.1) + await strategy.process_frame(BotSpeakingFrame()) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Wait for timeout to expire after last activity. + await asyncio.sleep(0.3) + self.assertEqual(strategy.state, _WakeState.IDLE) + + await strategy.cleanup() + + async def test_wake_phrase_detected_event(self): + strategy = self._create_strategy() + await self._setup_strategy(strategy) + + detected_phrase = None + + @strategy.event_handler("on_wake_phrase_detected") + async def on_wake_phrase_detected(strategy, phrase): + nonlocal detected_phrase + detected_phrase = phrase + + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + + # Event fires in a background task, give it a moment. + await asyncio.sleep(0.05) + self.assertEqual(detected_phrase, "hey pipecat") + + await strategy.cleanup() + + async def test_wake_phrase_timeout_event(self): + strategy = self._create_strategy(timeout=0.1) + await self._setup_strategy(strategy) + + timeout_fired = False + + @strategy.event_handler("on_wake_phrase_timeout") + async def on_wake_phrase_timeout(strategy): + nonlocal timeout_fired + timeout_fired = True + + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + + # Wait for timeout. + await asyncio.sleep(0.3) + self.assertTrue(timeout_fired) + + await strategy.cleanup() + + async def test_single_activation_stays_inactive_after_reset(self): + """In single activation mode, reset() keeps INACTIVE so the current turn can finish.""" + strategy = self._create_strategy(single_activation=True, timeout=0.5) + await self._setup_strategy(strategy) + + # Trigger wake phrase. + result = await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Simulate turn start (controller calls reset on all start strategies). + await strategy.reset() + # State remains INACTIVE so frames continue to flow. + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Subsequent frames should pass through (CONTINUE). + result = await strategy.process_frame(VADUserStartedSpeakingFrame()) + self.assertEqual(result, ProcessFrameResult.CONTINUE) + + result = await strategy.process_frame( + TranscriptionFrame(text="what is the weather", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.CONTINUE) + + await strategy.cleanup() + + async def test_single_activation_timeout_returns_to_listening(self): + """In single activation mode, the keepalive timeout returns to LISTENING.""" + strategy = self._create_strategy(single_activation=True, timeout=0.1) + await self._setup_strategy(strategy) + + # Trigger wake phrase. + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + # Wait for keepalive timeout to expire. + await asyncio.sleep(0.3) + self.assertEqual(strategy.state, _WakeState.IDLE) + + # Frames should now be blocked again. + result = await strategy.process_frame(VADUserStartedSpeakingFrame()) + self.assertEqual(result, ProcessFrameResult.STOP) + + await strategy.cleanup() + + async def test_single_activation_requires_wake_phrase_after_timeout(self): + """Single activation mode requires wake phrase again after keepalive timeout.""" + strategy = self._create_strategy(single_activation=True, timeout=0.1) + await self._setup_strategy(strategy) + + # First turn: wake phrase -> INACTIVE -> timeout -> LISTENING. + await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(strategy.state, _WakeState.AWAKE) + await asyncio.sleep(0.3) + self.assertEqual(strategy.state, _WakeState.IDLE) + + # Without wake phrase, frames are blocked. + result = await strategy.process_frame( + TranscriptionFrame(text="what is the weather", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + + # Second turn: wake phrase again. + result = await strategy.process_frame( + TranscriptionFrame(text="hey pipecat", user_id="user1", timestamp="") + ) + self.assertEqual(result, ProcessFrameResult.STOP) + self.assertEqual(strategy.state, _WakeState.AWAKE) + + await strategy.cleanup() + + +if __name__ == "__main__": + unittest.main() From 4aea7784c98d4b80c3cf96eec4b98f066cd65355 Mon Sep 17 00:00:00 2001 From: Filipi da Silva Fuchter Date: Wed, 18 Mar 2026 16:55:59 -0400 Subject: [PATCH 103/159] Fixed the ordering of `_maybe_pause_frame_processing` call in `TTSService` (#4071) * Fixing the invocation of pause_frame_processing at the correct time when receiving LLMFullResponseEndFrame and EndFrame. --- changelog/4071.fixed.md | 1 + src/pipecat/services/tts_service.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 changelog/4071.fixed.md diff --git a/changelog/4071.fixed.md b/changelog/4071.fixed.md new file mode 100644 index 000000000..0938127c3 --- /dev/null +++ b/changelog/4071.fixed.md @@ -0,0 +1 @@ +- Fixed audio overlap and potential dropped TTS content when multiple assistant turns occur in quick succession. `TTSService` now flushes remaining text before pausing frame processing on `LLMFullResponseEndFrame`/`EndFrame`, instead of pausing first. diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index 7583339c8..f93c13d79 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -720,11 +720,6 @@ class TTSService(AIService): self._turn_context_id = self.create_context_id() await self.push_frame(frame, direction) elif isinstance(frame, (LLMFullResponseEndFrame, EndFrame)): - # We pause processing incoming frames if the LLM response included - # text (it might be that it's only a function calling response). We - # pause to avoid audio overlapping. - await self._maybe_pause_frame_processing() - # Flush any remaining text (including text waiting for lookahead) remaining = await self._text_aggregator.flush() # Stop the aggregation metric (no-op if already stopped on first sentence). @@ -732,6 +727,11 @@ class TTSService(AIService): if remaining: await self._push_tts_frames(AggregatedTextFrame(remaining.text, remaining.type)) + # We pause processing incoming frames if the LLM response included + # text (it might be that it's only a function calling response). We + # pause to avoid audio overlapping. + await self._maybe_pause_frame_processing() + # Log accumulated streamed text and emit aggregated usage metric. if self._streamed_text: logger.debug(f"{self}: Generating TTS [{self._streamed_text}]") From 7dfcaf8096b057d4108998ec6b63c7d7571c0585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 18 Mar 2026 21:46:06 -0700 Subject: [PATCH 104/159] Add missing on_dtmf_event callback to Tavus transport The on_dtmf_event callback was added to DailyCallbacks in #4047 but the Tavus transport was not updated, causing a missing argument error. --- src/pipecat/transports/tavus/transport.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pipecat/transports/tavus/transport.py b/src/pipecat/transports/tavus/transport.py index 79be070c5..872e6eefc 100644 --- a/src/pipecat/transports/tavus/transport.py +++ b/src/pipecat/transports/tavus/transport.py @@ -241,6 +241,7 @@ class TavusTransportClient: on_dialout_stopped=partial(self._on_handle_callback, "on_dialout_stopped"), on_dialout_error=partial(self._on_handle_callback, "on_dialout_error"), on_dialout_warning=partial(self._on_handle_callback, "on_dialout_warning"), + on_dtmf_event=partial(self._on_handle_callback, "on_dtmf_event"), on_participant_joined=self._callbacks.on_participant_joined, on_participant_left=self._callbacks.on_participant_left, on_participant_updated=partial(self._on_handle_callback, "on_participant_updated"), From 3e0c536fe784e3ff252f138ff2ddda9b582d2ec5 Mon Sep 17 00:00:00 2001 From: aconchillo <951761+aconchillo@users.noreply.github.com> Date: Thu, 19 Mar 2026 04:48:16 +0000 Subject: [PATCH 105/159] Update changelog for version 0.0.106 --- CHANGELOG.md | 212 +++++++++++++++++++++++++++++++++++ changelog/3457.changed.md | 1 - changelog/3991.changed.md | 1 - changelog/3997.changed.md | 1 - changelog/4000.fixed.md | 1 - changelog/4001.changed.md | 1 - changelog/4001.deprecated.md | 1 - changelog/4004.added.md | 1 - changelog/4005.added.md | 1 - changelog/4006.fixed.md | 1 - changelog/4007.fixed.2.md | 1 - changelog/4007.fixed.md | 1 - changelog/4009.added.md | 1 - changelog/4012.deprecated.md | 1 - changelog/4023.changed.md | 1 - changelog/4024.fixed.md | 1 - changelog/4026.fixed.md | 1 - changelog/4035.security.md | 1 - changelog/4037.fixed.md | 1 - changelog/4042.changed.md | 1 - changelog/4042.fixed.md | 1 - changelog/4046.fixed.md | 1 - changelog/4047.added.md | 1 - changelog/4047.changed.md | 1 - changelog/4048.changed.md | 1 - changelog/4057.fixed.md | 1 - changelog/4058.fixed.md | 1 - changelog/4063.fixed.md | 1 - changelog/4064.added.2.md | 1 - changelog/4064.added.md | 1 - changelog/4064.changed.md | 1 - changelog/4064.deprecated.md | 1 - changelog/4064.fixed.md | 1 - changelog/4066.changed.2.md | 1 - changelog/4066.changed.md | 1 - changelog/4071.fixed.md | 1 - 36 files changed, 212 insertions(+), 35 deletions(-) delete mode 100644 changelog/3457.changed.md delete mode 100644 changelog/3991.changed.md delete mode 100644 changelog/3997.changed.md delete mode 100644 changelog/4000.fixed.md delete mode 100644 changelog/4001.changed.md delete mode 100644 changelog/4001.deprecated.md delete mode 100644 changelog/4004.added.md delete mode 100644 changelog/4005.added.md delete mode 100644 changelog/4006.fixed.md delete mode 100644 changelog/4007.fixed.2.md delete mode 100644 changelog/4007.fixed.md delete mode 100644 changelog/4009.added.md delete mode 100644 changelog/4012.deprecated.md delete mode 100644 changelog/4023.changed.md delete mode 100644 changelog/4024.fixed.md delete mode 100644 changelog/4026.fixed.md delete mode 100644 changelog/4035.security.md delete mode 100644 changelog/4037.fixed.md delete mode 100644 changelog/4042.changed.md delete mode 100644 changelog/4042.fixed.md delete mode 100644 changelog/4046.fixed.md delete mode 100644 changelog/4047.added.md delete mode 100644 changelog/4047.changed.md delete mode 100644 changelog/4048.changed.md delete mode 100644 changelog/4057.fixed.md delete mode 100644 changelog/4058.fixed.md delete mode 100644 changelog/4063.fixed.md delete mode 100644 changelog/4064.added.2.md delete mode 100644 changelog/4064.added.md delete mode 100644 changelog/4064.changed.md delete mode 100644 changelog/4064.deprecated.md delete mode 100644 changelog/4064.fixed.md delete mode 100644 changelog/4066.changed.2.md delete mode 100644 changelog/4066.changed.md delete mode 100644 changelog/4071.fixed.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 71938516a..dfd42c6c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,218 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [0.0.106] - 2026-03-18 + +### Added + +- Added optional `service` field to `ServiceUpdateSettingsFrame` (and its + subclasses `LLMUpdateSettingsFrame`, `TTSUpdateSettingsFrame`, + `STTUpdateSettingsFrame`) to target a specific service instance. When + `service` is set, only the matching service applies the settings; others + forward the frame unchanged. This enables updating a single service when + multiple services of the same type exist in the pipeline. + (PR [#4004](https://github.com/pipecat-ai/pipecat/pull/4004)) + +- Added `sip_provider` and `room_geo` parameters to `configure()` in the Daily + runner. These convenience parameters let callers specify a SIP provider name + and geographic region directly without manually constructing + `DailyRoomProperties` and `DailyRoomSipParams`. + (PR [#4005](https://github.com/pipecat-ai/pipecat/pull/4005)) + +- Added `PerplexityLLMAdapter` that automatically transforms conversation + messages to satisfy Perplexity's stricter API constraints (strict role + alternation, no non-initial system messages, last message must be user/tool). + Previously, certain conversation histories could cause Perplexity API errors + that didn't occur with OpenAI (`PerplexityLLMService` subclasses + `OpenAILLMService` since Perplexity uses an OpenAI-compatible API). + (PR [#4009](https://github.com/pipecat-ai/pipecat/pull/4009)) + +- Added DTMF input event support to the Daily transport. Incoming DTMF tones + are now received via Daily's `on_dtmf_event` callback and pushed into the + pipeline as `InputDTMFFrame`, enabling bots to react to keypad presses from + phone callers. + (PR [#4047](https://github.com/pipecat-ai/pipecat/pull/4047)) + +- Added `WakePhraseUserTurnStartStrategy` for triggering user turns based on + wake phrases, with support for `single_activation` mode. Deprecates + `WakeCheckFilter`. + (PR [#4064](https://github.com/pipecat-ai/pipecat/pull/4064)) + +- Added `default_user_turn_start_strategies()` and + `default_user_turn_stop_strategies()` helper functions for composing custom + strategy lists. + (PR [#4064](https://github.com/pipecat-ai/pipecat/pull/4064)) + +### Changed + +- Changed tool result JSON serialization to use `ensure_ascii=False`, + preserving UTF-8 characters instead of escaping them. This reduces context + size and token usage for non-English languages. + (PR [#3457](https://github.com/pipecat-ai/pipecat/pull/3457)) + +- `OpenAIRealtimeSTTService`'s `noise_reduction` parameter is now part of + `OpenAIRealtimeSTTSettings`, making it runtime-updatable via + `STTUpdateSettingsFrame`. The direct `noise_reduction` init argument is + deprecated as of 0.0.106. + (PR [#3991](https://github.com/pipecat-ai/pipecat/pull/3991)) + +- Updated `sarvamai` dependency from `0.1.26a2` (alpha) to `0.1.26` (stable + release). + (PR [#3997](https://github.com/pipecat-ai/pipecat/pull/3997)) + +- `SimliVideoService` now extends `AIService` instead of `FrameProcessor`, + aligning it with the HeyGen and Tavus video services. It supports + `SimliVideoService.Settings(...)` for configuration and uses + `start()`/`stop()`/`cancel()` lifecycle methods. Existing constructor usage + (`api_key`, `face_id`, etc.) remains unchanged. + (PR [#4001](https://github.com/pipecat-ai/pipecat/pull/4001)) + +- Update `pipecat-ai-small-webrtc-prebuilt` to `2.4.0`. + (PR [#4023](https://github.com/pipecat-ai/pipecat/pull/4023)) + +- Nova Sonic assistant text transcripts are now delivered in real-time using + speculative text events instead of delayed final text events. Previously, + assistant text only arrived after all audio had finished playing, causing + laggy transcripts in client UIs. Speculative text arrives before each audio + chunk, providing text synchronized with what the bot is saying. This also + simplifies the internal text handling by removing the interruption re-push + hack and assistant text buffer. + (PR [#4042](https://github.com/pipecat-ai/pipecat/pull/4042)) + +- Updated `daily-python` dependency to 0.25.0. + (PR [#4047](https://github.com/pipecat-ai/pipecat/pull/4047)) + +- Added `enable_dialout` parameter to `configure()` in `pipecat.runner.daily` + to support dial-out rooms. Also narrowed misleading `Optional` type hints and + deduplicated token expiry calculation. + (PR [#4048](https://github.com/pipecat-ai/pipecat/pull/4048)) + +- Extended `ProcessFrameResult` to stop strategies, allowing a stop strategy to + short-circuit evaluation of subsequent strategies by returning `STOP`. + (PR [#4064](https://github.com/pipecat-ai/pipecat/pull/4064)) + +- `GradiumSTTService` now takes both an `encoding` and `sample_rate` + constructor argument which is assmebled in the class to form the + `input_format`. PCM accepts `8000`, `16000`, and `24000` Hz sample rates. + (PR [#4066](https://github.com/pipecat-ai/pipecat/pull/4066)) + +- Improved `GradiumSTTService` transcription accuracy by reworking how text + fragments are accumulated and finalized. Previously, trailing words could be + dropped when the server's `flushed` response arrived before all text tokens + were delivered. The service now uses a short aggregation delay after flush to + capture trailing tokens, producing complete utterances. + (PR [#4066](https://github.com/pipecat-ai/pipecat/pull/4066)) + +### Deprecated + +- `SimliVideoService.InputParams` is deprecated. Use the direct constructor + parameters `max_session_length`, `max_idle_time`, and `enable_logging` + instead. + (PR [#4001](https://github.com/pipecat-ai/pipecat/pull/4001)) + +- Deprecated `LocalSmartTurnAnalyzerV2` and `LocalCoreMLSmartTurnAnalyzer`. Use + `LocalSmartTurnAnalyzerV3` instead. Instantiating these analyzers will now + emit a `DeprecationWarning`. + (PR [#4012](https://github.com/pipecat-ai/pipecat/pull/4012)) + +- Deprecated `WakeCheckFilter` in favor of `WakePhraseUserTurnStartStrategy`. + (PR [#4064](https://github.com/pipecat-ai/pipecat/pull/4064)) + +### Fixed + +- Fixed an issue where the default model for `OpenAILLMService` and + `AzureLLMService` was mistakenly reverted to `gpt-4o`. The defaults are now + restored to `gpt-4.1`. + (PR [#4000](https://github.com/pipecat-ai/pipecat/pull/4000)) + +- Fixed a race condition where `EndTaskFrame` could cause the pipeline to shut + down before in-flight frames (e.g. LLM function call responses) finished + processing. `EndTaskFrame` and `StopTaskFrame` now flow through the pipeline + as `ControlFrame`s, ensuring all pending work is flushed before shutdown + begins. `CancelTaskFrame` and `InterruptionTaskFrame` remain immediate + (`SystemFrame`). + (PR [#4006](https://github.com/pipecat-ai/pipecat/pull/4006)) + +- Fixed `ParallelPipeline` dropping or misordering frames during lifecycle + synchronization. Buffered frames are now flushed in the correct order + relative to synchronization frames (`StartFrame` goes first, + `EndFrame`/`CancelFrame` go after), and frames added to the buffer during + flush are also drained. + (PR [#4007](https://github.com/pipecat-ai/pipecat/pull/4007)) + +- Fixed `TTSService` potentially canceling in-flight audio during shutdown. The + stop sequence now waits for all queued audio contexts to finish processing + before canceling the stop frame task. + (PR [#4007](https://github.com/pipecat-ai/pipecat/pull/4007)) + +- Fixed `Language` enum values (e.g. `Language.ES`) not being converted to + service-specific codes when passed via + `settings=Service.Settings(language=Language.ES)` at init time. This caused + API errors (e.g. 400 from Rime) because the raw enum was sent instead of the + expected language code (e.g. `"spa"`). Runtime updates via + `UpdateSettingsFrame` were unaffected. The fix centralizes conversion in the + base `TTSService` and `STTService` classes so all services handle this + consistently. + (PR [#4024](https://github.com/pipecat-ai/pipecat/pull/4024)) + +- Fixed `DeepgramSTTService` ignoring the `base_url` scheme when using `ws://` + or `http://`. Previously these were silently overwritten with `wss://` / + `https://`, breaking air-gapped or private deployments that don't use TLS. + All scheme choices (`wss://`, `https://`, `ws://`, `http://`, or bare + hostname) are now respected. + (PR [#4026](https://github.com/pipecat-ai/pipecat/pull/4026)) + +- Fixed `LLMSwitcher.register_function()` and `register_direct_function()` not + accepting or forwarding the `timeout_secs` parameter. + (PR [#4037](https://github.com/pipecat-ai/pipecat/pull/4037)) + +- Fixed empty user transcriptions in Nova Sonic causing spurious interruptions. + Previously, an empty transcription could trigger an interruption of the + assistant's response even though the user hadn't actually spoken. + (PR [#4042](https://github.com/pipecat-ai/pipecat/pull/4042)) + +- Fixed `SonioxSTTService` and `OpenAIRealtimeSTTService` crash when language + parameters contain plain strings instead of `Language` enum values. + (PR [#4046](https://github.com/pipecat-ai/pipecat/pull/4046)) + +- Fixed premature user turn stops caused by late transcriptions arriving + between turns. A stale transcript from the previous turn could persist into + the next turn and trigger a stop before the current turn's real transcript + arrived. Stop strategies are now reset at both turn start and turn stop to + prevent state from leaking across turn boundaries. + (PR [#4057](https://github.com/pipecat-ai/pipecat/pull/4057)) + +- Fixed raw language strings like `"de-DE"` silently failing when passed to + TTS/STT services (e.g. ElevenLabs producing no audio). Raw strings now go + through the same `Language` enum resolution as enum values, so regional codes + like `"de-DE"` are properly converted to service-expected formats like + `"de"`. Unrecognized strings log a warning instead of failing silently. + (PR [#4058](https://github.com/pipecat-ai/pipecat/pull/4058)) + +- Fixed Deepgram STT list-type settings (`keyterm`, `keywords`, `search`, + `redact`, `replace`) being stringified instead of passed as lists to the SDK, + which caused them to be sent as literal strings (e.g. `"['pipecat']"`) in the + WebSocket query params. + (PR [#4063](https://github.com/pipecat-ai/pipecat/pull/4063)) + +- Fixed `MinWordsUserTurnStartStrategy` including text below the word threshold + in the output by resetting aggregation when the minimum word count is not + met. + (PR [#4064](https://github.com/pipecat-ai/pipecat/pull/4064)) + +- Fixed audio overlap and potential dropped TTS content when multiple assistant + turns occur in quick succession. `TTSService` now flushes remaining text + before pausing frame processing on `LLMFullResponseEndFrame`/`EndFrame`, + instead of pausing first. + (PR [#4071](https://github.com/pipecat-ai/pipecat/pull/4071)) + +### Security + +- Bumped PyJWT minimum version from 2.10.1 to 2.12.0 in the `livekit` extra to + address CVE-2026-32597 (GHSA-752w-5fwx-jx9f), where PyJWT <= 2.11.0 accepted + unknown `crit` header extensions. + (PR [#4035](https://github.com/pipecat-ai/pipecat/pull/4035)) + ## [0.0.105] - 2026-03-10 ### Added diff --git a/changelog/3457.changed.md b/changelog/3457.changed.md deleted file mode 100644 index d0d82ad2d..000000000 --- a/changelog/3457.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Changed tool result JSON serialization to use `ensure_ascii=False`, preserving UTF-8 characters instead of escaping them. This reduces context size and token usage for non-English languages. diff --git a/changelog/3991.changed.md b/changelog/3991.changed.md deleted file mode 100644 index 5767dc8aa..000000000 --- a/changelog/3991.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `OpenAIRealtimeSTTService`'s `noise_reduction` parameter is now part of `OpenAIRealtimeSTTSettings`, making it runtime-updatable via `STTUpdateSettingsFrame`. The direct `noise_reduction` init argument is deprecated as of 0.0.106. diff --git a/changelog/3997.changed.md b/changelog/3997.changed.md deleted file mode 100644 index 7d626a5fe..000000000 --- a/changelog/3997.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Updated `sarvamai` dependency from `0.1.26a2` (alpha) to `0.1.26` (stable release). diff --git a/changelog/4000.fixed.md b/changelog/4000.fixed.md deleted file mode 100644 index 871d71135..000000000 --- a/changelog/4000.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed an issue where the default model for `OpenAILLMService` and `AzureLLMService` was mistakenly reverted to `gpt-4o`. The defaults are now restored to `gpt-4.1`. diff --git a/changelog/4001.changed.md b/changelog/4001.changed.md deleted file mode 100644 index 1d0d4e4ef..000000000 --- a/changelog/4001.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `SimliVideoService` now extends `AIService` instead of `FrameProcessor`, aligning it with the HeyGen and Tavus video services. It supports `SimliVideoService.Settings(...)` for configuration and uses `start()`/`stop()`/`cancel()` lifecycle methods. Existing constructor usage (`api_key`, `face_id`, etc.) remains unchanged. diff --git a/changelog/4001.deprecated.md b/changelog/4001.deprecated.md deleted file mode 100644 index 749e7eea5..000000000 --- a/changelog/4001.deprecated.md +++ /dev/null @@ -1 +0,0 @@ -- `SimliVideoService.InputParams` is deprecated. Use the direct constructor parameters `max_session_length`, `max_idle_time`, and `enable_logging` instead. diff --git a/changelog/4004.added.md b/changelog/4004.added.md deleted file mode 100644 index f0fd28767..000000000 --- a/changelog/4004.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added optional `service` field to `ServiceUpdateSettingsFrame` (and its subclasses `LLMUpdateSettingsFrame`, `TTSUpdateSettingsFrame`, `STTUpdateSettingsFrame`) to target a specific service instance. When `service` is set, only the matching service applies the settings; others forward the frame unchanged. This enables updating a single service when multiple services of the same type exist in the pipeline. diff --git a/changelog/4005.added.md b/changelog/4005.added.md deleted file mode 100644 index 0a023f104..000000000 --- a/changelog/4005.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `sip_provider` and `room_geo` parameters to `configure()` in the Daily runner. These convenience parameters let callers specify a SIP provider name and geographic region directly without manually constructing `DailyRoomProperties` and `DailyRoomSipParams`. diff --git a/changelog/4006.fixed.md b/changelog/4006.fixed.md deleted file mode 100644 index ba12beea7..000000000 --- a/changelog/4006.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed a race condition where `EndTaskFrame` could cause the pipeline to shut down before in-flight frames (e.g. LLM function call responses) finished processing. `EndTaskFrame` and `StopTaskFrame` now flow through the pipeline as `ControlFrame`s, ensuring all pending work is flushed before shutdown begins. `CancelTaskFrame` and `InterruptionTaskFrame` remain immediate (`SystemFrame`). diff --git a/changelog/4007.fixed.2.md b/changelog/4007.fixed.2.md deleted file mode 100644 index 0c50b83e9..000000000 --- a/changelog/4007.fixed.2.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `TTSService` potentially canceling in-flight audio during shutdown. The stop sequence now waits for all queued audio contexts to finish processing before canceling the stop frame task. diff --git a/changelog/4007.fixed.md b/changelog/4007.fixed.md deleted file mode 100644 index 8a90aea43..000000000 --- a/changelog/4007.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `ParallelPipeline` dropping or misordering frames during lifecycle synchronization. Buffered frames are now flushed in the correct order relative to synchronization frames (`StartFrame` goes first, `EndFrame`/`CancelFrame` go after), and frames added to the buffer during flush are also drained. diff --git a/changelog/4009.added.md b/changelog/4009.added.md deleted file mode 100644 index 9ebbec7dd..000000000 --- a/changelog/4009.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `PerplexityLLMAdapter` that automatically transforms conversation messages to satisfy Perplexity's stricter API constraints (strict role alternation, no non-initial system messages, last message must be user/tool). Previously, certain conversation histories could cause Perplexity API errors that didn't occur with OpenAI (`PerplexityLLMService` subclasses `OpenAILLMService` since Perplexity uses an OpenAI-compatible API). diff --git a/changelog/4012.deprecated.md b/changelog/4012.deprecated.md deleted file mode 100644 index 4310b8aba..000000000 --- a/changelog/4012.deprecated.md +++ /dev/null @@ -1 +0,0 @@ -- Deprecated `LocalSmartTurnAnalyzerV2` and `LocalCoreMLSmartTurnAnalyzer`. Use `LocalSmartTurnAnalyzerV3` instead. Instantiating these analyzers will now emit a `DeprecationWarning`. diff --git a/changelog/4023.changed.md b/changelog/4023.changed.md deleted file mode 100644 index 7756f20b8..000000000 --- a/changelog/4023.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Update `pipecat-ai-small-webrtc-prebuilt` to `2.4.0`. diff --git a/changelog/4024.fixed.md b/changelog/4024.fixed.md deleted file mode 100644 index 7654cbdf5..000000000 --- a/changelog/4024.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `Language` enum values (e.g. `Language.ES`) not being converted to service-specific codes when passed via `settings=Service.Settings(language=Language.ES)` at init time. This caused API errors (e.g. 400 from Rime) because the raw enum was sent instead of the expected language code (e.g. `"spa"`). Runtime updates via `UpdateSettingsFrame` were unaffected. The fix centralizes conversion in the base `TTSService` and `STTService` classes so all services handle this consistently. diff --git a/changelog/4026.fixed.md b/changelog/4026.fixed.md deleted file mode 100644 index a12321ab4..000000000 --- a/changelog/4026.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `DeepgramSTTService` ignoring the `base_url` scheme when using `ws://` or `http://`. Previously these were silently overwritten with `wss://` / `https://`, breaking air-gapped or private deployments that don't use TLS. All scheme choices (`wss://`, `https://`, `ws://`, `http://`, or bare hostname) are now respected. diff --git a/changelog/4035.security.md b/changelog/4035.security.md deleted file mode 100644 index 9ffc17305..000000000 --- a/changelog/4035.security.md +++ /dev/null @@ -1 +0,0 @@ -- Bumped PyJWT minimum version from 2.10.1 to 2.12.0 in the `livekit` extra to address CVE-2026-32597 (GHSA-752w-5fwx-jx9f), where PyJWT <= 2.11.0 accepted unknown `crit` header extensions. diff --git a/changelog/4037.fixed.md b/changelog/4037.fixed.md deleted file mode 100644 index e55b6f998..000000000 --- a/changelog/4037.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `LLMSwitcher.register_function()` and `register_direct_function()` not accepting or forwarding the `timeout_secs` parameter. diff --git a/changelog/4042.changed.md b/changelog/4042.changed.md deleted file mode 100644 index f83c32f59..000000000 --- a/changelog/4042.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Nova Sonic assistant text transcripts are now delivered in real-time using speculative text events instead of delayed final text events. Previously, assistant text only arrived after all audio had finished playing, causing laggy transcripts in client UIs. Speculative text arrives before each audio chunk, providing text synchronized with what the bot is saying. This also simplifies the internal text handling by removing the interruption re-push hack and assistant text buffer. diff --git a/changelog/4042.fixed.md b/changelog/4042.fixed.md deleted file mode 100644 index a48996208..000000000 --- a/changelog/4042.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed empty user transcriptions in Nova Sonic causing spurious interruptions. Previously, an empty transcription could trigger an interruption of the assistant's response even though the user hadn't actually spoken. diff --git a/changelog/4046.fixed.md b/changelog/4046.fixed.md deleted file mode 100644 index 0f147e04e..000000000 --- a/changelog/4046.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed `SonioxSTTService` and `OpenAIRealtimeSTTService` crash when language parameters contain plain strings instead of `Language` enum values. diff --git a/changelog/4047.added.md b/changelog/4047.added.md deleted file mode 100644 index b0bda2ed4..000000000 --- a/changelog/4047.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added DTMF input event support to the Daily transport. Incoming DTMF tones are now received via Daily's `on_dtmf_event` callback and pushed into the pipeline as `InputDTMFFrame`, enabling bots to react to keypad presses from phone callers. diff --git a/changelog/4047.changed.md b/changelog/4047.changed.md deleted file mode 100644 index c93e95f76..000000000 --- a/changelog/4047.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Updated `daily-python` dependency to 0.25.0. diff --git a/changelog/4048.changed.md b/changelog/4048.changed.md deleted file mode 100644 index edef83283..000000000 --- a/changelog/4048.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Added `enable_dialout` parameter to `configure()` in `pipecat.runner.daily` to support dial-out rooms. Also narrowed misleading `Optional` type hints and deduplicated token expiry calculation. diff --git a/changelog/4057.fixed.md b/changelog/4057.fixed.md deleted file mode 100644 index b63b8540e..000000000 --- a/changelog/4057.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed premature user turn stops caused by late transcriptions arriving between turns. A stale transcript from the previous turn could persist into the next turn and trigger a stop before the current turn's real transcript arrived. Stop strategies are now reset at both turn start and turn stop to prevent state from leaking across turn boundaries. diff --git a/changelog/4058.fixed.md b/changelog/4058.fixed.md deleted file mode 100644 index 4b5baed82..000000000 --- a/changelog/4058.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed raw language strings like `"de-DE"` silently failing when passed to TTS/STT services (e.g. ElevenLabs producing no audio). Raw strings now go through the same `Language` enum resolution as enum values, so regional codes like `"de-DE"` are properly converted to service-expected formats like `"de"`. Unrecognized strings log a warning instead of failing silently. diff --git a/changelog/4063.fixed.md b/changelog/4063.fixed.md deleted file mode 100644 index ea703078a..000000000 --- a/changelog/4063.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed Deepgram STT list-type settings (`keyterm`, `keywords`, `search`, `redact`, `replace`) being stringified instead of passed as lists to the SDK, which caused them to be sent as literal strings (e.g. `"['pipecat']"`) in the WebSocket query params. diff --git a/changelog/4064.added.2.md b/changelog/4064.added.2.md deleted file mode 100644 index f2dc79a16..000000000 --- a/changelog/4064.added.2.md +++ /dev/null @@ -1 +0,0 @@ -- Added `default_user_turn_start_strategies()` and `default_user_turn_stop_strategies()` helper functions for composing custom strategy lists. diff --git a/changelog/4064.added.md b/changelog/4064.added.md deleted file mode 100644 index 5c28bde94..000000000 --- a/changelog/4064.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `WakePhraseUserTurnStartStrategy` for triggering user turns based on wake phrases, with support for `single_activation` mode. Deprecates `WakeCheckFilter`. diff --git a/changelog/4064.changed.md b/changelog/4064.changed.md deleted file mode 100644 index c66263fbe..000000000 --- a/changelog/4064.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Extended `ProcessFrameResult` to stop strategies, allowing a stop strategy to short-circuit evaluation of subsequent strategies by returning `STOP`. diff --git a/changelog/4064.deprecated.md b/changelog/4064.deprecated.md deleted file mode 100644 index b5f42c215..000000000 --- a/changelog/4064.deprecated.md +++ /dev/null @@ -1 +0,0 @@ -- Deprecated `WakeCheckFilter` in favor of `WakePhraseUserTurnStartStrategy`. diff --git a/changelog/4064.fixed.md b/changelog/4064.fixed.md deleted file mode 100644 index 6824ce26f..000000000 --- a/changelog/4064.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `MinWordsUserTurnStartStrategy` including text below the word threshold in the output by resetting aggregation when the minimum word count is not met. diff --git a/changelog/4066.changed.2.md b/changelog/4066.changed.2.md deleted file mode 100644 index 751961f10..000000000 --- a/changelog/4066.changed.2.md +++ /dev/null @@ -1 +0,0 @@ -- `GradiumSTTService` now takes both an `encoding` and `sample_rate` constructor argument which is assmebled in the class to form the `input_format`. PCM accepts `8000`, `16000`, and `24000` Hz sample rates. diff --git a/changelog/4066.changed.md b/changelog/4066.changed.md deleted file mode 100644 index 65e95ff2c..000000000 --- a/changelog/4066.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Improved `GradiumSTTService` transcription accuracy by reworking how text fragments are accumulated and finalized. Previously, trailing words could be dropped when the server's `flushed` response arrived before all text tokens were delivered. The service now uses a short aggregation delay after flush to capture trailing tokens, producing complete utterances. diff --git a/changelog/4071.fixed.md b/changelog/4071.fixed.md deleted file mode 100644 index 0938127c3..000000000 --- a/changelog/4071.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed audio overlap and potential dropped TTS content when multiple assistant turns occur in quick succession. `TTSService` now flushes remaining text before pausing frame processing on `LLMFullResponseEndFrame`/`EndFrame`, instead of pausing first. From 5fd98e13910eb42a377d5f0dbe1e94ad6c26248c Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 09:43:40 -0300 Subject: [PATCH 106/159] Fixing TTS frame order. --- src/pipecat/services/tts_service.py | 56 +++-- tests/test_tts_frame_ordering.py | 315 ++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+), 12 deletions(-) create mode 100644 tests/test_tts_frame_ordering.py diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index f93c13d79..303072373 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -43,6 +43,7 @@ from pipecat.frames.frames import ( LLMFullResponseEndFrame, LLMFullResponseStartFrame, StartFrame, + SystemFrame, TextFrame, TranscriptionFrame, TTSAudioRawFrame, @@ -557,9 +558,9 @@ class TTSService(AIService): """ await super().stop(frame) if self._audio_context_task: - # Indicate no more audio contexts are available; this will end the - # task cleanly after all contexts have been processed. - await self._contexts_queue.put(None) + # Sentinel None shuts down the serialization queue once all + # pending contexts and frames have been processed. + await self._serialization_queue.put(None) await self._audio_context_task self._audio_context_task = None if self._stop_frame_task: @@ -791,7 +792,15 @@ class TTSService(AIService): await self._maybe_resume_frame_processing() await self.push_frame(frame, direction) else: - await self.push_frame(frame, direction) + if direction == FrameDirection.DOWNSTREAM and not isinstance(frame, SystemFrame): + # Route non-system downstream frames through the serialization queue so they + # are emitted in the same order they arrive relative to any audio contexts that + # are already queued (e.g. a FooFrame sent right after a TTSSpeakFrame must + # not overtake the TTSStartedFrame / TTSAudioRawFrame / TTSStoppedFrame + # sequence from that speak frame). + await self._serialization_queue.put(frame) + else: + await self.push_frame(frame, direction) async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM): """Push a frame downstream with TTS-specific handling. @@ -994,7 +1003,10 @@ class TTSService(AIService): # is spoken, so we set append_to_context to False. src_frame.append_to_context = False src_frame.context_id = context_id - await self.push_frame(src_frame) + # Route AggregatedTextFrame through the serialization queue so it is emitted + # immediately before the TTSStartedFrame of the audio context it describes, + # rather than racing ahead of audio frames from a previous context. + await self._serialization_queue.put(src_frame) # Note: Text transformations are meant to only affect the text sent to the TTS for # TTS-specific purposes. This allows for explicit TTS modifications (e.g., inserting @@ -1203,7 +1215,7 @@ class TTSService(AIService): Args: context_id: Unique identifier for the audio context. """ - await self._contexts_queue.put(context_id) + await self._serialization_queue.put(context_id) self._audio_contexts[context_id] = asyncio.Queue() logger.trace(f"{self} created audio context {context_id}") @@ -1295,7 +1307,14 @@ class TTSService(AIService): def _create_audio_context_task(self): if not self._audio_context_task: - self._contexts_queue: asyncio.Queue = asyncio.Queue() + # Single FIFO queue that serializes everything the TTS service emits downstream. + # Items can be: + # str – an audio context ID: process the per-context audio queue in full before + # moving on (see _handle_audio_context). + # Frame – a non-system downstream frame (e.g. AggregatedTextFrame, FooFrame) that + # must be emitted in-order relative to surrounding audio contexts. + # None – shutdown sentinel (sent by stop()). + self._serialization_queue: asyncio.Queue = asyncio.Queue() self._audio_contexts: Dict[str, asyncio.Queue] = {} self._audio_context_task = self.create_task(self._audio_context_task_handler()) @@ -1305,13 +1324,26 @@ class TTSService(AIService): self._audio_context_task = None async def _audio_context_task_handler(self): - """In this task we process audio contexts in order.""" + """Drain the serialization queue, preserving downstream frame order. + + The queue carries three kinds of items (see _create_audio_context_task): + + * str – audio context ID: block until all audio for that context has been + pushed downstream, then call on_audio_context_completed(). + * Frame – a non-system downstream frame that must be emitted at this exact + position in the output stream (e.g. AggregatedTextFrame preceding + its audio, or an arbitrary frame that arrived between two speak frames). + * None – shutdown sentinel; exit the loop once reached. + """ running = True while running: - context_id = await self._contexts_queue.get() - self._playing_context_id = context_id + context_value = await self._serialization_queue.get() + if isinstance(context_value, Frame): + await self.push_frame(context_value) + elif isinstance(context_value, str): + context_id = context_value + self._playing_context_id = context_id - if context_id: # Process the audio context until the context doesn't have more # audio available (i.e. we find None). await self._handle_audio_context(context_id) @@ -1323,7 +1355,7 @@ class TTSService(AIService): else: running = False - self._contexts_queue.task_done() + self._serialization_queue.task_done() async def _handle_audio_context(self, context_id: str): """Process items from an audio context queue until it is exhausted.""" diff --git a/tests/test_tts_frame_ordering.py b/tests/test_tts_frame_ordering.py new file mode 100644 index 000000000..52d3df4e7 --- /dev/null +++ b/tests/test_tts_frame_ordering.py @@ -0,0 +1,315 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""Tests for frame ordering across TTS service types. + +Covers three patterns: +- HTTP TTS services (e.g. CartesiaHttpTTSService): yield audio frames synchronously. +- WebSocket TTS services without pause (e.g. CartesiaTTSService): deliver audio via + append_to_audio_context from a background receive loop, no frame-processing pause. +- WebSocket TTS services with pause (e.g. ElevenLabsTTSService): same delivery + mechanism, but pause downstream frame processing while audio is in flight. + +For all three patterns we verify: + AggregatedTextFrame → TTSStartedFrame → TTSAudioRawFrame (1+) → TTSStoppedFrame → FooFrame + +repeated for each TTSSpeakFrame, with no cross-group contamination. +""" + +import asyncio +import unittest +from dataclasses import dataclass +from typing import AsyncGenerator, List, Sequence, Tuple + +import pytest + +from pipecat.frames.frames import ( + AggregatedTextFrame, + DataFrame, + Frame, + TTSAudioRawFrame, + TTSSpeakFrame, + TTSStartedFrame, + TTSStoppedFrame, +) +from pipecat.services.tts_service import TTSService +from pipecat.tests.utils import run_test + +# --------------------------------------------------------------------------- +# Test-only frame +# --------------------------------------------------------------------------- + +_FAKE_AUDIO = b"\x00\x01" * 320 # 320 bytes of silence +_SAMPLE_RATE = 16000 + + +@dataclass +class FooFrame(DataFrame): + """Marker frame used to verify relative ordering against TTS audio frames.""" + + label: str = "" + + +# --------------------------------------------------------------------------- +# Mock TTS services +# --------------------------------------------------------------------------- + + +class MockHttpTTSService(TTSService): + """Simulates an HTTP TTS service (e.g. CartesiaHttpTTSService). + + Audio frames are yielded synchronously from run_tts(), so the audio context + is fully populated before the next downstream frame is processed. + TTSStoppedFrame is appended by the base class in on_turn_context_completed() + once it detects _is_yielding_frames_synchronously is True. + """ + + def __init__(self, **kwargs): + super().__init__( + push_start_frame=True, + push_stop_frames=True, + push_text_frames=False, + sample_rate=_SAMPLE_RATE, + **kwargs, + ) + + def can_generate_metrics(self) -> bool: + return False + + async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: + yield TTSAudioRawFrame( + audio=_FAKE_AUDIO, + sample_rate=_SAMPLE_RATE, + num_channels=1, + context_id=context_id, + ) + + +class MockWebSocketTTSService(TTSService): + """Simulates a WebSocket TTS service without frame-processing pause (e.g. CartesiaTTSService). + + run_tts() is an empty async generator (signals async delivery). A background + task appends audio frames and the TTSStoppedFrame to the audio context after a + short delay, mimicking real WebSocket receive-loop behaviour. + pause_frame_processing=False means downstream frames (FooFrame) are NOT held. + """ + + def __init__(self, **kwargs): + super().__init__( + push_start_frame=True, + push_text_frames=False, + pause_frame_processing=False, + sample_rate=_SAMPLE_RATE, + **kwargs, + ) + + def can_generate_metrics(self) -> bool: + return False + + async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: + async def _deliver_audio(): + await asyncio.sleep(0.01) + await self.append_to_audio_context( + context_id, + TTSAudioRawFrame( + audio=_FAKE_AUDIO, + sample_rate=_SAMPLE_RATE, + num_channels=1, + context_id=context_id, + ), + ) + await self.append_to_audio_context(context_id, TTSStoppedFrame(context_id=context_id)) + await self.remove_audio_context(context_id) + + self.create_task(_deliver_audio(), name=f"mock_ws_deliver_{context_id}") + if False: + yield # make this an async generator without yielding anything + + +class MockWebSocketPauseTTSService(TTSService): + """Simulates a WebSocket TTS service WITH frame-processing pause (e.g. ElevenLabsTTSService). + + Identical to MockWebSocketTTSService except pause_frame_processing=True. + on_audio_context_completed() resumes downstream processing once the full + audio context has been pushed, guaranteeing FooFrame arrives after TTSStoppedFrame. + """ + + def __init__(self, **kwargs): + super().__init__( + push_start_frame=True, + push_text_frames=False, + pause_frame_processing=True, + sample_rate=_SAMPLE_RATE, + **kwargs, + ) + + def can_generate_metrics(self) -> bool: + return False + + async def on_audio_context_completed(self, context_id: str): + # Resume frame processing after the audio context is fully played out. + await self._maybe_resume_frame_processing() + + async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]: + async def _deliver_audio(): + await asyncio.sleep(0.01) + await self.append_to_audio_context( + context_id, + TTSAudioRawFrame( + audio=_FAKE_AUDIO, + sample_rate=_SAMPLE_RATE, + num_channels=1, + context_id=context_id, + ), + ) + await self.append_to_audio_context(context_id, TTSStoppedFrame(context_id=context_id)) + await self.remove_audio_context(context_id) + + self.create_task(_deliver_audio(), name=f"mock_ws_pause_deliver_{context_id}") + if False: + yield + + +# --------------------------------------------------------------------------- +# Assertion helper +# --------------------------------------------------------------------------- + + +def _assert_group_ordering( + down_frames: Sequence[Frame], + expected_groups: List[Tuple[str, str]], +) -> None: + """Assert two (or more) TTS+FooFrame groups are in strict order. + + Args: + down_frames: All downstream frames received by the test sink. + expected_groups: List of (tts_text, foo_label) pairs, one per TTSSpeakFrame. + tts_text is unused in assertions today but included for readability. + """ + relevant = [ + f + for f in down_frames + if isinstance( + f, (AggregatedTextFrame, TTSStartedFrame, TTSAudioRawFrame, TTSStoppedFrame, FooFrame) + ) + ] + + # Locate the FooFrames that delimit groups. + foo_indices = [i for i, f in enumerate(relevant) if isinstance(f, FooFrame)] + assert len(foo_indices) == len(expected_groups), ( + f"Expected {len(expected_groups)} FooFrames, got {len(foo_indices)}.\n" + f"Relevant frames: {[type(f).__name__ for f in relevant]}" + ) + + # Build groups: everything up to and including each FooFrame. + groups: List[List[Frame]] = [] + prev = 0 + for idx in foo_indices: + groups.append(relevant[prev : idx + 1]) + prev = idx + 1 + + for group, (_, foo_label) in zip(groups, expected_groups): + types = [type(f) for f in group] + type_names = [t.__name__ for t in types] + + assert AggregatedTextFrame in types, ( + f"Group {foo_label!r}: missing AggregatedTextFrame. Got: {type_names}" + ) + assert TTSStartedFrame in types, ( + f"Group {foo_label!r}: missing TTSStartedFrame. Got: {type_names}" + ) + assert TTSAudioRawFrame in types, ( + f"Group {foo_label!r}: missing TTSAudioRawFrame. Got: {type_names}" + ) + assert TTSStoppedFrame in types, ( + f"Group {foo_label!r}: missing TTSStoppedFrame. Got: {type_names}" + ) + + started_idx = types.index(TTSStartedFrame) + stopped_idx = types.index(TTSStoppedFrame) + foo_idx = types.index(FooFrame) + + assert started_idx < stopped_idx, ( + f"Group {foo_label!r}: TTSStartedFrame (pos {started_idx}) must precede " + f"TTSStoppedFrame (pos {stopped_idx}). Got: {type_names}" + ) + assert stopped_idx < foo_idx, ( + f"Group {foo_label!r}: TTSStoppedFrame (pos {stopped_idx}) must precede " + f"FooFrame (pos {foo_idx}). Got: {type_names}" + ) + + # All frames between TTSStartedFrame and TTSStoppedFrame must be audio. + mid_types = types[started_idx + 1 : stopped_idx] + for t in mid_types: + assert t is TTSAudioRawFrame, ( + f"Group {foo_label!r}: unexpected frame {t.__name__!r} between " + f"TTSStartedFrame and TTSStoppedFrame. Got: {type_names}" + ) + + # Check the FooFrame label. + actual_label = group[foo_idx].label + assert actual_label == foo_label, ( + f"Expected FooFrame(label={foo_label!r}), got label={actual_label!r}" + ) + + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- + +_GROUPS = [("test 1", "1"), ("test 2", "2")] + + +def _make_frames_no_sleep() -> List[Frame]: + """Return two TTSSpeakFrame+FooFrame pairs sent back-to-back. + + Only correct for services that pause downstream processing until the audio + context is fully consumed (pause_frame_processing=True + on_audio_context_completed). + """ + return [ + TTSSpeakFrame(text="test 1", append_to_context=False), + FooFrame(label="1"), + TTSSpeakFrame(text="test 2", append_to_context=False), + FooFrame(label="2"), + ] + + +def _print_frames_received(frames_received) -> None: + print("FRAMES RECEIVED:") + for frame in frames_received[0]: + print(frame.name) + + +@pytest.mark.asyncio +async def test_http_tts_frame_ordering(): + """HTTP TTS services yield audio synchronously.""" + tts = MockHttpTTSService() + frames_received = await run_test(tts, frames_to_send=_make_frames_no_sleep()) + + # only for debugging + _print_frames_received(frames_received) + + _assert_group_ordering(frames_received[0], _GROUPS) + + +@pytest.mark.asyncio +async def test_websocket_tts_no_pause_frame_ordering(): + """WebSocket TTS services without pause_frame_processing.""" + tts = MockWebSocketTTSService() + frames_received = await run_test(tts, frames_to_send=_make_frames_no_sleep()) + _assert_group_ordering(frames_received[0], _GROUPS) + + +@pytest.mark.asyncio +async def test_websocket_tts_with_pause_frame_ordering(): + """WebSocket TTS services with pause_frame_processing=True.""" + tts = MockWebSocketPauseTTSService() + frames_received = await run_test(tts, frames_to_send=_make_frames_no_sleep()) + _assert_group_ordering(frames_received[0], _GROUPS) + + +if __name__ == "__main__": + unittest.main() From 2836b1ea7e7e6bc70e61c63551c8a3f526871f02 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 10:07:25 -0300 Subject: [PATCH 107/159] Fixing the frame ordering of the AggregatedTextFrame. --- src/pipecat/services/tts_service.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index 303072373..dd27314f5 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -1006,7 +1006,12 @@ class TTSService(AIService): # Route AggregatedTextFrame through the serialization queue so it is emitted # immediately before the TTSStartedFrame of the audio context it describes, # rather than racing ahead of audio frames from a previous context. - await self._serialization_queue.put(src_frame) + if not self.audio_context_available(context_id): + await self._serialization_queue.put(src_frame) + # Otherwise, if the context already exists, we append the AggregatedTextFrame + # to the existing context queue. + else: + await self.append_to_audio_context(context_id, src_frame) # Note: Text transformations are meant to only affect the text sent to the TTS for # TTS-specific purposes. This allows for explicit TTS modifications (e.g., inserting From 6841c0719bc2fc416c96879f92b8a6a25b40c06a Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 10:12:01 -0300 Subject: [PATCH 108/159] Always appending TTSTextFrame to the audio context. --- src/pipecat/services/tts_service.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index dd27314f5..f16c66191 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -1060,11 +1060,8 @@ class TTSService(AIService): # Only override append_to_context if explicitly set if append_tts_text_to_context is not None: frame.append_to_context = append_tts_text_to_context - # For services using the audio context we are appending to the context, so it preserves the ordering. - if self.audio_context_available(context_id): - await self.append_to_audio_context(context_id, frame) - else: - await self.push_frame(frame) + # Appending to the context, so it preserves the ordering. + await self.append_to_audio_context(context_id, frame) async def tts_process_generator( self, context_id: str, generator: AsyncGenerator[Frame | None, None] From 8f6dfc477731b1ca0122795b0ab19ac798dfd058 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 10:26:58 -0300 Subject: [PATCH 109/159] Mentioning the frame order fix in the changelog. --- changelog/4075.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4075.fixed.md diff --git a/changelog/4075.fixed.md b/changelog/4075.fixed.md new file mode 100644 index 000000000..97870d4bb --- /dev/null +++ b/changelog/4075.fixed.md @@ -0,0 +1 @@ +- Fixed TTS frame ordering so that non-system frames always arrive in correct order relative to the `TTSStartedFrame`/`TTSAudioRawFrame`/`TTSStoppedFrame` sequence. Previously these frames could race ahead of or behind audio context frames, producing out-of-order output downstream. From 0be40846834d8937f04712ca7a2348d800fdb90d Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 12:29:32 -0400 Subject: [PATCH 110/159] Fix bug resulting in `SyncParallelPipeline` breaking the Whisker debugger --- src/pipecat/pipeline/sync_parallel_pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/pipeline/sync_parallel_pipeline.py b/src/pipecat/pipeline/sync_parallel_pipeline.py index cb3f1bbe0..48d66d3b8 100644 --- a/src/pipecat/pipeline/sync_parallel_pipeline.py +++ b/src/pipecat/pipeline/sync_parallel_pipeline.py @@ -184,7 +184,7 @@ class SyncParallelPipeline(BasePipeline): Returns: The list of entry processors. """ - return self._sources + return [s["processor"] for s in self._sources] def processors_with_metrics(self) -> List[FrameProcessor]: """Collect processors that can generate metrics from all parallel pipelines. From 463db59bb5b1cda95cc697b0b2aabd2d70240571 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 13:40:26 -0400 Subject: [PATCH 111/159] Minor comment typo fix --- src/pipecat/pipeline/sync_parallel_pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/pipeline/sync_parallel_pipeline.py b/src/pipecat/pipeline/sync_parallel_pipeline.py index 48d66d3b8..3597b0e9e 100644 --- a/src/pipecat/pipeline/sync_parallel_pipeline.py +++ b/src/pipecat/pipeline/sync_parallel_pipeline.py @@ -225,7 +225,7 @@ class SyncParallelPipeline(BasePipeline): # this element won't work. Since, we know it should be synchronous we # push a SyncFrame. Since frames are ordered we know this frame will be # pushed after the synchronous processor has pushed its data allowing us - # to synchrnonize all the internal pipelines by waiting for the + # to synchronize all the internal pipelines by waiting for the # SyncFrame in all of them. async def wait_for_sync( obj, main_queue: asyncio.Queue, frame: Frame, direction: FrameDirection From 1ede8460a2d775ea9580f53af0f7a772b39c7df6 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 14:17:35 -0400 Subject: [PATCH 112/159] Fix SyncParallelPipeline race condition with concurrent SystemFrame processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FrameProcessor two-queue architecture processes SystemFrames and non-SystemFrames on separate concurrent async tasks. Both paths called SyncParallelPipeline.process_frame(), which used the same per-pipeline sink queues. A SystemFrame's wait_for_sync could steal frames from a concurrent non-SystemFrame's wait_for_sync, corrupting synchronization and stalling the pipeline. This was triggered by the auto-embedded RTVI processor (added in v0.0.101) which floods OutputTransportMessageUrgentFrame SystemFrames through the pipeline during LLM responses. Fix: SystemFrames (except EndFrame) now take a fast path — passed through internal pipelines and pushed downstream directly without touching the sink queues or drain logic. EndFrame retains the full drain behavior as a lifecycle frame. --- .../pipeline/sync_parallel_pipeline.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/pipecat/pipeline/sync_parallel_pipeline.py b/src/pipecat/pipeline/sync_parallel_pipeline.py index 3597b0e9e..00af35bbf 100644 --- a/src/pipecat/pipeline/sync_parallel_pipeline.py +++ b/src/pipecat/pipeline/sync_parallel_pipeline.py @@ -221,6 +221,20 @@ class SyncParallelPipeline(BasePipeline): """ await super().process_frame(frame, direction) + # SystemFrames (but not EndFrame) are simply passed through all + # internal pipelines without draining queued output. This avoids + # the race condition where a SystemFrame's wait_for_sync steals + # frames from a concurrent non-SystemFrame's wait_for_sync. + if isinstance(frame, SystemFrame) and not isinstance(frame, EndFrame): + if direction == FrameDirection.UPSTREAM: + for s in self._sinks: + await s["processor"].process_frame(frame, direction) + elif direction == FrameDirection.DOWNSTREAM: + for s in self._sources: + await s["processor"].process_frame(frame, direction) + await self.push_frame(frame, direction) + return + # The last processor of each pipeline needs to be synchronous otherwise # this element won't work. Since, we know it should be synchronous we # push a SyncFrame. Since frames are ordered we know this frame will be @@ -235,12 +249,12 @@ class SyncParallelPipeline(BasePipeline): await processor.process_frame(frame, direction) - if isinstance(frame, (SystemFrame, EndFrame)): + if isinstance(frame, EndFrame): new_frame = await queue.get() - if isinstance(new_frame, (SystemFrame, EndFrame)): + if isinstance(new_frame, EndFrame): await main_queue.put(new_frame) else: - while not isinstance(new_frame, (SystemFrame, EndFrame)): + while not isinstance(new_frame, EndFrame): await main_queue.put(new_frame) queue.task_done() new_frame = await queue.get() From 0f1ff16af12e26e9cf05e4c6b2add398ff6f96b5 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 15:15:45 -0400 Subject: [PATCH 113/159] Add sync_with_audio support for OutputImageRawFrame Add a `sync_with_audio` field to `OutputImageRawFrame` that routes image frames through the audio queue in the output transport, ensuring images are only displayed after all preceding audio has been sent. This enables proper audio/image synchronization in pipelines like the calendar month narration example. Update the 05-sync-speech-and-image example to use an `ImageAudioSync` processor that sets this flag on image frames. --- .../foundational/05-sync-speech-and-image.py | 18 +++++++++++++++++- src/pipecat/frames/frames.py | 8 ++++++++ src/pipecat/transports/base_output.py | 6 +++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index 1e4cf34a4..35f8f411c 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -16,6 +16,7 @@ from pipecat.frames.frames import ( Frame, LLMContextFrame, LLMFullResponseStartFrame, + OutputImageRawFrame, TextFrame, ) from pipecat.pipeline.pipeline import Pipeline @@ -44,6 +45,18 @@ class MonthFrame(DataFrame): return f"{self.name}(month: {self.month})" +class ImageAudioSync(FrameProcessor): + """Marks output image frames to be synchronized with audio playback.""" + + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + + if isinstance(frame, OutputImageRawFrame): + frame.sync_with_audio = True + + await self.push_frame(frame, direction) + + class MonthPrepender(FrameProcessor): def __init__(self): super().__init__() @@ -129,7 +142,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): sentence_aggregator, # Aggregates LLM output into full sentences SyncParallelPipeline( # Run pipelines in parallel aggregating the result [month_prepender, tts], # Create "Month: sentence" and output audio - [imagegen], # Generate image + [ + imagegen, # Generate image + ImageAudioSync(), # Mark image as needing sync output w/audio + ], ), transport.output(), # Transport output ] diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index fbc01c488..7107cfd97 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -274,8 +274,16 @@ class OutputImageRawFrame(DataFrame, ImageRawFrame): An image that will be shown by the transport. If the transport supports multiple video destinations (e.g. multiple video tracks) the destination name can be specified in transport_destination. + + Parameters: + sync_with_audio: If True, the image is queued with audio frames so + it is only displayed after all preceding audio has been sent. + Defaults to False (image is displayed immediately when the output + transport receives it). """ + sync_with_audio: bool = field(default=False, init=False) + def __str__(self): pts = format_pts(self.pts) return f"{self.name}(pts: {pts}, destination: {self.transport_destination}, size: {self.size}, format: {self.format})" diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index e14ae3828..01af97be8 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -569,7 +569,11 @@ class BaseOutputTransport(FrameProcessor): if not self._params.video_out_enabled: return - if self._params.video_out_is_live and isinstance(frame, OutputImageRawFrame): + if isinstance(frame, OutputImageRawFrame) and frame.sync_with_audio: + # Route through the audio queue so the image is only + # displayed after all preceding audio has been sent. + await self._audio_queue.put(frame) + elif self._params.video_out_is_live and isinstance(frame, OutputImageRawFrame): await self._video_queue.put(frame) elif isinstance(frame, OutputImageRawFrame): await self._set_video_image(frame) From c3d6e965d80f10f491d0cf62bec7a57663c79fd5 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 18:43:54 -0400 Subject: [PATCH 114/159] Use TextAggregationMode.TOKEN in the 05-sync-speech-and-image example since the SentenceAggregator already provides complete sentences. --- examples/foundational/05-sync-speech-and-image.py | 5 +++++ src/pipecat/processors/frame_processor.py | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index 35f8f411c..b896261ae 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -31,6 +31,7 @@ from pipecat.runner.utils import create_transport from pipecat.services.cartesia.tts import CartesiaHttpTTSService from pipecat.services.fal.image import FalImageGenService from pipecat.services.openai.llm import OpenAILLMService +from pipecat.services.tts_service import TextAggregationMode from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.daily.transport import DailyParams @@ -114,6 +115,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): settings=CartesiaHttpTTSService.Settings( voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady ), + # No need to aggregate by sentences (the default), as we already know we're getting full sentences + # (Otherwise the service will unnecessarily wait for follow-up input to confirm the sentence is complete, + # which, sadly, actually breaks the synchronization mechanism) + text_aggregation_mode=TextAggregationMode.TOKEN, ) imagegen = FalImageGenService( diff --git a/src/pipecat/processors/frame_processor.py b/src/pipecat/processors/frame_processor.py index f3d9fbdea..a74ee4f81 100644 --- a/src/pipecat/processors/frame_processor.py +++ b/src/pipecat/processors/frame_processor.py @@ -633,7 +633,7 @@ class FrameProcessor(BaseObject): async def pause_processing_frames(self): """Pause processing of queued frames.""" - logger.trace(f"{self}: pausing frame processing") + logger.debug(f"{self}: pausing frame processing") self.__should_block_frames = True if self.__process_event: self.__process_event.clear() @@ -647,7 +647,7 @@ class FrameProcessor(BaseObject): async def resume_processing_frames(self): """Resume processing of queued frames.""" - logger.trace(f"{self}: resuming frame processing") + logger.debug(f"{self}: resuming frame processing") if self.__process_event: self.__process_event.set() From ba779f920fcd2f7a3f8f48218299911ed782785d Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 21:04:16 -0400 Subject: [PATCH 115/159] Revert a couple of logs that were changed from `trace` to `debug` just for debugging --- src/pipecat/processors/frame_processor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipecat/processors/frame_processor.py b/src/pipecat/processors/frame_processor.py index a74ee4f81..f3d9fbdea 100644 --- a/src/pipecat/processors/frame_processor.py +++ b/src/pipecat/processors/frame_processor.py @@ -633,7 +633,7 @@ class FrameProcessor(BaseObject): async def pause_processing_frames(self): """Pause processing of queued frames.""" - logger.debug(f"{self}: pausing frame processing") + logger.trace(f"{self}: pausing frame processing") self.__should_block_frames = True if self.__process_event: self.__process_event.clear() @@ -647,7 +647,7 @@ class FrameProcessor(BaseObject): async def resume_processing_frames(self): """Resume processing of queued frames.""" - logger.debug(f"{self}: resuming frame processing") + logger.trace(f"{self}: resuming frame processing") if self.__process_event: self.__process_event.set() From 5e7639812aee582ab9f1435cfdfa5b25ba318706 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 22:09:44 -0400 Subject: [PATCH 116/159] Add ImageBeforeAudioReorderer to sync-speech-and-image example Add a processor after SyncParallelPipeline that ensures each image frame precedes its corresponding TTS audio frames. SyncParallelPipeline batches them together but doesn't guarantee branch ordering. The reorderer detects when TTS frames arrive before their image (via context_id tracking) and holds them until the image arrives. Also rename ImageAudioSync to MarkImageForPlaybackSync for clarity. --- .../foundational/05-sync-speech-and-image.py | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index b896261ae..646235e7c 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -12,12 +12,17 @@ from dotenv import load_dotenv from loguru import logger from pipecat.frames.frames import ( + AggregatedTextFrame, DataFrame, Frame, LLMContextFrame, LLMFullResponseStartFrame, OutputImageRawFrame, TextFrame, + TTSAudioRawFrame, + TTSStartedFrame, + TTSStoppedFrame, + TTSTextFrame, ) from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -46,7 +51,7 @@ class MonthFrame(DataFrame): return f"{self.name}(month: {self.month})" -class ImageAudioSync(FrameProcessor): +class MarkImageForPlaybackSync(FrameProcessor): """Marks output image frames to be synchronized with audio playback.""" async def process_frame(self, frame: Frame, direction: FrameDirection): @@ -58,6 +63,61 @@ class ImageAudioSync(FrameProcessor): await self.push_frame(frame, direction) +class ImageBeforeAudioReorderer(FrameProcessor): + """Ensures each image frame precedes its corresponding TTS audio frames. + + SyncParallelPipeline guarantees that each image is in the same synchronized + batch as its audio, but doesn't guarantee which branch's output comes first. + This processor detects when TTS frames arrive before their image and holds + them until the image arrives. + + All frames pass through immediately unless we detect an ordering problem: + TTS frames arrived without a preceding image for the current batch (identified + by context_id). In that case, the TTS frames are held until the next image + frame, which is pushed first. + """ + + def __init__(self): + super().__init__() + self._held_tts_frames = [] + self._seen_image = False + self._current_context_id = None + + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + + if isinstance(frame, OutputImageRawFrame): + self._seen_image = True + if self._held_tts_frames: + # Image arrived after TTS frames — push image first, then release held frames. + logger.debug("ImageBeforeAudioReorderer: reordered — moved image before audio") + await self.push_frame(frame, direction) + for f in self._held_tts_frames: + await self.push_frame(f, direction) + self._held_tts_frames = [] + else: + logger.debug( + "ImageBeforeAudioReorderer: no reorder needed — image was already first" + ) + await self.push_frame(frame, direction) + elif isinstance( + frame, + (AggregatedTextFrame, TTSStartedFrame, TTSAudioRawFrame, TTSStoppedFrame, TTSTextFrame), + ): + # A new context_id means a new batch — reset image tracking. + context_id = frame.context_id + if context_id and context_id != self._current_context_id: + self._current_context_id = context_id + self._seen_image = False + + if self._seen_image: + await self.push_frame(frame, direction) + else: + self._held_tts_frames.append(frame) + else: + await self.push_frame(frame, direction) + + class MonthPrepender(FrameProcessor): def __init__(self): super().__init__() @@ -149,9 +209,10 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): [month_prepender, tts], # Create "Month: sentence" and output audio [ imagegen, # Generate image - ImageAudioSync(), # Mark image as needing sync output w/audio + MarkImageForPlaybackSync(), # Mark image as needing sync w/audio during playback ], ), + ImageBeforeAudioReorderer(), # Ensure each image precedes its audio (important for playback) transport.output(), # Transport output ] ) From 61ff53f2b968c8acc435c8492e2027fa8035ab0e Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 22:15:32 -0400 Subject: [PATCH 117/159] Add changelog entries for PR #4029 --- changelog/4029.added.md | 1 + changelog/4029.fixed.2.md | 1 + changelog/4029.fixed.md | 1 + 3 files changed, 3 insertions(+) create mode 100644 changelog/4029.added.md create mode 100644 changelog/4029.fixed.2.md create mode 100644 changelog/4029.fixed.md diff --git a/changelog/4029.added.md b/changelog/4029.added.md new file mode 100644 index 000000000..ba3714483 --- /dev/null +++ b/changelog/4029.added.md @@ -0,0 +1 @@ +- Added `sync_with_audio` field to `OutputImageRawFrame`. When set to `True`, the output transport queues image frames with audio so they are displayed only after all preceding audio has been sent, enabling synchronized audio/image playback. diff --git a/changelog/4029.fixed.2.md b/changelog/4029.fixed.2.md new file mode 100644 index 000000000..faf54592e --- /dev/null +++ b/changelog/4029.fixed.2.md @@ -0,0 +1 @@ +- Fixed TTS frame ordering when an audio context is active. Pass-through frames are now routed through the audio context queue so they stay properly ordered with TTS audio frames. Previously, a frame that preceded some incoming text to a TTS service could "jump the queue", getting ahead of the outgoing audio. diff --git a/changelog/4029.fixed.md b/changelog/4029.fixed.md new file mode 100644 index 000000000..57930a997 --- /dev/null +++ b/changelog/4029.fixed.md @@ -0,0 +1 @@ +- Fixed `SyncParallelPipeline` race condition where concurrent SystemFrame processing (e.g. from RTVI) could corrupt sink queues and cause deadlocks. SystemFrames now take a fast path that passes them through without draining queued output. From 26fc238eb7e558ee414e3faf00bb0b3456b3d546 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 13 Mar 2026 22:20:53 -0400 Subject: [PATCH 118/159] Add changelog entry for Whisker debugger fix --- changelog/4029.fixed.3.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4029.fixed.3.md diff --git a/changelog/4029.fixed.3.md b/changelog/4029.fixed.3.md new file mode 100644 index 000000000..3c812d590 --- /dev/null +++ b/changelog/4029.fixed.3.md @@ -0,0 +1 @@ +- Fixed `SyncParallelPipeline` breaking the Whisker debugger. From d702ebd6a29519af2ea50f5a6b8d208901342139 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 16 Mar 2026 09:59:13 -0400 Subject: [PATCH 119/159] Add frame_order parameter to SyncParallelPipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a FrameOrder enum with ARRIVAL (default, existing behavior) and PIPELINE (pushes frames in pipeline definition order). This lets callers guarantee output ordering between parallel pipelines — e.g. ensuring image frames precede audio frames — without needing a separate reordering processor downstream. Updates the 05-sync-speech-and-image example to use FrameOrder.PIPELINE, removing the ImageBeforeAudioReorderer class entirely. --- changelog/4029.added.2.md | 1 + .../foundational/05-sync-speech-and-image.py | 73 ++--------- .../pipeline/sync_parallel_pipeline.py | 111 +++++++++++++---- tests/test_sync_parallel_pipeline.py | 117 ++++++++++++++++++ 4 files changed, 214 insertions(+), 88 deletions(-) create mode 100644 changelog/4029.added.2.md create mode 100644 tests/test_sync_parallel_pipeline.py diff --git a/changelog/4029.added.2.md b/changelog/4029.added.2.md new file mode 100644 index 000000000..1ae691442 --- /dev/null +++ b/changelog/4029.added.2.md @@ -0,0 +1 @@ +- Added `frame_order` parameter to `SyncParallelPipeline`. Set `frame_order=FrameOrder.PIPELINE` to push synchronized output frames in pipeline definition order (all frames from the first pipeline, then the second, etc.) instead of the default arrival order. diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index 646235e7c..f0e2ff9c7 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -12,21 +12,16 @@ from dotenv import load_dotenv from loguru import logger from pipecat.frames.frames import ( - AggregatedTextFrame, DataFrame, Frame, LLMContextFrame, LLMFullResponseStartFrame, OutputImageRawFrame, TextFrame, - TTSAudioRawFrame, - TTSStartedFrame, - TTSStoppedFrame, - TTSTextFrame, ) from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.sync_parallel_pipeline import SyncParallelPipeline +from pipecat.pipeline.sync_parallel_pipeline import FrameOrder, SyncParallelPipeline from pipecat.pipeline.task import PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.sentence import SentenceAggregator @@ -63,61 +58,6 @@ class MarkImageForPlaybackSync(FrameProcessor): await self.push_frame(frame, direction) -class ImageBeforeAudioReorderer(FrameProcessor): - """Ensures each image frame precedes its corresponding TTS audio frames. - - SyncParallelPipeline guarantees that each image is in the same synchronized - batch as its audio, but doesn't guarantee which branch's output comes first. - This processor detects when TTS frames arrive before their image and holds - them until the image arrives. - - All frames pass through immediately unless we detect an ordering problem: - TTS frames arrived without a preceding image for the current batch (identified - by context_id). In that case, the TTS frames are held until the next image - frame, which is pushed first. - """ - - def __init__(self): - super().__init__() - self._held_tts_frames = [] - self._seen_image = False - self._current_context_id = None - - async def process_frame(self, frame: Frame, direction: FrameDirection): - await super().process_frame(frame, direction) - - if isinstance(frame, OutputImageRawFrame): - self._seen_image = True - if self._held_tts_frames: - # Image arrived after TTS frames — push image first, then release held frames. - logger.debug("ImageBeforeAudioReorderer: reordered — moved image before audio") - await self.push_frame(frame, direction) - for f in self._held_tts_frames: - await self.push_frame(f, direction) - self._held_tts_frames = [] - else: - logger.debug( - "ImageBeforeAudioReorderer: no reorder needed — image was already first" - ) - await self.push_frame(frame, direction) - elif isinstance( - frame, - (AggregatedTextFrame, TTSStartedFrame, TTSAudioRawFrame, TTSStoppedFrame, TTSTextFrame), - ): - # A new context_id means a new batch — reset image tracking. - context_id = frame.context_id - if context_id and context_id != self._current_context_id: - self._current_context_id = context_id - self._seen_image = False - - if self._seen_image: - await self.push_frame(frame, direction) - else: - self._held_tts_frames.append(frame) - else: - await self.push_frame(frame, direction) - - class MonthPrepender(FrameProcessor): def __init__(self): super().__init__() @@ -197,22 +137,27 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): # that, each pipeline runs concurrently and `SyncParallelPipeline` will # wait for the input frame to be processed. # + # We use `FrameOrder.PIPELINE` so that each synchronized batch of output + # frames is pushed in the order the pipelines are listed: image first, + # then audio. This ensures the transport receives the image before the + # audio frames it should accompany. + # # Note that `SyncParallelPipeline` requires the last processor in each # of the pipelines to be synchronous. In this case, we use - # `CartesiaHttpTTSService` and `FalImageGenService` which make HTTP + # `FalImageGenService` and `CartesiaHttpTTSService` which make HTTP # requests and wait for the response. pipeline = Pipeline( [ llm, # LLM sentence_aggregator, # Aggregates LLM output into full sentences SyncParallelPipeline( # Run pipelines in parallel aggregating the result - [month_prepender, tts], # Create "Month: sentence" and output audio [ imagegen, # Generate image MarkImageForPlaybackSync(), # Mark image as needing sync w/audio during playback ], + [month_prepender, tts], # Create "Month: sentence" and output audio + frame_order=FrameOrder.PIPELINE, ), - ImageBeforeAudioReorderer(), # Ensure each image precedes its audio (important for playback) transport.output(), # Transport output ] ) diff --git a/src/pipecat/pipeline/sync_parallel_pipeline.py b/src/pipecat/pipeline/sync_parallel_pipeline.py index 00af35bbf..9870f03f3 100644 --- a/src/pipecat/pipeline/sync_parallel_pipeline.py +++ b/src/pipecat/pipeline/sync_parallel_pipeline.py @@ -13,6 +13,7 @@ and prevent duplicate processing. import asyncio from dataclasses import dataclass +from enum import Enum from itertools import chain from typing import List @@ -24,6 +25,25 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.processors.frame_processor import FrameDirection, FrameProcessor, FrameProcessorSetup +class FrameOrder(Enum): + """Controls the order in which synchronized frames are pushed downstream. + + When multiple parallel pipelines produce output for the same input frame, + this setting determines the order in which those output frames are pushed. + + Attributes: + ARRIVAL: Frames are pushed in the order they arrive from any pipeline. + This is the default and matches the behavior of prior versions. + PIPELINE: Frames are pushed in pipeline definition order — all frames + from the first pipeline are pushed, then all frames from the second + pipeline, and so on. Useful when the relative ordering between + pipelines matters (e.g. ensuring image frames precede audio frames). + """ + + ARRIVAL = "arrival" + PIPELINE = "pipeline" + + @dataclass class SyncFrame(ControlFrame): """Control frame used to synchronize parallel pipeline processing. @@ -109,20 +129,30 @@ class SyncParallelPipeline(BasePipeline): The pipeline uses SyncFrame control frames to coordinate between parallel paths and ensure all paths have completed processing before moving to the next frame. + + By default, output frames are pushed in the order they arrive from any pipeline + (``FrameOrder.ARRIVAL``). Set ``frame_order=FrameOrder.PIPELINE`` to push frames + in pipeline definition order instead — all output from the first pipeline, then + the second, and so on. """ - def __init__(self, *args): + def __init__(self, *args, frame_order: FrameOrder = FrameOrder.ARRIVAL): """Initialize the synchronous parallel pipeline. Args: - *args: Variable number of processor lists, each representing a parallel pipeline path. - Each argument should be a list of FrameProcessor instances. + *args: Variable number of processor lists, each representing a parallel + pipeline path. Each argument should be a list of FrameProcessor instances. + frame_order: Controls the order in which synchronized output frames are + pushed. ``FrameOrder.ARRIVAL`` (default) pushes frames in the order they arrive. + ``FrameOrder.PIPELINE`` pushes all frames from the first pipeline + before the second, and so on. Raises: Exception: If no arguments are provided. TypeError: If any argument is not a list of processors. """ super().__init__() + self._frame_order = frame_order if len(args) == 0: raise Exception(f"SyncParallelPipeline needs at least one argument") @@ -215,6 +245,11 @@ class SyncParallelPipeline(BasePipeline): to maintain proper ordering and prevent duplicate processing. Uses SyncFrame control frames to coordinate between parallel paths. + When ``frame_order`` is ``FrameOrder.ARRIVAL``, output frames are pushed in + the order they arrive from any pipeline (via a shared queue). When it is + ``FrameOrder.PIPELINE``, each pipeline collects its output into a separate + list and the lists are drained in pipeline definition order. + Args: frame: The frame to process. direction: The direction of frame flow. @@ -235,60 +270,88 @@ class SyncParallelPipeline(BasePipeline): await self.push_frame(frame, direction) return + use_pipeline_order = self._frame_order == FrameOrder.PIPELINE + # The last processor of each pipeline needs to be synchronous otherwise - # this element won't work. Since, we know it should be synchronous we + # this element won't work. Since we know it should be synchronous we # push a SyncFrame. Since frames are ordered we know this frame will be # pushed after the synchronous processor has pushed its data allowing us # to synchronize all the internal pipelines by waiting for the # SyncFrame in all of them. + # + # In ARRIVAL mode, output frames are put onto a shared main_queue as + # they arrive. In PIPELINE mode, they are accumulated in a per-pipeline + # list and returned so the caller can drain them in definition order. async def wait_for_sync( obj, main_queue: asyncio.Queue, frame: Frame, direction: FrameDirection - ): + ) -> list[Frame]: processor = obj["processor"] queue = obj["queue"] + output_frames: list[Frame] = [] await processor.process_frame(frame, direction) if isinstance(frame, EndFrame): new_frame = await queue.get() if isinstance(new_frame, EndFrame): - await main_queue.put(new_frame) + if use_pipeline_order: + output_frames.append(new_frame) + else: + await main_queue.put(new_frame) else: while not isinstance(new_frame, EndFrame): - await main_queue.put(new_frame) + if use_pipeline_order: + output_frames.append(new_frame) + else: + await main_queue.put(new_frame) queue.task_done() new_frame = await queue.get() else: await processor.process_frame(SyncFrame(), direction) new_frame = await queue.get() while not isinstance(new_frame, SyncFrame): - await main_queue.put(new_frame) + if use_pipeline_order: + output_frames.append(new_frame) + else: + await main_queue.put(new_frame) queue.task_done() new_frame = await queue.get() + return output_frames + if direction == FrameDirection.UPSTREAM: # If we get an upstream frame we process it in each sink. - await asyncio.gather( + frames_per_pipeline = await asyncio.gather( *[wait_for_sync(s, self._up_queue, frame, direction) for s in self._sinks] ) elif direction == FrameDirection.DOWNSTREAM: # If we get a downstream frame we process it in each source. - await asyncio.gather( + frames_per_pipeline = await asyncio.gather( *[wait_for_sync(s, self._down_queue, frame, direction) for s in self._sources] ) - seen_ids = set() - while not self._up_queue.empty(): - frame = await self._up_queue.get() - if frame.id not in seen_ids: - await self.push_frame(frame, FrameDirection.UPSTREAM) - seen_ids.add(frame.id) - self._up_queue.task_done() + if use_pipeline_order: + # Push frames in pipeline definition order, deduplicating by id. + seen_ids = set() + for pipeline_frames in frames_per_pipeline: + for f in pipeline_frames: + if f.id not in seen_ids: + await self.push_frame(f, direction) + seen_ids.add(f.id) + else: + # ARRIVAL mode: drain the shared queues in the order frames arrived. + seen_ids = set() + while not self._up_queue.empty(): + frame = await self._up_queue.get() + if frame.id not in seen_ids: + await self.push_frame(frame, FrameDirection.UPSTREAM) + seen_ids.add(frame.id) + self._up_queue.task_done() - seen_ids = set() - while not self._down_queue.empty(): - frame = await self._down_queue.get() - if frame.id not in seen_ids: - await self.push_frame(frame, FrameDirection.DOWNSTREAM) - seen_ids.add(frame.id) - self._down_queue.task_done() + seen_ids = set() + while not self._down_queue.empty(): + frame = await self._down_queue.get() + if frame.id not in seen_ids: + await self.push_frame(frame, FrameDirection.DOWNSTREAM) + seen_ids.add(frame.id) + self._down_queue.task_done() diff --git a/tests/test_sync_parallel_pipeline.py b/tests/test_sync_parallel_pipeline.py new file mode 100644 index 000000000..6c6faf7c7 --- /dev/null +++ b/tests/test_sync_parallel_pipeline.py @@ -0,0 +1,117 @@ +# +# Copyright (c) 2024-2026, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +import unittest +from dataclasses import dataclass + +from pipecat.frames.frames import Frame, TextFrame +from pipecat.pipeline.sync_parallel_pipeline import FrameOrder, SyncParallelPipeline +from pipecat.processors.filters.identity_filter import IdentityFilter +from pipecat.processors.frame_processor import FrameDirection, FrameProcessor +from pipecat.tests.utils import run_test + + +@dataclass +class TaggedFrame(Frame): + """A simple tagged frame for testing pipeline ordering.""" + + tag: str = "" + + def __str__(self): + return f"{self.name}(tag: {self.tag})" + + +class EmitTaggedFrameProcessor(FrameProcessor): + """Emits a TaggedFrame for every TextFrame it receives. + + Used to produce distinguishable output from different pipelines so tests + can verify ordering. + """ + + def __init__(self, tag: str, *, delay: float = 0, **kwargs): + super().__init__(**kwargs) + self._tag = tag + self._delay = delay + + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + + if isinstance(frame, TextFrame): + if self._delay > 0: + await asyncio.sleep(self._delay) + await self.push_frame(TaggedFrame(tag=self._tag)) + else: + await self.push_frame(frame, direction) + + +class TestSyncParallelPipeline(unittest.IsolatedAsyncioTestCase): + async def test_dedup_multiple_frames(self): + """Identical frames from multiple paths should be deduplicated.""" + pipeline = SyncParallelPipeline([IdentityFilter()], [IdentityFilter()]) + + frames_to_send = [TextFrame(text="one"), TextFrame(text="two")] + expected_down_frames = [TextFrame, TextFrame] + await run_test( + pipeline, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + async def test_arrival_order(self): + """With FrameOrder.ARRIVAL, a slow first pipeline's frames should + arrive after a fast second pipeline's frames.""" + pipeline = SyncParallelPipeline( + [EmitTaggedFrameProcessor("slow", delay=0.05)], + [EmitTaggedFrameProcessor("fast")], + frame_order=FrameOrder.ARRIVAL, + ) + + frames_to_send = [TextFrame(text="one"), TextFrame(text="two")] + (down_frames, _) = await run_test( + pipeline, + frames_to_send=frames_to_send, + ) + + tags = [f.tag for f in down_frames if isinstance(f, TaggedFrame)] + assert tags == [ + "fast", + "slow", + "fast", + "slow", + ], f"Expected fast before slow in each batch, got {tags}" + + async def test_pipeline_order(self): + """With FrameOrder.PIPELINE and multiple input frames, each batch + should follow pipeline definition order regardless of processing speed.""" + pipeline = SyncParallelPipeline( + [EmitTaggedFrameProcessor("slow", delay=0.05)], + [EmitTaggedFrameProcessor("fast")], + frame_order=FrameOrder.PIPELINE, + ) + + frames_to_send = [TextFrame(text="one"), TextFrame(text="two")] + (down_frames, _) = await run_test( + pipeline, + frames_to_send=frames_to_send, + ) + + tags = [f.tag for f in down_frames if isinstance(f, TaggedFrame)] + assert tags == [ + "slow", + "fast", + "slow", + "fast", + ], f"Expected pipeline definition order (slow, fast) in each batch, got {tags}" + + async def test_default_is_arrival(self): + """The default frame_order should be ARRIVAL.""" + pipeline = SyncParallelPipeline([IdentityFilter()]) + assert pipeline._frame_order == FrameOrder.ARRIVAL + + +if __name__ == "__main__": + unittest.main() From 06f7da44f19b8fa949b8335273df3d3a970aabd8 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Wed, 18 Mar 2026 16:39:20 -0400 Subject: [PATCH 120/159] Clarify SyncParallelPipeline docstrings Rewrite docstrings to more clearly explain what SyncParallelPipeline does: hold all output until every parallel branch finishes, so frames produced in response to a single input are released together. --- .../pipeline/sync_parallel_pipeline.py | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/pipecat/pipeline/sync_parallel_pipeline.py b/src/pipecat/pipeline/sync_parallel_pipeline.py index 9870f03f3..148d29b25 100644 --- a/src/pipecat/pipeline/sync_parallel_pipeline.py +++ b/src/pipecat/pipeline/sync_parallel_pipeline.py @@ -4,11 +4,16 @@ # SPDX-License-Identifier: BSD 2-Clause License # -"""Synchronous parallel pipeline implementation for concurrent frame processing. +"""Synchronized parallel pipeline that holds output until all branches finish. -This module provides a pipeline that processes frames through multiple parallel -pipelines simultaneously, synchronizing their output to maintain frame ordering -and prevent duplicate processing. +A SyncParallelPipeline fans each inbound frame out to multiple parallel pipelines +and waits for every pipeline to finish processing before releasing any of the +resulting output frames. This ensures that all frames produced in response to a +single input frame are emitted together. + +System frames (except EndFrame) are exempt from this synchronization — they pass +straight through without waiting, since they are expected to race ahead of +regular data frames. """ import asyncio @@ -46,20 +51,21 @@ class FrameOrder(Enum): @dataclass class SyncFrame(ControlFrame): - """Control frame used to synchronize parallel pipeline processing. + """Sentinel frame used to detect when a parallel pipeline has finished processing. - This frame is sent through parallel pipelines to determine when the - internal pipelines have finished processing a batch of frames. + After sending a real frame into a parallel pipeline, a SyncFrame is sent + behind it. When the SyncFrame emerges from the pipeline's output, we know + all output frames for the preceding input have been produced. """ pass class SyncParallelPipelineSource(FrameProcessor): - """Source processor for synchronous parallel pipeline processing. + """Bookend processor placed at the start of each parallel pipeline. - Routes frames to parallel pipelines and collects upstream responses - for synchronization purposes. + Forwards downstream frames into the pipeline and captures upstream frames + into a queue so the parent SyncParallelPipeline can release them later. """ def __init__(self, upstream_queue: asyncio.Queue): @@ -88,10 +94,11 @@ class SyncParallelPipelineSource(FrameProcessor): class SyncParallelPipelineSink(FrameProcessor): - """Sink processor for synchronous parallel pipeline processing. + """Bookend processor placed at the end of each parallel pipeline. - Collects downstream frames from parallel pipelines and routes - upstream frames back through the pipeline. + Captures downstream output frames into a queue so the parent + SyncParallelPipeline can release them later, and forwards upstream + frames back through the pipeline. """ def __init__(self, downstream_queue: asyncio.Queue): @@ -120,15 +127,20 @@ class SyncParallelPipelineSink(FrameProcessor): class SyncParallelPipeline(BasePipeline): - """Pipeline that processes frames through multiple parallel pipelines synchronously. + """Fans each input frame to parallel pipelines then holds output until every pipeline finishes. - Creates multiple parallel processing paths that all receive the same input frames - and produces synchronized output. Each parallel path is a separate pipeline that - processes frames independently, with synchronization points to ensure consistent - ordering and prevent duplicate frame processing. + For each inbound frame the pipeline: - The pipeline uses SyncFrame control frames to coordinate between parallel paths - and ensure all paths have completed processing before moving to the next frame. + 1. Sends the frame into every parallel pipeline. + 2. Sends a ``SyncFrame`` sentinel behind it in each pipeline. + 3. Waits until every pipeline has produced its ``SyncFrame``, meaning all + output for that input is ready. + 4. Releases the collected output frames (deduplicating by frame id, since + the same frame may emerge from more than one branch). + + System frames (except ``EndFrame``) bypass this mechanism entirely — they are + forwarded through each pipeline and pushed immediately, since system frames + are expected to race ahead of regular data frames. By default, output frames are pushed in the order they arrive from any pipeline (``FrameOrder.ARRIVAL``). Set ``frame_order=FrameOrder.PIPELINE`` to push frames @@ -239,16 +251,11 @@ class SyncParallelPipeline(BasePipeline): await asyncio.gather(*[p.cleanup() for p in self._pipelines]) async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process frames through all parallel pipelines with synchronization. + """Send a frame through all parallel pipelines and release output once all finish. - Distributes frames to all parallel pipelines and synchronizes their output - to maintain proper ordering and prevent duplicate processing. Uses SyncFrame - control frames to coordinate between parallel paths. - - When ``frame_order`` is ``FrameOrder.ARRIVAL``, output frames are pushed in - the order they arrive from any pipeline (via a shared queue). When it is - ``FrameOrder.PIPELINE``, each pipeline collects its output into a separate - list and the lists are drained in pipeline definition order. + System frames (except EndFrame) skip synchronization and pass straight + through. All other frames are fanned out to every pipeline, and output is + held until every pipeline signals completion (via SyncFrame). Args: frame: The frame to process. From 57fd29f0c4204ca5df58468108325394cb211a95 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 09:57:26 -0400 Subject: [PATCH 121/159] Remove changelog fragment that no longer applies after a rebase --- changelog/4029.fixed.2.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 changelog/4029.fixed.2.md diff --git a/changelog/4029.fixed.2.md b/changelog/4029.fixed.2.md deleted file mode 100644 index faf54592e..000000000 --- a/changelog/4029.fixed.2.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed TTS frame ordering when an audio context is active. Pass-through frames are now routed through the audio context queue so they stay properly ordered with TTS audio frames. Previously, a frame that preceded some incoming text to a TTS service could "jump the queue", getting ahead of the outgoing audio. From fd8c6c88bb76e1b7ab541eea9937679f2a23cfe1 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 11:13:17 -0300 Subject: [PATCH 122/159] Improvements to SarvamTTSService. --- src/pipecat/services/sarvam/tts.py | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index c926270de..0478f8faf 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -1031,23 +1031,6 @@ class SarvamTTSService(InterruptibleTTSService): except Exception as e: await self.push_error(error_msg=f"Error sending flush to Sarvam: {e}", exception=e) - async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM): - """Push a frame downstream with special handling for stop conditions. - - Args: - frame: The frame to push. - direction: The direction to push the frame. - """ - await super().push_frame(frame, direction) - - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process a frame and flush audio if it's the end of a full response.""" - await super().process_frame(frame, direction) - - # When the LLM finishes responding, flush any remaining text in Sarvam's buffer - if isinstance(frame, (LLMFullResponseEndFrame, EndFrame)): - await self.flush_audio() - async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]: """Apply a settings delta and resend config if voice changed.""" changed = await super()._update_settings(delta) @@ -1168,14 +1151,15 @@ class SarvamTTSService(InterruptibleTTSService): async for message in self._get_websocket(): if isinstance(message, str): msg = json.loads(message) + context_id = self.get_active_audio_context_id() if msg.get("type") == "audio": # Check for interruption before processing audio await self.stop_ttfb_metrics() audio = base64.b64decode(msg["data"]["audio"]) frame = TTSAudioRawFrame( - audio, self.sample_rate, 1, context_id=self.get_active_audio_context_id() + audio, self.sample_rate, 1, context_id=context_id ) - await self.push_frame(frame) + await self.append_to_audio_context(context_id, frame) elif msg.get("type") == "error": error_msg = msg["data"]["message"] await self.push_error(error_msg=f"TTS Error: {error_msg}") @@ -1183,8 +1167,7 @@ class SarvamTTSService(InterruptibleTTSService): # If it's a timeout error, the connection might need to be reset if "too long" in error_msg.lower() or "timeout" in error_msg.lower(): logger.warning("Connection timeout detected, service may need restart") - - await self.push_frame(ErrorFrame(error=f"TTS Error: {error_msg}")) + await self.append_to_audio_context(context_id, ErrorFrame(error=f"TTS Error: {error_msg}")) async def _keepalive_task_handler(self): """Handle keepalive messages to maintain WebSocket connection.""" From c4d1b89049c02ca1e61c91a469ea600b667bbe22 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 11:17:39 -0300 Subject: [PATCH 123/159] Adding changelog entry for the Sarvam fixes. --- changelog/4082.fixed.md | 1 + src/pipecat/services/sarvam/tts.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 changelog/4082.fixed.md diff --git a/changelog/4082.fixed.md b/changelog/4082.fixed.md new file mode 100644 index 000000000..e17e84fea --- /dev/null +++ b/changelog/4082.fixed.md @@ -0,0 +1 @@ +- Fixed `SarvamTTSService` audio and error frames now route through `append_to_audio_context()` instead of `push_frame()`, ensuring correct behavior with audio contexts and interruptions. diff --git a/src/pipecat/services/sarvam/tts.py b/src/pipecat/services/sarvam/tts.py index 0478f8faf..8bfeea8c6 100644 --- a/src/pipecat/services/sarvam/tts.py +++ b/src/pipecat/services/sarvam/tts.py @@ -1156,9 +1156,7 @@ class SarvamTTSService(InterruptibleTTSService): # Check for interruption before processing audio await self.stop_ttfb_metrics() audio = base64.b64decode(msg["data"]["audio"]) - frame = TTSAudioRawFrame( - audio, self.sample_rate, 1, context_id=context_id - ) + frame = TTSAudioRawFrame(audio, self.sample_rate, 1, context_id=context_id) await self.append_to_audio_context(context_id, frame) elif msg.get("type") == "error": error_msg = msg["data"]["message"] @@ -1167,7 +1165,9 @@ class SarvamTTSService(InterruptibleTTSService): # If it's a timeout error, the connection might need to be reset if "too long" in error_msg.lower() or "timeout" in error_msg.lower(): logger.warning("Connection timeout detected, service may need restart") - await self.append_to_audio_context(context_id, ErrorFrame(error=f"TTS Error: {error_msg}")) + await self.append_to_audio_context( + context_id, ErrorFrame(error=f"TTS Error: {error_msg}") + ) async def _keepalive_task_handler(self): """Handle keepalive messages to maintain WebSocket connection.""" From 39425a675a60bbffc8de1fc50cc485baafd7d344 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 11:32:56 -0300 Subject: [PATCH 124/159] Improvements to DeepgramSageMakerTTSService. --- .../services/deepgram/sagemaker/tts.py | 47 +++---------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/src/pipecat/services/deepgram/sagemaker/tts.py b/src/pipecat/services/deepgram/sagemaker/tts.py index 70be4a00e..6add3f951 100644 --- a/src/pipecat/services/deepgram/sagemaker/tts.py +++ b/src/pipecat/services/deepgram/sagemaker/tts.py @@ -20,18 +20,13 @@ from typing import Any, AsyncGenerator, Optional from loguru import logger from pipecat.frames.frames import ( - BotStoppedSpeakingFrame, CancelFrame, EndFrame, ErrorFrame, Frame, - InterruptionFrame, - LLMFullResponseEndFrame, StartFrame, TTSAudioRawFrame, - TTSStartedFrame, ) -from pipecat.processors.frame_processor import FrameDirection from pipecat.services.aws.sagemaker.bidi_client import SageMakerBidiClient from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import TTSService @@ -115,6 +110,7 @@ class DeepgramSageMakerTTSService(TTSService): super().__init__( sample_rate=sample_rate, + push_start_frame=True, push_stop_frames=True, pause_frame_processing=True, append_trailing_space=True, @@ -128,8 +124,6 @@ class DeepgramSageMakerTTSService(TTSService): self._client: Optional[SageMakerBidiClient] = None self._response_task: Optional[asyncio.Task] = None - self._context_id: Optional[str] = None - self._ttfb_started: bool = False def can_generate_metrics(self) -> bool: """Check if this service can generate processing metrics. @@ -166,20 +160,6 @@ class DeepgramSageMakerTTSService(TTSService): await super().cancel(frame) await self._disconnect() - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process frames with special handling for LLM response end. - - Args: - frame: The frame to process. - direction: The direction of frame processing. - """ - await super().process_frame(frame, direction) - - if isinstance(frame, (LLMFullResponseEndFrame, EndFrame)): - await self.flush_audio() - elif isinstance(frame, BotStoppedSpeakingFrame): - self._ttfb_started = False - async def _connect(self): """Connect to the SageMaker endpoint and start the BiDi session. @@ -305,7 +285,7 @@ class DeepgramSageMakerTTSService(TTSService): payload, self.sample_rate, 1, - context_id=self._context_id, + context_id=self.get_active_audio_context_id(), ) await self.push_frame(frame) @@ -316,15 +296,13 @@ class DeepgramSageMakerTTSService(TTSService): finally: logger.debug("TTS response processor stopped") - async def _handle_interruption(self, frame: InterruptionFrame, direction: FrameDirection): - """Handle interruption by sending Clear message to Deepgram. + async def on_audio_context_interrupted(self, context_id: str): + """Called when an audio context is cancelled due to an interruption. - The Clear message will clear Deepgram's internal text buffer and stop - sending audio, allowing for a new response to be generated. + Args: + context_id: The ID of the audio context that was interrupted, or + ``None`` if no context was active at the time. """ - await super()._handle_interruption(frame, direction) - self._ttfb_started = False - if self._client and self._client.is_active: try: await self._client.send_json({"type": "Clear"}) @@ -356,19 +334,8 @@ class DeepgramSageMakerTTSService(TTSService): the response processor). """ logger.debug(f"{self}: Generating TTS [{text}]") - try: - if not self.audio_context_available(context_id): - await self.create_audio_context(context_id) - if not self._ttfb_started: - await self.start_ttfb_metrics() - self._ttfb_started = True - yield TTSStartedFrame(context_id=context_id) - self._context_id = context_id - await self._client.send_json({"type": "Speak", "text": text}) - yield None - except Exception as e: yield ErrorFrame(error=f"Unknown error occurred: {e}") From d3ca034c4f14b2a138c68586e54275e89114263c Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 11:40:43 -0300 Subject: [PATCH 125/159] Routing the audio through the audio context queue. --- src/pipecat/services/deepgram/sagemaker/tts.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/deepgram/sagemaker/tts.py b/src/pipecat/services/deepgram/sagemaker/tts.py index 6add3f951..36541b4be 100644 --- a/src/pipecat/services/deepgram/sagemaker/tts.py +++ b/src/pipecat/services/deepgram/sagemaker/tts.py @@ -281,13 +281,14 @@ class DeepgramSageMakerTTSService(TTSService): except (UnicodeDecodeError, json.JSONDecodeError): # Not JSON — treat as raw audio bytes await self.stop_ttfb_metrics() + context_id = self.get_active_audio_context_id() frame = TTSAudioRawFrame( payload, self.sample_rate, 1, - context_id=self.get_active_audio_context_id(), + context_id=context_id, ) - await self.push_frame(frame) + await self.append_to_audio_context(context_id, frame) except asyncio.CancelledError: logger.debug("TTS response processor cancelled") From a0f311158dd70ab3ddd683a5f6ac530432bfff99 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Thu, 19 Mar 2026 11:46:49 -0300 Subject: [PATCH 126/159] Changelog entry for the DeepgramSageMakerTTSService improvements. --- changelog/4083.changed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4083.changed.md diff --git a/changelog/4083.changed.md b/changelog/4083.changed.md new file mode 100644 index 000000000..d9d46957a --- /dev/null +++ b/changelog/4083.changed.md @@ -0,0 +1 @@ +- `DeepgramSageMakerTTSService` now correctly routes audio through the base `TTSService` audio context queue. Audio frames are delivered via `append_to_audio_context()` instead of being pushed directly, enabling proper ordering, interruption handling, and start/stop frame lifecycle management. Interruptions now trigger a `Clear` message to Deepgram (flushing its text buffer) at the right time via `on_audio_context_interrupted`. From 348df9d4ced25de0f6c36fb0fb38e1b31743d28b Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 13:34:41 -0400 Subject: [PATCH 127/159] fix: remove redundant instructions override in run_inference The override would re-add `instructions` after the adapter had intentionally converted it to a developer message for empty contexts. Added a regression test. --- src/pipecat/services/openai/responses/llm.py | 4 --- tests/test_run_inference.py | 33 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index f62c6e758..4f0c81dc7 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -385,10 +385,6 @@ class OpenAIResponsesLLMService(LLMService): # Override for non-streaming params["stream"] = False - # Override instructions if caller provided one explicitly - if system_instruction is not None: - params["instructions"] = system_instruction - if max_tokens is not None: params["max_output_tokens"] = max_tokens diff --git a/tests/test_run_inference.py b/tests/test_run_inference.py index f67c725ae..cef13fb27 100644 --- a/tests/test_run_inference.py +++ b/tests/test_run_inference.py @@ -902,3 +902,36 @@ async def test_openai_responses_run_inference_max_tokens_override(): assert result == "Summary" call_kwargs = service._client.responses.create.call_args.kwargs assert call_kwargs["max_output_tokens"] == 200 + + +@pytest.mark.asyncio +async def test_openai_responses_run_inference_system_instruction_param_with_empty_context(): + """Test that system_instruction param becomes a developer message when context is empty. + + The Responses API rejects requests with instructions but no input items. + When run_inference is called with an explicit system_instruction and an + empty context, the instruction must become a developer message — not be + sent as the instructions parameter. + """ + with patch.object(OpenAIResponsesLLMService, "_create_client"): + service = OpenAIResponsesLLMService( + settings=OpenAIResponsesLLMService.Settings(model="gpt-4.1"), + ) + service._client = AsyncMock() + + context = LLMContext(messages=[]) + + mock_response = MagicMock() + mock_response.output_text = "Response" + service._client.responses.create = AsyncMock(return_value=mock_response) + + result = await service.run_inference( + context, system_instruction="Summarize the conversation" + ) + + assert result == "Response" + call_kwargs = service._client.responses.create.call_args.kwargs + assert call_kwargs["input"] == [ + {"role": "developer", "content": "Summarize the conversation"} + ] + assert "instructions" not in call_kwargs From a3431d3b0176c4740dc5f27fe5f59977ae1d54bc Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 13:55:26 -0400 Subject: [PATCH 128/159] fix: prefer _full_model_name over _settings.model in tracing The API-provided full model name is more specific than the user-provided model name (e.g. includes version/snapshot details). Reorder the lookup in _get_model_name and add a comment where the Responses service sets the field. --- src/pipecat/services/openai/responses/llm.py | 2 ++ src/pipecat/utils/tracing/service_decorators.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index 4f0c81dc7..8c7269323 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -292,6 +292,8 @@ class OpenAIResponsesLLMService(LLMService): model = getattr(response, "model", None) if model: + # This field is used by @traced_llm for more detailed + # model name in tracing spans self._full_model_name = model # Process any function calls diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index f4764d248..5292a5d3f 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -51,8 +51,10 @@ def _get_model_name(service) -> str: check all the places we used to store it. """ return ( - getattr(getattr(service, "_settings", None), "model", None) - or getattr(service, "_full_model_name", None) + # Some services store an API-response-provided detailed "full" name, + # which is distinct from the user-provided model name + getattr(service, "_full_model_name", None) + or getattr(getattr(service, "_settings", None), "model", None) or getattr(service, "model_name", None) or getattr(service, "_model_name", None) or "unknown" From 0533ea7b7fdd45a906cc05349109606b3314f236 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 14:19:10 -0400 Subject: [PATCH 129/159] refactor: use direct attribute access for typed stream events Replace getattr() calls with direct attribute access and isinstance() checks on the strongly-typed OpenAI SDK event models. --- src/pipecat/services/openai/responses/llm.py | 36 +++++++++----------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index 8c7269323..a412d9627 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -18,6 +18,7 @@ from openai.types.responses import ( ResponseCompletedEvent, ResponseFunctionCallArgumentsDeltaEvent, ResponseFunctionCallArgumentsDoneEvent, + ResponseFunctionToolCall, ResponseOutputItemAddedEvent, ResponseOutputItemDoneEvent, ResponseStreamEvent, @@ -251,11 +252,11 @@ class OpenAIResponsesLLMService(LLMService): elif isinstance(event, ResponseOutputItemAddedEvent): await self.stop_ttfb_metrics() item = event.item - if getattr(item, "type", None) == "function_call": - item_id = getattr(item, "id", "") or "" + if isinstance(item, ResponseFunctionToolCall): + item_id = item.id or "" function_calls[item_id] = { - "name": getattr(item, "name", ""), - "call_id": getattr(item, "call_id", ""), + "name": item.name, + "call_id": item.call_id, "arguments": "", } current_arguments[item_id] = "" @@ -272,29 +273,26 @@ class OpenAIResponsesLLMService(LLMService): elif isinstance(event, ResponseOutputItemDoneEvent): item = event.item - if getattr(item, "type", None) == "function_call": - item_id = getattr(item, "id", "") or "" + if isinstance(item, ResponseFunctionToolCall): + item_id = item.id or "" if item_id in function_calls: - function_calls[item_id]["name"] = getattr(item, "name", "") - function_calls[item_id]["call_id"] = getattr(item, "call_id", "") - function_calls[item_id]["arguments"] = getattr(item, "arguments", "") + function_calls[item_id]["name"] = item.name + function_calls[item_id]["call_id"] = item.call_id + function_calls[item_id]["arguments"] = item.arguments elif isinstance(event, ResponseCompletedEvent): response = event.response - usage = getattr(response, "usage", None) - if usage: + if response.usage: tokens = LLMTokenUsage( - prompt_tokens=getattr(usage, "input_tokens", 0), - completion_tokens=getattr(usage, "output_tokens", 0), - total_tokens=getattr(usage, "total_tokens", 0), + prompt_tokens=response.usage.input_tokens, + completion_tokens=response.usage.output_tokens, + total_tokens=response.usage.total_tokens, ) await self.start_llm_usage_metrics(tokens) - model = getattr(response, "model", None) - if model: - # This field is used by @traced_llm for more detailed - # model name in tracing spans - self._full_model_name = model + # This field is used by @traced_llm for more detailed + # model name in tracing spans + self._full_model_name = response.model # Process any function calls if function_calls: From 4ec7be88507eaae006d41c283d80ce1f805de920 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 14:23:39 -0400 Subject: [PATCH 130/159] feat: include cached_tokens and reasoning_tokens in usage metrics --- src/pipecat/services/openai/responses/llm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index a412d9627..bcc6274b4 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -287,6 +287,8 @@ class OpenAIResponsesLLMService(LLMService): prompt_tokens=response.usage.input_tokens, completion_tokens=response.usage.output_tokens, total_tokens=response.usage.total_tokens, + cache_read_input_tokens=response.usage.input_tokens_details.cached_tokens, + reasoning_tokens=response.usage.output_tokens_details.reasoning_tokens, ) await self.start_llm_usage_metrics(tokens) From 05e344b9ec67fe94ab22f5fe8047bd9a41b40ed6 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 14:30:34 -0400 Subject: [PATCH 131/159] docs: port _closing comments from BaseOpenAILLMService --- src/pipecat/services/openai/responses/llm.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index bcc6274b4..b12a33510 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -230,14 +230,22 @@ class OpenAIResponsesLLMService(LLMService): function_calls: Dict[str, Dict[str, str]] = {} # item_id -> {name, call_id, arguments} current_arguments: Dict[str, str] = {} # item_id -> accumulated arguments + # Ensure stream and its async iterator are closed on cancellation/exception + # to prevent socket leaks and uvloop crashes. Closing the iterator first + # cascades cleanup through nested async generators (httpx/httpcore internals), + # preventing uvloop's broken asyncgen finalizer from firing on Python 3.12+ + # (MagicStack/uvloop#699). @asynccontextmanager async def _closing(stream): chunk_iter = stream.__aiter__() try: yield chunk_iter finally: + # Close the iterator first to cascade cleanup through + # nested async generators (httpx/httpcore internals). if hasattr(chunk_iter, "aclose"): await chunk_iter.aclose() + # Then close the stream to release HTTP resources. if hasattr(stream, "close"): await stream.close() elif hasattr(stream, "aclose"): From 6424c36666bd7755c7fe5c26d342e5431bb5d3c4 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 14:37:46 -0400 Subject: [PATCH 132/159] refactor: remove model init param from OpenAIResponsesLLMService Model is only configurable via settings, matching the canonical API. --- src/pipecat/services/openai/responses/llm.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/pipecat/services/openai/responses/llm.py b/src/pipecat/services/openai/responses/llm.py index b12a33510..e9e5d3a1f 100644 --- a/src/pipecat/services/openai/responses/llm.py +++ b/src/pipecat/services/openai/responses/llm.py @@ -79,7 +79,6 @@ class OpenAIResponsesLLMService(LLMService): def __init__( self, *, - model: Optional[str] = None, api_key=None, base_url=None, organization=None, @@ -92,15 +91,13 @@ class OpenAIResponsesLLMService(LLMService): """Initialize the OpenAI Responses API LLM service. Args: - model: The OpenAI model name to use. Defaults to "gpt-4.1". api_key: OpenAI API key. If None, uses environment variable. base_url: Custom base URL for OpenAI API. If None, uses default. organization: OpenAI organization ID. project: OpenAI project ID. default_headers: Additional HTTP headers to include in requests. service_tier: Service tier to use (e.g., "auto", "flex", "priority"). - settings: Runtime-updatable settings. When provided alongside - other parameters, ``settings`` values take precedence. + settings: Runtime-updatable settings. **kwargs: Additional arguments passed to the parent LLMService. """ default_settings = self.Settings( @@ -119,9 +116,6 @@ class OpenAIResponsesLLMService(LLMService): extra={}, ) - if model is not None: - default_settings.model = model - if settings is not None: default_settings.apply_update(settings) From ea1534f9f8e757ee0fa86c46aea79eafdcb86a95 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 15:36:23 -0400 Subject: [PATCH 133/159] docs: note input_audio coming soon, no conversion needed The LLMContext format already matches the expected Responses API shape for input_audio, so no adapter conversion will be needed once OpenAI enables support. --- src/pipecat/adapters/services/open_ai_responses_adapter.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pipecat/adapters/services/open_ai_responses_adapter.py b/src/pipecat/adapters/services/open_ai_responses_adapter.py index 0d586fb12..70627fe5d 100644 --- a/src/pipecat/adapters/services/open_ai_responses_adapter.py +++ b/src/pipecat/adapters/services/open_ai_responses_adapter.py @@ -246,6 +246,9 @@ class OpenAIResponsesLLMAdapter(BaseLLMAdapter[OpenAIResponsesLLMInvocationParam } ) else: - # Pass through unknown types as-is + # Pass through other types as-is. Note: "input_audio" is not + # yet supported by the Responses API (coming soon per OpenAI + # docs) but the LLMContext format already matches the expected + # shape, so it should work once support is enabled. result.append(part) return result From dafbb2eb6628692b3cba8e7659c99abdf124e97d Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 15:38:38 -0400 Subject: [PATCH 134/159] =?UTF-8?q?fix:=20typo=20"conversatione"=20?= =?UTF-8?q?=E2=86=92=20"conversation"=20in=2020-=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../foundational/20a-persistent-context-openai-responses.py | 2 +- examples/foundational/20a-persistent-context-openai.py | 2 +- .../foundational/20b-persistent-context-openai-realtime-beta.py | 2 +- examples/foundational/20b-persistent-context-openai-realtime.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/foundational/20a-persistent-context-openai-responses.py b/examples/foundational/20a-persistent-context-openai-responses.py index 730e90197..5fd9c7657 100644 --- a/examples/foundational/20a-persistent-context-openai-responses.py +++ b/examples/foundational/20a-persistent-context-openai-responses.py @@ -116,7 +116,7 @@ weather_function = FunctionSchema( save_conversation_function = FunctionSchema( name="save_conversation", - description="Save the current conversatione. Use this function to persist the current conversation to external storage.", + description="Save the current conversation. Use this function to persist the current conversation to external storage.", properties={}, required=[], ) diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index f6a4dc937..7f744fd46 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -116,7 +116,7 @@ weather_function = FunctionSchema( save_conversation_function = FunctionSchema( name="save_conversation", - description="Save the current conversatione. Use this function to persist the current conversation to external storage.", + description="Save the current conversation. Use this function to persist the current conversation to external storage.", properties={}, required=[], ) diff --git a/examples/foundational/20b-persistent-context-openai-realtime-beta.py b/examples/foundational/20b-persistent-context-openai-realtime-beta.py index fa59e1674..4b05db618 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime-beta.py +++ b/examples/foundational/20b-persistent-context-openai-realtime-beta.py @@ -119,7 +119,7 @@ tools = [ { "type": "function", "name": "save_conversation", - "description": "Save the current conversatione. Use this function to persist the current conversation to external storage.", + "description": "Save the current conversation. Use this function to persist the current conversation to external storage.", "parameters": { "type": "object", "properties": {}, diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index de4215ab8..bceca410d 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -125,7 +125,7 @@ tools = ToolsSchema( ), FunctionSchema( name="save_conversation", - description="Save the current conversatione. Use this function to persist the current conversation to external storage.", + description="Save the current conversation. Use this function to persist the current conversation to external storage.", properties={}, required=[], ), From 4c456ada0411509c1c22d2182d2bb4009973780a Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 19 Mar 2026 15:52:48 -0400 Subject: [PATCH 135/159] Remove 05a example, which was broken and isn't currently a priority to fix --- .../05a-local-sync-speech-and-image.py | 202 ------------------ 1 file changed, 202 deletions(-) delete mode 100644 examples/foundational/05a-local-sync-speech-and-image.py diff --git a/examples/foundational/05a-local-sync-speech-and-image.py b/examples/foundational/05a-local-sync-speech-and-image.py deleted file mode 100644 index d0c5a103a..000000000 --- a/examples/foundational/05a-local-sync-speech-and-image.py +++ /dev/null @@ -1,202 +0,0 @@ -# -# Copyright (c) 2024-2026, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -import asyncio -import os -import sys -import tkinter as tk - -import aiohttp -from dotenv import load_dotenv -from loguru import logger - -from pipecat.frames.frames import ( - Frame, - LLMContextFrame, - OutputAudioRawFrame, - TextFrame, - TTSAudioRawFrame, - URLImageRawFrame, -) -from pipecat.pipeline.pipeline import Pipeline -from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.sync_parallel_pipeline import SyncParallelPipeline -from pipecat.pipeline.task import PipelineTask -from pipecat.processors.aggregators.llm_context import LLMContext -from pipecat.processors.aggregators.sentence import SentenceAggregator -from pipecat.processors.frame_processor import FrameDirection, FrameProcessor -from pipecat.services.cartesia.tts import CartesiaHttpTTSService -from pipecat.services.fal.image import FalImageGenService -from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.local.tk import TkLocalTransport, TkTransportParams - -load_dotenv(override=True) - -logger.remove(0) -logger.add(sys.stderr, level="DEBUG") - - -async def main(): - async with aiohttp.ClientSession() as session: - tk_root = tk.Tk() - tk_root.title("Calendar") - - runner = PipelineRunner() - - async def get_month_data(month): - messages = [ - { - "role": "user", - "content": f"Describe a nature photograph suitable for use in a calendar, for the month of {month}. Include only the image description with no preamble. Limit the description to one sentence, please.", - } - ] - - class ImageDescription(FrameProcessor): - def __init__(self): - super().__init__() - self.text = "" - - async def process_frame(self, frame: Frame, direction: FrameDirection): - await super().process_frame(frame, direction) - - if isinstance(frame, TextFrame): - self.text = frame.text - await self.push_frame(frame, direction) - - class AudioGrabber(FrameProcessor): - def __init__(self): - super().__init__() - self.audio = bytearray() - self.frame = None - - async def process_frame(self, frame: Frame, direction: FrameDirection): - await super().process_frame(frame, direction) - - if isinstance(frame, TTSAudioRawFrame): - self.audio.extend(frame.audio) - self.frame = OutputAudioRawFrame( - bytes(self.audio), frame.sample_rate, frame.num_channels - ) - await self.push_frame(frame, direction) - - class ImageGrabber(FrameProcessor): - def __init__(self): - super().__init__() - self.frame = None - - async def process_frame(self, frame: Frame, direction: FrameDirection): - await super().process_frame(frame, direction) - - if isinstance(frame, URLImageRawFrame): - self.frame = frame - await self.push_frame(frame, direction) - - llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) - - tts = CartesiaHttpTTSService( - api_key=os.getenv("CARTESIA_API_KEY"), - settings=CartesiaHttpTTSService.Settings( - voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady - ), - ) - - imagegen = FalImageGenService( - settings=FalImageGenService.Settings( - image_size="square_hd", - ), - aiohttp_session=session, - key=os.getenv("FAL_KEY"), - ) - - sentence_aggregator = SentenceAggregator() - - description = ImageDescription() - - audio_grabber = AudioGrabber() - - image_grabber = ImageGrabber() - - # With `SyncParallelPipeline` we synchronize audio and images by - # pushing them basically in order (e.g. I1 A1 A1 A1 I2 A2 A2 A2 A2 - # I3 A3). To do that, each pipeline runs concurrently and - # `SyncParallelPipeline` will wait for the input frame to be - # processed. - # - # Note that `SyncParallelPipeline` requires the last processor in - # each of the pipelines to be synchronous. In this case, we use - # `CartesiaHttpTTSService` and `FalImageGenService` which make HTTP - # requests and wait for the response. - pipeline = Pipeline( - [ - llm, # LLM - sentence_aggregator, # Aggregates LLM output into full sentences - description, # Store sentence - SyncParallelPipeline( - [tts, audio_grabber], # Generate and store audio for the given sentence - [imagegen, image_grabber], # Generate and storeimage for the given sentence - ), - ] - ) - - task = PipelineTask(pipeline) - await task.queue_frame(LLMContextFrame(LLMContext(messages))) - await task.stop_when_done() - - await runner.run(task) - - return { - "month": month, - "text": description.text, - "image": image_grabber.frame, - "audio": audio_grabber.frame, - } - - transport = TkLocalTransport( - tk_root, - TkTransportParams( - audio_out_enabled=True, - video_out_enabled=True, - video_out_width=1024, - video_out_height=1024, - ), - ) - - pipeline = Pipeline([transport.output()]) - - task = PipelineTask(pipeline) - - # We only specify a few months as we create tasks all at once and we - # might get rate limited otherwise. - months: list[str] = [ - "January", - "February", - ] - - # We create one task per month. This will be executed concurrently. - month_tasks = [asyncio.create_task(get_month_data(month)) for month in months] - - # Now we wait for each month task in the order they're completed. The - # benefit is we'll have as little delay as possible before the first - # month, and likely no delay between months, but the months won't - # display in order. - async def show_images(month_tasks): - for month_data_task in asyncio.as_completed(month_tasks): - data = await month_data_task - await task.queue_frames([data["image"], data["audio"]]) - - await runner.stop_when_done() - - async def run_tk(): - while not task.has_finished(): - tk_root.update() - tk_root.update_idletasks() - await asyncio.sleep(0.1) - - await asyncio.gather(runner.run(task), show_images(month_tasks), run_tk()) - - -if __name__ == "__main__": - asyncio.run(main()) From a11c48d5b04f6c592b9f69c5d19b76280adda32e Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 10:09:58 -0400 Subject: [PATCH 136/159] Add community integrations to README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8af4f942c..1afc7f7a0 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ claude plugin marketplace add pipecat-ai/skills and install any of the available plugins. +### 🧩 Community Integrations + +Build and share your own Pipecat service integrations! Browse existing [community integrations](https://docs.pipecat.ai/server/services/community-integrations) or check out our [guide](COMMUNITY_INTEGRATIONS.md) to create your own. + ### 📺️ Pipecat TV Channel Catch new features, interviews, and how-tos on our [Pipecat TV](https://www.youtube.com/playlist?list=PLzU2zoMTQIHjqC3v4q2XVSR3hGSzwKFwH) channel. @@ -94,6 +98,7 @@ Catch new features, interviews, and how-tos on our [Pipecat TV](https://www.yout | Vision & Image | [fal](https://docs.pipecat.ai/server/services/image-generation/fal), [Google Imagen](https://docs.pipecat.ai/server/services/image-generation/google-imagen), [Moondream](https://docs.pipecat.ai/server/services/vision/moondream) | | Audio Processing | [Silero VAD](https://docs.pipecat.ai/server/utilities/audio/silero-vad-analyzer), [Krisp](https://docs.pipecat.ai/server/utilities/audio/krisp-filter), [Koala](https://docs.pipecat.ai/server/utilities/audio/koala-filter), [ai-coustics](https://docs.pipecat.ai/server/utilities/audio/aic-filter) | | Analytics & Metrics | [OpenTelemetry](https://docs.pipecat.ai/server/utilities/opentelemetry), [Sentry](https://docs.pipecat.ai/server/services/analytics/sentry) | +| Community | [Browse community integrations →](https://docs.pipecat.ai/server/services/community-integrations) | 📚 [View full services documentation →](https://docs.pipecat.ai/server/services/supported-services) From b98ad7fb64efb8b91f992d1bfe112c5a63a8461e Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 10:41:43 -0400 Subject: [PATCH 137/159] fix: route TTS audio through audio context queue in Fish, LMNT, Neuphonic, Rime NonJson These services were pushing audio frames directly via push_frame() in their WebSocket receive loops, bypassing the base TTSService audio context serialization queue. This causes incorrect frame ordering and broken interruption handling. Changes per service: - Fish Audio: use append_to_audio_context(), replace _handle_interruption with on_audio_context_interrupted() - LMNT: use append_to_audio_context(), remove redundant push_frame override - Neuphonic: use append_to_audio_context(), remove redundant push_frame and process_frame overrides (base class handles pause/resume) - Rime NonJson: use append_to_audio_context(), remove redundant push_frame override --- src/pipecat/services/fish/tts.py | 16 +++++++---- src/pipecat/services/lmnt/tts.py | 20 +++++-------- src/pipecat/services/neuphonic/tts.py | 41 +++++---------------------- src/pipecat/services/rime/tts.py | 14 ++------- 4 files changed, 27 insertions(+), 64 deletions(-) diff --git a/src/pipecat/services/fish/tts.py b/src/pipecat/services/fish/tts.py index 92cb54701..ab57522d4 100644 --- a/src/pipecat/services/fish/tts.py +++ b/src/pipecat/services/fish/tts.py @@ -21,12 +21,10 @@ from pipecat.frames.frames import ( EndFrame, ErrorFrame, Frame, - InterruptionFrame, StartFrame, TTSAudioRawFrame, TTSStoppedFrame, ) -from pipecat.processors.frame_processor import FrameDirection from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import InterruptibleTTSService from pipecat.transcriptions.language import Language @@ -362,8 +360,8 @@ class FishAudioTTSService(InterruptibleTTSService): return self._websocket raise Exception("Websocket not connected") - async def _handle_interruption(self, frame: InterruptionFrame, direction: FrameDirection): - await super()._handle_interruption(frame, direction) + async def on_audio_context_interrupted(self, context_id: str): + """Stop all metrics when audio context is interrupted.""" await self.stop_all_metrics() async def _receive_messages(self): @@ -377,8 +375,14 @@ class FishAudioTTSService(InterruptibleTTSService): audio_data = msg.get("audio") # Only process larger chunks to remove msgpack overhead if audio_data and len(audio_data) > 1024: - frame = TTSAudioRawFrame(audio_data, self.sample_rate, 1) - await self.push_frame(frame) + context_id = self.get_active_audio_context_id() + frame = TTSAudioRawFrame( + audio_data, + self.sample_rate, + 1, + context_id=context_id, + ) + await self.append_to_audio_context(context_id, frame) await self.stop_ttfb_metrics() elif event == "finish": reason = msg.get("reason", "unknown") diff --git a/src/pipecat/services/lmnt/tts.py b/src/pipecat/services/lmnt/tts.py index 29d0b60ca..0df70fd19 100644 --- a/src/pipecat/services/lmnt/tts.py +++ b/src/pipecat/services/lmnt/tts.py @@ -21,7 +21,6 @@ from pipecat.frames.frames import ( TTSAudioRawFrame, TTSStoppedFrame, ) -from pipecat.processors.frame_processor import FrameDirection from pipecat.services.settings import TTSSettings from pipecat.services.tts_service import InterruptibleTTSService from pipecat.transcriptions.language import Language, resolve_language @@ -212,15 +211,6 @@ class LmntTTSService(InterruptibleTTSService): await super().cancel(frame) await self._disconnect() - async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM): - """Push a frame downstream with special handling for stop conditions. - - Args: - frame: The frame to push. - direction: The direction to push the frame. - """ - await super().push_frame(frame, direction) - async def _connect(self): """Connect to LMNT WebSocket and start receive task.""" await super()._connect() @@ -322,18 +312,22 @@ class LmntTTSService(InterruptibleTTSService): if isinstance(message, bytes): # Raw audio data await self.stop_ttfb_metrics() + context_id = self.get_active_audio_context_id() frame = TTSAudioRawFrame( audio=message, sample_rate=self.sample_rate, num_channels=1, - context_id=self.get_active_audio_context_id(), + context_id=context_id, ) - await self.push_frame(frame) + await self.append_to_audio_context(context_id, frame) else: try: msg = json.loads(message) if "error" in msg: - await self.push_frame(TTSStoppedFrame()) + context_id = self.get_active_audio_context_id() + await self.append_to_audio_context( + context_id, TTSStoppedFrame(context_id=context_id) + ) await self.stop_all_metrics() await self.push_error(error_msg=f"Error: {msg['error']}") return diff --git a/src/pipecat/services/neuphonic/tts.py b/src/pipecat/services/neuphonic/tts.py index 6fb9d3e28..1fad281d8 100644 --- a/src/pipecat/services/neuphonic/tts.py +++ b/src/pipecat/services/neuphonic/tts.py @@ -21,18 +21,14 @@ from loguru import logger from pydantic import BaseModel from pipecat.frames.frames import ( - BotStoppedSpeakingFrame, CancelFrame, EndFrame, ErrorFrame, Frame, - LLMFullResponseEndFrame, StartFrame, TTSAudioRawFrame, - TTSSpeakFrame, TTSStoppedFrame, ) -from pipecat.processors.frame_processor import FrameDirection from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import InterruptibleTTSService, TextAggregationMode, TTSService from pipecat.transcriptions.language import Language, resolve_language @@ -180,6 +176,7 @@ class NeuphonicTTSService(InterruptibleTTSService): text_aggregation_mode=text_aggregation_mode, push_stop_frames=True, push_start_frame=True, + pause_frame_processing=True, stop_frame_timeout_s=2.0, sample_rate=sample_rate, settings=default_settings, @@ -254,34 +251,6 @@ class NeuphonicTTSService(InterruptibleTTSService): msg = {"text": ""} await self._websocket.send(json.dumps(msg)) - async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM): - """Push a frame downstream with special handling for stop conditions. - - Args: - frame: The frame to push. - direction: The direction to push the frame. - """ - await super().push_frame(frame, direction) - - async def process_frame(self, frame: Frame, direction: FrameDirection): - """Process frames with special handling for speech control. - - Args: - frame: The frame to process. - direction: The direction of frame processing. - """ - await super().process_frame(frame, direction) - - # If we received a TTSSpeakFrame and the LLM response included text (it - # might be that it's only a function calling response) we pause - # processing more frames until we receive a BotStoppedSpeakingFrame. - if isinstance(frame, TTSSpeakFrame): - await self.pause_processing_frames() - elif isinstance(frame, LLMFullResponseEndFrame): - await self.pause_processing_frames() - elif isinstance(frame, BotStoppedSpeakingFrame): - await self.resume_processing_frames() - async def _connect(self): """Connect to Neuphonic WebSocket and start background tasks.""" await super()._connect() @@ -366,10 +335,14 @@ class NeuphonicTTSService(InterruptibleTTSService): await self.stop_ttfb_metrics() audio = base64.b64decode(msg["data"]["audio"]) + context_id = self.get_active_audio_context_id() frame = TTSAudioRawFrame( - audio, self.sample_rate, 1, context_id=self.get_active_audio_context_id() + audio, + self.sample_rate, + 1, + context_id=context_id, ) - await self.push_frame(frame) + await self.append_to_audio_context(context_id, frame) async def _keepalive_task_handler(self): """Handle keepalive messages to maintain WebSocket connection.""" diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index aec5630b6..4dca789e5 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -1054,15 +1054,6 @@ class RimeNonJsonTTSService(InterruptibleTTSService): await super().cancel(frame) await self._disconnect() - async def push_frame(self, frame: Frame, direction: FrameDirection = FrameDirection.DOWNSTREAM): - """Push a frame downstream with special handling for stop conditions. - - Args: - frame: The frame to push. - direction: The direction to push the frame. - """ - await super().push_frame(frame, direction) - async def _connect(self): """Establish WebSocket connection and start receive task.""" await super()._connect() @@ -1153,13 +1144,14 @@ class RimeNonJsonTTSService(InterruptibleTTSService): if isinstance(message, bytes): await self.stop_ttfb_metrics() + context_id = self.get_active_audio_context_id() frame = TTSAudioRawFrame( audio=message, sample_rate=self.sample_rate, num_channels=1, - context_id=self.get_active_audio_context_id(), + context_id=context_id, ) - await self.push_frame(frame) + await self.append_to_audio_context(context_id, frame) except Exception as e: await self.push_error(error_msg=f"Error: {e}", exception=e) From da8070e98e9bd9d07da7f671cafcd33ae00b3b40 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 10:46:36 -0400 Subject: [PATCH 138/159] Add changelog entry for #4090 --- changelog/4090.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4090.fixed.md diff --git a/changelog/4090.fixed.md b/changelog/4090.fixed.md new file mode 100644 index 000000000..ff42f09fe --- /dev/null +++ b/changelog/4090.fixed.md @@ -0,0 +1 @@ +- Fixed audio frame ordering and interruption handling in Fish Audio, LMNT, Neuphonic, and Rime NonJson TTS services. These services were bypassing the base `TTSService` audio context serialization queue by pushing audio frames directly, which could cause out-of-order frames and broken interruptions during speech. From a12ad273482fdaa1a59793a0cf154ea098e510af Mon Sep 17 00:00:00 2001 From: Varun Singh <382354+vr000m@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:01:31 -0700 Subject: [PATCH 139/159] enable_dialout should not depend on sip_caller_phone being set (#4087) * enable_dialout should not depend on SIP being set * we still need room_prefix to have pipecat-sip, s/sip/telephony in room prefix --- src/pipecat/runner/daily.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pipecat/runner/daily.py b/src/pipecat/runner/daily.py index b9bcb6e3d..a60b84a6a 100644 --- a/src/pipecat/runner/daily.py +++ b/src/pipecat/runner/daily.py @@ -207,7 +207,7 @@ async def configure( return DailyRoomConfig(room_url=room_url, token=token) # Create a new room - room_prefix = "pipecat-sip" if sip_enabled else "pipecat" + room_prefix = "pipecat-telephony" if (sip_enabled or enable_dialout) else "pipecat" room_name = f"{room_prefix}-{uuid.uuid4().hex[:8]}" logger.info(f"Creating new Daily room: {room_name}") @@ -225,6 +225,9 @@ async def configure( if room_geo: room_properties.geo = room_geo + if enable_dialout: + room_properties.enable_dialout = True + # Add SIP configuration if enabled if sip_enabled: sip_params = DailyRoomSipParams( @@ -236,7 +239,6 @@ async def configure( provider=sip_provider, ) room_properties.sip = sip_params - room_properties.enable_dialout = enable_dialout room_properties.start_video_off = not sip_enable_video # Voice-only by default # Create room parameters From e5aaa4c4ebc6eda8d297140587cfaadd95498afb Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 13:12:40 -0400 Subject: [PATCH 140/159] fix: read system_instruction from _settings instead of removed attribute Replace adapter-based extraction in traced_llm with direct reads from _settings.system_instruction (priority) and context messages (fallback). The old approach had three bugs: signature mismatch with Anthropic adapter, key name inconsistency, and unnecessary overhead from full message/tools conversion. Also deduplicate the system instruction in spans -- it was appearing as both "system" and "param.system_instruction". --- changelog/3449.fixed.md | 2 +- .../utils/tracing/service_decorators.py | 42 ++++++++++++------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/changelog/3449.fixed.md b/changelog/3449.fixed.md index a8663e004..7cf01c0cb 100644 --- a/changelog/3449.fixed.md +++ b/changelog/3449.fixed.md @@ -1 +1 @@ -- Fixed telemetry record correct system_instruction in span for LLM services +- Fixed stale `system_instruction` in LLM tracing spans by reading from `_settings.system_instruction` instead of the removed `_system_instruction` attribute. diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index 0b63989ea..2b189feb5 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -502,35 +502,45 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - # Handle system message for different services system_message = None - # Handle system message for different services if isinstance(context, LLMContext): - # Universal LLMContext - use adapter to convert and get system message - if hasattr(self, "get_llm_adapter"): - adapter = self.get_llm_adapter() - try: - # Get LLM invocation params which includes system_instruction - params = adapter.get_llm_invocation_params(context) + # settings.system_instruction takes priority (matches service behavior) + if hasattr(self, "_settings") and getattr( + self._settings, "system_instruction", None + ): + system_message = self._settings.system_instruction + else: + # Fall back to extracting from context messages + ctx_messages = context.get_messages() + if ctx_messages: + first = ctx_messages[0] if ( - isinstance(params, dict) - and "system_instruction" in params + isinstance(first, dict) + and first.get("role") == "system" ): - system_message = params["system_instruction"] - except Exception as e: - logging.debug( - f"Could not extract system instruction from adapter: {e}" - ) + content = first.get("content") + if isinstance(content, str): + system_message = content + elif isinstance(content, list): + system_message = " ".join( + part.get("text", "") + for part in content + if isinstance(part, dict) + and part.get("type") == "text" + ) elif hasattr(context, "system"): system_message = context.system elif hasattr(context, "system_message"): system_message = context.system_message - elif hasattr(self, "_system_instruction"): - system_message = self._system_instruction # Use given_fields() defensively in case a service doesn't # initialize all settings. params = {} if hasattr(self, "_settings"): for key, value in self._settings.given_fields().items(): + # system_instruction is already captured as the + # "system" span attribute above. + if key == "system_instruction": + continue if isinstance(value, (int, float, bool, str)): params[key] = value elif value is None: From b5c362d6e61afaa0d2ac1dc958ef68da5be78241 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 13:20:03 -0400 Subject: [PATCH 141/159] refactor: rename tracing span attribute "system" to "system_instructions" Align with the OpenTelemetry GenAI semantic convention gen_ai.system_instructions for system prompts. The old "system" attribute name was unrelated to gen_ai.system (which is for provider name). --- changelog/3449.changed.md | 1 + src/pipecat/utils/tracing/service_attributes.py | 8 ++++---- src/pipecat/utils/tracing/service_decorators.py | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 changelog/3449.changed.md diff --git a/changelog/3449.changed.md b/changelog/3449.changed.md new file mode 100644 index 000000000..d06ad37c4 --- /dev/null +++ b/changelog/3449.changed.md @@ -0,0 +1 @@ +- Renamed tracing span attribute `system` to `system_instructions` to align with the OpenTelemetry GenAI semantic conventions. diff --git a/src/pipecat/utils/tracing/service_attributes.py b/src/pipecat/utils/tracing/service_attributes.py index 21a22341f..1a6537f2b 100644 --- a/src/pipecat/utils/tracing/service_attributes.py +++ b/src/pipecat/utils/tracing/service_attributes.py @@ -193,7 +193,7 @@ def add_llm_span_attributes( tools: Optional[str] = None, tool_count: Optional[int] = None, tool_choice: Optional[str] = None, - system: Optional[str] = None, + system_instructions: Optional[str] = None, parameters: Optional[Dict[str, Any]] = None, extra_parameters: Optional[Dict[str, Any]] = None, ttfb: Optional[float] = None, @@ -211,7 +211,7 @@ def add_llm_span_attributes( tools: JSON-serialized tools configuration. tool_count: Number of tools available. tool_choice: Tool selection configuration. - system: System message. + system_instructions: System instructions. parameters: Service parameters. extra_parameters: Additional parameters. ttfb: Time to first byte in seconds. @@ -240,8 +240,8 @@ def add_llm_span_attributes( if tool_choice: span.set_attribute("tool_choice", tool_choice) - if system: - span.set_attribute("system", system) + if system_instructions: + span.set_attribute("system_instructions", system_instructions) if ttfb is not None: span.set_attribute("metrics.ttfb", ttfb) diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index 2b189feb5..2e54732f1 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -538,7 +538,7 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - if hasattr(self, "_settings"): for key, value in self._settings.given_fields().items(): # system_instruction is already captured as the - # "system" span attribute above. + # "system_instructions" span attribute above. if key == "system_instruction": continue if isinstance(value, (int, float, bool, str)): @@ -561,7 +561,7 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - attribute_kwargs["tools"] = serialized_tools attribute_kwargs["tool_count"] = tool_count if system_message: - attribute_kwargs["system"] = system_message + attribute_kwargs["system_instructions"] = system_message # Add all gathered attributes to the span add_llm_span_attributes(span=current_span, **attribute_kwargs) From c89e36673912839fb2c5262ac6ecf2d231591545 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 13:36:20 -0400 Subject: [PATCH 142/159] refactor: align tracing attributes with OpenTelemetry GenAI conventions - gen_ai.system -> gen_ai.provider.name (deprecated) - system / system_instructions -> gen_ai.system_instructions - gen_ai.usage.cache_read_input_tokens -> gen_ai.usage.cache_read.input_tokens - gen_ai.usage.cache_creation_input_tokens -> gen_ai.usage.cache_creation.input_tokens --- changelog/3449.changed.md | 2 +- .../utils/tracing/service_attributes.py | 22 +++++++++---------- .../utils/tracing/service_decorators.py | 8 +++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/changelog/3449.changed.md b/changelog/3449.changed.md index d06ad37c4..d5ea2f6d7 100644 --- a/changelog/3449.changed.md +++ b/changelog/3449.changed.md @@ -1 +1 @@ -- Renamed tracing span attribute `system` to `system_instructions` to align with the OpenTelemetry GenAI semantic conventions. +- Renamed tracing span attributes to align with OpenTelemetry GenAI semantic conventions: `gen_ai.system` to `gen_ai.provider.name`, `system` to `gen_ai.system_instructions`, `gen_ai.usage.cache_read_input_tokens` to `gen_ai.usage.cache_read.input_tokens`, and `gen_ai.usage.cache_creation_input_tokens` to `gen_ai.usage.cache_creation.input_tokens`. diff --git a/src/pipecat/utils/tracing/service_attributes.py b/src/pipecat/utils/tracing/service_attributes.py index 1a6537f2b..e5cf4a83f 100644 --- a/src/pipecat/utils/tracing/service_attributes.py +++ b/src/pipecat/utils/tracing/service_attributes.py @@ -25,20 +25,20 @@ if is_tracing_available(): from opentelemetry.trace import Span -def _get_gen_ai_system_from_service_name(service_name: str) -> str: - """Extract the standardized gen_ai.system value from a service class name. +def _get_provider_name_from_service_name(service_name: str) -> str: + """Extract the standardized gen_ai.provider.name value from a service class name. Source: - https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/#gen-ai-system + https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/ Uses standard OTel names where possible, with special case mappings for service names that don't follow the pattern. Args: - service_name: The service class name to extract system name from. + service_name: The service class name to extract provider name from. Returns: - The standardized gen_ai.system value. + The standardized gen_ai.provider.name value. """ SPECIAL_CASE_MAPPINGS = { # AWS @@ -91,7 +91,7 @@ def add_tts_span_attributes( **kwargs: Additional attributes to add. """ # Add standard attributes - span.set_attribute("gen_ai.system", service_name.replace("TTSService", "").lower()) + span.set_attribute("gen_ai.provider.name", service_name.replace("TTSService", "").lower()) span.set_attribute("gen_ai.request.model", model) span.set_attribute("gen_ai.operation.name", operation_name) span.set_attribute("gen_ai.output.type", "speech") @@ -150,7 +150,7 @@ def add_stt_span_attributes( **kwargs: Additional attributes to add. """ # Add standard attributes - span.set_attribute("gen_ai.system", service_name.replace("STTService", "").lower()) + span.set_attribute("gen_ai.provider.name", service_name.replace("STTService", "").lower()) span.set_attribute("gen_ai.request.model", model) span.set_attribute("gen_ai.operation.name", operation_name) span.set_attribute("vad_enabled", vad_enabled) @@ -218,7 +218,7 @@ def add_llm_span_attributes( **kwargs: Additional attributes to add. """ # Add standard attributes - span.set_attribute("gen_ai.system", _get_gen_ai_system_from_service_name(service_name)) + span.set_attribute("gen_ai.provider.name", _get_provider_name_from_service_name(service_name)) span.set_attribute("gen_ai.request.model", model) span.set_attribute("gen_ai.operation.name", "chat") span.set_attribute("gen_ai.output.type", "text") @@ -241,7 +241,7 @@ def add_llm_span_attributes( span.set_attribute("tool_choice", tool_choice) if system_instructions: - span.set_attribute("system_instructions", system_instructions) + span.set_attribute("gen_ai.system_instructions", system_instructions) if ttfb is not None: span.set_attribute("metrics.ttfb", ttfb) @@ -313,7 +313,7 @@ def add_gemini_live_span_attributes( **kwargs: Additional attributes to add. """ # Add standard attributes - span.set_attribute("gen_ai.system", "gcp.gemini") + span.set_attribute("gen_ai.provider.name", "gcp.gemini") span.set_attribute("gen_ai.request.model", model) span.set_attribute("gen_ai.operation.name", operation_name) span.set_attribute("service.operation", operation_name) @@ -414,7 +414,7 @@ def add_openai_realtime_span_attributes( **kwargs: Additional attributes to add. """ # Add standard attributes - span.set_attribute("gen_ai.system", "openai") + span.set_attribute("gen_ai.provider.name", "openai") span.set_attribute("gen_ai.request.model", model) span.set_attribute("gen_ai.operation.name", operation_name) span.set_attribute("service.operation", operation_name) diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index 2e54732f1..cc353e1a3 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -137,14 +137,14 @@ def _add_token_usage_to_span(span, token_usage): and token_usage["cache_read_input_tokens"] is not None ): span.set_attribute( - "gen_ai.usage.cache_read_input_tokens", token_usage["cache_read_input_tokens"] + "gen_ai.usage.cache_read.input_tokens", token_usage["cache_read_input_tokens"] ) if ( "cache_creation_input_tokens" in token_usage and token_usage["cache_creation_input_tokens"] is not None ): span.set_attribute( - "gen_ai.usage.cache_creation_input_tokens", + "gen_ai.usage.cache_creation.input_tokens", token_usage["cache_creation_input_tokens"], ) if "reasoning_tokens" in token_usage and token_usage["reasoning_tokens"] is not None: @@ -159,11 +159,11 @@ def _add_token_usage_to_span(span, token_usage): # Add cached token metrics for LLMTokenUsage object cache_read_tokens = getattr(token_usage, "cache_read_input_tokens", None) if cache_read_tokens is not None: - span.set_attribute("gen_ai.usage.cache_read_input_tokens", cache_read_tokens) + span.set_attribute("gen_ai.usage.cache_read.input_tokens", cache_read_tokens) cache_creation_tokens = getattr(token_usage, "cache_creation_input_tokens", None) if cache_creation_tokens is not None: - span.set_attribute("gen_ai.usage.cache_creation_input_tokens", cache_creation_tokens) + span.set_attribute("gen_ai.usage.cache_creation.input_tokens", cache_creation_tokens) reasoning_tokens = getattr(token_usage, "reasoning_tokens", None) if reasoning_tokens is not None: From bc0e7130b8c6b6365e4ff73c5de66c2d9d38f700 Mon Sep 17 00:00:00 2001 From: Pablo Ois Lagarde Date: Fri, 20 Mar 2026 16:37:53 -0300 Subject: [PATCH 143/159] fix: always include parameters field in Genesys AudioHook messages The AudioHook protocol requires every message to carry a `parameters` object. `_create_message` conditionally included it only when parameters were truthy, so pong responses and closed responses without outputVariables were sent without the field. Clients that validate message structure (including the Genesys reference implementation) rejected these messages, which broke server sequence tracking and prevented outputVariables from reaching the Architect flow. Co-Authored-By: Claude Opus 4.6 (1M context) --- changelog/0000.fixed.md | 7 +++++++ src/pipecat/serializers/genesys.py | 3 +-- tests/genesys/test_genesys_serializer.py | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 changelog/0000.fixed.md diff --git a/changelog/0000.fixed.md b/changelog/0000.fixed.md new file mode 100644 index 000000000..39fabc029 --- /dev/null +++ b/changelog/0000.fixed.md @@ -0,0 +1,7 @@ +- Fixed Genesys AudioHook serializer to always include the `parameters` field in + protocol messages. The AudioHook protocol requires every message to carry a + `parameters` object (even if empty), but `_create_message` omitted it when no + parameters were provided. This caused clients that validate message structure + (including the Genesys reference implementation) to reject `pong` and + parameter-less `closed` responses, breaking server sequence tracking and + preventing `outputVariables` from reaching the Architect flow. diff --git a/src/pipecat/serializers/genesys.py b/src/pipecat/serializers/genesys.py index 2cc3c32db..4e0c11504 100644 --- a/src/pipecat/serializers/genesys.py +++ b/src/pipecat/serializers/genesys.py @@ -336,8 +336,7 @@ class GenesysAudioHookSerializer(FrameSerializer): if include_position: msg["position"] = self._format_position(self._position) - if parameters: - msg["parameters"] = parameters + msg["parameters"] = parameters if parameters is not None else {} return msg diff --git a/tests/genesys/test_genesys_serializer.py b/tests/genesys/test_genesys_serializer.py index 03132d7cb..1f63ae94e 100644 --- a/tests/genesys/test_genesys_serializer.py +++ b/tests/genesys/test_genesys_serializer.py @@ -76,6 +76,7 @@ class TestGenesysAudioHookSerializer: assert msg["type"] == "pong" assert msg["id"] == serializer.session_id + assert msg["parameters"] == {} def test_create_closed_response(self): """Test creating a closed response message.""" @@ -86,7 +87,7 @@ class TestGenesysAudioHookSerializer: assert msg["type"] == "closed" assert serializer.is_open is False - assert "parameters" not in msg # No parameters when no output_variables + assert msg["parameters"] == {} # Empty parameters when no output_variables def test_create_closed_response_with_output_variables(self): """Test creating a closed response with custom output variables.""" From 53e0136366eed70d84dafa707054e02e12a9c3d1 Mon Sep 17 00:00:00 2001 From: Pablo Ois Lagarde Date: Fri, 20 Mar 2026 16:46:35 -0300 Subject: [PATCH 144/159] chore: rename changelog fragment to PR #4093 Co-Authored-By: Claude Opus 4.6 (1M context) --- changelog/{0000.fixed.md => 4093.fixed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{0000.fixed.md => 4093.fixed.md} (100%) diff --git a/changelog/0000.fixed.md b/changelog/4093.fixed.md similarity index 100% rename from changelog/0000.fixed.md rename to changelog/4093.fixed.md From 622ebd5d74bcbc43c909d72e27d43de2a25bab64 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 21 Mar 2026 07:02:06 -0400 Subject: [PATCH 145/159] Update MiniMaxHttpTTSService platform docs link --- src/pipecat/services/minimax/tts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/minimax/tts.py b/src/pipecat/services/minimax/tts.py index d41fb6255..46502409d 100644 --- a/src/pipecat/services/minimax/tts.py +++ b/src/pipecat/services/minimax/tts.py @@ -137,7 +137,7 @@ class MiniMaxHttpTTSService(TTSService): Supports real-time audio streaming with configurable voice parameters. Platform documentation: - https://www.minimax.io/platform/document/T2A%20V2?key=66719005a427f0c8a5701643 + https://platform.minimax.io/docs/api-reference/speech-t2a-http """ Settings = MiniMaxTTSSettings From ce364871436a04072e509380588065f0c37c2599 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Sun, 22 Mar 2026 13:09:09 -0300 Subject: [PATCH 146/159] Allow defining whether to insert silence in the output transport. --- src/pipecat/transports/base_transport.py | 3 +++ .../transports/smallwebrtc/transport.py | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/pipecat/transports/base_transport.py b/src/pipecat/transports/base_transport.py index a78e1046c..33616a6ea 100644 --- a/src/pipecat/transports/base_transport.py +++ b/src/pipecat/transports/base_transport.py @@ -84,6 +84,8 @@ class TransportParams(BaseModel): audio_out_mixer: Audio mixer instance or destination mapping. audio_out_destinations: List of audio output destination identifiers. audio_out_end_silence_secs: How much silence to send after an EndFrame (0 for no silence). + audio_out_insert_silence: Insert silence frames when the audio output queue is empty. + When False, the transport will wait for audio data instead of inserting silence. audio_in_enabled: Enable audio input streaming. audio_in_sample_rate: Input audio sample rate in Hz. audio_in_channels: Number of input audio channels. @@ -144,6 +146,7 @@ class TransportParams(BaseModel): audio_out_mixer: Optional[BaseAudioMixer | Mapping[Optional[str], BaseAudioMixer]] = None audio_out_destinations: List[str] = Field(default_factory=list) audio_out_end_silence_secs: int = 2 + audio_out_insert_silence: bool = True audio_in_enabled: bool = False audio_in_sample_rate: Optional[int] = None audio_in_channels: int = 1 diff --git a/src/pipecat/transports/smallwebrtc/transport.py b/src/pipecat/transports/smallwebrtc/transport.py index 36f883278..cfe2b7f49 100644 --- a/src/pipecat/transports/smallwebrtc/transport.py +++ b/src/pipecat/transports/smallwebrtc/transport.py @@ -79,14 +79,17 @@ class RawAudioTrack(AudioStreamTrack): supporting queued audio data with proper synchronization. """ - def __init__(self, sample_rate): + def __init__(self, sample_rate: int, insert_silence: bool = True): """Initialize the raw audio track. Args: sample_rate: The audio sample rate in Hz. + insert_silence: If True, emit silence when the queue is empty. If False, + wait until audio data is available. """ super().__init__() self._sample_rate = sample_rate + self._insert_silence = insert_silence self._samples_per_10ms = sample_rate * 10 // 1000 self._bytes_per_10ms = self._samples_per_10ms * 2 # 16-bit (2 bytes per sample) self._timestamp = 0 @@ -131,12 +134,19 @@ class RawAudioTrack(AudioStreamTrack): if wait > 0: await asyncio.sleep(wait) - if self._chunk_queue: + if not self._chunk_queue: + if self._insert_silence: + chunk = bytes(self._bytes_per_10ms) + else: + while not self._chunk_queue: + await asyncio.sleep(0.005) + chunk, future = self._chunk_queue.popleft() + if future and not future.done(): + future.set_result(True) + else: chunk, future = self._chunk_queue.popleft() if future and not future.done(): future.set_result(True) - else: - chunk = bytes(self._bytes_per_10ms) # silence # Convert the byte data to an ndarray of int16 samples samples = np.frombuffer(chunk, dtype=np.int16) @@ -484,7 +494,10 @@ class SmallWebRTCClient: self._video_input_track = self._webrtc_connection.video_input_track() self._screen_video_track = self._webrtc_connection.screen_video_input_track() if self._params.audio_out_enabled: - self._audio_output_track = RawAudioTrack(sample_rate=self._out_sample_rate) + self._audio_output_track = RawAudioTrack( + sample_rate=self._out_sample_rate, + insert_silence=self._params.audio_out_insert_silence, + ) self._webrtc_connection.replace_audio_track(self._audio_output_track) if self._params.video_out_enabled: From 3b1cb309264668272aada1c7be8fce156d0afaaf Mon Sep 17 00:00:00 2001 From: filipi87 Date: Sun, 22 Mar 2026 13:26:00 -0300 Subject: [PATCH 147/159] Adding changelog entry. --- changelog/4104.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4104.added.md diff --git a/changelog/4104.added.md b/changelog/4104.added.md new file mode 100644 index 000000000..ce948530e --- /dev/null +++ b/changelog/4104.added.md @@ -0,0 +1 @@ +- Added `audio_out_insert_silence` parameter to `TransportParams` (defaults to `True`). When set to `False`, the SmallWebRTC transport waits for audio data instead of inserting silence when the output queue is empty, which is useful for scenarios that require uninterrupted audio playback without artificial gaps. From 936a39f4a1dd0d83af8cae1f316f4be30884a97b Mon Sep 17 00:00:00 2001 From: filipi87 Date: Sun, 22 Mar 2026 14:41:23 -0300 Subject: [PATCH 148/159] Updating tavus examples to not send silence. --- examples/foundational/21a-tavus-video-service.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/foundational/21a-tavus-video-service.py b/examples/foundational/21a-tavus-video-service.py index 6e03a2418..0b84b0f7b 100644 --- a/examples/foundational/21a-tavus-video-service.py +++ b/examples/foundational/21a-tavus-video-service.py @@ -42,6 +42,7 @@ transport_params = { video_out_is_live=True, video_out_width=1280, video_out_height=720, + audio_out_insert_silence=False, ), "webrtc": lambda: TransportParams( audio_in_enabled=True, @@ -50,6 +51,7 @@ transport_params = { video_out_is_live=True, video_out_width=1280, video_out_height=720, + audio_out_insert_silence=False, ), } From 9a30b18f2119228e0b0dab8647c8d7afc01e395f Mon Sep 17 00:00:00 2001 From: filipi87 Date: Sun, 22 Mar 2026 17:29:01 -0300 Subject: [PATCH 149/159] Configuring Daily CustomAudioSource to automatically inject silence or not. --- src/pipecat/transports/daily/transport.py | 22 +++++++++++++++---- .../transports/smallwebrtc/transport.py | 3 ++- src/pipecat/transports/tavus/transport.py | 8 +++++-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/pipecat/transports/daily/transport.py b/src/pipecat/transports/daily/transport.py index 2798f6d5e..f33fa79ff 100644 --- a/src/pipecat/transports/daily/transport.py +++ b/src/pipecat/transports/daily/transport.py @@ -676,15 +676,19 @@ class DailyTransportClient(EventHandler): await asyncio.sleep(0.01) return None - async def register_audio_destination(self, destination: str): + async def register_audio_destination( + self, destination: str, auto_silence: Optional[bool] = True + ): """Register a custom audio destination for multi-track output. Args: destination: The destination identifier to register. + auto_silence: If True, the audio source inserts silence when no audio is available. + If False, the source waits for audio data. Defaults to True. """ params = (self._params.custom_audio_track_params or {}).get(destination) self._custom_audio_tracks[destination] = await self.add_custom_audio_track( - destination, params=params + destination, params=params, auto_silence=auto_silence ) publishing: Dict[str, Any] = {"customAudio": {destination: True}} if params and params.send_settings: @@ -831,7 +835,14 @@ class DailyTransportClient(EventHandler): self._camera_track = DailyVideoTrack(source=video_source, track=video_track) if self._params.audio_out_enabled and not self._microphone_track: - audio_source = CustomAudioSource(self._out_sample_rate, self._params.audio_out_channels) + logger.debug( + f"Creating custom audio source, auto silence {self._params.audio_out_insert_silence}" + ) + audio_source = CustomAudioSource( + self._out_sample_rate, + self._params.audio_out_channels, + self._params.audio_out_insert_silence, + ) audio_track = CustomAudioTrack(audio_source) self._microphone_track = DailyAudioTrack(source=audio_source, track=audio_track) @@ -1269,12 +1280,15 @@ class DailyTransportClient(EventHandler): self, track_name: str, params: Optional[DailyCustomAudioTrackParams] = None, + auto_silence: Optional[bool] = True, ) -> DailyAudioTrack: """Add a custom audio track for multi-stream output. Args: track_name: Name for the custom audio track. params: Optional per-track configuration for sample rate, channels, and sendSettings. + auto_silence: If True, the audio source inserts silence when no audio is available. + If False, the source waits for audio data. Defaults to True. Returns: The created DailyAudioTrack instance. @@ -1284,7 +1298,7 @@ class DailyTransportClient(EventHandler): sample_rate = params.sample_rate if params and params.sample_rate else self._out_sample_rate channels = params.channels if params else 1 - audio_source = CustomAudioSource(sample_rate, channels) + audio_source = CustomAudioSource(sample_rate, channels, auto_silence) audio_track = CustomAudioTrack(audio_source) diff --git a/src/pipecat/transports/smallwebrtc/transport.py b/src/pipecat/transports/smallwebrtc/transport.py index cfe2b7f49..b239f3831 100644 --- a/src/pipecat/transports/smallwebrtc/transport.py +++ b/src/pipecat/transports/smallwebrtc/transport.py @@ -126,7 +126,8 @@ class RawAudioTrack(AudioStreamTrack): """Return the next audio frame for WebRTC transmission. Returns: - An AudioFrame containing the next audio data or silence. + An AudioFrame containing the next audio data, or silence if the queue is empty + and ``insert_silence`` is True. """ # Compute required wait time for synchronization if self._timestamp > 0: diff --git a/src/pipecat/transports/tavus/transport.py b/src/pipecat/transports/tavus/transport.py index 872e6eefc..426b7f72c 100644 --- a/src/pipecat/transports/tavus/transport.py +++ b/src/pipecat/transports/tavus/transport.py @@ -417,16 +417,20 @@ class TavusTransportClient: return False return await self._client.write_audio_frame(frame) - async def register_audio_destination(self, destination: str): + async def register_audio_destination( + self, destination: str, auto_silence: Optional[bool] = True + ): """Register an audio destination for output. Args: destination: The destination identifier to register. + auto_silence: If True, the audio source inserts silence when no audio is available. + If False, the source waits for audio data. Defaults to True. """ if not self._client: return - await self._client.register_audio_destination(destination) + await self._client.register_audio_destination(destination, auto_silence=auto_silence) class TavusInputTransport(BaseInputTransport): From e6602f9244593ed4674ae60771401e5cf05effbc Mon Sep 17 00:00:00 2001 From: filipi87 Date: Sun, 22 Mar 2026 18:28:57 -0300 Subject: [PATCH 150/159] Disabling auto_silence for tavus video service. --- src/pipecat/services/tavus/video.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/tavus/video.py b/src/pipecat/services/tavus/video.py index 9043710c8..a41bee5b4 100644 --- a/src/pipecat/services/tavus/video.py +++ b/src/pipecat/services/tavus/video.py @@ -214,7 +214,9 @@ class TavusVideoService(AIService): await super().start(frame) await self._client.start(frame) if self._transport_destination: - await self._client.register_audio_destination(self._transport_destination) + await self._client.register_audio_destination( + self._transport_destination, auto_silence=False + ) await self._create_send_task() async def stop(self, frame: EndFrame): From e93b0ace0662e5839ccd5f4ec1bb1ec224bf80db Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Mon, 23 Mar 2026 10:00:32 -0400 Subject: [PATCH 151/159] Remove an unnecessary check in `SyncParallelPipeline` --- src/pipecat/pipeline/sync_parallel_pipeline.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pipecat/pipeline/sync_parallel_pipeline.py b/src/pipecat/pipeline/sync_parallel_pipeline.py index 148d29b25..4463b8a22 100644 --- a/src/pipecat/pipeline/sync_parallel_pipeline.py +++ b/src/pipecat/pipeline/sync_parallel_pipeline.py @@ -263,11 +263,11 @@ class SyncParallelPipeline(BasePipeline): """ await super().process_frame(frame, direction) - # SystemFrames (but not EndFrame) are simply passed through all - # internal pipelines without draining queued output. This avoids - # the race condition where a SystemFrame's wait_for_sync steals - # frames from a concurrent non-SystemFrame's wait_for_sync. - if isinstance(frame, SystemFrame) and not isinstance(frame, EndFrame): + # SystemFrames are simply passed through all internal pipelines without + # draining queued output. This avoids the race condition where a + # SystemFrame's wait_for_sync steals frames from a concurrent + # non-SystemFrame's wait_for_sync. + if isinstance(frame, SystemFrame): if direction == FrameDirection.UPSTREAM: for s in self._sinks: await s["processor"].process_frame(frame, direction) From 84c2a24c9fdb4778b773d26c99310f56f3057fc9 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 11:33:18 -0400 Subject: [PATCH 152/159] feat: send per-context setup messages in Gradium TTS multiplexing Send a setup message with client_req_id before the first text message for each context, matching Gradium multiplexing protocol. This allows Gradium to associate each session with its setup configuration when using close_ws_on_eos=False. --- src/pipecat/services/gradium/tts.py | 52 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/pipecat/services/gradium/tts.py b/src/pipecat/services/gradium/tts.py index f63f931ad..a0b32df31 100644 --- a/src/pipecat/services/gradium/tts.py +++ b/src/pipecat/services/gradium/tts.py @@ -140,6 +140,7 @@ class GradiumTTSService(WebsocketTTSService): # State tracking self._receive_task = None + self._setup_context_ids: set[str] = set() def can_generate_metrics(self) -> bool: """Check if this service can generate processing metrics. @@ -166,8 +167,25 @@ class GradiumTTSService(WebsocketTTSService): self._warn_unhandled_updated_settings(changed) return changed - def _build_msg(self, text: str = "", context_id: str = "") -> dict: - """Build JSON message for Gradium API.""" + def _build_setup_msg(self, context_id: str) -> dict: + """Build setup message for Gradium API. + + Args: + context_id: Context ID to use as ``client_req_id``. + """ + setup_msg: dict[str, Any] = { + "type": "setup", + "output_format": "pcm", + "voice_id": self._settings.voice, + "close_ws_on_eos": False, + "client_req_id": context_id, + } + if self._json_config is not None: + setup_msg["json_config"] = self._json_config + return setup_msg + + def _build_text_msg(self, text: str = "", context_id: str = "") -> dict: + """Build text message for Gradium API.""" msg = {"text": text, "type": "text", "client_req_id": context_id} return msg @@ -236,22 +254,6 @@ class GradiumTTSService(WebsocketTTSService): headers = {"x-api-key": self._api_key, "x-api-source": "pipecat"} self._websocket = await websocket_connect(self._url, additional_headers=headers) - setup_msg = { - "type": "setup", - "output_format": "pcm", - "voice_id": self._settings.voice, - "close_ws_on_eos": False, - } - if self._json_config is not None: - setup_msg["json_config"] = self._json_config - await self._websocket.send(json.dumps(setup_msg)) - ready_msg = await self._websocket.recv() - ready_msg = json.loads(ready_msg) - if ready_msg["type"] == "error": - raise Exception(f"received error {ready_msg['message']}") - if ready_msg["type"] != "ready": - raise Exception(f"unexpected first message type {ready_msg['type']}") - await self._call_event_handler("on_connected") except Exception as e: await self.push_error(error_msg=f"Unknown error occurred: {e}", exception=e) @@ -269,6 +271,7 @@ class GradiumTTSService(WebsocketTTSService): finally: await self.remove_active_audio_context() self._websocket = None + self._setup_context_ids.clear() await self._call_event_handler("on_disconnected") def _get_websocket(self): @@ -334,10 +337,15 @@ class GradiumTTSService(WebsocketTTSService): if ctx_id and self.audio_context_available(ctx_id): await self.add_word_timestamps([(msg["text"], msg["start_s"])], ctx_id) + elif msg["type"] == "ready": + pass + elif msg["type"] == "end_of_stream": if ctx_id and self.audio_context_available(ctx_id): await self.add_word_timestamps([("TTSStoppedFrame", 0), ("Reset", 0)], ctx_id) await self.remove_audio_context(ctx_id) + if ctx_id: + self._setup_context_ids.discard(ctx_id) await self.stop_all_metrics() elif msg["type"] == "error": @@ -363,8 +371,12 @@ class GradiumTTSService(WebsocketTTSService): await self._connect() try: - msg = self._build_msg(text=text, context_id=context_id) - await self._get_websocket().send(json.dumps(msg)) + ws = self._get_websocket() + if context_id not in self._setup_context_ids: + await ws.send(json.dumps(self._build_setup_msg(context_id))) + self._setup_context_ids.add(context_id) + msg = self._build_text_msg(text=text, context_id=context_id) + await ws.send(json.dumps(msg)) await self.start_tts_usage_metrics(text) except Exception as e: yield ErrorFrame(error=f"Unknown error occurred: {e}") From 70552d76974786c07ae8a54616e9c9d4bbb34042 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 20 Mar 2026 11:35:25 -0400 Subject: [PATCH 153/159] Add changelog entry for #4091 --- changelog/4091.changed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4091.changed.md diff --git a/changelog/4091.changed.md b/changelog/4091.changed.md new file mode 100644 index 000000000..a05a981a3 --- /dev/null +++ b/changelog/4091.changed.md @@ -0,0 +1 @@ +- `GradiumTTSService` now sends a per-context `setup` message with `client_req_id` before the first text message for each TTS context, following Gradium's multiplexing protocol. Previously, a single setup message was sent at connection time without a `client_req_id`, which prevented Gradium from associating requests with their sessions when using `close_ws_on_eos=False`. From 3042929989fd47bad494835dacd7c6b27afb4106 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Mon, 23 Mar 2026 15:57:25 -0300 Subject: [PATCH 154/159] Fixing changelog description. --- changelog/4104.added.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/4104.added.md b/changelog/4104.added.md index ce948530e..284288159 100644 --- a/changelog/4104.added.md +++ b/changelog/4104.added.md @@ -1 +1 @@ -- Added `audio_out_insert_silence` parameter to `TransportParams` (defaults to `True`). When set to `False`, the SmallWebRTC transport waits for audio data instead of inserting silence when the output queue is empty, which is useful for scenarios that require uninterrupted audio playback without artificial gaps. +- Added `audio_out_insert_silence` parameter to `TransportParams` (defaults to `True`). When set to `False`, the transport waits for audio data instead of inserting silence when the output queue is empty, which is useful for scenarios that require uninterrupted audio playback without artificial gaps. From 8612c9f50a4c68ac48f510889aefa94b3fb28ce4 Mon Sep 17 00:00:00 2001 From: filipi87 Date: Mon, 23 Mar 2026 17:52:41 -0300 Subject: [PATCH 155/159] Updating to use daily-python 0.27.0 --- pyproject.toml | 2 +- uv.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 91afcc794..03fe4ebd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ azure = [ "azure-cognitiveservices-speech>=1.47.0,<2"] cartesia = [ "pipecat-ai[websockets-base]" ] camb = [ "camb-sdk>=1.5.4,<2" ] cerebras = [] -daily = [ "daily-python~=0.25.0" ] +daily = [ "daily-python~=0.27.0" ] deepgram = [ "deepgram-sdk>=6.0.1,<7", "pipecat-ai[websockets-base]" ] deepseek = [] elevenlabs = [ "pipecat-ai[websockets-base]" ] diff --git a/uv.lock b/uv.lock index 1263097a8..96b469aea 100644 --- a/uv.lock +++ b/uv.lock @@ -1402,13 +1402,13 @@ wheels = [ [[package]] name = "daily-python" -version = "0.25.0" +version = "0.27.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/58/074c6fca866fa13006b880eab521985d39300ea0d1df75a60d6dac4b2d47/daily_python-0.25.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:bff92b598863201bdffeea17819b7418d5c03a7ffc665a02be0333237fb3e4be", size = 13312157, upload-time = "2026-03-17T00:10:03.273Z" }, - { url = "https://files.pythonhosted.org/packages/ca/d1/0b623e8e6d06713e8d193335db1e5ec46760af276f08d8b378a0a8e4696b/daily_python-0.25.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:733c70e77bb1c8933a7fa779722592a109617b2d098c192399c2bfffcb210847", size = 11831685, upload-time = "2026-03-17T00:10:05.36Z" }, - { url = "https://files.pythonhosted.org/packages/e4/36/45c3a59e92a37e3a51dbe2603f9e855408ecdb48f1b4cd396de83839e6f9/daily_python-0.25.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a6da5f8c99ccbdb59042a4c5e48f9a85f68a32f65f16b3778da5f319fd6d7e17", size = 13865923, upload-time = "2026-03-17T00:10:07.391Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5e/4574dedb8faa6a578079c89825f17c07a0bd4e1b4c2d2161966e62a6c6aa/daily_python-0.25.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0258ce43b92682379ecfcee1fa276799ccf41db25410f86395ae9927c2661cba", size = 14439736, upload-time = "2026-03-17T00:10:09.51Z" }, + { url = "https://files.pythonhosted.org/packages/6a/0c/57a46f95b58548503b4633ca1067e25cee401c6274b61446b340a891483d/daily_python-0.27.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:1a8143e33b024ee9bfa25646bb3263d41e11e2b99344cd214f9306ea47554d56", size = 13310124, upload-time = "2026-03-23T20:19:56.234Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4a/e691c1d3f4682fc564e59781cea1fff6c5b850112dcdd78bc69c930f8bed/daily_python-0.27.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:dfc724d788b47c2d522fba7b37e38aaba00fd5a04ebbf964e5d52503ccadc1ff", size = 11834682, upload-time = "2026-03-23T20:19:58.487Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ac/00adc5a83dc0474ab4ae9404157ea8435c99c52f41270e93b8a64b1e7ea8/daily_python-0.27.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:386671b415a5c31f5ffc9e473128ec228abc77d8cefd43cdc3ca0b5ca416122b", size = 13866110, upload-time = "2026-03-23T20:20:00.438Z" }, + { url = "https://files.pythonhosted.org/packages/f3/0a/f9162dccde2d0ad9dea1cfe43f02fc0abf7f535cd4172984855d66c27bc7/daily_python-0.27.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:740be060cc173bfb42b1f788f2c34f46a32769fe06ffc23931768e004b5dea85", size = 14412100, upload-time = "2026-03-23T20:20:02.304Z" }, ] [[package]] @@ -4810,7 +4810,7 @@ requires-dist = [ { name = "azure-cognitiveservices-speech", marker = "extra == 'azure'", specifier = ">=1.47.0,<2" }, { name = "camb-sdk", marker = "extra == 'camb'", specifier = ">=1.5.4,<2" }, { name = "coremltools", marker = "extra == 'local-smart-turn'", specifier = ">=8.0" }, - { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.25.0" }, + { name = "daily-python", marker = "extra == 'daily'", specifier = "~=0.27.0" }, { name = "deepgram-sdk", marker = "extra == 'deepgram'", specifier = ">=6.0.1,<7" }, { name = "docstring-parser", specifier = ">=0.16,<1" }, { name = "einops", marker = "extra == 'moondream'", specifier = "~=0.8.0" }, From ddd1b71b5622ecaef97aa8e776364b60c842b80a Mon Sep 17 00:00:00 2001 From: filipi87 Date: Mon, 23 Mar 2026 17:57:42 -0300 Subject: [PATCH 156/159] Renaming audio_out_insert_silence to audio_out_auto_silence --- changelog/4104.added.md | 2 +- examples/foundational/21a-tavus-video-service.py | 4 ++-- src/pipecat/transports/base_transport.py | 4 ++-- src/pipecat/transports/daily/transport.py | 4 ++-- src/pipecat/transports/smallwebrtc/transport.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/changelog/4104.added.md b/changelog/4104.added.md index 284288159..31c6f3873 100644 --- a/changelog/4104.added.md +++ b/changelog/4104.added.md @@ -1 +1 @@ -- Added `audio_out_insert_silence` parameter to `TransportParams` (defaults to `True`). When set to `False`, the transport waits for audio data instead of inserting silence when the output queue is empty, which is useful for scenarios that require uninterrupted audio playback without artificial gaps. +- Added `audio_out_auto_silence` parameter to `TransportParams` (defaults to `True`). When set to `False`, the transport waits for audio data instead of inserting silence when the output queue is empty, which is useful for scenarios that require uninterrupted audio playback without artificial gaps. diff --git a/examples/foundational/21a-tavus-video-service.py b/examples/foundational/21a-tavus-video-service.py index 0b84b0f7b..5658951b5 100644 --- a/examples/foundational/21a-tavus-video-service.py +++ b/examples/foundational/21a-tavus-video-service.py @@ -42,7 +42,7 @@ transport_params = { video_out_is_live=True, video_out_width=1280, video_out_height=720, - audio_out_insert_silence=False, + audio_out_auto_silence=False, ), "webrtc": lambda: TransportParams( audio_in_enabled=True, @@ -51,7 +51,7 @@ transport_params = { video_out_is_live=True, video_out_width=1280, video_out_height=720, - audio_out_insert_silence=False, + audio_out_auto_silence=False, ), } diff --git a/src/pipecat/transports/base_transport.py b/src/pipecat/transports/base_transport.py index 33616a6ea..5b6c8673c 100644 --- a/src/pipecat/transports/base_transport.py +++ b/src/pipecat/transports/base_transport.py @@ -84,7 +84,7 @@ class TransportParams(BaseModel): audio_out_mixer: Audio mixer instance or destination mapping. audio_out_destinations: List of audio output destination identifiers. audio_out_end_silence_secs: How much silence to send after an EndFrame (0 for no silence). - audio_out_insert_silence: Insert silence frames when the audio output queue is empty. + audio_out_auto_silence: Insert silence frames when the audio output queue is empty. When False, the transport will wait for audio data instead of inserting silence. audio_in_enabled: Enable audio input streaming. audio_in_sample_rate: Input audio sample rate in Hz. @@ -146,7 +146,7 @@ class TransportParams(BaseModel): audio_out_mixer: Optional[BaseAudioMixer | Mapping[Optional[str], BaseAudioMixer]] = None audio_out_destinations: List[str] = Field(default_factory=list) audio_out_end_silence_secs: int = 2 - audio_out_insert_silence: bool = True + audio_out_auto_silence: bool = True audio_in_enabled: bool = False audio_in_sample_rate: Optional[int] = None audio_in_channels: int = 1 diff --git a/src/pipecat/transports/daily/transport.py b/src/pipecat/transports/daily/transport.py index f33fa79ff..8ef94ae40 100644 --- a/src/pipecat/transports/daily/transport.py +++ b/src/pipecat/transports/daily/transport.py @@ -836,12 +836,12 @@ class DailyTransportClient(EventHandler): if self._params.audio_out_enabled and not self._microphone_track: logger.debug( - f"Creating custom audio source, auto silence {self._params.audio_out_insert_silence}" + f"Creating custom audio source, auto silence {self._params.audio_out_auto_silence}" ) audio_source = CustomAudioSource( self._out_sample_rate, self._params.audio_out_channels, - self._params.audio_out_insert_silence, + self._params.audio_out_auto_silence, ) audio_track = CustomAudioTrack(audio_source) self._microphone_track = DailyAudioTrack(source=audio_source, track=audio_track) diff --git a/src/pipecat/transports/smallwebrtc/transport.py b/src/pipecat/transports/smallwebrtc/transport.py index b239f3831..42679de45 100644 --- a/src/pipecat/transports/smallwebrtc/transport.py +++ b/src/pipecat/transports/smallwebrtc/transport.py @@ -497,7 +497,7 @@ class SmallWebRTCClient: if self._params.audio_out_enabled: self._audio_output_track = RawAudioTrack( sample_rate=self._out_sample_rate, - insert_silence=self._params.audio_out_insert_silence, + insert_silence=self._params.audio_out_auto_silence, ) self._webrtc_connection.replace_audio_track(self._audio_output_track) From 066b206b3d24aecdf7c00ce4c98505134eedf17d Mon Sep 17 00:00:00 2001 From: filipi87 Date: Mon, 23 Mar 2026 18:12:26 -0300 Subject: [PATCH 157/159] Renaming insert_silence to auto_silence --- src/pipecat/transports/smallwebrtc/transport.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pipecat/transports/smallwebrtc/transport.py b/src/pipecat/transports/smallwebrtc/transport.py index 42679de45..76ea34464 100644 --- a/src/pipecat/transports/smallwebrtc/transport.py +++ b/src/pipecat/transports/smallwebrtc/transport.py @@ -79,17 +79,17 @@ class RawAudioTrack(AudioStreamTrack): supporting queued audio data with proper synchronization. """ - def __init__(self, sample_rate: int, insert_silence: bool = True): + def __init__(self, sample_rate: int, auto_silence: bool = True): """Initialize the raw audio track. Args: sample_rate: The audio sample rate in Hz. - insert_silence: If True, emit silence when the queue is empty. If False, + auto_silence: If True, emit silence when the queue is empty. If False, wait until audio data is available. """ super().__init__() self._sample_rate = sample_rate - self._insert_silence = insert_silence + self._auto_silence = auto_silence self._samples_per_10ms = sample_rate * 10 // 1000 self._bytes_per_10ms = self._samples_per_10ms * 2 # 16-bit (2 bytes per sample) self._timestamp = 0 @@ -127,7 +127,7 @@ class RawAudioTrack(AudioStreamTrack): Returns: An AudioFrame containing the next audio data, or silence if the queue is empty - and ``insert_silence`` is True. + and ``auto_silence`` is True. """ # Compute required wait time for synchronization if self._timestamp > 0: @@ -136,7 +136,7 @@ class RawAudioTrack(AudioStreamTrack): await asyncio.sleep(wait) if not self._chunk_queue: - if self._insert_silence: + if self._auto_silence: chunk = bytes(self._bytes_per_10ms) else: while not self._chunk_queue: @@ -497,7 +497,7 @@ class SmallWebRTCClient: if self._params.audio_out_enabled: self._audio_output_track = RawAudioTrack( sample_rate=self._out_sample_rate, - insert_silence=self._params.audio_out_auto_silence, + auto_silence=self._params.audio_out_auto_silence, ) self._webrtc_connection.replace_audio_track(self._audio_output_track) From 9211379720d0feddd754ed5a4da9c21454ad803d Mon Sep 17 00:00:00 2001 From: Aleix Conchillo Flaque Date: Mon, 23 Mar 2026 20:06:28 -0700 Subject: [PATCH 158/159] update uv.lock --- uv.lock | 1075 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 555 insertions(+), 520 deletions(-) diff --git a/uv.lock b/uv.lock index 96b469aea..c12dfc16d 100644 --- a/uv.lock +++ b/uv.lock @@ -30,44 +30,44 @@ wheels = [ [[package]] name = "aic-sdk" -version = "2.1.0" +version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/d1/faca6596c0d598063b4b9e879f4110fde9dee1496273d6410505bc81fdcc/aic_sdk-2.1.0.tar.gz", hash = "sha256:b661743dce36413ddd264b909d818dfc997c3a189e4c52fed263f2177ee3bb17", size = 5315216, upload-time = "2026-02-27T23:04:43.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/31/1631b1c8abbdcaa5978d2561b9f13430b1e9ed2b6e27d6e9cecc890ad4bd/aic_sdk-2.1.1.tar.gz", hash = "sha256:2d56a29ea79cc5373e44bdb09db58c837b4e6b5b09ce4c8a932ef709f535230e", size = 5315222, upload-time = "2026-03-19T10:50:59.52Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/69/c1585d8b1e98cc1614cb3825714c5dac962db4ca7febf0ebcd5fbdc125d7/aic_sdk-2.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ecbc90101cfa86f8428c699ffbbacd60b11f3d683f9cb82976a5e6dca7ab1bf1", size = 3592958, upload-time = "2026-02-27T23:03:56.657Z" }, - { url = "https://files.pythonhosted.org/packages/08/34/f1d09f74ff6e8c830777109008761a0452144ff14790b498bf423a99ff93/aic_sdk-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54b21f2fd8388e5077ae597e11e0222d4bdf2c2f15ed49296a8a231a3b19be82", size = 3204056, upload-time = "2026-02-27T23:03:58.204Z" }, - { url = "https://files.pythonhosted.org/packages/03/04/aa22b45ef00909ec1964fae1871b08e1c2282ebe29c71e51fc9e8702baef/aic_sdk-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57cd0938370b37ebab31d63912dbf93ac0dd727d9d95840f1bdcba71e1a885e9", size = 3120257, upload-time = "2026-02-27T23:03:59.505Z" }, - { url = "https://files.pythonhosted.org/packages/f8/17/44fc4a4da7e792fd6e00e720237ac3183b10f02fa77d68fa151632753ab0/aic_sdk-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b41c51553e37cf811cb8ba5e957581e3cd0c87f833789dcdb6ab62ba803bfdbf", size = 3476457, upload-time = "2026-02-27T23:04:00.84Z" }, - { url = "https://files.pythonhosted.org/packages/4b/7c/666d348e2502f0b8a58b858f45a528f02980ffff661512a73d6eb8b17c54/aic_sdk-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7359a5c1db01a916ebeea24906bdafa5f2bb320aada50bcca75e28a09be1f54e", size = 2961331, upload-time = "2026-02-27T23:04:02.254Z" }, - { url = "https://files.pythonhosted.org/packages/4d/18/5e02f96c52ee92cf91d4044ef426a45a65863617b388cd45f487d334bf62/aic_sdk-2.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:3a78f68b6073113a41615614de760c734d776bcdd5a0340c7d77e8dbb45bde55", size = 2647972, upload-time = "2026-02-27T23:04:03.916Z" }, - { url = "https://files.pythonhosted.org/packages/b1/33/db0009e8c0337a14c4501d0bcf081ed7949e95dcc48fd49734b4f7a32715/aic_sdk-2.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b574c041e1624dd0a4c6a34517dfe40bef26749716d398e169c6729a44eb166c", size = 3592590, upload-time = "2026-02-27T23:04:05.343Z" }, - { url = "https://files.pythonhosted.org/packages/dc/79/345a3c1cacd14d12bcfa0a45563ae0470cc227f3bc8ab2c64753b886036e/aic_sdk-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9027253eb9d438e1e859578c381b57f0f71cfd96186e3999c25cbcfc6ac9a6e6", size = 3204174, upload-time = "2026-02-27T23:04:07.056Z" }, - { url = "https://files.pythonhosted.org/packages/9e/28/109e9d69a95980d0805a95ffe934a7653fcef64fdb8f2ed0dffe2c6ba4d9/aic_sdk-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6003104cc44e588a022d0bbdcc09db38de20a058e07e8ae160006f0abdbe9874", size = 3120225, upload-time = "2026-02-27T23:04:08.679Z" }, - { url = "https://files.pythonhosted.org/packages/bd/74/ba9be31a9e47c6297cae39008b6893801626f0a45b61f7543d2bc42449e3/aic_sdk-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:134d847967549c45f3bc952c47c7f1c6cf76be4e61e56c9ae1df60fc1657ccb4", size = 3476431, upload-time = "2026-02-27T23:04:10.276Z" }, - { url = "https://files.pythonhosted.org/packages/6a/50/f94aabf70fcf443d1b6c256675e7a67ce25bec47daeefc93ec561b0fb0da/aic_sdk-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d85c3419825d154889bfebaa5d8a50855ddfb4d9b24722f174fe7e4666d1deb", size = 2961012, upload-time = "2026-02-27T23:04:11.996Z" }, - { url = "https://files.pythonhosted.org/packages/52/ec/ca4dae8adb9b799c1a4455a1e5f39242c73e87c10b71e57eaf650d6c455a/aic_sdk-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:c612f246ccf2f10e57151972926b7aefdf0d006633a2496f0e5f31dae5ce6b4a", size = 2648071, upload-time = "2026-02-27T23:04:13.658Z" }, - { url = "https://files.pythonhosted.org/packages/7f/57/6628d40bee36683fc0326cb04cda86110117cac1d8f0be01853fe5947901/aic_sdk-2.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:aa48d55ae3fa8768616f79e1d6794558edb35aa622b3741380b266ff62d7e109", size = 3592839, upload-time = "2026-02-27T23:04:15.968Z" }, - { url = "https://files.pythonhosted.org/packages/6f/1c/b4dd728d224159b282c23a1da2f3469b4c73c786067214e11e4612948e99/aic_sdk-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d67c00d462342381f44de49340482e1f29625af71f5266bd2a96bbfe5beb684", size = 3204605, upload-time = "2026-02-27T23:04:18.658Z" }, - { url = "https://files.pythonhosted.org/packages/6e/1b/0826e0fe91efd84899afe645ac184514a1f326beccc741c7a2dda65a44a5/aic_sdk-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e36280c0d6adfff644c8c29c6274e12fb0d805b7c97cf6395f35c10f45e1150", size = 3119984, upload-time = "2026-02-27T23:04:20.309Z" }, - { url = "https://files.pythonhosted.org/packages/6b/34/aa722c8fb6770713caf85c3ead104ddbe64d099f0627da67acc95f9dad40/aic_sdk-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b110813c3784ba37c4df992a157ac53f4a65fd17aec604f233eda031857607d2", size = 3477716, upload-time = "2026-02-27T23:04:21.643Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a7/e2173e19153e91520b0926f53649fdda37bda40082b66e42039bfacfbb27/aic_sdk-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:98ce4ba6d3afe8c04425a0b3fd630369f1300ed1bfee82d9f197d521827aa257", size = 2958707, upload-time = "2026-02-27T23:04:22.992Z" }, - { url = "https://files.pythonhosted.org/packages/61/83/4bb29c673e739453309c6e5acac1fd451bf9ecc12ba8f0bfeb6497e9658a/aic_sdk-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:aedeedc12009ca0e0dd662aa5df55fd3cfcbe4d67eb84e08b7df92288c6f2908", size = 2644622, upload-time = "2026-02-27T23:04:24.328Z" }, - { url = "https://files.pythonhosted.org/packages/1f/07/84856156fb2fbe17f502b6e37468010eeb5d699ab818047524e1ea98fe28/aic_sdk-2.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7b837806b43959ee7405ffd5a129bd5414651664ae503b6cee742331fbb86c72", size = 3592075, upload-time = "2026-02-27T23:04:25.699Z" }, - { url = "https://files.pythonhosted.org/packages/44/8d/29beab45bd22f95adf5f1db51d6c56386b9e83b4518c1db37f867523b419/aic_sdk-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e0b5d2117f203de43e1e4edb85ca751cb62ccc74a1a216a9f76f0f171c36c25", size = 3204125, upload-time = "2026-02-27T23:04:27.046Z" }, - { url = "https://files.pythonhosted.org/packages/7b/d1/cc87240ad4907d23c96cc9b4645e6fd9b73f48e3a2e337dd2c7bf7f72d6e/aic_sdk-2.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322b56a6c636acd5b869c09a55deb280f8eb56e7b9d13cb9ac833616491b0c46", size = 3119501, upload-time = "2026-02-27T23:04:28.28Z" }, - { url = "https://files.pythonhosted.org/packages/87/b3/a15bb0721c54b440291c4f0f58cacb3f8f7a6848394956aca7e5dcad59a4/aic_sdk-2.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ce9b1f54150644825e2ffda7f2a8eb6a60d207c4e2265124afb90277351065", size = 3476855, upload-time = "2026-02-27T23:04:29.642Z" }, - { url = "https://files.pythonhosted.org/packages/26/f9/76ac25c997569248d3bfa0c812468f3917a15bcd957c3ed7e066ab928d56/aic_sdk-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:4290a6d0220fe8e9edd1c328d5c2b6bc8c4f4c36b50c24ab0eb073a71d8c58be", size = 2958258, upload-time = "2026-02-27T23:04:31.493Z" }, - { url = "https://files.pythonhosted.org/packages/ee/12/c463bbbc71c19fb1b74015f72301c3d762acc83a8311c1f2f9d9150c9926/aic_sdk-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:bee85004a44bcc50146c07796d3f71ab58df465a1110a98a04d715c54748910e", size = 2644105, upload-time = "2026-02-27T23:04:32.728Z" }, - { url = "https://files.pythonhosted.org/packages/f5/05/f78557c1c8636d3f2a25a74b38fb805ef98ca90aaaa772f83b77f2123072/aic_sdk-2.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:4c1f4cf18e7f44bc554dc90d94d6ff5250f9ffcc0edf91fab0e95f11ae859ac9", size = 3593602, upload-time = "2026-02-27T23:04:34.699Z" }, - { url = "https://files.pythonhosted.org/packages/17/87/67c333feb57df6a288f8b7c5245feab5b2248870499a958121f103f60b1c/aic_sdk-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:679260aaa6ecb8ebf531c10d2eaa148edfc91c1c31a84bf63d8c3aa691d09afc", size = 3204838, upload-time = "2026-02-27T23:04:36.326Z" }, - { url = "https://files.pythonhosted.org/packages/4e/a6/9964ab0e93139f214a23717101b6834ca3422f5a6787ca61ea852f6772ec/aic_sdk-2.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcf8e9d84a3cefff9aca3a84224c210439e9590d8461d8c718e0cee5a7054744", size = 3120147, upload-time = "2026-02-27T23:04:37.701Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f2/c6823e1f02559884eed7aeb4b580d5155568628748af844952fdf833858e/aic_sdk-2.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e845faeaab18ce76d8ad81d9dbbeab8bca12c7f8503a1e5a6a638e2fac5ee8d", size = 3478437, upload-time = "2026-02-27T23:04:39.092Z" }, - { url = "https://files.pythonhosted.org/packages/96/9c/21e7a85d4ad27cce9e8e385f5ab2f1b25b547cc74597a806945025d75efc/aic_sdk-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:31434001b7963bbc8a92602b6e57149c95f94e7430edbb956230f29235b9098a", size = 2960332, upload-time = "2026-02-27T23:04:40.376Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e4/0a537fb4653deedfbd0b8f55eceef618f0555c641751533cbeaf9f302717/aic_sdk-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:2efff1e70704f0cc44bab078a2d227eaf94e0c63c32f6bfbe63915dc5335161e", size = 2645446, upload-time = "2026-02-27T23:04:42.107Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ec/295e05cc0fdbdd96ae903385ada7a7c03292d9db995f936c6e06de42d3a6/aic_sdk-2.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:8bef2d11f8cd97d79a38b8e22d6e4ed16a095e8cf90e45f7ca981c1a7910e987", size = 3577606, upload-time = "2026-03-19T10:50:07.204Z" }, + { url = "https://files.pythonhosted.org/packages/70/92/4d53d0e60698215aab1532774ee13d4160e060939c1b03924b7f8f1f75ee/aic_sdk-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b24763ad32fd63c64165123e164bf7df535927f1f6dbf7c950e63f650fec56a7", size = 3192682, upload-time = "2026-03-19T10:50:09.303Z" }, + { url = "https://files.pythonhosted.org/packages/6b/2f/64bb951ab9da66eeac362a2e7f3ed021e783624220bc7c073fe64b836f8b/aic_sdk-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e7c78915f435c1ce39eb3ff98b3851e9da4d9eb5426b7e17cc65b59e33b0205", size = 3125008, upload-time = "2026-03-19T10:50:10.88Z" }, + { url = "https://files.pythonhosted.org/packages/da/25/8104d87fb8a8d7729b60232ff6319ca85887d6726770db14a1275900b4dd/aic_sdk-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15a182d4b6a570141f604a3a23b08d0256e31d82621ea0fd4808913f08a80b7b", size = 3476000, upload-time = "2026-03-19T10:50:12.308Z" }, + { url = "https://files.pythonhosted.org/packages/02/69/8615772a97a549dfda2329dff866cfa6ec6674732c412437ed518f392385/aic_sdk-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d2bc3198635b19dd0fbc62ead94d6422c8019023e8b2d528c443ffb5dfc7b8bb", size = 2961107, upload-time = "2026-03-19T10:50:14.443Z" }, + { url = "https://files.pythonhosted.org/packages/78/5a/849ead73f04edd55231818d6f4ef2aeca6e8b32a8040d5d44e0ed8dbb0b6/aic_sdk-2.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:43635dfe8c8ab1ebad12d647a9a98c1536a43d0df2f170f4aa9a2819f751cc07", size = 2659711, upload-time = "2026-03-19T10:50:16.178Z" }, + { url = "https://files.pythonhosted.org/packages/49/55/6ba78de1d50304358d6044d5d955e252a2249a0f4b2cee09236fdbf45f5c/aic_sdk-2.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:83f9de8456b0e029e103a25ab85145a63594531e5ba97bc42f3c930e936f4bae", size = 3577462, upload-time = "2026-03-19T10:50:17.706Z" }, + { url = "https://files.pythonhosted.org/packages/ec/dc/c4b53ed252cf5bc882481bdf0473b1a2ca8ee0131aeaeb4bae6955db8e7a/aic_sdk-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:074d3fba2e31cf79e7834107e00910681fa94bd97d773b4230db54edf0f741c1", size = 3192533, upload-time = "2026-03-19T10:50:19.547Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1c/b86207570a96d643317b3bbe3c6141daa3cef3a0982266fe39163613593c/aic_sdk-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:490611f754c6ac3392f54b52f6f6ccd0787bae940217e0e5800bc3ab69ed4c9a", size = 3124929, upload-time = "2026-03-19T10:50:21.441Z" }, + { url = "https://files.pythonhosted.org/packages/d3/dc/b5a45a0447431161dd9ffd2de7ac80bb456399395127d0e8bcf7c9a30e81/aic_sdk-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:babc3ad0b4850ac389fb36fda72800b30262af2371bb6e7704ef2e5de6922766", size = 3476070, upload-time = "2026-03-19T10:50:23.016Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ee7c17c147641b0df140b5a5a444d57dc1581aebe5de8a9ad8fec09f1067/aic_sdk-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb06ad8f1999fef7239d193886daf8f378ed09633e46b33b53d378be1060e271", size = 2961137, upload-time = "2026-03-19T10:50:25Z" }, + { url = "https://files.pythonhosted.org/packages/ad/38/561b3e3577e0ea1c947689a9726b13129e2595f175bfb2dd32423767cd14/aic_sdk-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d78cd33338815d282f996b0febb5c21f47f5217befc96b9d3e93783189ef6e2", size = 2659726, upload-time = "2026-03-19T10:50:26.609Z" }, + { url = "https://files.pythonhosted.org/packages/23/e6/526373728757dec7fdbcbcbcb44152dc9233235a03e90f27df96ba9a5960/aic_sdk-2.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:eaddc53ad93c642895beaaf04a5157b65d00b63a986a2ed60ea970017949725a", size = 3578492, upload-time = "2026-03-19T10:50:28.357Z" }, + { url = "https://files.pythonhosted.org/packages/c8/35/cdc914c1b0fd2292b404d689f0948d96135903f437bb20bf729dba04e899/aic_sdk-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b790c72a278217bdbd8cbef2261d7e902684eb979dde5ec891d075d1c4c7e97e", size = 3192709, upload-time = "2026-03-19T10:50:30.617Z" }, + { url = "https://files.pythonhosted.org/packages/2f/88/7c00e5683a729e8294171dcb59430af7a429d54666c567b30b20be7f6174/aic_sdk-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db6f2794db724e7f55d4cf1233d0bf802777fbf943eae9a1c05786dbe6e43910", size = 3124760, upload-time = "2026-03-19T10:50:32.587Z" }, + { url = "https://files.pythonhosted.org/packages/42/b1/a575df4e2ee5e6f9e3dec777fd91c40b1bdcbf30a36c74eeac4e7425d085/aic_sdk-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2413fb23dceef8232cd4b4553775f1ca668e1270dd2b35b73359ac3dbd58d701", size = 3477318, upload-time = "2026-03-19T10:50:34.336Z" }, + { url = "https://files.pythonhosted.org/packages/36/8d/6009b04f27053258673c260b0cd6f72bda8f5c50bfe6500b7f2f574dd304/aic_sdk-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:d370ef2166675ad79e16822cc26f67d3b7e0f8fe52567dc42e77339ec11f124e", size = 2958807, upload-time = "2026-03-19T10:50:35.966Z" }, + { url = "https://files.pythonhosted.org/packages/4f/cb/298d765c6b62867ed3eec21dcd566e4926955041612494b793a57e08db3f/aic_sdk-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:c60e9d8e3c363b3ba03e3d666f99c706700b3636cbc98443d08fdc2084d0651b", size = 2656909, upload-time = "2026-03-19T10:50:37.749Z" }, + { url = "https://files.pythonhosted.org/packages/89/e2/966a72c4c0c5b771187a0494cdef1a8f27d137225ccddb5fa04612f4a7ad/aic_sdk-2.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2db12980bf64c4a66a1d0bb469aa45c314315b8ac8328dc2a94f6e65480da359", size = 3577887, upload-time = "2026-03-19T10:50:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/8e/78/61518fce28d058859d9065baeff6d0dbabd709245fa42a6c20286dd49fa3/aic_sdk-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c168ed46cba940b1808ffe8a6c709b09091b918c9120f1318672fd340caa35b4", size = 3192498, upload-time = "2026-03-19T10:50:40.978Z" }, + { url = "https://files.pythonhosted.org/packages/94/98/fbf4800329a2e51cdfd9ee19f0db998495374b16a935ae5d9183a40c5bb8/aic_sdk-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46fe3f666f2e6488e421c6d5d8abe12e5905940cfa67ea63ca6535f51cca2233", size = 3124253, upload-time = "2026-03-19T10:50:42.518Z" }, + { url = "https://files.pythonhosted.org/packages/90/bf/6485d64c3c2d9c579a95b05e96b08152ffa5e0d133b7ee7f1f07c10d8535/aic_sdk-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8a3518aae32a8f60acb9ff598d2a04d1b2de82c9614990b2a20a17db38657d4", size = 3476777, upload-time = "2026-03-19T10:50:44.034Z" }, + { url = "https://files.pythonhosted.org/packages/6d/28/ad19c96dcba75a5cb93683d123cb2356734e39df4dd7a52545e8c494786f/aic_sdk-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1a706954c0bd18552703ea792f16789e9e823795c16dd8a94b5e1a54ec451d95", size = 2958541, upload-time = "2026-03-19T10:50:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f8/36a32f14ccdf55535176a1da44619647df53fc99560869204a6e2f741926/aic_sdk-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:e30fa36e3e516e403f0770b1e213d2217c9f3c61a36615f59316d0d6d0f81e6a", size = 2656460, upload-time = "2026-03-19T10:50:47.785Z" }, + { url = "https://files.pythonhosted.org/packages/21/e0/3a9dd88872eae43e8885d171840f97a4a9c31578d81be588402b42fd242c/aic_sdk-2.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:0b54b143e34f0adaa72863999a3913faab938f3444d9ee7bb97d2edce2fb10d8", size = 3579058, upload-time = "2026-03-19T10:50:49.32Z" }, + { url = "https://files.pythonhosted.org/packages/38/d4/58715c8188afb594a8673edfa5a04b016a62a96d18e883522667f27032e2/aic_sdk-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:357feeee734e8338118fb6f4d8496709756006d780eaac484da06c314e133298", size = 3193006, upload-time = "2026-03-19T10:50:50.701Z" }, + { url = "https://files.pythonhosted.org/packages/7e/1a/bbcef1e4dad8b653516b94fab9602b8292d821d3d0ecee9a66ea6487845a/aic_sdk-2.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85e0d5092e70fa746fd758d1c7fc9896740da0a57e698949afc10bac9fe12216", size = 3125311, upload-time = "2026-03-19T10:50:52.576Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7f/482760d46ec39dcc91dfc8500be9e032b81124907aa6cafce22df94b6194/aic_sdk-2.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38087d418d1ff8215ce6c634db3eb7c40f109f096e7e5736f56f7a35a751e886", size = 3478229, upload-time = "2026-03-19T10:50:54.613Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f2/8aa7bb5c140ae2f8c0fb3e3b2e75a4125f3614b49a1da0897e301ca73446/aic_sdk-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:7fd3e2b7f2ac79b83722abc66db0813061f6a53143cb39aaabff034ef1811665", size = 2960375, upload-time = "2026-03-19T10:50:56.467Z" }, + { url = "https://files.pythonhosted.org/packages/92/25/76a52222b8f096218cb4dc24d3d4930bbf5769987cc6c6c4999cdbf404eb/aic_sdk-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ac14f55a31906e36f51d8e5585d0096a1e1de685711bb16d3f6baaac120c4102", size = 2658269, upload-time = "2026-03-19T10:50:57.96Z" }, ] [[package]] @@ -589,15 +589,15 @@ wheels = [ [[package]] name = "azure-core" -version = "1.38.3" +version = "1.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c8/29/9641b73248745774a52c7ce7f965ed1febbdea787ec21caad3ae6891d18a/azure_core-1.38.3.tar.gz", hash = "sha256:a7931fd445cb4af8802c6f39c6a326bbd1e34b115846550a8245fa656ead6f8e", size = 367267, upload-time = "2026-03-12T20:28:21.122Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/83/bbde3faa84ddcb8eb0eca4b3ffb3221252281db4ce351300fe248c5c70b1/azure_core-1.39.0.tar.gz", hash = "sha256:8a90a562998dd44ce84597590fff6249701b98c0e8797c95fcdd695b54c35d74", size = 367531, upload-time = "2026-03-19T01:31:29.461Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3d/ac86083efa45a439d0bbfb7947615227813d368b9e1e93d23fd30de6fec0/azure_core-1.38.3-py3-none-any.whl", hash = "sha256:bf59d29765bf4748ab9edf25f98a30b7ea9797f43e367c06d846a30b29c1f845", size = 218231, upload-time = "2026-03-12T20:28:22.462Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d6/8ebcd05b01a580f086ac9a97fb9fac65c09a4b012161cc97c21a336e880b/azure_core-1.39.0-py3-none-any.whl", hash = "sha256:4ac7b70fab5438c3f68770649a78daf97833caa83827f91df9c14e0e0ea7d34f", size = 218318, upload-time = "2026-03-19T01:31:31.25Z" }, ] [[package]] @@ -1130,115 +1130,115 @@ wheels = [ [[package]] name = "coverage" -version = "7.13.4" +version = "7.13.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/d4/7827d9ffa34d5d4d752eec907022aa417120936282fc488306f5da08c292/coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415", size = 219152, upload-time = "2026-02-09T12:56:11.974Z" }, - { url = "https://files.pythonhosted.org/packages/35/b0/d69df26607c64043292644dbb9dc54b0856fabaa2cbb1eeee3331cc9e280/coverage-7.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b", size = 219667, upload-time = "2026-02-09T12:56:13.33Z" }, - { url = "https://files.pythonhosted.org/packages/82/a4/c1523f7c9e47b2271dbf8c2a097e7a1f89ef0d66f5840bb59b7e8814157b/coverage-7.13.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a", size = 246425, upload-time = "2026-02-09T12:56:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/f8/02/aa7ec01d1a5023c4b680ab7257f9bfde9defe8fdddfe40be096ac19e8177/coverage-7.13.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f", size = 248229, upload-time = "2026-02-09T12:56:16.31Z" }, - { url = "https://files.pythonhosted.org/packages/35/98/85aba0aed5126d896162087ef3f0e789a225697245256fc6181b95f47207/coverage-7.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012", size = 250106, upload-time = "2026-02-09T12:56:18.024Z" }, - { url = "https://files.pythonhosted.org/packages/96/72/1db59bd67494bc162e3e4cd5fbc7edba2c7026b22f7c8ef1496d58c2b94c/coverage-7.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def", size = 252021, upload-time = "2026-02-09T12:56:19.272Z" }, - { url = "https://files.pythonhosted.org/packages/9d/97/72899c59c7066961de6e3daa142d459d47d104956db43e057e034f015c8a/coverage-7.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256", size = 247114, upload-time = "2026-02-09T12:56:21.051Z" }, - { url = "https://files.pythonhosted.org/packages/39/1f/f1885573b5970235e908da4389176936c8933e86cb316b9620aab1585fa2/coverage-7.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda", size = 248143, upload-time = "2026-02-09T12:56:22.585Z" }, - { url = "https://files.pythonhosted.org/packages/a8/cf/e80390c5b7480b722fa3e994f8202807799b85bc562aa4f1dde209fbb7be/coverage-7.13.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92", size = 246152, upload-time = "2026-02-09T12:56:23.748Z" }, - { url = "https://files.pythonhosted.org/packages/44/bf/f89a8350d85572f95412debb0fb9bb4795b1d5b5232bd652923c759e787b/coverage-7.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c", size = 249959, upload-time = "2026-02-09T12:56:25.209Z" }, - { url = "https://files.pythonhosted.org/packages/f7/6e/612a02aece8178c818df273e8d1642190c4875402ca2ba74514394b27aba/coverage-7.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58", size = 246416, upload-time = "2026-02-09T12:56:26.475Z" }, - { url = "https://files.pythonhosted.org/packages/cb/98/b5afc39af67c2fa6786b03c3a7091fc300947387ce8914b096db8a73d67a/coverage-7.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9", size = 247025, upload-time = "2026-02-09T12:56:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/51/30/2bba8ef0682d5bd210c38fe497e12a06c9f8d663f7025e9f5c2c31ce847d/coverage-7.13.4-cp310-cp310-win32.whl", hash = "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf", size = 221758, upload-time = "2026-02-09T12:56:29.051Z" }, - { url = "https://files.pythonhosted.org/packages/78/13/331f94934cf6c092b8ea59ff868eb587bc8fe0893f02c55bc6c0183a192e/coverage-7.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95", size = 222693, upload-time = "2026-02-09T12:56:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/b4/ad/b59e5b451cf7172b8d1043dc0fa718f23aab379bc1521ee13d4bd9bfa960/coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053", size = 219278, upload-time = "2026-02-09T12:56:31.673Z" }, - { url = "https://files.pythonhosted.org/packages/f1/17/0cb7ca3de72e5f4ef2ec2fa0089beafbcaaaead1844e8b8a63d35173d77d/coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11", size = 219783, upload-time = "2026-02-09T12:56:33.104Z" }, - { url = "https://files.pythonhosted.org/packages/ab/63/325d8e5b11e0eaf6d0f6a44fad444ae58820929a9b0de943fa377fe73e85/coverage-7.13.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa", size = 250200, upload-time = "2026-02-09T12:56:34.474Z" }, - { url = "https://files.pythonhosted.org/packages/76/53/c16972708cbb79f2942922571a687c52bd109a7bd51175aeb7558dff2236/coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7", size = 252114, upload-time = "2026-02-09T12:56:35.749Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c2/7ab36d8b8cc412bec9ea2d07c83c48930eb4ba649634ba00cb7e4e0f9017/coverage-7.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00", size = 254220, upload-time = "2026-02-09T12:56:37.796Z" }, - { url = "https://files.pythonhosted.org/packages/d6/4d/cf52c9a3322c89a0e6febdfbc83bb45c0ed3c64ad14081b9503adee702e7/coverage-7.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef", size = 256164, upload-time = "2026-02-09T12:56:39.016Z" }, - { url = "https://files.pythonhosted.org/packages/78/e9/eb1dd17bd6de8289df3580e967e78294f352a5df8a57ff4671ee5fc3dcd0/coverage-7.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903", size = 250325, upload-time = "2026-02-09T12:56:40.668Z" }, - { url = "https://files.pythonhosted.org/packages/71/07/8c1542aa873728f72267c07278c5cc0ec91356daf974df21335ccdb46368/coverage-7.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f", size = 251913, upload-time = "2026-02-09T12:56:41.97Z" }, - { url = "https://files.pythonhosted.org/packages/74/d7/c62e2c5e4483a748e27868e4c32ad3daa9bdddbba58e1bc7a15e252baa74/coverage-7.13.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299", size = 249974, upload-time = "2026-02-09T12:56:43.323Z" }, - { url = "https://files.pythonhosted.org/packages/98/9f/4c5c015a6e98ced54efd0f5cf8d31b88e5504ecb6857585fc0161bb1e600/coverage-7.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505", size = 253741, upload-time = "2026-02-09T12:56:45.155Z" }, - { url = "https://files.pythonhosted.org/packages/bd/59/0f4eef89b9f0fcd9633b5d350016f54126ab49426a70ff4c4e87446cabdc/coverage-7.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6", size = 249695, upload-time = "2026-02-09T12:56:46.636Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2c/b7476f938deb07166f3eb281a385c262675d688ff4659ad56c6c6b8e2e70/coverage-7.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9", size = 250599, upload-time = "2026-02-09T12:56:48.13Z" }, - { url = "https://files.pythonhosted.org/packages/b8/34/c3420709d9846ee3785b9f2831b4d94f276f38884032dca1457fa83f7476/coverage-7.13.4-cp311-cp311-win32.whl", hash = "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9", size = 221780, upload-time = "2026-02-09T12:56:50.479Z" }, - { url = "https://files.pythonhosted.org/packages/61/08/3d9c8613079d2b11c185b865de9a4c1a68850cfda2b357fae365cf609f29/coverage-7.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f", size = 222715, upload-time = "2026-02-09T12:56:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/18/1a/54c3c80b2f056164cc0a6cdcb040733760c7c4be9d780fe655f356f433e4/coverage-7.13.4-cp311-cp311-win_arm64.whl", hash = "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f", size = 221385, upload-time = "2026-02-09T12:56:53.194Z" }, - { url = "https://files.pythonhosted.org/packages/d1/81/4ce2fdd909c5a0ed1f6dedb88aa57ab79b6d1fbd9b588c1ac7ef45659566/coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", size = 219449, upload-time = "2026-02-09T12:56:54.889Z" }, - { url = "https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", size = 219810, upload-time = "2026-02-09T12:56:56.33Z" }, - { url = "https://files.pythonhosted.org/packages/78/72/2f372b726d433c9c35e56377cf1d513b4c16fe51841060d826b95caacec1/coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", size = 251308, upload-time = "2026-02-09T12:56:57.858Z" }, - { url = "https://files.pythonhosted.org/packages/5d/a0/2ea570925524ef4e00bb6c82649f5682a77fac5ab910a65c9284de422600/coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3", size = 254052, upload-time = "2026-02-09T12:56:59.754Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ac/45dc2e19a1939098d783c846e130b8f862fbb50d09e0af663988f2f21973/coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa", size = 255165, upload-time = "2026-02-09T12:57:01.287Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4d/26d236ff35abc3b5e63540d3386e4c3b192168c1d96da5cb2f43c640970f/coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3", size = 257432, upload-time = "2026-02-09T12:57:02.637Z" }, - { url = "https://files.pythonhosted.org/packages/ec/55/14a966c757d1348b2e19caf699415a2a4c4f7feaa4bbc6326a51f5c7dd1b/coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a", size = 251716, upload-time = "2026-02-09T12:57:04.056Z" }, - { url = "https://files.pythonhosted.org/packages/77/33/50116647905837c66d28b2af1321b845d5f5d19be9655cb84d4a0ea806b4/coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7", size = 253089, upload-time = "2026-02-09T12:57:05.503Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b4/8efb11a46e3665d92635a56e4f2d4529de6d33f2cb38afd47d779d15fc99/coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc", size = 251232, upload-time = "2026-02-09T12:57:06.879Z" }, - { url = "https://files.pythonhosted.org/packages/51/24/8cd73dd399b812cc76bb0ac260e671c4163093441847ffe058ac9fda1e32/coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47", size = 255299, upload-time = "2026-02-09T12:57:08.245Z" }, - { url = "https://files.pythonhosted.org/packages/03/94/0a4b12f1d0e029ce1ccc1c800944a9984cbe7d678e470bb6d3c6bc38a0da/coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985", size = 250796, upload-time = "2026-02-09T12:57:10.142Z" }, - { url = "https://files.pythonhosted.org/packages/73/44/6002fbf88f6698ca034360ce474c406be6d5a985b3fdb3401128031eef6b/coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0", size = 252673, upload-time = "2026-02-09T12:57:12.197Z" }, - { url = "https://files.pythonhosted.org/packages/de/c6/a0279f7c00e786be75a749a5674e6fa267bcbd8209cd10c9a450c655dfa7/coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246", size = 221990, upload-time = "2026-02-09T12:57:14.085Z" }, - { url = "https://files.pythonhosted.org/packages/77/4e/c0a25a425fcf5557d9abd18419c95b63922e897bc86c1f327f155ef234a9/coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126", size = 222800, upload-time = "2026-02-09T12:57:15.944Z" }, - { url = "https://files.pythonhosted.org/packages/47/ac/92da44ad9a6f4e3a7debd178949d6f3769bedca33830ce9b1dcdab589a37/coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d", size = 221415, upload-time = "2026-02-09T12:57:17.497Z" }, - { url = "https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", size = 219474, upload-time = "2026-02-09T12:57:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", size = 219844, upload-time = "2026-02-09T12:57:20.66Z" }, - { url = "https://files.pythonhosted.org/packages/97/fd/7e859f8fab324cef6c4ad7cff156ca7c489fef9179d5749b0c8d321281c2/coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", size = 250832, upload-time = "2026-02-09T12:57:22.007Z" }, - { url = "https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", size = 253434, upload-time = "2026-02-09T12:57:23.339Z" }, - { url = "https://files.pythonhosted.org/packages/5a/88/6728a7ad17428b18d836540630487231f5470fb82454871149502f5e5aa2/coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", size = 254676, upload-time = "2026-02-09T12:57:24.774Z" }, - { url = "https://files.pythonhosted.org/packages/7c/bc/21244b1b8cedf0dff0a2b53b208015fe798d5f2a8d5348dbfece04224fff/coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", size = 256807, upload-time = "2026-02-09T12:57:26.125Z" }, - { url = "https://files.pythonhosted.org/packages/97/a0/ddba7ed3251cff51006737a727d84e05b61517d1784a9988a846ba508877/coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", size = 251058, upload-time = "2026-02-09T12:57:27.614Z" }, - { url = "https://files.pythonhosted.org/packages/9b/55/e289addf7ff54d3a540526f33751951bf0878f3809b47f6dfb3def69c6f7/coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", size = 252805, upload-time = "2026-02-09T12:57:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/13/4e/cc276b1fa4a59be56d96f1dabddbdc30f4ba22e3b1cd42504c37b3313255/coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", size = 250766, upload-time = "2026-02-09T12:57:30.522Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/1093b8f93018f8b41a8cf29636c9292502f05e4a113d4d107d14a3acd044/coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", size = 254923, upload-time = "2026-02-09T12:57:31.946Z" }, - { url = "https://files.pythonhosted.org/packages/8b/55/ea2796da2d42257f37dbea1aab239ba9263b31bd91d5527cdd6db5efe174/coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", size = 250591, upload-time = "2026-02-09T12:57:33.842Z" }, - { url = "https://files.pythonhosted.org/packages/d4/fa/7c4bb72aacf8af5020675aa633e59c1fbe296d22aed191b6a5b711eb2bc7/coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", size = 252364, upload-time = "2026-02-09T12:57:35.743Z" }, - { url = "https://files.pythonhosted.org/packages/5c/38/a8d2ec0146479c20bbaa7181b5b455a0c41101eed57f10dd19a78ab44c80/coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d", size = 222010, upload-time = "2026-02-09T12:57:37.25Z" }, - { url = "https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd", size = 222818, upload-time = "2026-02-09T12:57:38.734Z" }, - { url = "https://files.pythonhosted.org/packages/04/d1/934918a138c932c90d78301f45f677fb05c39a3112b96fd2c8e60503cdc7/coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af", size = 221438, upload-time = "2026-02-09T12:57:40.223Z" }, - { url = "https://files.pythonhosted.org/packages/52/57/ee93ced533bcb3e6df961c0c6e42da2fc6addae53fb95b94a89b1e33ebd7/coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", size = 220165, upload-time = "2026-02-09T12:57:41.639Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e0/969fc285a6fbdda49d91af278488d904dcd7651b2693872f0ff94e40e84a/coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", size = 220516, upload-time = "2026-02-09T12:57:44.215Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b8/9531944e16267e2735a30a9641ff49671f07e8138ecf1ca13db9fd2560c7/coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", size = 261804, upload-time = "2026-02-09T12:57:45.989Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f3/e63df6d500314a2a60390d1989240d5f27318a7a68fa30ad3806e2a9323e/coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", size = 263885, upload-time = "2026-02-09T12:57:47.42Z" }, - { url = "https://files.pythonhosted.org/packages/f3/67/7654810de580e14b37670b60a09c599fa348e48312db5b216d730857ffe6/coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", size = 266308, upload-time = "2026-02-09T12:57:49.345Z" }, - { url = "https://files.pythonhosted.org/packages/37/6f/39d41eca0eab3cc82115953ad41c4e77935286c930e8fad15eaed1389d83/coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", size = 267452, upload-time = "2026-02-09T12:57:50.811Z" }, - { url = "https://files.pythonhosted.org/packages/50/6d/39c0fbb8fc5cd4d2090811e553c2108cf5112e882f82505ee7495349a6bf/coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", size = 261057, upload-time = "2026-02-09T12:57:52.447Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a2/60010c669df5fa603bb5a97fb75407e191a846510da70ac657eb696b7fce/coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", size = 263875, upload-time = "2026-02-09T12:57:53.938Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d9/63b22a6bdbd17f1f96e9ed58604c2a6b0e72a9133e37d663bef185877cf6/coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", size = 261500, upload-time = "2026-02-09T12:57:56.012Z" }, - { url = "https://files.pythonhosted.org/packages/70/bf/69f86ba1ad85bc3ad240e4c0e57a2e620fbc0e1645a47b5c62f0e941ad7f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", size = 265212, upload-time = "2026-02-09T12:57:57.5Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f2/5f65a278a8c2148731831574c73e42f57204243d33bedaaf18fa79c5958f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", size = 260398, upload-time = "2026-02-09T12:57:59.027Z" }, - { url = "https://files.pythonhosted.org/packages/ef/80/6e8280a350ee9fea92f14b8357448a242dcaa243cb2c72ab0ca591f66c8c/coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", size = 262584, upload-time = "2026-02-09T12:58:01.129Z" }, - { url = "https://files.pythonhosted.org/packages/22/63/01ff182fc95f260b539590fb12c11ad3e21332c15f9799cb5e2386f71d9f/coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9", size = 222688, upload-time = "2026-02-09T12:58:02.736Z" }, - { url = "https://files.pythonhosted.org/packages/a9/43/89de4ef5d3cd53b886afa114065f7e9d3707bdb3e5efae13535b46ae483d/coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd", size = 223746, upload-time = "2026-02-09T12:58:05.362Z" }, - { url = "https://files.pythonhosted.org/packages/35/39/7cf0aa9a10d470a5309b38b289b9bb07ddeac5d61af9b664fe9775a4cb3e/coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997", size = 222003, upload-time = "2026-02-09T12:58:06.952Z" }, - { url = "https://files.pythonhosted.org/packages/92/11/a9cf762bb83386467737d32187756a42094927150c3e107df4cb078e8590/coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", size = 219522, upload-time = "2026-02-09T12:58:08.623Z" }, - { url = "https://files.pythonhosted.org/packages/d3/28/56e6d892b7b052236d67c95f1936b6a7cf7c3e2634bf27610b8cbd7f9c60/coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", size = 219855, upload-time = "2026-02-09T12:58:10.176Z" }, - { url = "https://files.pythonhosted.org/packages/e5/69/233459ee9eb0c0d10fcc2fe425a029b3fa5ce0f040c966ebce851d030c70/coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", size = 250887, upload-time = "2026-02-09T12:58:12.503Z" }, - { url = "https://files.pythonhosted.org/packages/06/90/2cdab0974b9b5bbc1623f7876b73603aecac11b8d95b85b5b86b32de5eab/coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", size = 253396, upload-time = "2026-02-09T12:58:14.615Z" }, - { url = "https://files.pythonhosted.org/packages/ac/15/ea4da0f85bf7d7b27635039e649e99deb8173fe551096ea15017f7053537/coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", size = 254745, upload-time = "2026-02-09T12:58:16.162Z" }, - { url = "https://files.pythonhosted.org/packages/99/11/bb356e86920c655ca4d61daee4e2bbc7258f0a37de0be32d233b561134ff/coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", size = 257055, upload-time = "2026-02-09T12:58:17.892Z" }, - { url = "https://files.pythonhosted.org/packages/c9/0f/9ae1f8cb17029e09da06ca4e28c9e1d5c1c0a511c7074592e37e0836c915/coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", size = 250911, upload-time = "2026-02-09T12:58:19.495Z" }, - { url = "https://files.pythonhosted.org/packages/89/3a/adfb68558fa815cbc29747b553bc833d2150228f251b127f1ce97e48547c/coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", size = 252754, upload-time = "2026-02-09T12:58:21.064Z" }, - { url = "https://files.pythonhosted.org/packages/32/b1/540d0c27c4e748bd3cd0bd001076ee416eda993c2bae47a73b7cc9357931/coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", size = 250720, upload-time = "2026-02-09T12:58:22.622Z" }, - { url = "https://files.pythonhosted.org/packages/c7/95/383609462b3ffb1fe133014a7c84fc0dd01ed55ac6140fa1093b5af7ebb1/coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", size = 254994, upload-time = "2026-02-09T12:58:24.548Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ba/1761138e86c81680bfc3c49579d66312865457f9fe405b033184e5793cb3/coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", size = 250531, upload-time = "2026-02-09T12:58:26.271Z" }, - { url = "https://files.pythonhosted.org/packages/f8/8e/05900df797a9c11837ab59c4d6fe94094e029582aab75c3309a93e6fb4e3/coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", size = 252189, upload-time = "2026-02-09T12:58:27.807Z" }, - { url = "https://files.pythonhosted.org/packages/00/bd/29c9f2db9ea4ed2738b8a9508c35626eb205d51af4ab7bf56a21a2e49926/coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb", size = 222258, upload-time = "2026-02-09T12:58:29.441Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4d/1f8e723f6829977410efeb88f73673d794075091c8c7c18848d273dc9d73/coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505", size = 223073, upload-time = "2026-02-09T12:58:31.026Z" }, - { url = "https://files.pythonhosted.org/packages/51/5b/84100025be913b44e082ea32abcf1afbf4e872f5120b7a1cab1d331b1e13/coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2", size = 221638, upload-time = "2026-02-09T12:58:32.599Z" }, - { url = "https://files.pythonhosted.org/packages/a7/e4/c884a405d6ead1370433dad1e3720216b4f9fd8ef5b64bfd984a2a60a11a/coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", size = 220246, upload-time = "2026-02-09T12:58:34.181Z" }, - { url = "https://files.pythonhosted.org/packages/81/5c/4d7ed8b23b233b0fffbc9dfec53c232be2e695468523242ea9fd30f97ad2/coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", size = 220514, upload-time = "2026-02-09T12:58:35.704Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6f/3284d4203fd2f28edd73034968398cd2d4cb04ab192abc8cff007ea35679/coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", size = 261877, upload-time = "2026-02-09T12:58:37.864Z" }, - { url = "https://files.pythonhosted.org/packages/09/aa/b672a647bbe1556a85337dc95bfd40d146e9965ead9cc2fe81bde1e5cbce/coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", size = 264004, upload-time = "2026-02-09T12:58:39.492Z" }, - { url = "https://files.pythonhosted.org/packages/79/a1/aa384dbe9181f98bba87dd23dda436f0c6cf2e148aecbb4e50fc51c1a656/coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", size = 266408, upload-time = "2026-02-09T12:58:41.852Z" }, - { url = "https://files.pythonhosted.org/packages/53/5e/5150bf17b4019bc600799f376bb9606941e55bd5a775dc1e096b6ffea952/coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", size = 267544, upload-time = "2026-02-09T12:58:44.093Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ed/f1de5c675987a4a7a672250d2c5c9d73d289dbf13410f00ed7181d8017dd/coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", size = 260980, upload-time = "2026-02-09T12:58:45.721Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e3/fe758d01850aa172419a6743fe76ba8b92c29d181d4f676ffe2dae2ba631/coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", size = 263871, upload-time = "2026-02-09T12:58:47.334Z" }, - { url = "https://files.pythonhosted.org/packages/b6/76/b829869d464115e22499541def9796b25312b8cf235d3bb00b39f1675395/coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", size = 261472, upload-time = "2026-02-09T12:58:48.995Z" }, - { url = "https://files.pythonhosted.org/packages/14/9e/caedb1679e73e2f6ad240173f55218488bfe043e38da577c4ec977489915/coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", size = 265210, upload-time = "2026-02-09T12:58:51.178Z" }, - { url = "https://files.pythonhosted.org/packages/3a/10/0dd02cb009b16ede425b49ec344aba13a6ae1dc39600840ea6abcb085ac4/coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", size = 260319, upload-time = "2026-02-09T12:58:53.081Z" }, - { url = "https://files.pythonhosted.org/packages/92/8e/234d2c927af27c6d7a5ffad5bd2cf31634c46a477b4c7adfbfa66baf7ebb/coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", size = 262638, upload-time = "2026-02-09T12:58:55.258Z" }, - { url = "https://files.pythonhosted.org/packages/2f/64/e5547c8ff6964e5965c35a480855911b61509cce544f4d442caa759a0702/coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea", size = 223040, upload-time = "2026-02-09T12:58:56.936Z" }, - { url = "https://files.pythonhosted.org/packages/c7/96/38086d58a181aac86d503dfa9c47eb20715a79c3e3acbdf786e92e5c09a8/coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932", size = 224148, upload-time = "2026-02-09T12:58:58.645Z" }, - { url = "https://files.pythonhosted.org/packages/ce/72/8d10abd3740a0beb98c305e0c3faf454366221c0f37a8bcf8f60020bb65a/coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b", size = 222172, upload-time = "2026-02-09T12:59:00.396Z" }, - { url = "https://files.pythonhosted.org/packages/0d/4a/331fe2caf6799d591109bb9c08083080f6de90a823695d412a935622abb2/coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", size = 211242, upload-time = "2026-02-09T12:59:02.032Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/e8c48488c29a73fd089f9d71f9653c1be7478f2ad6b5bc870db11a55d23d/coverage-7.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5", size = 219255, upload-time = "2026-03-17T10:29:51.081Z" }, + { url = "https://files.pythonhosted.org/packages/da/bd/b0ebe9f677d7f4b74a3e115eec7ddd4bcf892074963a00d91e8b164a6386/coverage-7.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf", size = 219772, upload-time = "2026-03-17T10:29:52.867Z" }, + { url = "https://files.pythonhosted.org/packages/48/cc/5cb9502f4e01972f54eedd48218bb203fe81e294be606a2bc93970208013/coverage-7.13.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8", size = 246532, upload-time = "2026-03-17T10:29:54.688Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d8/3217636d86c7e7b12e126e4f30ef1581047da73140614523af7495ed5f2d/coverage-7.13.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4", size = 248333, upload-time = "2026-03-17T10:29:56.221Z" }, + { url = "https://files.pythonhosted.org/packages/2b/30/2002ac6729ba2d4357438e2ed3c447ad8562866c8c63fc16f6dfc33afe56/coverage-7.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d", size = 250211, upload-time = "2026-03-17T10:29:57.938Z" }, + { url = "https://files.pythonhosted.org/packages/6c/85/552496626d6b9359eb0e2f86f920037c9cbfba09b24d914c6e1528155f7d/coverage-7.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930", size = 252125, upload-time = "2026-03-17T10:29:59.388Z" }, + { url = "https://files.pythonhosted.org/packages/44/21/40256eabdcbccdb6acf6b381b3016a154399a75fe39d406f790ae84d1f3c/coverage-7.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d", size = 247219, upload-time = "2026-03-17T10:30:01.199Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/96e2a6c3f21a0ea77d7830b254a1542d0328acc8d7bdf6a284ba7e529f77/coverage-7.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40", size = 248248, upload-time = "2026-03-17T10:30:03.317Z" }, + { url = "https://files.pythonhosted.org/packages/da/ba/8477f549e554827da390ec659f3c38e4b6d95470f4daafc2d8ff94eaa9c2/coverage-7.13.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878", size = 246254, upload-time = "2026-03-17T10:30:04.832Z" }, + { url = "https://files.pythonhosted.org/packages/55/59/bc22aef0e6aa179d5b1b001e8b3654785e9adf27ef24c93dc4228ebd5d68/coverage-7.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400", size = 250067, upload-time = "2026-03-17T10:30:06.535Z" }, + { url = "https://files.pythonhosted.org/packages/de/1b/c6a023a160806a5137dca53468fd97530d6acad24a22003b1578a9c2e429/coverage-7.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0", size = 246521, upload-time = "2026-03-17T10:30:08.486Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3f/3532c85a55aa2f899fa17c186f831cfa1aa434d88ff792a709636f64130e/coverage-7.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0", size = 247126, upload-time = "2026-03-17T10:30:09.966Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2e/b9d56af4a24ef45dfbcda88e06870cb7d57b2b0bfa3a888d79b4c8debd76/coverage-7.13.5-cp310-cp310-win32.whl", hash = "sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58", size = 221860, upload-time = "2026-03-17T10:30:11.393Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cc/d938417e7a4d7f0433ad4edee8bb2acdc60dc7ac5af19e2a07a048ecbee3/coverage-7.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e", size = 222788, upload-time = "2026-03-17T10:30:12.886Z" }, + { url = "https://files.pythonhosted.org/packages/4b/37/d24c8f8220ff07b839b2c043ea4903a33b0f455abe673ae3c03bbdb7f212/coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d", size = 219381, upload-time = "2026-03-17T10:30:14.68Z" }, + { url = "https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587", size = 219880, upload-time = "2026-03-17T10:30:16.231Z" }, + { url = "https://files.pythonhosted.org/packages/55/2f/e0e5b237bffdb5d6c530ce87cc1d413a5b7d7dfd60fb067ad6d254c35c76/coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642", size = 250303, upload-time = "2026-03-17T10:30:17.748Z" }, + { url = "https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b", size = 252218, upload-time = "2026-03-17T10:30:19.804Z" }, + { url = "https://files.pythonhosted.org/packages/da/69/2f47bb6fa1b8d1e3e5d0c4be8ccb4313c63d742476a619418f85740d597b/coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686", size = 254326, upload-time = "2026-03-17T10:30:21.321Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d0/79db81da58965bd29dabc8f4ad2a2af70611a57cba9d1ec006f072f30a54/coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743", size = 256267, upload-time = "2026-03-17T10:30:23.094Z" }, + { url = "https://files.pythonhosted.org/packages/e5/32/d0d7cc8168f91ddab44c0ce4806b969df5f5fdfdbb568eaca2dbc2a04936/coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75", size = 250430, upload-time = "2026-03-17T10:30:25.311Z" }, + { url = "https://files.pythonhosted.org/packages/4d/06/a055311d891ddbe231cd69fdd20ea4be6e3603ffebddf8704b8ca8e10a3c/coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209", size = 252017, upload-time = "2026-03-17T10:30:27.284Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f6/d0fd2d21e29a657b5f77a2fe7082e1568158340dceb941954f776dce1b7b/coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a", size = 250080, upload-time = "2026-03-17T10:30:29.481Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ab/0d7fb2efc2e9a5eb7ddcc6e722f834a69b454b7e6e5888c3a8567ecffb31/coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e", size = 253843, upload-time = "2026-03-17T10:30:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/ba/6f/7467b917bbf5408610178f62a49c0ed4377bb16c1657f689cc61470da8ce/coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd", size = 249802, upload-time = "2026-03-17T10:30:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/75/2c/1172fb689df92135f5bfbbd69fc83017a76d24ea2e2f3a1154007e2fb9f8/coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8", size = 250707, upload-time = "2026-03-17T10:30:35.2Z" }, + { url = "https://files.pythonhosted.org/packages/67/21/9ac389377380a07884e3b48ba7a620fcd9dbfaf1d40565facdc6b36ec9ef/coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf", size = 221880, upload-time = "2026-03-17T10:30:36.775Z" }, + { url = "https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9", size = 222816, upload-time = "2026-03-17T10:30:38.891Z" }, + { url = "https://files.pythonhosted.org/packages/12/a6/1d3f6155fb0010ca68eba7fe48ca6c9da7385058b77a95848710ecf189b1/coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028", size = 221483, upload-time = "2026-03-17T10:30:40.463Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c3/a396306ba7db865bf96fc1fb3b7fd29bcbf3d829df642e77b13555163cd6/coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01", size = 219554, upload-time = "2026-03-17T10:30:42.208Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422", size = 219908, upload-time = "2026-03-17T10:30:43.906Z" }, + { url = "https://files.pythonhosted.org/packages/29/72/20b917c6793af3a5ceb7fb9c50033f3ec7865f2911a1416b34a7cfa0813b/coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f", size = 251419, upload-time = "2026-03-17T10:30:45.545Z" }, + { url = "https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5", size = 254159, upload-time = "2026-03-17T10:30:47.204Z" }, + { url = "https://files.pythonhosted.org/packages/9d/00/7b0edcfe64e2ed4c0340dac14a52ad0f4c9bd0b8b5e531af7d55b703db7c/coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376", size = 255270, upload-time = "2026-03-17T10:30:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/7ffc4ba0f5d0a55c1e84ea7cee39c9fc06af7b170513d83fbf3bbefce280/coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256", size = 257538, upload-time = "2026-03-17T10:30:50.77Z" }, + { url = "https://files.pythonhosted.org/packages/81/bd/73ddf85f93f7e6fa83e77ccecb6162d9415c79007b4bc124008a4995e4a7/coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c", size = 251821, upload-time = "2026-03-17T10:30:52.5Z" }, + { url = "https://files.pythonhosted.org/packages/a0/81/278aff4e8dec4926a0bcb9486320752811f543a3ce5b602cc7a29978d073/coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5", size = 253191, upload-time = "2026-03-17T10:30:54.543Z" }, + { url = "https://files.pythonhosted.org/packages/70/ee/fe1621488e2e0a58d7e94c4800f0d96f79671553488d401a612bebae324b/coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09", size = 251337, upload-time = "2026-03-17T10:30:56.663Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/f79fb37aa104b562207cc23cb5711ab6793608e246cae1e93f26b2236ed9/coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9", size = 255404, upload-time = "2026-03-17T10:30:58.427Z" }, + { url = "https://files.pythonhosted.org/packages/75/f0/ed15262a58ec81ce457ceb717b7f78752a1713556b19081b76e90896e8d4/coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf", size = 250903, upload-time = "2026-03-17T10:31:00.093Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e9/9129958f20e7e9d4d56d51d42ccf708d15cac355ff4ac6e736e97a9393d2/coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c", size = 252780, upload-time = "2026-03-17T10:31:01.916Z" }, + { url = "https://files.pythonhosted.org/packages/a4/d7/0ad9b15812d81272db94379fe4c6df8fd17781cc7671fdfa30c76ba5ff7b/coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf", size = 222093, upload-time = "2026-03-17T10:31:03.642Z" }, + { url = "https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810", size = 222900, upload-time = "2026-03-17T10:31:05.651Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fa/2238c2ad08e35cf4f020ea721f717e09ec3152aea75d191a7faf3ef009a8/coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de", size = 221515, upload-time = "2026-03-17T10:31:07.293Z" }, + { url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1", size = 219576, upload-time = "2026-03-17T10:31:09.045Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3", size = 219942, upload-time = "2026-03-17T10:31:10.708Z" }, + { url = "https://files.pythonhosted.org/packages/5f/13/93419671cee82b780bab7ea96b67c8ef448f5f295f36bf5031154ec9a790/coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26", size = 250935, upload-time = "2026-03-17T10:31:12.392Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3", size = 253541, upload-time = "2026-03-17T10:31:14.247Z" }, + { url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b", size = 254780, upload-time = "2026-03-17T10:31:16.193Z" }, + { url = "https://files.pythonhosted.org/packages/44/b3/cb5bd1a04cfcc49ede6cd8409d80bee17661167686741e041abc7ee1b9a9/coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a", size = 256912, upload-time = "2026-03-17T10:31:17.89Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/c1dceb7b9714473800b075f5c8a84f4588f887a90eb8645282031676e242/coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969", size = 251165, upload-time = "2026-03-17T10:31:19.605Z" }, + { url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161", size = 252908, upload-time = "2026-03-17T10:31:21.312Z" }, + { url = "https://files.pythonhosted.org/packages/7d/37/7792c2d69854397ca77a55c4646e5897c467928b0e27f2d235d83b5d08c6/coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15", size = 250873, upload-time = "2026-03-17T10:31:23.565Z" }, + { url = "https://files.pythonhosted.org/packages/a3/23/bc866fb6163be52a8a9e5d708ba0d3b1283c12158cefca0a8bbb6e247a43/coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1", size = 255030, upload-time = "2026-03-17T10:31:25.58Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8b/ef67e1c222ef49860701d346b8bbb70881bef283bd5f6cbba68a39a086c7/coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6", size = 250694, upload-time = "2026-03-17T10:31:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17", size = 252469, upload-time = "2026-03-17T10:31:29.472Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f5/be742fec31118f02ce42b21c6af187ad6a344fed546b56ca60caacc6a9a0/coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85", size = 222112, upload-time = "2026-03-17T10:31:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b", size = 222923, upload-time = "2026-03-17T10:31:33.633Z" }, + { url = "https://files.pythonhosted.org/packages/48/af/fea819c12a095781f6ccd504890aaddaf88b8fab263c4940e82c7b770124/coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664", size = 221540, upload-time = "2026-03-17T10:31:35.445Z" }, + { url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d", size = 220262, upload-time = "2026-03-17T10:31:37.184Z" }, + { url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0", size = 220617, upload-time = "2026-03-17T10:31:39.245Z" }, + { url = "https://files.pythonhosted.org/packages/29/9c/f9f5277b95184f764b24e7231e166dfdb5780a46d408a2ac665969416d61/coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806", size = 261912, upload-time = "2026-03-17T10:31:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3", size = 263987, upload-time = "2026-03-17T10:31:43.724Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9", size = 266416, upload-time = "2026-03-17T10:31:45.769Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f6/df63d8660e1a0bff6125947afda112a0502736f470d62ca68b288ea762d8/coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd", size = 267558, upload-time = "2026-03-17T10:31:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/5b/02/353ca81d36779bd108f6d384425f7139ac3c58c750dcfaafe5d0bee6436b/coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606", size = 261163, upload-time = "2026-03-17T10:31:50.125Z" }, + { url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e", size = 263981, upload-time = "2026-03-17T10:31:51.961Z" }, + { url = "https://files.pythonhosted.org/packages/29/c7/c29e0c59ffa6942030ae6f50b88ae49988e7e8da06de7ecdbf49c6d4feae/coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0", size = 261604, upload-time = "2026-03-17T10:31:53.872Z" }, + { url = "https://files.pythonhosted.org/packages/40/48/097cdc3db342f34006a308ab41c3a7c11c3f0d84750d340f45d88a782e00/coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87", size = 265321, upload-time = "2026-03-17T10:31:55.997Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/4994af354689e14fd03a75f8ec85a9a68d94e0188bbdab3fc1516b55e512/coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479", size = 260502, upload-time = "2026-03-17T10:31:58.308Z" }, + { url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2", size = 262688, upload-time = "2026-03-17T10:32:00.141Z" }, + { url = "https://files.pythonhosted.org/packages/14/4f/f5df9007e50b15e53e01edea486814783a7f019893733d9e4d6caad75557/coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a", size = 222788, upload-time = "2026-03-17T10:32:02.246Z" }, + { url = "https://files.pythonhosted.org/packages/e1/98/aa7fccaa97d0f3192bec013c4e6fd6d294a6ed44b640e6bb61f479e00ed5/coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819", size = 223851, upload-time = "2026-03-17T10:32:04.416Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8b/e5c469f7352651e5f013198e9e21f97510b23de957dd06a84071683b4b60/coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911", size = 222104, upload-time = "2026-03-17T10:32:06.65Z" }, + { url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" }, + { url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" }, + { url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" }, + { url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" }, + { url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" }, + { url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" }, + { url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" }, + { url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" }, + { url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" }, + { url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" }, + { url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" }, + { url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, ] [[package]] @@ -1368,27 +1368,75 @@ wheels = [ [[package]] name = "cuda-bindings" -version = "12.9.4" +version = "13.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cuda-pathfinder" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218, upload-time = "2025-10-21T14:51:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/45/e7/b47792cc2d01c7e1d37c32402182524774dadd2d26339bd224e0e913832e/cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9", size = 12210593, upload-time = "2025-10-21T14:51:36.574Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c1/dabe88f52c3e3760d861401bb994df08f672ec893b8f7592dc91626adcf3/cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8", size = 12151019, upload-time = "2025-10-21T14:51:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, - { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, - { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, + { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, + { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, + { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, ] [[package]] name = "cuda-pathfinder" -version = "1.4.3" +version = "1.4.4" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/59/911a1a597264f1fb7ac176995a0f0b6062e37f8c1b6e0f23071a76838507/cuda_pathfinder-1.4.3-py3-none-any.whl", hash = "sha256:4345d8ead1f701c4fb8a99be6bc1843a7348b6ba0ef3b031f5a2d66fb128ae4c", size = 47951, upload-time = "2026-03-16T21:31:25.526Z" }, + { url = "https://files.pythonhosted.org/packages/c0/66/7b2c3d23dac4bb9629b4d9702f1f796bd41c01142c2b47be6fcfdeaf4ee4/cuda_pathfinder-1.4.4-py3-none-any.whl", hash = "sha256:1a9e7feccae0d969ad88545d0462f2ed2750df8e6732309798dc1e1ca603a28b", size = 48834, upload-time = "2026-03-23T20:50:00.706Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] [[package]] @@ -1570,7 +1618,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.135.1" +version = "0.135.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1579,9 +1627,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/73/5903c4b13beae98618d64eb9870c3fac4f605523dd0312ca5c80dadbd5b9/fastapi-0.135.2.tar.gz", hash = "sha256:88a832095359755527b7f63bb4c6bc9edb8329a026189eed83d6c1afcf419d56", size = 395833, upload-time = "2026-03-23T14:12:41.697Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ea/18f6d0457f9efb2fc6fa594857f92810cadb03024975726db6546b3d6fcf/fastapi-0.135.2-py3-none-any.whl", hash = "sha256:0af0447d541867e8db2a6a25c23a8c4bd80e2394ac5529bd87501bbb9e240ca5", size = 117407, upload-time = "2026-03-23T14:12:43.284Z" }, ] [package.optional-dependencies] @@ -1640,123 +1688,118 @@ wheels = [ [[package]] name = "fastar" -version = "0.8.0" +version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/00/dab9ca274cf1fde19223fea7104631bea254751026e75bf99f2b6d0d1568/fastar-0.9.0.tar.gz", hash = "sha256:d49114d5f0b76c5cc242875d90fa4706de45e0456ddedf416608ecd0787fb410", size = 70124, upload-time = "2026-03-20T14:26:34.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536, upload-time = "2025-11-26T02:34:35.236Z" }, - { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235, upload-time = "2025-11-26T02:34:19.367Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386, upload-time = "2025-11-26T02:33:47.613Z" }, - { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955, upload-time = "2025-11-26T02:32:44.279Z" }, - { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987, upload-time = "2025-11-26T02:32:59.701Z" }, - { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900, upload-time = "2025-11-26T02:33:16.059Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523, upload-time = "2025-11-26T02:33:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268, upload-time = "2025-11-26T02:34:04.003Z" }, - { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286, upload-time = "2025-11-26T02:34:50.279Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921, upload-time = "2025-11-26T02:35:07.292Z" }, - { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302, upload-time = "2025-11-26T02:35:24.995Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141, upload-time = "2025-11-26T02:35:42.449Z" }, - { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544, upload-time = "2025-11-26T02:36:23.801Z" }, - { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701, upload-time = "2025-11-26T02:36:09.625Z" }, - { url = "https://files.pythonhosted.org/packages/cd/15/1c764530b81b266f6d27d78d49b6bef22a73b3300cd83a280bfd244908c5/fastar-0.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cd9c0d3ebf7a0a6f642f771cf41b79f7c98d40a3072a8abe1174fbd9bd615bd3", size = 708427, upload-time = "2025-11-26T02:34:36.502Z" }, - { url = "https://files.pythonhosted.org/packages/41/fc/75d42c008516543219e4293e4d8ac55da57a5c63147484f10468bd1bc24e/fastar-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2875a077340fe4f8099bd3ed8fa90d9595e1ac3cd62ae19ab690d5bf550eeb35", size = 631740, upload-time = "2025-11-26T02:34:20.718Z" }, - { url = "https://files.pythonhosted.org/packages/50/8d/9632984f7824ed2210157dcebd8e9821ef6d4f2b28510d0516db6625ff9b/fastar-0.8.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a999263d9f87184bf2801833b2ecf105e03c0dd91cac78685673b70da564fd64", size = 871628, upload-time = "2025-11-26T02:33:49.279Z" }, - { url = "https://files.pythonhosted.org/packages/05/97/3eb6ea71b7544d45cd29cacb764ca23cde8ce0aed1a6a02251caa4c0a818/fastar-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c41111da56430f638cbfc498ebdcc7d30f63416e904b27b7695c29bd4889cb8", size = 765005, upload-time = "2025-11-26T02:32:45.833Z" }, - { url = "https://files.pythonhosted.org/packages/d6/45/3eb0ee945a0b5d5f9df7e7c25c037ce7fa441cd0b4d44f76d286e2f4396a/fastar-0.8.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3719541a12bb09ab1eae91d2c987a9b2b7d7149c52e7109ba6e15b74aabc49b1", size = 765587, upload-time = "2025-11-26T02:33:01.174Z" }, - { url = "https://files.pythonhosted.org/packages/51/bb/7defd6ec0d9570b1987d8ebde52d07d97f3f26e10b592fb3e12738eba39a/fastar-0.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a9b0fff8079b18acdface7ef1b7f522fd9a589f65ca4a1a0dd7c92a0886c2a2", size = 931150, upload-time = "2025-11-26T02:33:17.374Z" }, - { url = "https://files.pythonhosted.org/packages/28/54/62e51e684dab347c61878afbf09e177029c1a91eb1e39ef244e6b3ef9efa/fastar-0.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac073576c1931959191cb20df38bab21dd152f66c940aa3ca8b22e39f753b2f3", size = 821354, upload-time = "2025-11-26T02:33:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/53/a8/12708ea4d21e3cf9f485b2a67d44ce84d949a6eddcc9aa5b3d324585ab43/fastar-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003b59a7c3e405b6a7bff8fab17d31e0ccbc7f06730a8f8ca1694eeea75f3c76", size = 821626, upload-time = "2025-11-26T02:34:05.685Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c4/1b4d3347c7a759853f963410bf6baf42fe014d587c50c39c8e145f4bf1a0/fastar-0.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a7b96748425efd9fc155cd920d65088a1b0d754421962418ea73413d02ff515a", size = 986187, upload-time = "2025-11-26T02:34:52.047Z" }, - { url = "https://files.pythonhosted.org/packages/dc/59/2dbe0dc2570764475e60030403738faa261a9d3bff16b08629c378ab939a/fastar-0.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:90957a30e64418b02df5b4d525bea50403d98a4b1f29143ce5914ddfa7e54ee4", size = 1041536, upload-time = "2025-11-26T02:35:08.926Z" }, - { url = "https://files.pythonhosted.org/packages/d9/0f/639b295669c7ca6fbc2b4be2a7832aaeac1a5e06923f15a8a6d6daecbc7d/fastar-0.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f6e784a8015623fbb7ccca1af372fd82cb511b408ddd2348dc929fc6e415df73", size = 1047149, upload-time = "2025-11-26T02:35:26.597Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e7/23e3a19e06d261d1894f98eca9458f98c090c505a0c712dafc0ff1fc2965/fastar-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a03eaf287bbc93064688a1220580ce261e7557c8898f687f4d0b281c85b28d3c", size = 994992, upload-time = "2025-11-26T02:35:44.009Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7a/3ea4726bae3ac9358d02107ae48f3e10ee186dbed554af79e00b7b498c44/fastar-0.8.0-cp311-cp311-win32.whl", hash = "sha256:661a47ed90762f419406c47e802f46af63a08254ba96abd1c8191e4ce967b665", size = 456449, upload-time = "2025-11-26T02:36:25.291Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3c/0142bee993c431ee91cf5535e6e4b079ad491f620c215fcd79b7e5ffeb2b/fastar-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b48abd6056fef7bc3d414aafb453c5b07fdf06d2df5a2841d650288a3aa1e9d3", size = 490863, upload-time = "2025-11-26T02:36:11.114Z" }, - { url = "https://files.pythonhosted.org/packages/3b/18/d119944f6bdbf6e722e204e36db86390ea45684a1bf6be6e3aa42abd471f/fastar-0.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:50c18788b3c6ffb85e176dcb8548bb8e54616a0519dcdbbfba66f6bbc4316933", size = 462230, upload-time = "2025-11-26T02:36:01.917Z" }, - { url = "https://files.pythonhosted.org/packages/58/f1/5b2ff898abac7f1a418284aad285e3a4f68d189c572ab2db0f6c9079dd16/fastar-0.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f10d2adfe40f47ff228f4efaa32d409d732ded98580e03ed37c9535b5fc923d", size = 706369, upload-time = "2025-11-26T02:34:37.783Z" }, - { url = "https://files.pythonhosted.org/packages/23/60/8046a386dca39154f80c927cbbeeb4b1c1267a3271bffe61552eb9995757/fastar-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b930da9d598e3bc69513d131f397e6d6be4643926ef3de5d33d1e826631eb036", size = 629097, upload-time = "2025-11-26T02:34:21.888Z" }, - { url = "https://files.pythonhosted.org/packages/22/7e/1ae005addc789924a9268da2394d3bb5c6f96836f7e37b7e3d23c2362675/fastar-0.8.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9d210da2de733ca801de83e931012349d209f38b92d9630ccaa94bd445bdc9b8", size = 868938, upload-time = "2025-11-26T02:33:51.119Z" }, - { url = "https://files.pythonhosted.org/packages/a6/77/290a892b073b84bf82e6b2259708dfe79c54f356e252c2dd40180b16fe07/fastar-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa02270721517078a5bd61a38719070ac2537a4aa6b6c48cf369cf2abc59174a", size = 765204, upload-time = "2025-11-26T02:32:47.02Z" }, - { url = "https://files.pythonhosted.org/packages/d0/00/c3155171b976003af3281f5258189f1935b15d1221bfc7467b478c631216/fastar-0.8.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83c391e5b789a720e4d0029b9559f5d6dee3226693c5b39c0eab8eaece997e0f", size = 764717, upload-time = "2025-11-26T02:33:02.453Z" }, - { url = "https://files.pythonhosted.org/packages/b7/43/405b7ad76207b2c11b7b59335b70eac19e4a2653977f5588a1ac8fed54f4/fastar-0.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3258d7a78a72793cdd081545da61cabe85b1f37634a1d0b97ffee0ff11d105ef", size = 931502, upload-time = "2025-11-26T02:33:18.619Z" }, - { url = "https://files.pythonhosted.org/packages/da/8a/a3dde6d37cc3da4453f2845cdf16675b5686b73b164f37e2cc579b057c2c/fastar-0.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6eab95dd985cdb6a50666cbeb9e4814676e59cfe52039c880b69d67cfd44767", size = 821454, upload-time = "2025-11-26T02:33:33.427Z" }, - { url = "https://files.pythonhosted.org/packages/da/c1/904fe2468609c8990dce9fe654df3fbc7324a8d8e80d8240ae2c89757064/fastar-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:829b1854166141860887273c116c94e31357213fa8e9fe8baeb18bd6c38aa8d9", size = 821647, upload-time = "2025-11-26T02:34:07Z" }, - { url = "https://files.pythonhosted.org/packages/c8/73/a0642ab7a400bc07528091785e868ace598fde06fcd139b8f865ec1b6f3c/fastar-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1667eae13f9457a3c737f4376d68e8c3e548353538b28f7e4273a30cb3965cd", size = 986342, upload-time = "2025-11-26T02:34:53.371Z" }, - { url = "https://files.pythonhosted.org/packages/af/af/60c1bfa6edab72366461a95f053d0f5f7ab1825fe65ca2ca367432cd8629/fastar-0.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b864a95229a7db0814cd9ef7987cb713fd43dce1b0d809dd17d9cd6f02fdde3e", size = 1040207, upload-time = "2025-11-26T02:35:10.65Z" }, - { url = "https://files.pythonhosted.org/packages/f6/a0/0d624290dec622e7fa084b6881f456809f68777d54a314f5dde932714506/fastar-0.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c05fbc5618ce17675a42576fa49858d79734627f0a0c74c0875ab45ee8de340c", size = 1045031, upload-time = "2025-11-26T02:35:28.108Z" }, - { url = "https://files.pythonhosted.org/packages/a7/74/cf663af53c4706ba88e6b4af44a6b0c3bd7d7ca09f079dc40647a8f06585/fastar-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7f41c51ee96f338662ee3c3df4840511ba3f9969606840f1b10b7cb633a3c716", size = 994877, upload-time = "2025-11-26T02:35:45.797Z" }, - { url = "https://files.pythonhosted.org/packages/52/17/444c8be6e77206050e350da7c338102b6cab384be937fa0b1d6d1f9ede73/fastar-0.8.0-cp312-cp312-win32.whl", hash = "sha256:d949a1a2ea7968b734632c009df0571c94636a5e1622c87a6e2bf712a7334f47", size = 455996, upload-time = "2025-11-26T02:36:26.938Z" }, - { url = "https://files.pythonhosted.org/packages/dc/34/fc3b5e56d71a17b1904800003d9251716e8fd65f662e1b10a26881698a74/fastar-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:fc645994d5b927d769121094e8a649b09923b3c13a8b0b98696d8f853f23c532", size = 490429, upload-time = "2025-11-26T02:36:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/35/a8/5608cc837417107c594e2e7be850b9365bcb05e99645966a5d6a156285fe/fastar-0.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:d81ee82e8dc78a0adb81728383bd39611177d642a8fa2d601d4ad5ad59e5f3bd", size = 461297, upload-time = "2025-11-26T02:36:03.546Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a5/79ecba3646e22d03eef1a66fb7fc156567213e2e4ab9faab3bbd4489e483/fastar-0.8.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a3253a06845462ca2196024c7a18f5c0ba4de1532ab1c4bad23a40b332a06a6a", size = 706112, upload-time = "2025-11-26T02:34:39.237Z" }, - { url = "https://files.pythonhosted.org/packages/0a/03/4f883bce878218a8676c2d7ca09b50c856a5470bb3b7f63baf9521ea6995/fastar-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5cbeb3ebfa0980c68ff8b126295cc6b208ccd81b638aebc5a723d810a7a0e5d2", size = 628954, upload-time = "2025-11-26T02:34:23.705Z" }, - { url = "https://files.pythonhosted.org/packages/4f/f1/892e471f156b03d10ba48ace9384f5a896702a54506137462545f38e40b8/fastar-0.8.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1c0d5956b917daac77d333d48b3f0f3ff927b8039d5b32d8125462782369f761", size = 868685, upload-time = "2025-11-26T02:33:53.077Z" }, - { url = "https://files.pythonhosted.org/packages/39/ba/e24915045852e30014ec6840446975c03f4234d1c9270394b51d3ad18394/fastar-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27b404db2b786b65912927ce7f3790964a4bcbde42cdd13091b82a89cd655e1c", size = 765044, upload-time = "2025-11-26T02:32:48.187Z" }, - { url = "https://files.pythonhosted.org/packages/14/2c/1aa11ac21a99984864c2fca4994e094319ff3a2046e7a0343c39317bd5b9/fastar-0.8.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0902fc89dcf1e7f07b8563032a4159fe2b835e4c16942c76fd63451d0e5f76a3", size = 764322, upload-time = "2025-11-26T02:33:03.859Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f0/4b91902af39fe2d3bae7c85c6d789586b9fbcf618d7fdb3d37323915906d/fastar-0.8.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:069347e2f0f7a8b99bbac8cd1bc0e06c7b4a31dc964fc60d84b95eab3d869dc1", size = 931016, upload-time = "2025-11-26T02:33:19.902Z" }, - { url = "https://files.pythonhosted.org/packages/c9/97/8fc43a5a9c0a2dc195730f6f7a0f367d171282cd8be2511d0e87c6d2dad0/fastar-0.8.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd135306f6bfe9a835918280e0eb440b70ab303e0187d90ab51ca86e143f70d", size = 821308, upload-time = "2025-11-26T02:33:34.664Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e9/058615b63a7fd27965e8c5966f393ed0c169f7ff5012e1674f21684de3ba/fastar-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d06d6897f43c27154b5f2d0eb930a43a81b7eec73f6f0b0114814d4a10ab38", size = 821171, upload-time = "2025-11-26T02:34:08.498Z" }, - { url = "https://files.pythonhosted.org/packages/ca/cf/69e16a17961570a755c37ffb5b5aa7610d2e77807625f537989da66f2a9d/fastar-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a922f8439231fa0c32b15e8d70ff6d415619b9d40492029dabbc14a0c53b5f18", size = 986227, upload-time = "2025-11-26T02:34:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/fb/83/2100192372e59b56f4ace37d7d9cabda511afd71b5febad1643d1c334271/fastar-0.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a739abd51eb766384b4caff83050888e80cd75bbcfec61e6d1e64875f94e4a40", size = 1039395, upload-time = "2025-11-26T02:35:12.166Z" }, - { url = "https://files.pythonhosted.org/packages/75/15/cdd03aca972f55872efbb7cf7540c3fa7b97a75d626303a3ea46932163dc/fastar-0.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a65f419d808b23ac89d5cd1b13a2f340f15bc5d1d9af79f39fdb77bba48ff1b", size = 1044766, upload-time = "2025-11-26T02:35:29.62Z" }, - { url = "https://files.pythonhosted.org/packages/3d/29/945e69e4e2652329ace545999334ec31f1431fbae3abb0105587e11af2ae/fastar-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7bb2ae6c0cce58f0db1c9f20495e7557cca2c1ee9c69bbd90eafd54f139171c5", size = 994740, upload-time = "2025-11-26T02:35:47.887Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5d/dbfe28f8cd1eb484bba0c62e5259b2cf6fea229d6ef43e05c06b5a78c034/fastar-0.8.0-cp313-cp313-win32.whl", hash = "sha256:b28753e0d18a643272597cb16d39f1053842aa43131ad3e260c03a2417d38401", size = 455990, upload-time = "2025-11-26T02:36:28.502Z" }, - { url = "https://files.pythonhosted.org/packages/e1/01/e965740bd36e60ef4c5aa2cbe42b6c4eb1dc3551009238a97c2e5e96bd23/fastar-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:620e5d737dce8321d49a5ebb7997f1fd0047cde3512082c27dc66d6ac8c1927a", size = 490227, upload-time = "2025-11-26T02:36:14.363Z" }, - { url = "https://files.pythonhosted.org/packages/dd/10/c99202719b83e5249f26902ae53a05aea67d840eeb242019322f20fc171c/fastar-0.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:c4c4bd08df563120cd33e854fe0a93b81579e8571b11f9b7da9e84c37da2d6b6", size = 461078, upload-time = "2025-11-26T02:36:04.94Z" }, - { url = "https://files.pythonhosted.org/packages/96/4a/9573b87a0ef07580ed111e7230259aec31bb33ca3667963ebee77022ec61/fastar-0.8.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:50b36ce654ba44b0e13fae607ae17ee6e1597b69f71df1bee64bb8328d881dfc", size = 706041, upload-time = "2025-11-26T02:34:40.638Z" }, - { url = "https://files.pythonhosted.org/packages/4a/19/f95444a1d4f375333af49300aa75ee93afa3335c0e40fda528e460ed859c/fastar-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63a892762683d7ab00df0227d5ea9677c62ff2cde9b875e666c0be569ed940f3", size = 628617, upload-time = "2025-11-26T02:34:24.893Z" }, - { url = "https://files.pythonhosted.org/packages/b3/c9/b51481b38b7e3f16ef2b9e233b1a3623386c939d745d6e41bbd389eaae30/fastar-0.8.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4ae6a145c1bff592644bde13f2115e0239f4b7babaf506d14e7d208483cf01a5", size = 869299, upload-time = "2025-11-26T02:33:54.274Z" }, - { url = "https://files.pythonhosted.org/packages/bf/02/3ba1267ee5ba7314e29c431cf82eaa68586f2c40cdfa08be3632b7d07619/fastar-0.8.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ae0ff7c0a1c7e1428404b81faee8aebef466bfd0be25bfe4dabf5d535c68741", size = 764667, upload-time = "2025-11-26T02:32:49.606Z" }, - { url = "https://files.pythonhosted.org/packages/1b/84/bf33530fd015b5d7c2cc69e0bce4a38d736754a6955487005aab1af6adcd/fastar-0.8.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dbfd87dbd217b45c898b2dbcd0169aae534b2c1c5cbe3119510881f6a5ac8ef5", size = 763993, upload-time = "2025-11-26T02:33:05.782Z" }, - { url = "https://files.pythonhosted.org/packages/da/e0/9564d24e7cea6321a8d921c6d2a457044a476ef197aa4708e179d3d97f0d/fastar-0.8.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5abd99fcba83ef28c8fe6ae2927edc79053db43a0457a962ed85c9bf150d37", size = 930153, upload-time = "2025-11-26T02:33:21.53Z" }, - { url = "https://files.pythonhosted.org/packages/35/b1/6f57fcd8d6e192cfebf97e58eb27751640ad93784c857b79039e84387b51/fastar-0.8.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91d4c685620c3a9d6b5ae091dbabab4f98b20049b7ecc7976e19cc9016c0d5d6", size = 821177, upload-time = "2025-11-26T02:33:35.839Z" }, - { url = "https://files.pythonhosted.org/packages/b3/78/9e004ea9f3aa7466f5ddb6f9518780e1d2f0ed3ca55f093632982598bace/fastar-0.8.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f77c2f2cad76e9dc7b6701297adb1eba87d0485944b416fc2ccf5516c01219a3", size = 820652, upload-time = "2025-11-26T02:34:09.776Z" }, - { url = "https://files.pythonhosted.org/packages/42/95/b604ed536544005c9f1aee7c4c74b00150db3d8d535cd8232dc20f947063/fastar-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e7f07c4a3dada7757a8fc430a5b4a29e6ef696d2212747213f57086ffd970316", size = 985961, upload-time = "2025-11-26T02:34:56.401Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7b/fa9d4d96a5d494bdb8699363bb9de8178c0c21a02e1d89cd6f913d127018/fastar-0.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:90c0c3fe55105c0aed8a83135dbdeb31e683455dbd326a1c48fa44c378b85616", size = 1039316, upload-time = "2025-11-26T02:35:13.807Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f9/8462789243bc3f33e8401378ec6d54de4e20cfa60c96a0e15e3e9d1389bb/fastar-0.8.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fb9ee51e5bffe0dab3d3126d3a4fac8d8f7235cedcb4b8e74936087ce1c157f3", size = 1045028, upload-time = "2025-11-26T02:35:31.079Z" }, - { url = "https://files.pythonhosted.org/packages/a5/71/9abb128777e616127194b509e98fcda3db797d76288c1a8c23dd22afc14f/fastar-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e380b1e8d30317f52406c43b11e98d11e1d68723bbd031e18049ea3497b59a6d", size = 994677, upload-time = "2025-11-26T02:35:49.391Z" }, - { url = "https://files.pythonhosted.org/packages/de/c1/b81b3f194853d7ad232a67a1d768f5f51a016f165cfb56cb31b31bbc6177/fastar-0.8.0-cp314-cp314-win32.whl", hash = "sha256:1c4ffc06e9c4a8ca498c07e094670d8d8c0d25b17ca6465b9774da44ea997ab1", size = 456687, upload-time = "2025-11-26T02:36:30.205Z" }, - { url = "https://files.pythonhosted.org/packages/cb/87/9e0cd4768a98181d56f0cdbab2363404cc15deb93f4aad3b99cd2761bbaa/fastar-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:5517a8ad4726267c57a3e0e2a44430b782e00b230bf51c55b5728e758bb3a692", size = 490578, upload-time = "2025-11-26T02:36:16.218Z" }, - { url = "https://files.pythonhosted.org/packages/aa/1e/580a76cf91847654f2ad6520e956e93218f778540975bc4190d363f709e2/fastar-0.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:58030551046ff4a8616931e52a36c83545ff05996db5beb6e0cd2b7e748aa309", size = 461473, upload-time = "2025-11-26T02:36:06.373Z" }, - { url = "https://files.pythonhosted.org/packages/58/4c/bdb5c6efe934f68708529c8c9d4055ebef5c4be370621966438f658b29bd/fastar-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:1e7d29b6bfecb29db126a08baf3c04a5ab667f6cea2b7067d3e623a67729c4a6", size = 705570, upload-time = "2025-11-26T02:34:42.01Z" }, - { url = "https://files.pythonhosted.org/packages/6d/78/f01ac7e71d5a37621bd13598a26e948a12b85ca8042f7ee1a0a8c9f59cda/fastar-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05eb7b96940f9526b485f1d0b02393839f0f61cac4b1f60024984f8b326d2640", size = 627761, upload-time = "2025-11-26T02:34:26.152Z" }, - { url = "https://files.pythonhosted.org/packages/06/45/6df0ecda86ea9d2e95053c1a655d153dee55fc121b6e13ea6d1e246a50b6/fastar-0.8.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:619352d8ac011794e2345c462189dc02ba634750d23cd9d86a9267dd71b1f278", size = 869414, upload-time = "2025-11-26T02:33:55.618Z" }, - { url = "https://files.pythonhosted.org/packages/b2/72/486421f5a8c0c377cc82e7a50c8a8ea899a6ec2aa72bde8f09fb667a2dc8/fastar-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74ebfecef3fe6d7a90355fac1402fd30636988332a1d33f3e80019a10782bb24", size = 763863, upload-time = "2025-11-26T02:32:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/d4/64/39f654dbb41a3867fb1f2c8081c014d8f1d32ea10585d84cacbef0b32995/fastar-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2975aca5a639e26a3ab0d23b4b0628d6dd6d521146c3c11486d782be621a35aa", size = 763065, upload-time = "2025-11-26T02:33:07.274Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bd/c011a34fb3534c4c3301f7c87c4ffd7e47f6113c904c092ddc8a59a303ea/fastar-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afc438eaed8ff0dcdd9308268be5cb38c1db7e94c3ccca7c498ca13a4a4535a3", size = 930530, upload-time = "2025-11-26T02:33:23.117Z" }, - { url = "https://files.pythonhosted.org/packages/55/9d/aa6e887a7033c571b1064429222bbe09adc9a3c1e04f3d1788ba5838ebd5/fastar-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ced0a5399cc0a84a858ef0a31ca2d0c24d3bbec4bcda506a9192d8119f3590a", size = 820572, upload-time = "2025-11-26T02:33:37.542Z" }, - { url = "https://files.pythonhosted.org/packages/ad/9c/7a3a2278a1052e1a5d98646de7c095a00cffd2492b3b84ce730e2f1cd93a/fastar-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec9b23da8c4c039da3fe2e358973c66976a0c8508aa06d6626b4403cb5666c19", size = 820649, upload-time = "2025-11-26T02:34:11.108Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d38edc1f4438cd047e56137c26d94783ffade42e1b3bde620ccf17b771ef/fastar-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dfba078fcd53478032fd0ceed56960ec6b7ff0511cfc013a8a3a4307e3a7bac4", size = 985653, upload-time = "2025-11-26T02:34:57.884Z" }, - { url = "https://files.pythonhosted.org/packages/69/d9/2147d0c19757e165cd62d41cec3f7b38fad2ad68ab784978b5f81716c7ea/fastar-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ade56c94c14be356d295fecb47a3fcd473dd43a8803ead2e2b5b9e58feb6dcfa", size = 1038140, upload-time = "2025-11-26T02:35:15.778Z" }, - { url = "https://files.pythonhosted.org/packages/7f/1d/ec4c717ffb8a308871e9602ec3197d957e238dc0227127ac573ec9bca952/fastar-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e48d938f9366db5e59441728f70b7f6c1ccfab7eff84f96f9b7e689b07786c52", size = 1045195, upload-time = "2025-11-26T02:35:32.865Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/637334dc8c8f3bb391388b064ae13f0ad9402bc5a6c3e77b8887d0c31921/fastar-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:79c441dc1482ff51a54fb3f57ae6f7bb3d2cff88fa2cc5d196c519f8aab64a56", size = 994686, upload-time = "2025-11-26T02:35:51.392Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e2/dfa19a4b260b8ab3581b7484dcb80c09b25324f4daa6b6ae1c7640d1607a/fastar-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:187f61dc739afe45ac8e47ed7fd1adc45d52eac110cf27d579155720507d6fbe", size = 455767, upload-time = "2025-11-26T02:36:34.758Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/df65c72afc1297797b255f90c4778b5d6f1f0f80282a134d5ab610310ed9/fastar-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:40e9d763cf8bf85ce2fa256e010aa795c0fe3d3bd1326d5c3084e6ce7857127e", size = 489971, upload-time = "2025-11-26T02:36:22.081Z" }, - { url = "https://files.pythonhosted.org/packages/85/11/0aa8455af26f0ae89e42be67f3a874255ee5d7f0f026fc86e8d56f76b428/fastar-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e59673307b6a08210987059a2bdea2614fe26e3335d0e5d1a3d95f49a05b1418", size = 460467, upload-time = "2025-11-26T02:36:07.978Z" }, - { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544, upload-time = "2025-11-26T02:34:46.195Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020, upload-time = "2025-11-26T02:34:31.085Z" }, - { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735, upload-time = "2025-11-26T02:34:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779, upload-time = "2025-11-26T02:32:55.109Z" }, - { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755, upload-time = "2025-11-26T02:33:11.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732, upload-time = "2025-11-26T02:33:27.122Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571, upload-time = "2025-11-26T02:33:42.986Z" }, - { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440, upload-time = "2025-11-26T02:34:15.439Z" }, - { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424, upload-time = "2025-11-26T02:35:02.742Z" }, - { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675, upload-time = "2025-11-26T02:35:20.252Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098, upload-time = "2025-11-26T02:35:37.643Z" }, - { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397, upload-time = "2025-11-26T02:35:56.215Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/6c46aa7f8c8734e7f96ee5141acd3877667ce66f34eea10703aa7571d191/fastar-0.8.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:998e3fa4b555b63eb134e6758437ed739ad1652fdd2a61dfe1dacbfddc35fe66", size = 710662, upload-time = "2025-11-26T02:34:47.593Z" }, - { url = "https://files.pythonhosted.org/packages/70/27/fd622442f2fbd4ff5459677987481ef1c60e077cb4e63a2ed4d8dce6f869/fastar-0.8.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5f83e60d845091f3a12bc37f412774264d161576eaf810ed8b43567eb934b7e5", size = 634049, upload-time = "2025-11-26T02:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/8f/ee/aa4d08aea25b5419a7277132e738ab1cd775f26aebddce11413b07e2fdff/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:299672e1c74d8b73c61684fac9159cfc063d35f4b165996a88facb0e26862cb5", size = 872055, upload-time = "2025-11-26T02:34:01.377Z" }, - { url = "https://files.pythonhosted.org/packages/92/9a/2bf2f77aade575e67997e0c759fd55cb1c66b7a5b437b1cd0e97d8b241bc/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3d3a27066b84d015deab5faee78565509bb33b137896443e4144cb1be1a5f90", size = 766787, upload-time = "2025-11-26T02:32:57.161Z" }, - { url = "https://files.pythonhosted.org/packages/0b/90/23a3f6c252f11b10c70f854bce09abc61f71b5a0e6a4b0eac2bcb9a2c583/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef0bcf4385bbdd3c1acecce2d9ea7dab7cc9b8ee0581bbccb7ab11908a7ce288", size = 766861, upload-time = "2025-11-26T02:33:12.824Z" }, - { url = "https://files.pythonhosted.org/packages/76/bb/beeb9078380acd4484db5c957d066171695d9340e3526398eb230127b0c2/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f10ef62b6eda6cb6fd9ba8e1fe08a07d7b2bdcc8eaa00eb91566143b92ed7eee", size = 932667, upload-time = "2025-11-26T02:33:28.405Z" }, - { url = "https://files.pythonhosted.org/packages/f4/6d/b034cc637bd0ee638d5a85d08e941b0b8ffd44cf391fb751ba98233734f7/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4f6c82a8ee98c17aa48585ee73b51c89c1b010e5c951af83e07c3436180e3fc", size = 822712, upload-time = "2025-11-26T02:33:44.27Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2b/7d183c63f59227c4689792042d6647f2586a5e7273b55e81745063088d81/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6129067fcb86276635b5857010f4e9b9c7d5d15dd571bb03c6c1ed73c40fd92", size = 822659, upload-time = "2025-11-26T02:34:16.815Z" }, - { url = "https://files.pythonhosted.org/packages/3e/f9/716e0cd9de2427fdf766bc68176f76226cd01fffef3a56c5046fa863f5f0/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4cc9e77019e489f1ddac446b6a5b9dfb5c3d9abd142652c22a1d9415dbcc0e47", size = 987412, upload-time = "2025-11-26T02:35:04.259Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b9/9a8c3fd59958c1c8027bc075af11722cdc62c4968bb277e841d131232289/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:382bfe82c026086487cb17fee12f4c1e2b4e67ce230f2e04487d3e7ddfd69031", size = 1042911, upload-time = "2025-11-26T02:35:21.857Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2f/c3f30963b47022134b8a231c12845f4d7cfba520f59bbc1a82468aea77c7/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:908d2b9a1ff3d549cc304b32f95706a536da8f0bcb0bc0f9e4c1cce39b80e218", size = 1047464, upload-time = "2025-11-26T02:35:39.376Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8a/218ab6d9a2bab3b07718e6cd8405529600edc1e9c266320e8524c8f63251/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1aa7dbde2d2d73eb5b6203d0f74875cb66350f0f1b4325b4839fc8fbbf5d074e", size = 997309, upload-time = "2025-11-26T02:35:57.722Z" }, + { url = "https://files.pythonhosted.org/packages/24/48/3d8e24c9ae7796e59231f50133640463c6a20b00ce684b308dc6de0e28fe/fastar-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:19a384395f26daa3ecb6c24054f3a50ce919e250e06b82614a252a0fadcbca17", size = 709092, upload-time = "2026-03-20T14:25:30.007Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/4d7dc06f3ad5457b9a1510a75e3f9ec431ad020688fcf954012a2bcae6e8/fastar-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b9c82b1fef26d8fd4abad1152f4c74eeb86bc9d46c814757b695847a751b9b0b", size = 630252, upload-time = "2026-03-20T14:25:17.673Z" }, + { url = "https://files.pythonhosted.org/packages/79/d4/ebb285a263cc2070d04d39917288b5d1c7f49e1c47ed5544e86283e091c6/fastar-0.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e5c91cb4527a6e634e7477a01aa52ccfbb978df1d9803172685c1e0802a2c18c", size = 869584, upload-time = "2026-03-20T14:24:52.067Z" }, + { url = "https://files.pythonhosted.org/packages/23/19/a293b6f75ea1b9e14d384859253ee65f966a73be306cea39552a557c9e34/fastar-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6bc32f40a3e8ab12b8ebce48c4808d2bcf89bd3dac3023980b8a9b4aaf719f2", size = 762379, upload-time = "2026-03-20T14:23:47.429Z" }, + { url = "https://files.pythonhosted.org/packages/95/2f/a31f00c31f16a3bffd6f6ab3414964100fb35a79983f21283fc8b81d3cec/fastar-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee4a1d85a58cd955a5409b221450762b851879ce6e080d6d717265fb9a4e939d", size = 759567, upload-time = "2026-03-20T14:24:00.677Z" }, + { url = "https://files.pythonhosted.org/packages/b0/46/5a4b1fb1e5c8b6cd1eb464e658ed75d667f1f53834f353e6323ca71bd113/fastar-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b72e25ec1cbad0fc2a5f93a147978cc41e054ce5789807ebd3bcece5f276c0c2", size = 925850, upload-time = "2026-03-20T14:24:13.669Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ec/a5543fb1b059a82ce4c6fc571fe429390294e8150c09bb537d228471eac6/fastar-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862ddfaf73c7388d708bcbeb75e2e336605465b88d952407621c847bab5d3cb", size = 818858, upload-time = "2026-03-20T14:24:39.431Z" }, + { url = "https://files.pythonhosted.org/packages/53/9a/af5ae6d24e1170702d096225989b4ee3470b22bbecb5c09c899e816aefd7/fastar-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3471fa2627b9703830d13c8b0a6ba19eeff4e2e0ff924631065ecceca56abb2b", size = 821941, upload-time = "2026-03-20T14:25:05.534Z" }, + { url = "https://files.pythonhosted.org/packages/54/3f/399d8b080f7c5fe1fa88dadaa7a30bd0bb885ad490d3c2ef2c667877c5c4/fastar-0.9.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:e3d2e68e0239ab24b65b0674f2b74ac71d8fb5ea221a3e0d0ab966292bd83e12", size = 886548, upload-time = "2026-03-20T14:24:26.209Z" }, + { url = "https://files.pythonhosted.org/packages/63/cd/034b5f61e99df67e092e1d3d538150a5f562d00c0259e6402cbcb62e15a9/fastar-0.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dcabfe31c48ff6a994c3dc4ddc27287b15d78a09c737beef8a6b1f210b720a6a", size = 970244, upload-time = "2026-03-20T14:25:42.928Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8f/3a8b0d711050b300a3448c9d145c6d234958e148e456ab4a15daca6e4b05/fastar-0.9.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f1723bb9cc3dcd087b5dd066a0369f27529a925d467ccc896d1f6cd0212417bf", size = 1036944, upload-time = "2026-03-20T14:25:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/34/11/cd5ebd16529c5fbff2431b494bd6f3f8ecafeca8f874449bf65ccf58c77b/fastar-0.9.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3ec2e699af02ba78f359b1cf1f4b3da22f41dec3a327f1cda6a1d31a43365a71", size = 1078612, upload-time = "2026-03-20T14:26:09.042Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/752c184e3c5e8de592e5d7ce3d081bf665ae5dbbe4a3df816daf38043143/fastar-0.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:761708eb2f6e402d4cda04ac81d0c2406b1c10375601e238083d2e885ec52a42", size = 1029368, upload-time = "2026-03-20T14:26:21.79Z" }, + { url = "https://files.pythonhosted.org/packages/68/b3/2a5551942adaecb9874ebc0d0922f3ab9dd058298b7a36a7900da93a3e68/fastar-0.9.0-cp310-cp310-win32.whl", hash = "sha256:a5ea0969c94845faed7bf681850df704da9617ad7231850dbc7ca4017080133a", size = 454507, upload-time = "2026-03-20T14:26:54.124Z" }, + { url = "https://files.pythonhosted.org/packages/23/30/7a2f25837ee7353ff5eaa815d9a6321f8704fcc39a94570a1b2d958639c0/fastar-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:e5646f10a747282904f2def929612ed19cace4bd702029c3d7c78205ef604abd", size = 486500, upload-time = "2026-03-20T14:26:42.142Z" }, + { url = "https://files.pythonhosted.org/packages/6f/01/4ecbe0b4938608f9c6c5c4d4f6b872975fe30152bfaa8e44fe0e3b6cbcc4/fastar-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:facc7522bd1c1e7569bedb602932fc7292408a320f415d72180634d58f661bf0", size = 708809, upload-time = "2026-03-20T14:25:31.299Z" }, + { url = "https://files.pythonhosted.org/packages/11/6a/085b3cae0e04da4d42306dc07e2cc4f95d9c8f27df4dfd1a25d0f80516cb/fastar-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c8ac3e8aaee57dfc822b04f570f0a963c2381a9dc8990fe0c6e965efd23fd451", size = 629764, upload-time = "2026-03-20T14:25:19.017Z" }, + { url = "https://files.pythonhosted.org/packages/3c/c2/cdd996a37837e6cc5edc4d09775d2a2bc63e9e931129db69947cf4c77148/fastar-0.9.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d90493b4bb56db728b38eb18a551df386113d72ad4e7f1a97572f3662a9b8a85", size = 869631, upload-time = "2026-03-20T14:24:53.779Z" }, + { url = "https://files.pythonhosted.org/packages/30/d4/4a5a3c341d26197ea3ae6bed79fc9bb4ead8ddc74a93bdb74e4ee0bac18e/fastar-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17e2c3b46408193ea13c1e1177275ca7951e88bd3dce16baccb8de4f5e0dc2e8", size = 762096, upload-time = "2026-03-20T14:23:49.175Z" }, + { url = "https://files.pythonhosted.org/packages/bc/dd/1d346cdfcd3064f6c435eff90a8d7cf0021487e3681453bdd681b9488d81/fastar-0.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:52f96a3d4cfbe4f06b376706fa0562f3a1d2329bc37168119af0e47e1ac21cab", size = 759627, upload-time = "2026-03-20T14:24:01.984Z" }, + { url = "https://files.pythonhosted.org/packages/02/a1/e91eb7ae1e41c0d3ead86dc199beb13a0b80101e2948d66adeb578b09e60/fastar-0.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57e9b94e485713c79bb259f7ecff1213527d05e9aa43a157c3fbc88812cf163e", size = 926211, upload-time = "2026-03-20T14:24:15.218Z" }, + { url = "https://files.pythonhosted.org/packages/9b/63/9fea9604e7aecc2f062f0df5729f74712d81615a1b18fa6a1a13106184fa/fastar-0.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb06d0a0cc3cf52a9c07559bb16ab99eb75afe0b3d5ce68f5c299569460851ac", size = 818748, upload-time = "2026-03-20T14:24:40.765Z" }, + { url = "https://files.pythonhosted.org/packages/b0/f8/521438041d69873bb68b144b09080ae4f1621cebb8238b1e54821057206b/fastar-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c75e779f72d845037d4bf6692d01ac66f014eaef965c9231d41d5cc1276b89fc", size = 822380, upload-time = "2026-03-20T14:25:06.825Z" }, + { url = "https://files.pythonhosted.org/packages/92/05/f33cc3f5f96ffb7d81a7f06c9239d4eea584527292a030a73d3218148f41/fastar-0.9.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:24b13fc4ef3f1e3c9cc2dcf07ad9445900db9d3ce09b73021547a55994d0407f", size = 886569, upload-time = "2026-03-20T14:24:27.567Z" }, + { url = "https://files.pythonhosted.org/packages/60/32/6e7cb45dce544f97b0199325084a0a5a895cb903e0539690619e78d8d7cf/fastar-0.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec7852de506d022ad36ad56f4aefb10c259dd59e485bf87af827954d404ba9d5", size = 969993, upload-time = "2026-03-20T14:25:44.222Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ee/04cf9374e5e6a82ddc87073d684c1fa7a9ca368bf85c2786535b1bfc38a9/fastar-0.9.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a79c53c3003958dca88a7ec3dd805bf9c2fb2a659110039f44571d57e329e3d4", size = 1036738, upload-time = "2026-03-20T14:25:57.551Z" }, + { url = "https://files.pythonhosted.org/packages/b6/94/e6f6ad29c25c5f531a406e3a35ef5c034ea177748f9fb621073519adb3d5/fastar-0.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:00328ce7ae76be7f9e2faa6a221a0b41212e4115c27e2ac5e585bcf226bfc2eb", size = 1078557, upload-time = "2026-03-20T14:26:10.358Z" }, + { url = "https://files.pythonhosted.org/packages/1f/44/a1c9f6afe93d1cc1abb68a7cda2bada509d756d24e22d5d949ca86b4f45e/fastar-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5c03fad1ad9ac57cf03a4db9e18c7109c37416ff4eb9ebfca98fcd2b233a26c4", size = 1029251, upload-time = "2026-03-20T14:26:23.215Z" }, + { url = "https://files.pythonhosted.org/packages/75/31/9e77bc2af3c8b8a433b7175d14b9c75d0ab901542c7452fdf942ece5a155/fastar-0.9.0-cp311-cp311-win32.whl", hash = "sha256:163ba4c543d2112c8186be2f134d11456b593071ba9ea3faba4f155bde7c5dac", size = 454633, upload-time = "2026-03-20T14:26:55.344Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d4/a78d51d1290cdce2d6d3162a18d12c736b71d3feef5a446b3fe021443eb3/fastar-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:2137d5d26044b44bb19197a8fc959256c772615ee959cddd0f74320b548fc966", size = 486772, upload-time = "2026-03-20T14:26:43.569Z" }, + { url = "https://files.pythonhosted.org/packages/fa/39/471aefca4c8180689cc0dc6f2f23bc283a3ca07114f713307fb947d320af/fastar-0.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:ecb94de3bc96d9fae95641a7907385541517a4c17416153d3b952d37dce0a2a3", size = 463586, upload-time = "2026-03-20T14:26:35.483Z" }, + { url = "https://files.pythonhosted.org/packages/4d/9b/300bc0dafa8495718976076db216f42d57b251a582589566a63b4ed2cb82/fastar-0.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7a8b5daa50d9b4c07367dffc40880467170bf1c31ca63a2286506edbe6d3d65b", size = 706914, upload-time = "2026-03-20T14:25:32.501Z" }, + { url = "https://files.pythonhosted.org/packages/95/97/f1e34c8224dc373c6fab5b33e33be0d184751fdc27013af3278b1e4e6e6c/fastar-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ec841a69fea73361c6df6d9183915c09e9ce3bd96493763fa46019e79918400", size = 627422, upload-time = "2026-03-20T14:25:20.318Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ad/e2499d136e24c2d896f2ec58183c91c6f8185d758177537724ed2f3e1b54/fastar-0.9.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad46bc23040142e9be4b4005ea366834dbf0f1b6a90b8ecdc3ec96c42dec4adf", size = 865265, upload-time = "2026-03-20T14:24:55.418Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/b6ad68b2ab1d7b74b0d38725d817418016bdd64880b36108be80d2460b4d/fastar-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de264da9e8ef6407aa0b23c7c47ed4e34fde867e7c1f6e3cb98945a93e5f89f2", size = 760583, upload-time = "2026-03-20T14:23:50.447Z" }, + { url = "https://files.pythonhosted.org/packages/b8/96/086116ad46e3b98f6c217919d680e619f2857ffa6b5cc0d7e46e4f214b83/fastar-0.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75c70be3a7da3ff9342f64c15ec3749c13ef56bc28e69075d82d03768532a8d0", size = 758000, upload-time = "2026-03-20T14:24:03.471Z" }, + { url = "https://files.pythonhosted.org/packages/9b/e6/ea642ea61eea98d609343080399a296a9ff132bd0492a6638d6e0d9e41a7/fastar-0.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a734506b071d2a8844771fe735fbd6d67dd0eec80eef5f189bbe763ebe7a0b8", size = 923647, upload-time = "2026-03-20T14:24:16.875Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/53874aad61e4a664af555a2aa7a52fe46cfadd423db0e592fa0cfe0fa668/fastar-0.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eac084ab215aaf65fa406c9b9da1ac4e697c3d3a1a183e09c488e555802f62d", size = 816528, upload-time = "2026-03-20T14:24:42.048Z" }, + { url = "https://files.pythonhosted.org/packages/41/df/d663214d35380b07a24a796c48d7d7d4dc3a28ec0756edbcb7e2a81dc572/fastar-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acb62e2369834fb23d26327157f0a2dbec40b230c709fa85b1ce96cf010e6fbf", size = 819050, upload-time = "2026-03-20T14:25:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5a/455b53f11527568100ba6d5847635430645bad62d676f0bae4173fc85c90/fastar-0.9.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:f2f399fffb74bcd9e9d4507e253ace2430b5ccf61000596bda41e90414bcf4f2", size = 885257, upload-time = "2026-03-20T14:24:28.86Z" }, + { url = "https://files.pythonhosted.org/packages/4f/dd/0a8ea7b910293b07f8c82ef4e6451262ccf2a6f2020e880f184dc4abd6c2/fastar-0.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87006c8770dfc558aefe927590bbcdaf9648ca4472a9ee6d10dfb7c0bda4ce5b", size = 968135, upload-time = "2026-03-20T14:25:45.614Z" }, + { url = "https://files.pythonhosted.org/packages/6b/cb/5c7e9231d6ba00e225623947068db09ddd4e401800b0afaf39eece14bfee/fastar-0.9.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d012644421d669d9746157193f4eafd371e8ae56ff7aef97612a4922418664c", size = 1034940, upload-time = "2026-03-20T14:25:58.893Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b4/eccfcf7fe9d2a0cea6d71630acc48a762404058c9b3ae1323f74abcda005/fastar-0.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:094fd03b2e41b20a2602d340e2b52ad10051d82caa1263411cf247c1b1bc139f", size = 1073807, upload-time = "2026-03-20T14:26:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/8b/53/6ddda28545b428d54c42f341d797046467c689616a36eae9a43ba56f2545/fastar-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:59bc500d7b6bdaf2ffb2b632bc6b0f97ddfb3bb7d31b54d61ceb00b5698d6484", size = 1025314, upload-time = "2026-03-20T14:26:24.624Z" }, + { url = "https://files.pythonhosted.org/packages/03/cf/71e2a67b0a69971044ad57fe7d196287ac32ab710bfc47f34745bb4a7834/fastar-0.9.0-cp312-cp312-win32.whl", hash = "sha256:25a1fd512ce23eb5aaab514742e7c6120244c211c349b86af068c3ae35792ec3", size = 452740, upload-time = "2026-03-20T14:26:56.604Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c5/0ffa2fffac0d80d2283db577ff23f8d91886010ea858c657f8278c2a222c/fastar-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:b10a409797d01ee4062547e95e4a89f6bb52677b144076fd5a1f9d28d463ab10", size = 485282, upload-time = "2026-03-20T14:26:44.926Z" }, + { url = "https://files.pythonhosted.org/packages/14/20/999d72dc12e793a6c7889176fc42ad917d568d802c91b4126629e9be45a9/fastar-0.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea4d98fc62990986ce00d2021f08ff2aa6eae71636415c5a5f65f3a6a657dc5e", size = 461795, upload-time = "2026-03-20T14:26:36.728Z" }, + { url = "https://files.pythonhosted.org/packages/9a/26/ea9339facfe4ee224be673c6888dbf077f28b0f81185f80353966c9f4925/fastar-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7b55ae4a3a481fd90a63ac558a7e8aab652ac1dfd15d8657266e71bf65346408", size = 706740, upload-time = "2026-03-20T14:25:33.741Z" }, + { url = "https://files.pythonhosted.org/packages/77/52/f3b06867e5ca8d5b2c1c15a1563415e0037b5831f2058ee72b03960296d9/fastar-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f07c6bdeedfeb30ef459f21fa9ab06e2b6727f7e7653176d3abb7a85f447c400", size = 627615, upload-time = "2026-03-20T14:25:21.608Z" }, + { url = "https://files.pythonhosted.org/packages/52/32/021b0a633bca18bca4f831392c2938c15c4605de2d9895b783ad6d64679c/fastar-0.9.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:90f46492e05141089766699e95c79d470e8013192fbbb16ef16b576281f3b8ee", size = 864584, upload-time = "2026-03-20T14:24:56.941Z" }, + { url = "https://files.pythonhosted.org/packages/3f/54/e2e1b4c8512d670373047e5e585b1d1ff9ffd722b0a17647d22c9c9bd248/fastar-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:108bb46c080ca152bb331f1e0576177d36e9badba51b1d5724d2823542e0dd1f", size = 760246, upload-time = "2026-03-20T14:23:51.964Z" }, + { url = "https://files.pythonhosted.org/packages/fa/7d/1e283dd8dbb3647049594bb477bdc053045c6fff2d3f06386d2dcacce7aa/fastar-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d17d311cfbb559154ba940972b6d07a3a7ac221a2a01208f119ad03495f01d32", size = 757024, upload-time = "2026-03-20T14:24:04.69Z" }, + { url = "https://files.pythonhosted.org/packages/87/ac/82d3cb64d318ce16c5d1a26a40b8aa570fcc9b23684221aece838c4cbada/fastar-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2ef34e7088f308e73460e1b8d9b0479a743f679816782a80db6ae87ee68714a", size = 921630, upload-time = "2026-03-20T14:24:18.155Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b8/3e7892f1a25a1a2054a20de6c846c0794b8fa361e5b9d3d00915b41e97bd/fastar-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c93bf4732d0dd6adae4a8b3bbebe19af76ee1072b7688bf39c5a1d120425a772", size = 815791, upload-time = "2026-03-20T14:24:43.28Z" }, + { url = "https://files.pythonhosted.org/packages/db/5e/8fcc662db1fd0985f4f8a54e79276416565a0d1fcb8da66665b2061ead30/fastar-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a67b061b1099cf3b8b6234dd3605fa16f5078ab6b51c8d77ad7a5d11c3cf834", size = 818980, upload-time = "2026-03-20T14:25:09.545Z" }, + { url = "https://files.pythonhosted.org/packages/68/ed/37291fbd6c9b5b0905712da6191bdfc25a7dc236efbf130e3a1a7d1b9440/fastar-0.9.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:912efe3121dc1f3c05940cfa1c6b09b8868d702d24566506aa1d0d96e429923a", size = 884578, upload-time = "2026-03-20T14:24:30.584Z" }, + { url = "https://files.pythonhosted.org/packages/94/19/7b3b7af978ae4f012664781554716d67549ab19ddbcb6e6d1adc04d7a5e7/fastar-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2394980cc126a3263e115600bc4ff9e7320cddde83c99fc334ab530be5b7166e", size = 967790, upload-time = "2026-03-20T14:25:46.975Z" }, + { url = "https://files.pythonhosted.org/packages/e6/38/4cce2a8e529a7d3e99e427c9bbcccd7013ff6b3ba295613e6f1c573c9e6c/fastar-0.9.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d0aff74ea98642784c941d3cd8c35943258d4b9626157858901c5b181683339b", size = 1033892, upload-time = "2026-03-20T14:26:00.22Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3f/86f25d79b1b369c2756ee338b76d1696a9cac3a737e819459b0ad7822ede/fastar-0.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3e8a1deaf490f4ec15eca7e66127ff89cdefd20217f358739d4b7b1cb322f663", size = 1072969, upload-time = "2026-03-20T14:26:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/10/4f/6ec0c123c15bbcb9a9b82e979dc81273789ebbfbb4a2b41a1a6941577c94/fastar-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c9bd8879ebf05aa247e60e454bb7568cbdd44f016b8c58e31e5398039403e61d", size = 1025768, upload-time = "2026-03-20T14:26:25.957Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d1/cbdcdb78ca034ed51a9f53c2650885873d8b06727452c1cc33f56ad0c66a/fastar-0.9.0-cp313-cp313-win32.whl", hash = "sha256:11b35e6453a2da8715dd8415b3999ea57805125493e44ce41a32404bf9a510a7", size = 452742, upload-time = "2026-03-20T14:26:58.014Z" }, + { url = "https://files.pythonhosted.org/packages/74/ee/138d2f8e3504232a279afa224d3e5922c15dc7126613e6c135cfc8e10ec9/fastar-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:10a1e7f7bfa1c6f03e4c657fdc0a32ebe42d8e48f681403dc0c67258e1cb5bef", size = 484917, upload-time = "2026-03-20T14:26:46.135Z" }, + { url = "https://files.pythonhosted.org/packages/db/ca/f518ee9dccc45097560a2cff245590c65b7b348171c8d2f2e487cf92a69f/fastar-0.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:e5484ac1415e0ca8bc7b69231e3e3afb52887fed10b839ca676767635a13f06f", size = 461202, upload-time = "2026-03-20T14:26:37.937Z" }, + { url = "https://files.pythonhosted.org/packages/cf/00/99700dd33273c118d7d9ab7ad5db6650b430448d4cfae62aec6ef6ca4cb7/fastar-0.9.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ccb2289f24ee6555330eb77149486d3a2ec8926450a96157dd20c636a0eec085", size = 707059, upload-time = "2026-03-20T14:25:35.086Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a4/4808dcfa8dddb9d7f50d830a39a9084d9d148ed06fcac8b040620848bc24/fastar-0.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2bfee749a46666785151b33980aef8f916e6e0341c3d241bde4d3de6be23f00c", size = 627135, upload-time = "2026-03-20T14:25:23.134Z" }, + { url = "https://files.pythonhosted.org/packages/da/cb/9c92e97d760d769846cae6ce53332a5f2a9246eb07b369ac2a4ebf10480c/fastar-0.9.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f6096ec3f216a21fa9ac430ce509447f56c5bd979170c4c0c3b4f3cb2051c1a8", size = 864974, upload-time = "2026-03-20T14:24:58.624Z" }, + { url = "https://files.pythonhosted.org/packages/84/38/9dadebd0b7408b4f415827db35169bbd0741e726e38e3afd3e491b589c61/fastar-0.9.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7a806e54d429f7f57e35dc709e801da8c0ba9095deb7331d6574c05ae4537ea", size = 760262, upload-time = "2026-03-20T14:23:53.275Z" }, + { url = "https://files.pythonhosted.org/packages/d6/7d/7afc5721429515aa0873b268513f656f905d27ff1ca54d875af6be9e9bc6/fastar-0.9.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9a06abf8c7f74643a75003334683eb6e94fabef05f60449b7841eeb093a47b0", size = 757575, upload-time = "2026-03-20T14:24:06.143Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5d/7498842c62bd6057553aa598cd175a0db41fdfeda7bdfde48dab63ffb285/fastar-0.9.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e9b5c155946f20ce3f999fb1362ed102876156ad6539e1b73a921f14efb758c", size = 924827, upload-time = "2026-03-20T14:24:19.364Z" }, + { url = "https://files.pythonhosted.org/packages/69/ab/13322e98fe1a00ed6efbfa5bf06fcfff8a6979804ef7fcef884b5e0c6f85/fastar-0.9.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdedac6a84ef9ebc1cee6d777599ad51c9e98ceb8ebb386159483dcd60d0e16", size = 816536, upload-time = "2026-03-20T14:24:44.844Z" }, + { url = "https://files.pythonhosted.org/packages/fe/fd/0aa5b9994c8dba75b73a9527be4178423cb926db9f7eca562559e27ccdfd/fastar-0.9.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51df60a2f7af09f75b2a4438b25cb903d8774e24c492acf2bca8b0863026f34c", size = 818686, upload-time = "2026-03-20T14:25:10.799Z" }, + { url = "https://files.pythonhosted.org/packages/46/d6/e000cd49ef85c11a8350e461e6c48a4345ace94fb52242ac8c1d5dad1dfc/fastar-0.9.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:15016d0da7dbc664f09145fc7db549ba8fe32628c6e44e20926655b82de10658", size = 885043, upload-time = "2026-03-20T14:24:32.231Z" }, + { url = "https://files.pythonhosted.org/packages/68/28/ee734fe273475b9b25554370d92a21fc809376cf79aa072de29d23c17518/fastar-0.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c66a8e1f7dae6357be8c1f83ce6330febbc08e49fc40a5a2e91061e7867bbcbf", size = 967965, upload-time = "2026-03-20T14:25:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/c1/35/165b3a75f1ee8045af9478c8aae5b5e20913cca2d4a5adb1be445e8d015a/fastar-0.9.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:1c6829be3f55d2978cb62921ef4d7c3dd58fe68ee994f81d49bd0a3c5240c977", size = 1034507, upload-time = "2026-03-20T14:26:01.518Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4e/4097b5015da02484468c16543db2f8dec2fe827d321a798acbd9068e0f13/fastar-0.9.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:68db849e01d49543f31d56ef2fe15527afe2b9e0fb21794edc4d772553d83407", size = 1073388, upload-time = "2026-03-20T14:26:14.448Z" }, + { url = "https://files.pythonhosted.org/packages/07/d7/3b86af4e63a551398763a1bbbbac91e1c0754ece7ac7157218b33a065f4c/fastar-0.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5569510407c0ded580cfeec99e46ebe85ce27e199e020c5c1ea6f570e302c946", size = 1025190, upload-time = "2026-03-20T14:26:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/39/07/8c50a60f03e095053306fcf57d9d99343bce0e99d5b758bf96de31aec849/fastar-0.9.0-cp314-cp314-win32.whl", hash = "sha256:3f7be0a34ffbead52ab5f4a1e445e488bf39736acb006298d3b3c5b4f2c5915e", size = 452301, upload-time = "2026-03-20T14:26:59.234Z" }, + { url = "https://files.pythonhosted.org/packages/ee/69/aa6d67b09485ba031408296d6ff844c7d83cdcb9f8fcc240422c6f83be87/fastar-0.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:cf7f68b98ed34ce628994c9bbd4f56cf6b4b175b3f7b8cbe35c884c8efec0a5b", size = 484948, upload-time = "2026-03-20T14:26:48.45Z" }, + { url = "https://files.pythonhosted.org/packages/20/6d/dba29d87ca929f95a5a7025c7d30720ad8478beed29fff482f29e1e8b045/fastar-0.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:155dae97aca4b245eabb25e23fd16bfd42a0447f9db7f7789ab1299b02d94487", size = 461170, upload-time = "2026-03-20T14:26:39.191Z" }, + { url = "https://files.pythonhosted.org/packages/96/8f/c3ea0adac50a8037987ee7f15ff94767ebb604faf6008cbd2b8efa46c372/fastar-0.9.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a63df018232623e136178953031057c7ac0dbf0acc6f0e8c1dc7dbc19e64c22f", size = 705857, upload-time = "2026-03-20T14:25:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b3/e0e1aad1778065559680a73cdf982ed07b04300c2e5bf778dec8668eda6f/fastar-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6fb44f8675ef87087cb08f9bf4dfa15e818571a5f567ff692f3ea007cff867b5", size = 626210, upload-time = "2026-03-20T14:25:24.361Z" }, + { url = "https://files.pythonhosted.org/packages/94/f3/3c117335cbea26b3bc05382c27e6028278ed048d610b8de427c68f2fec84/fastar-0.9.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81092daa991d0f095424e0e28ed589e03c81a21eeddc9b981184ddda5869bf9d", size = 864879, upload-time = "2026-03-20T14:25:00.131Z" }, + { url = "https://files.pythonhosted.org/packages/26/5d/e8d00ec3b2692d14ea111ddae25bf10e0cb60d5d79915c3d8ea393a87d5c/fastar-0.9.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e8793e2618d0d6d5a7762d6007371f57f02544364864e40e6b9d304b0f151b2", size = 759117, upload-time = "2026-03-20T14:23:54.826Z" }, + { url = "https://files.pythonhosted.org/packages/1a/61/6e080fdbc28c72dded8b6ff396035d6dc292f9b1c67b8797ac2372ca5733/fastar-0.9.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83f7ef7056791fc95b6afa987238368c9a73ad0edcedc6bc80076f9fbd3a2a78", size = 756527, upload-time = "2026-03-20T14:24:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/e8/97/2cf1a07884d171c028bd4ae5ecf7ded6f31581f79ab26711dcdad0a3d5ab/fastar-0.9.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3a456230fcc0e560823f5d04ae8e4c867300d8ee710b14ddcdd1b316ac3dd8d", size = 921763, upload-time = "2026-03-20T14:24:20.787Z" }, + { url = "https://files.pythonhosted.org/packages/f6/e3/c1d698a45f9f5dc892ed7d64badc9c38f1e5c1667048191969c438d2b428/fastar-0.9.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a60b117ebadc46c10c87852d2158a4d6489adbfbbec37be036b4cfbeca07b449", size = 815493, upload-time = "2026-03-20T14:24:46.482Z" }, + { url = "https://files.pythonhosted.org/packages/25/38/e124a404043fba75a8cb2f755ca49e4f01e18400bb6607a5f76526e07164/fastar-0.9.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a6199b4ca0c092a7ae47f5f387492d46a0a2d82cb3b7aa0bf50d7f7d5d8d57f", size = 819166, upload-time = "2026-03-20T14:25:12.027Z" }, + { url = "https://files.pythonhosted.org/packages/85/4a/5b1ea5c8d0dbdfcec2fd1e6a243d6bb5a1c7cd55e132cc532eb8b1cbd6d9/fastar-0.9.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:34efe114caf10b4d5ea404069ff1f6cc0e55a708c7091059b0fc087f65c0a331", size = 883618, upload-time = "2026-03-20T14:24:33.552Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/ae46e5722a67a3c2e0ff83d539b0907d6e5092f6395840c0eb6ede81c5d6/fastar-0.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4d44c1f8d9c5a3e4e58e6ffb77f4ca023ba9d9ddd88e7c613b3419a8feaa3db7", size = 966294, upload-time = "2026-03-20T14:25:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/98/58/b161cf8711f4a50a3e57b6f89bc703c1aed282cad50434b3bc8524738b20/fastar-0.9.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d2af970a1f773965b05f1765017a417380ad080ea49590516eb25b23c039158a", size = 1033177, upload-time = "2026-03-20T14:26:02.868Z" }, + { url = "https://files.pythonhosted.org/packages/e2/76/faac7292bce9b30106a6b6a9f5ddb658fdb03abe2644688b82023c8f76b9/fastar-0.9.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1675346d7cbdde0d21869c3b597be19b5e31a36442bdf3a48d83a49765b269dc", size = 1073620, upload-time = "2026-03-20T14:26:16.121Z" }, + { url = "https://files.pythonhosted.org/packages/b8/be/dd55ffcc302d6f0ff4aba1616a0da3edc8fcefb757869cad81de74604a35/fastar-0.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dc440daa28591aeb4d387c171e824f179ad2ab256ce7a315472395b8d5f80392", size = 1025147, upload-time = "2026-03-20T14:26:28.767Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c7/080bbb2b3c4e739fe6486fd765a09905f6c16c1068b2fcf2bb51a5e83937/fastar-0.9.0-cp314-cp314t-win32.whl", hash = "sha256:32787880600a988d11547628034993ef948499ae4514a30509817242c4eb98b1", size = 452317, upload-time = "2026-03-20T14:27:03.243Z" }, + { url = "https://files.pythonhosted.org/packages/42/39/00553739a7e9e35f78a0c5911d181acf6b6e132337adc9bbc3575f5f6f04/fastar-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92fa18ec4958f33473259980685d29248ac44c96eed34026ad7550f93dd9ee23", size = 483994, upload-time = "2026-03-20T14:26:52.76Z" }, + { url = "https://files.pythonhosted.org/packages/4f/36/a7af08d233624515d9a0f5d41b7a01a51fd825b8c795e41800215a3200e7/fastar-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:34f646ac4f5bed3661a106ca56c1744e7146a02aacf517d47b24fd3f25dc1ff6", size = 460604, upload-time = "2026-03-20T14:26:40.771Z" }, + { url = "https://files.pythonhosted.org/packages/69/9f/4aeaa0a1ac2aca142a276ea136e651e94ba1341bd840ba455ed250d1970b/fastar-0.9.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b74ce299066288f3b90221dca8507f59c7d9e8df91387948006b9a0fea4f9bdc", size = 710738, upload-time = "2026-03-20T14:25:41.17Z" }, + { url = "https://files.pythonhosted.org/packages/d0/19/9f8fb5c0e803254c5d535c362102dd604d9bdb206d5a36150f4637cadf09/fastar-0.9.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:76be31936cabce31cbb6381128f851cf0a6da2d5c25357615cd1504b26dc31cf", size = 633000, upload-time = "2026-03-20T14:25:28.496Z" }, + { url = "https://files.pythonhosted.org/packages/ef/8d/0d1d9a87a78f1e686bb6c7c69688a4c9ad1efb65e49cc66310b97fdf900b/fastar-0.9.0-pp311-pypy311_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c4c9ea0e0d69445b0ca3b0bd80bd8237fec8a914275b0472ecca2b555c12f3a3", size = 871226, upload-time = "2026-03-20T14:25:04.351Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/366937320b1cca522570c527a45b1254bd68d057e68956baefc49eacae27/fastar-0.9.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b665c33afcd1d581b82235b690d999c5446ccc2c4d80c4a95f30df3b43d22494", size = 763872, upload-time = "2026-03-20T14:23:59.122Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f2/121c5432bb152da68fc466a0d0206d66383a40a2f9beff5583d9277aceee/fastar-0.9.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d2a9a49f9217f4f60f9ba23fdd1f7f3f04fed97391145eb9460ec83ca0b4bd33", size = 762897, upload-time = "2026-03-20T14:24:11.932Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/88d3a603b997063e032f94cc0fff74031d76903f38cc30416a400395df03/fastar-0.9.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d860e82a531e9cc67e7f500a299bffbe6e93d80bbf48401fd8f452a0c58f28", size = 927024, upload-time = "2026-03-20T14:24:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/a6/17/d6dc778c45b0c7d9a279706d7a5d62122dab0a7a0cb39aac6f5ef42f13f6/fastar-0.9.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3feede2d72ec0782b5ccc18568f36cbe33816be396551aa47b3e1b73c322cdd2", size = 821265, upload-time = "2026-03-20T14:24:50.407Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e0/cec25d43df7ea4b4e3e875352c6d51c848c855792ba276c546732a7170af/fastar-0.9.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9ac410d32cbb514e966c45f0fedd0f9447b0dea9e734af714648da503603df6", size = 824024, upload-time = "2026-03-20T14:25:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/52/90/c354969770d21d1b07c9281b5e23052392c288d22984a1917d30940e86cb/fastar-0.9.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:40b8c08df809e5e58d1839ccb37bafe4485deb6ee56bb7c5f0cbb72d701eb965", size = 888886, upload-time = "2026-03-20T14:24:38.229Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ac/eb2a01ed94e79b72003840448d2b69644a54a47f615c7d693432a1337caa/fastar-0.9.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d62a4fd86eda3bea7cc32efd64d43b6d0fcdbbec009558b750fc362f20142789", size = 972503, upload-time = "2026-03-20T14:25:54.207Z" }, + { url = "https://files.pythonhosted.org/packages/8d/88/f7e28100fa7ff4a26a3493ad7a5d45d70f6de858c05f5c34aca3570c5839/fastar-0.9.0-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:7bf6958bb6f94e5ec522e4a255b8e940d3561ad973f0be5dde6115b5a0854af5", size = 1039106, upload-time = "2026-03-20T14:26:07.686Z" }, + { url = "https://files.pythonhosted.org/packages/c0/de/52c578180fdaaf0f3289de8a878f1ac070f7e3e18a0689d3fd44dd7dae2c/fastar-0.9.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:c210b839c0a33cf8d08270963ad237bcb63029dddf6d6025333f7e5ca63930bd", size = 1080754, upload-time = "2026-03-20T14:26:20.299Z" }, + { url = "https://files.pythonhosted.org/packages/a4/45/1ea024be428ad9d89e9f738c9379507e97df9f9ed97e50e4a1d10ff90fef/fastar-0.9.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:fad70e257daefb42bab68dcd68beaf2e2a99da056d65f2c9f988449a4e869306", size = 1031304, upload-time = "2026-03-20T14:26:33.294Z" }, ] [[package]] @@ -2086,7 +2129,7 @@ wheels = [ [[package]] name = "google-genai" -version = "1.67.0" +version = "1.68.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2100,9 +2143,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/07/59a498f81f2c7b0649eacda2ea470b7fd8bd7149f20caba22962081bdd51/google_genai-1.67.0.tar.gz", hash = "sha256:897195a6a9742deb6de240b99227189ada8b2d901d61bdfba836c3092021eab6", size = 506972, upload-time = "2026-03-12T20:39:16.241Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/2c/f059982dbcb658cc535c81bbcbe7e2c040d675f4b563b03cdb01018a4bc3/google_genai-1.68.0.tar.gz", hash = "sha256:ac30c0b8bc630f9372993a97e4a11dae0e36f2e10d7c55eacdca95a9fa14ca96", size = 511285, upload-time = "2026-03-18T01:03:18.243Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c2/562aa1f086e53529ffbeb5b43d5d8bc42c1b968102b5e2163fad005ce298/google_genai-1.67.0-py3-none-any.whl", hash = "sha256:58b0484ff2d4335fa53c724b489e9f807fcca8115d9cdbd8fdf341121fbd6d2d", size = 733542, upload-time = "2026-03-12T20:39:14.615Z" }, + { url = "https://files.pythonhosted.org/packages/84/de/7d3ee9c94b74c3578ea4f88d45e8de9405902f857932334d81e89bce3dfa/google_genai-1.68.0-py3-none-any.whl", hash = "sha256:a1bc9919c0e2ea2907d1e319b65471d3d6d58c54822039a249fe1323e4178d15", size = 750912, upload-time = "2026-03-18T01:03:15.983Z" }, ] [[package]] @@ -2126,7 +2169,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747, upload-time = "2026-02-20T20:16:21.325Z" }, { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202, upload-time = "2026-02-20T20:47:28.955Z" }, { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620, upload-time = "2026-02-20T20:55:55.581Z" }, - { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132, upload-time = "2026-02-20T21:02:43.261Z" }, { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729, upload-time = "2026-02-20T20:20:58.395Z" }, { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946, upload-time = "2026-02-20T20:49:31.102Z" }, { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494, upload-time = "2026-02-20T20:21:06.541Z" }, @@ -2134,7 +2176,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, @@ -2143,7 +2184,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, @@ -2152,7 +2192,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, - { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, @@ -2161,7 +2200,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, - { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, @@ -2170,7 +2208,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, - { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, @@ -2824,11 +2861,11 @@ wheels = [ [[package]] name = "jsonpointer" -version = "3.0.0" +version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068, upload-time = "2026-03-23T22:32:32.458Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659, upload-time = "2026-03-23T22:32:31.568Z" }, ] [[package]] @@ -3088,7 +3125,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.7.18" +version = "0.7.22" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -3101,9 +3138,9 @@ dependencies = [ { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/a8/31e9e7b42089cf194a0d873cbfc1ecec6d6dd0cc693808f1cb64494cbd0c/langsmith-0.7.18.tar.gz", hash = "sha256:d7e6e1f9c9300ee83b9f201c9254b4a32799218de102a5b1d2b217e00be2dfa2", size = 1134635, upload-time = "2026-03-16T18:54:19.131Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/2a/2d5e6c67396fd228670af278c4da7bd6db2b8d11deaf6f108490b6d3f561/langsmith-0.7.22.tar.gz", hash = "sha256:35bfe795d648b069958280760564632fd28ebc9921c04f3e209c0db6a6c7dc04", size = 1134923, upload-time = "2026-03-19T22:45:23.492Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/58/244a14e29c7feccf06ed3929c9ab65a747a9ee94d5ac43d40862053b2f54/langsmith-0.7.18-py3-none-any.whl", hash = "sha256:3253c171fe2f6506056a42f9077983a34749b7a1629e41d8fb8e2005d8960886", size = 359268, upload-time = "2026-03-16T18:54:17.397Z" }, + { url = "https://files.pythonhosted.org/packages/1a/94/1f5d72655ab6534129540843776c40eff757387b88e798d8b3bf7e313fd4/langsmith-0.7.22-py3-none-any.whl", hash = "sha256:6e9d5148314d74e86748cb9d3898632cad0320c9323d95f70f969e5bc078eee4", size = 359927, upload-time = "2026-03-19T22:45:21.603Z" }, ] [[package]] @@ -3117,7 +3154,7 @@ wheels = [ [[package]] name = "livekit" -version = "1.1.2" +version = "1.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiofiles" }, @@ -3126,13 +3163,13 @@ dependencies = [ { name = "protobuf" }, { name = "types-protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/9c/0a9b9e1f88226df372b9ccc58868ce9a1eb97c8bf5257463a643dda2a6da/livekit-1.1.2.tar.gz", hash = "sha256:ef826e94dd039767fcabc2f0d810b1b2335c9cf249f52320b6ab018b06d5ccd7", size = 320006, upload-time = "2026-02-17T01:18:46.828Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/28/293ae58c752ed40ae3e266130d787fe6270f123808fbbfca033038b736e5/livekit-1.1.3.tar.gz", hash = "sha256:68825cf74225c3d4af93187953240fbfeb56227d4e11369c9c5b7d8202714d1b", size = 320483, upload-time = "2026-03-23T22:42:09.755Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/7b/1b2d448d8976b14794bfba3d003e1451c79afa77e6b9fa1593f19175ef90/livekit-1.1.2-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:78be23f3f6315354aaacee664eb19b793009bc06faa8184ad9c07cffbe8d7f74", size = 9844157, upload-time = "2026-02-17T01:18:34.036Z" }, - { url = "https://files.pythonhosted.org/packages/f9/fb/19f7fc4a7df3b1385ba2e32b907c5a502fe01c10e6ee2fb76629c068667d/livekit-1.1.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:656f4d9e4692f3d7ab2e51b0bbf4ec03b356a487b7ff220576dab496e60f99f3", size = 8651703, upload-time = "2026-02-17T01:18:36.416Z" }, - { url = "https://files.pythonhosted.org/packages/29/59/ac6a3987bfd11687f86156627a8c7f6a0047a72877748facbd427e63b157/livekit-1.1.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:a20e681d8a4929e27df69f818888ae649700c9d52a262abbec296c84937bc337", size = 14085891, upload-time = "2026-02-17T01:18:38.561Z" }, - { url = "https://files.pythonhosted.org/packages/0a/a5/680548ef7bf5034144ae01f1f742a6c49e4428942b3119ac6553207fea9b/livekit-1.1.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:0e4d8105a0c317b513a118b48340b5773239103cb6305768451ef99ac7567823", size = 11448902, upload-time = "2026-02-17T01:18:42.006Z" }, - { url = "https://files.pythonhosted.org/packages/00/05/e484f8fc079c5de7690e58ed48f44e77436b8732c3050da742bdfca51a5c/livekit-1.1.2-py3-none-win_amd64.whl", hash = "sha256:dd4a436fa16de589353bfbabde91068ab64241afd05b04f21fb1f22bfe155dc0", size = 10394562, upload-time = "2026-02-17T01:18:44.589Z" }, + { url = "https://files.pythonhosted.org/packages/db/8d/5d3a399904f6cbde1bc23e5fac87a4f056a8e76c8b3ac995ed677f3ac7ec/livekit-1.1.3-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:db7bfb25141c2f665054044f8b3ef2c93c5ee633c8f5a9f9f43bd0f9c1e6f035", size = 9890232, upload-time = "2026-03-23T22:41:59.521Z" }, + { url = "https://files.pythonhosted.org/packages/85/37/4a6d2aff39bbe2daa965c6f71498059338878e98d632e8b1a14dd0d863ff/livekit-1.1.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2ef963a9731215c542e142df94c4d6ba385490a058d01328a66161340b181bf4", size = 8691926, upload-time = "2026-03-23T22:42:01.937Z" }, + { url = "https://files.pythonhosted.org/packages/ec/93/bbee45bd3e5ea46a9683716a7aebd5b2e3e4c2d82ebc537abbc11b87671f/livekit-1.1.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:97e47e0723127a0a98775634334dac799fb2cb72894b743e0c78b6961f710092", size = 9712001, upload-time = "2026-03-23T22:42:04.021Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c9/47da7ae46ab666a7151044627e75621e4a77fb8275edace915294d837565/livekit-1.1.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:b268998a06d57b32335b017f859566e7548aeebf9c64deea3a1add2fbb3baa05", size = 11085841, upload-time = "2026-03-23T22:42:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a8/68a7cf4b710b0eb80e3657098e50bebfca5b5b4e19d8ac0e3efb19ed5393/livekit-1.1.3-py3-none-win_amd64.whl", hash = "sha256:87647d68c5b64a024098d167a503ee5e69061fe4b16e431f7a011aa4c73dd4d9", size = 10413627, upload-time = "2026-03-23T22:42:08.006Z" }, ] [[package]] @@ -3153,15 +3190,15 @@ wheels = [ [[package]] name = "livekit-protocol" -version = "1.1.2" +version = "1.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, { name = "types-protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/6c/f5f7cb226441b3a357c2ea5444899b133dd13a5875894c6a9cd52fc5aa74/livekit_protocol-1.1.2.tar.gz", hash = "sha256:4550bf78fb9d365f19ea9875e565d86a2fb798854c8bd2e9100d7f7640dd9072", size = 79620, upload-time = "2026-01-20T01:27:23.437Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ca/d15e2a2cc8c8aa4ba621fe5f9ffd1806d88ac91c7b8fa4c09a3c0304dd92/livekit_protocol-1.1.3.tar.gz", hash = "sha256:cb4948d2513e81d91583f4a795bf80faa9026cedda509c5714999c7e33564287", size = 88746, upload-time = "2026-03-18T05:25:43.562Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/f9/40e81d1b126d79a00b4a8a472a4f7c655b0a7736bb4d08f936be550b3bd8/livekit_protocol-1.1.2-py3-none-any.whl", hash = "sha256:8a26d592a87f5f70fee23aa88e47490727158ee8799c82742585aa8f73b160c5", size = 98854, upload-time = "2026-01-20T01:27:22.139Z" }, + { url = "https://files.pythonhosted.org/packages/5f/0e/f3d3e48628294df4559cffd0f8e1adf030127029e5a8da9beff9979090a0/livekit_protocol-1.1.3-py3-none-any.whl", hash = "sha256:fdae5640e064ab6549ec3d62d8bac75a3ef44d7ea73716069b419cbe8b360a5c", size = 107498, upload-time = "2026-03-18T05:25:42.077Z" }, ] [[package]] @@ -3940,137 +3977,152 @@ wheels = [ ] [[package]] -name = "nvidia-cublas-cu12" -version = "12.8.4.1" +name = "nvidia-cublas" +version = "13.1.0.3" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226, upload-time = "2025-10-09T08:59:04.818Z" }, + { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236, upload-time = "2025-10-09T08:59:32.536Z" }, ] [[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" +name = "nvidia-cuda-cupti" +version = "13.0.85" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, ] [[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" +name = "nvidia-cuda-nvrtc" +version = "13.0.88" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, ] [[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" +name = "nvidia-cuda-runtime" +version = "13.0.96" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, ] [[package]] -name = "nvidia-cudnn-cu12" -version = "9.10.2.21" +name = "nvidia-cudnn-cu13" +version = "9.19.0.56" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cublas" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, + { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, + { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321, upload-time = "2026-02-03T20:44:52.837Z" }, ] [[package]] -name = "nvidia-cufft-cu12" -version = "11.3.3.83" +name = "nvidia-cufft" +version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, ] [[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" +name = "nvidia-cufile" +version = "1.15.1.6" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, ] [[package]] -name = "nvidia-curand-cu12" -version = "10.3.9.90" +name = "nvidia-curand" +version = "10.4.0.35" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, ] [[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.3.90" +name = "nvidia-cusolver" +version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-cublas" }, + { name = "nvidia-cusparse" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, ] [[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.8.93" +name = "nvidia-cusparse" +version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, ] [[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" +name = "nvidia-cusparselt-cu13" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, + { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277, upload-time = "2025-08-13T19:22:40.982Z" }, + { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119, upload-time = "2025-08-13T19:23:41.967Z" }, ] [[package]] -name = "nvidia-nccl-cu12" -version = "2.27.5" +name = "nvidia-nccl-cu13" +version = "2.28.9" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, + { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677, upload-time = "2025-11-18T05:49:03.45Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177, upload-time = "2025-11-18T05:49:17.677Z" }, ] [[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.8.93" +name = "nvidia-nvjitlink" +version = "13.0.88" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, ] [[package]] -name = "nvidia-nvshmem-cu12" +name = "nvidia-nvshmem-cu13" version = "3.4.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, ] [[package]] -name = "nvidia-nvtx-cu12" -version = "12.8.90" +name = "nvidia-nvtx" +version = "13.0.85" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, ] [[package]] @@ -4789,7 +4841,7 @@ docs = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autodoc-typehints", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "sphinx-autodoc-typehints", version = "3.9.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sphinx-autodoc-typehints", version = "3.9.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-markdown-builder" }, { name = "sphinx-rtd-theme" }, { name = "toml" }, @@ -5673,15 +5725,15 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.1.3" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/7e/9f3b0dd3a074a6c3e1e79f35e465b1f2ee4b262d619de00cfce523cc9b24/python_discovery-1.1.3.tar.gz", hash = "sha256:7acca36e818cd88e9b2ba03e045ad7e93e1713e29c6bbfba5d90202310b7baa5", size = 56945, upload-time = "2026-03-10T15:08:15.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/80/73211fc5bfbfc562369b4aa61dc1e4bf07dc7b34df7b317e4539316b809c/python_discovery-1.1.3-py3-none-any.whl", hash = "sha256:90e795f0121bc84572e737c9aa9966311b9fde44ffb88a5953b3ec9b31c6945e", size = 31485, upload-time = "2026-03-10T15:08:13.06Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" }, ] [[package]] @@ -5727,23 +5779,23 @@ binary = [ [[package]] name = "pyvips-binary" -version = "8.18.0" +version = "8.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/94/65b69d93df3bef0b45f4ca83b7a231df3caeab110844ab7d0960158ac5bd/pyvips_binary-8.18.0.tar.gz", hash = "sha256:2f9e509de6d0cf04ea9b429ff0649130a9cf04de8a4f0887d2bcb72e3973225a", size = 3725, upload-time = "2026-01-01T11:16:57.306Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/4e/146e6f1e61a6e00d88698dc40d1f85a04633a470fb451a5dba7e5eab1734/pyvips_binary-8.18.1.tar.gz", hash = "sha256:61e41738e95629f10ab1408c9d9ea22edd9de8b5eedacaec60e2ef41430973ae", size = 3808, upload-time = "2026-03-20T12:12:43.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/d9/18563c9cccf5852d458e692ca15d87df08f0f06ce327a2388d01ef606009/pyvips_binary-8.18.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:6ff72bd6c60bb6cf75b7827083b64e275a15a7d862628b5716998350c17426c8", size = 8383964, upload-time = "2026-01-01T11:16:37.016Z" }, - { url = "https://files.pythonhosted.org/packages/35/96/3c642e25921217c51caff7c1cffcf26bc7f3a6c64f983f2949d8732bffc4/pyvips_binary-8.18.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a570dbf76bb620efc9745d82d6493da504d56b21b035ccd876e358a0c182e018", size = 7500206, upload-time = "2026-01-01T11:16:39.577Z" }, - { url = "https://files.pythonhosted.org/packages/37/5d/01d77f7620b24dace147d11d7ee68a466c29f4463b2d7123376fd89d8a7a/pyvips_binary-8.18.0-cp37-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dad3012233b7b12f48180f2a407a50854e44654f37168fa8d42583d9e4f15882", size = 7645104, upload-time = "2026-01-01T11:16:41.491Z" }, - { url = "https://files.pythonhosted.org/packages/3b/a7/8d8acdae7c507734d9d34c6076700606fec7557fb943cc125fcdfd451678/pyvips_binary-8.18.0-cp37-abi3-manylinux_2_26_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0906be336b8f775e2d33dfe61ffc480ff83c91c08d5eeff904c27c2c5164ff3a", size = 7400818, upload-time = "2026-01-01T11:16:43.595Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e9/065cdee9c5e004a3fc593e61b7ae56ca1675fd55f7714945f73546beedda/pyvips_binary-8.18.0-cp37-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4ddd4d344f758483d1630a9a08f201ab95162599acc6a8e6c62bb1563e94fe0", size = 7556718, upload-time = "2026-01-01T11:16:45.287Z" }, - { url = "https://files.pythonhosted.org/packages/05/35/3529e40931a92b879b7fa23e8228627dea0a56b0ddd0bd667d49e361ef89/pyvips_binary-8.18.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:076fb0affa2901af0fee90c728ded6eed2c72f00356af9895fa7a1fb6c9a2288", size = 7806440, upload-time = "2026-01-01T11:16:47.331Z" }, - { url = "https://files.pythonhosted.org/packages/21/a7/4588ab9bda60b0ed0d5b2be6caf9bd5f19216328b96825a4cd32ded9a1ff/pyvips_binary-8.18.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:659ef1e4af04b3472e7762a95caa1038fdeea530807c84a23a0f4c706af0338f", size = 7683956, upload-time = "2026-01-01T11:16:49.163Z" }, - { url = "https://files.pythonhosted.org/packages/4c/6f/8ee7e74a878941c661d25b6518a8a9bf7a2b12c20b28040c0d047798aa21/pyvips_binary-8.18.0-cp37-abi3-win32.whl", hash = "sha256:fd331bcd75bff8651d73d09687d55ac8fb9014baa5682b770a4ea0fbcedf5f97", size = 8323911, upload-time = "2026-01-01T11:16:51.351Z" }, - { url = "https://files.pythonhosted.org/packages/38/55/12550311fea85253acbb89808bed4b5f516f8e8245333ee3713d9d55ee52/pyvips_binary-8.18.0-cp37-abi3-win_amd64.whl", hash = "sha256:a67d73683f70c21bf2c336b6d5ddc2bd54ec36db72cc54ab63cb48bc2373feac", size = 8288206, upload-time = "2026-01-01T11:16:53.553Z" }, - { url = "https://files.pythonhosted.org/packages/fa/88/a80cba68aef1faea4137d004548003074bc6468b07d5c8a974b6a64b8a8f/pyvips_binary-8.18.0-cp37-abi3-win_arm64.whl", hash = "sha256:0c1f9af910866bc8c2d55182e7a6e8684a828ee4d6084dd814e88e2ee9ec4be3", size = 7492382, upload-time = "2026-01-01T11:16:55.508Z" }, + { url = "https://files.pythonhosted.org/packages/de/fd/ac1698bebcfc7ea1d38a8fa8fc7f7dc88b0e365d0b370e21ca9429b1ce98/pyvips_binary-8.18.1-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:b96a0ef1340d6133e8e38d9381137861d706c696d0d82f2010039b20f6fcc825", size = 8399481, upload-time = "2026-03-20T12:12:24.592Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6a/6ed36423031b39c4a29312d9422f4968a55a1ce60607a84bf12aaf973dd3/pyvips_binary-8.18.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:3fb9da863e169550a66efe552a741328f3b13ec4dbe96c518ed8ee60c7ef7313", size = 7560954, upload-time = "2026-03-20T12:12:26.988Z" }, + { url = "https://files.pythonhosted.org/packages/24/7b/9fd56b14cf63e70b91d433c61ec37405a782d8d51ac56afe6480c3d5cf91/pyvips_binary-8.18.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:77491a0c8d48e98f94284856a12e205a8ac182586a346be7d944bab541395f5e", size = 7809204, upload-time = "2026-03-20T12:12:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/4d/83/110e628807b3b71f65c1cade4c6b959b171840a046c538df262044775f37/pyvips_binary-8.18.1-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c2105fd5677f56ed18524ab898397aea6dd39e55652cf4299c592a77104352ba", size = 7529581, upload-time = "2026-03-20T12:12:30.506Z" }, + { url = "https://files.pythonhosted.org/packages/b3/02/04843f8afb6f25f214a2962cb707888326e845c58d45bed5211dd16623c8/pyvips_binary-8.18.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:d8ef3154a8288ffebff7a8918154c8b1b128856c53d43b7b9a5dacc47ffd0a82", size = 7751300, upload-time = "2026-03-20T12:12:32.445Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/360d6c0b85e2494562d7180d494c566d2df260b033693ae2b9a180790b23/pyvips_binary-8.18.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0e67f90d2054259d81516e322fd8d7abb42c44888026a4056e1e080e91ef2f5", size = 7861944, upload-time = "2026-03-20T12:12:34.775Z" }, + { url = "https://files.pythonhosted.org/packages/37/c0/d6f1a5459c88d87931a623484dbf8deace51ead862e84c6e9590433b4922/pyvips_binary-8.18.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:105e28a5545d755dcf6dacf345c1379a81481314a8176d401ce657af7ec22779", size = 7773899, upload-time = "2026-03-20T12:12:36.81Z" }, + { url = "https://files.pythonhosted.org/packages/a2/d5/ef25863389d51fc8d9ab59147a2c6398678eeb2ff5ed4872d112958f3242/pyvips_binary-8.18.1-cp37-abi3-win32.whl", hash = "sha256:d74e9e6a02bf70cf9c6c27031557d3f9b3d1f4f790f268d12a9e0de5f2b3acff", size = 8392437, upload-time = "2026-03-20T12:12:38.371Z" }, + { url = "https://files.pythonhosted.org/packages/5a/74/be2084ec74dcf0fa7d372206e0c9b3fb90d66c587a8750cf0e95dbff2ec6/pyvips_binary-8.18.1-cp37-abi3-win_amd64.whl", hash = "sha256:c3223c27ab4b4fe246068667b5311e2199ab725cf31f3c5cbbbeb263f99533f5", size = 8339205, upload-time = "2026-03-20T12:12:40.589Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2e/803aae0fb020096c633906916e2e06087147a9753655fde579eac2e9c74e/pyvips_binary-8.18.1-cp37-abi3-win_arm64.whl", hash = "sha256:bbb12996c4f86ef441ded65cd1cf44f90818e615744297005df3355a23f76c31", size = 7532524, upload-time = "2026-03-20T12:12:42.254Z" }, ] [[package]] @@ -6330,27 +6382,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.6" +version = "0.15.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, - { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, - { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, - { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, - { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, - { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, - { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, - { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, - { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, - { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, + { url = "https://files.pythonhosted.org/packages/41/2f/0b08ced94412af091807b6119ca03755d651d3d93a242682bf020189db94/ruff-0.15.7-py3-none-linux_armv6l.whl", hash = "sha256:a81cc5b6910fb7dfc7c32d20652e50fa05963f6e13ead3c5915c41ac5d16668e", size = 10489037, upload-time = "2026-03-19T16:26:32.47Z" }, + { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, + { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f1/f0001cabe86173aaacb6eb9bb734aa0605f9a6aa6fa7d43cb49cbc4af9c9/ruff-0.15.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87768c151808505f2bfc93ae44e5f9e7c8518943e5074f76ac21558ef5627c85", size = 10324743, upload-time = "2026-03-19T16:27:09.791Z" }, + { url = "https://files.pythonhosted.org/packages/7a/87/b8a8f3d56b8d848008559e7c9d8bf367934d5367f6d932ba779456e2f73b/ruff-0.15.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb0511670002c6c529ec66c0e30641c976c8963de26a113f3a30456b702468b0", size = 11138536, upload-time = "2026-03-19T16:27:06.101Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f2/4fd0d05aab0c5934b2e1464784f85ba2eab9d54bffc53fb5430d1ed8b829/ruff-0.15.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0d19644f801849229db8345180a71bee5407b429dd217f853ec515e968a6912", size = 11994292, upload-time = "2026-03-19T16:26:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/64/22/fc4483871e767e5e95d1622ad83dad5ebb830f762ed0420fde7dfa9d9b08/ruff-0.15.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4806d8e09ef5e84eb19ba833d0442f7e300b23fe3f0981cae159a248a10f0036", size = 11398981, upload-time = "2026-03-19T16:26:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/5d/3a/a7060f145bfdcce4c987ea27788b30c60e2c81d6e9a65157ca8afe646328/ruff-0.15.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1852ce241d2bc89e5dc823e03cff4ce73d816b5c6cdadd27dbfe7b03217d2a12", size = 11232158, upload-time = "2026-03-19T16:26:42.321Z" }, + { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/aa/5f486226538fe4d0f0439e2da1716e1acf895e2a232b26f2459c55f8ddad/ruff-0.15.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b15e48602c9c1d9bdc504b472e90b90c97dc7d46c7028011ae67f3861ceba7b4", size = 10327310, upload-time = "2026-03-19T16:26:35.909Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/271afdffb81fe7bfc8c43ba079e9d96238f674380099457a74ccb3863857/ruff-0.15.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1b4705e0e85cedc74b0a23cf6a179dbb3df184cb227761979cc76c0440b5ab0d", size = 10840752, upload-time = "2026-03-19T16:26:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, + { url = "https://files.pythonhosted.org/packages/26/6b/8786ba5736562220d588a2f6653e6c17e90c59ced34a2d7b512ef8956103/ruff-0.15.7-py3-none-win32.whl", hash = "sha256:6d39e2d3505b082323352f733599f28169d12e891f7dd407f2d4f54b4c2886de", size = 10582538, upload-time = "2026-03-19T16:26:15.992Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl", hash = "sha256:4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1", size = 11755839, upload-time = "2026-03-19T16:26:19.897Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e8/726643a3ea68c727da31570bde48c7a10f1aa60eddd628d94078fec586ff/ruff-0.15.7-py3-none-win_arm64.whl", hash = "sha256:18e8d73f1c3fdf27931497972250340f92e8c861722161a9caeb89a58ead6ed2", size = 11023304, upload-time = "2026-03-19T16:26:51.669Z" }, ] [[package]] @@ -6558,15 +6610,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.54.0" +version = "2.55.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c8/e9/2e3a46c304e7fa21eaa70612f60354e32699c7102eb961f67448e222ad7c/sentry_sdk-2.54.0.tar.gz", hash = "sha256:2620c2575128d009b11b20f7feb81e4e4e8ae08ec1d36cbc845705060b45cc1b", size = 413813, upload-time = "2026-03-02T15:12:41.355Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/b8/285293dc60fc198fffc3fcdbc7c6d4e646e0f74e61461c355d40faa64ceb/sentry_sdk-2.55.0.tar.gz", hash = "sha256:3774c4d8820720ca4101548131b9c162f4c9426eb7f4d24aca453012a7470f69", size = 424505, upload-time = "2026-03-17T14:15:51.707Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/39/be412cc86bc6247b8f69e9383d7950711bd86f8d0a4a4b0fe8fad685bc21/sentry_sdk-2.54.0-py2.py3-none-any.whl", hash = "sha256:fd74e0e281dcda63afff095d23ebcd6e97006102cdc8e78a29f19ecdf796a0de", size = 439198, upload-time = "2026-03-02T15:12:39.546Z" }, + { url = "https://files.pythonhosted.org/packages/9a/66/20465097782d7e1e742d846407ea7262d338c6e876ddddad38ca8907b38f/sentry_sdk-2.55.0-py2.py3-none-any.whl", hash = "sha256:97026981cb15699394474a196b88503a393cbc58d182ece0d3abe12b9bd978d4", size = 449284, upload-time = "2026-03-17T14:15:49.604Z" }, ] [[package]] @@ -6784,14 +6836,14 @@ wheels = [ [[package]] name = "speechmatics-rt" -version = "0.5.3" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/a3/bb4d063a4405744951066c45ffbf7cd714a6fc00a20ef0cc83fe2494ed79/speechmatics_rt-0.5.3.tar.gz", hash = "sha256:c98d21041e5a0c90a66e463c3d5b98879c17eac0bbebb4100fd9d0f2b330bb19", size = 27333, upload-time = "2025-12-16T19:20:50.199Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/d4/51ea939b00b2e917a88fec02d8a966716d286f44090af823074a094d9e6b/speechmatics_rt-1.0.0.tar.gz", hash = "sha256:a9dffbd8e8f800e2d7362acb0244fc9ff3ec350c9d8b9c9170339c7c6ca837e1", size = 28100, upload-time = "2026-03-19T10:47:39.106Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/5a/35dd924f9bfeb1604e01806ad0e16a9c596f3c44d13e66794f10d10f828b/speechmatics_rt-0.5.3-py3-none-any.whl", hash = "sha256:12f97f19bb989852b8ff3c6d1e28f4f0ea6fd9356e19da75d0e9877545931ce6", size = 33365, upload-time = "2025-12-16T19:20:49.031Z" }, + { url = "https://files.pythonhosted.org/packages/2d/44/657b21ca91ae258980b02b680d1d339031db02473e539fb947b6cac62605/speechmatics_rt-1.0.0-py3-none-any.whl", hash = "sha256:fa368c63f07427976bbe93aaed2f6c6c59697207c52cfd05533ae09c0ce32e2e", size = 34088, upload-time = "2026-03-19T10:47:38.068Z" }, ] [[package]] @@ -6943,7 +6995,7 @@ wheels = [ [[package]] name = "sphinx-autodoc-typehints" -version = "3.9.8" +version = "3.9.9" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14'", @@ -6953,9 +7005,9 @@ resolution-markers = [ dependencies = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/4d/02216afa475c838c123b41f30e3a2875ad27c14fae3f5bbed4ba4e7fd894/sphinx_autodoc_typehints-3.9.8.tar.gz", hash = "sha256:1e36b31ee593b7e838988045918b7fa965f5062abbd6800af96d5e2c3f17130e", size = 68763, upload-time = "2026-03-09T15:40:01.02Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/26/3fb37400637a3fbb099bd454298b21c420decde96c4b5acedeefee14d714/sphinx_autodoc_typehints-3.9.9.tar.gz", hash = "sha256:c862859c7d679a1495de5bcac150f6b1a6ebc24a1547379ca2aac1831588aa0d", size = 69333, upload-time = "2026-03-20T15:14:15.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/6c/f275f59095b2fec6627c3ce2caba4e18f55a3925718cf0547cde04821a37/sphinx_autodoc_typehints-3.9.8-py3-none-any.whl", hash = "sha256:df123ec82479934fed27e31d4ccdcf382901c5d9481450fc224054496e574466", size = 36685, upload-time = "2026-03-09T15:39:59.567Z" }, + { url = "https://files.pythonhosted.org/packages/7c/64/2dc63a88a3010e9b2ea86788d5ef1ec37bc9b9c6b544cea4f764ff343ea4/sphinx_autodoc_typehints-3.9.9-py3-none-any.whl", hash = "sha256:53c849d74ab67b51fade73c398d08aa3003158c1af88fb84876440d7382143c5", size = 36846, upload-time = "2026-03-20T15:14:14.384Z" }, ] [[package]] @@ -7122,33 +7174,33 @@ wheels = [ [[package]] name = "sse-starlette" -version = "3.3.2" +version = "3.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/9f/c3695c2d2d4ef70072c3a06992850498b01c6bc9be531950813716b426fa/sse_starlette-3.3.2.tar.gz", hash = "sha256:678fca55a1945c734d8472a6cad186a55ab02840b4f6786f5ee8770970579dcd", size = 32326, upload-time = "2026-02-28T11:24:34.36Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/2f/9223c24f568bb7a0c03d751e609844dce0968f13b39a3f73fbb3a96cd27a/sse_starlette-3.3.3.tar.gz", hash = "sha256:72a95d7575fd5129bd0ae15275ac6432bb35ac542fdebb82889c24bb9f3f4049", size = 32420, upload-time = "2026-03-17T20:05:55.529Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/28/8cb142d3fe80c4a2d8af54ca0b003f47ce0ba920974e7990fa6e016402d1/sse_starlette-3.3.2-py3-none-any.whl", hash = "sha256:5c3ea3dad425c601236726af2f27689b74494643f57017cafcb6f8c9acfbb862", size = 14270, upload-time = "2026-02-28T11:24:32.984Z" }, + { url = "https://files.pythonhosted.org/packages/78/e2/b8cff57a67dddf9a464d7e943218e031617fb3ddc133aeeb0602ff5f6c85/sse_starlette-3.3.3-py3-none-any.whl", hash = "sha256:c5abb5082a1cc1c6294d89c5290c46b5f67808cfdb612b7ec27e8ba061c22e8d", size = 14329, upload-time = "2026-03-17T20:05:54.35Z" }, ] [[package]] name = "starlette" -version = "0.52.1" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, ] [[package]] name = "strands-agents" -version = "1.30.0" +version = "1.32.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "boto3" }, @@ -7164,9 +7216,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/82/6c193a8ea19ed91a368a4cf7d20c87457793e1286dac5811a5c2a60a5cc2/strands_agents-1.30.0.tar.gz", hash = "sha256:358db9d78304fc1fe324763be545243e3f9cb030ed0f6f51d0c91d37caff7746", size = 773031, upload-time = "2026-03-11T18:38:32.257Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/b9/c18d13bbf85c23eb66d9f61bced0c1f5a3ac18916331eabde049f6c4dd33/strands_agents-1.32.0.tar.gz", hash = "sha256:2e399bc5ea98d91dbcdf79913115aa579a6bb3251dfe6c15be114821cad893a4", size = 776171, upload-time = "2026-03-20T14:07:41.75Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/94/ecc2df8100fdf745d41d10ac2de4c9cb0325384d0e28b4bb90c82a6ec63b/strands_agents-1.30.0-py3-none-any.whl", hash = "sha256:457ba7b063df61d00f122c913b6b85ba6431d17741b9e34484a7e16fb7e00430", size = 386493, upload-time = "2026-03-11T18:38:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a9/7e00371130dec1ae16df3fd0f7450aa2b92859b625818c793d178b2f1835/strands_agents-1.32.0-py3-none-any.whl", hash = "sha256:60c0eaae32ee1fc366ecebb10bca07681015104c111472a7378f71bbcdedbba4", size = 387030, upload-time = "2026-03-20T14:07:39.754Z" }, ] [[package]] @@ -7271,7 +7323,7 @@ wheels = [ [[package]] name = "timm" -version = "1.0.25" +version = "1.0.26" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, @@ -7280,9 +7332,9 @@ dependencies = [ { name = "torch" }, { name = "torchvision" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/2c/593109822fe735e637382aca6640c1102c19797f7791f1fd1dab2d6c3cb1/timm-1.0.25.tar.gz", hash = "sha256:47f59fc2754725735cc81bb83bcbfce5bec4ebd5d4bb9e69da57daa92fcfa768", size = 2414743, upload-time = "2026-02-23T16:49:00.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/1e/e924b3b2326a856aaf68586f9c52a5fc81ef45715eca408393b68c597e0e/timm-1.0.26.tar.gz", hash = "sha256:f66f082f2f381cf68431c22714c8b70f723837fa2a185b155961eab90f2d5b10", size = 2419859, upload-time = "2026-03-23T18:12:10.272Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/50/de09f69a74278a16f08f1d562047a2d6713783765ee3c6971881a2b21a3f/timm-1.0.25-py3-none-any.whl", hash = "sha256:bef7f61dd717cb2dbbb7e326f143e13d660a47ecbd84116e6fe33732bed5c484", size = 2565837, upload-time = "2026-02-23T16:48:58.324Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e9/bebf3d50e3fc847378988235f87c37ad3ac26d386041ab915d15e92025cd/timm-1.0.26-py3-none-any.whl", hash = "sha256:985c330de5ccc3a2aa0224eb7272e6a336084702390bb7e3801f3c91603d3683", size = 2568766, upload-time = "2026-03-23T18:12:08.062Z" }, ] [[package]] @@ -7380,118 +7432,94 @@ wheels = [ [[package]] name = "torch" -version = "2.10.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools", marker = "python_full_version >= '3.12'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "triton", marker = "sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/30/bfebdd8ec77db9a79775121789992d6b3b75ee5494971294d7b4b7c999bc/torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313", size = 79411457, upload-time = "2026-02-10T21:44:59.189Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8b/4b61d6e13f7108f36910df9ab4b58fd389cc2520d54d81b88660804aad99/torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f", size = 79423467, upload-time = "2026-02-10T21:44:48.711Z" }, - { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, - { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/16/ee/efbd56687be60ef9af0c9c0ebe106964c07400eade5b0af8902a1d8cd58c/torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321", size = 915510070, upload-time = "2026-03-11T14:16:39.386Z" }, - { url = "https://files.pythonhosted.org/packages/36/ab/7b562f1808d3f65414cd80a4f7d4bb00979d9355616c034c171249e1a303/torch-2.10.0-3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac5bdcbb074384c66fa160c15b1ead77839e3fe7ed117d667249afce0acabfac", size = 915518691, upload-time = "2026-03-11T14:15:43.147Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, - { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, - { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" }, - { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" }, - { url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" }, - { url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" }, - { url = "https://files.pythonhosted.org/packages/76/bb/d820f90e69cda6c8169b32a0c6a3ab7b17bf7990b8f2c680077c24a3c14c/torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d", size = 79411450, upload-time = "2026-01-21T16:25:30.692Z" }, - { url = "https://files.pythonhosted.org/packages/78/89/f5554b13ebd71e05c0b002f95148033e730d3f7067f67423026cc9c69410/torch-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4", size = 145992610, upload-time = "2026-01-21T16:25:26.327Z" }, - { url = "https://files.pythonhosted.org/packages/ae/30/a3a2120621bf9c17779b169fc17e3dc29b230c29d0f8222f499f5e159aa8/torch-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763", size = 915607863, upload-time = "2026-01-21T16:25:06.696Z" }, - { url = "https://files.pythonhosted.org/packages/6f/3d/c87b33c5f260a2a8ad68da7147e105f05868c281c63d65ed85aa4da98c66/torch-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd", size = 113723116, upload-time = "2026-01-21T16:25:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/61/d8/15b9d9d3a6b0c01b883787bd056acbe5cc321090d4b216d3ea89a8fcfdf3/torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b", size = 79423461, upload-time = "2026-01-21T16:24:50.266Z" }, - { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, - { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, - { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5c/dee910b87c4d5c0fcb41b50839ae04df87c1cfc663cf1b5fca7ea565eeaa/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294", size = 79498198, upload-time = "2026-01-21T16:24:34.704Z" }, - { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, - { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, - { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, - { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, - { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, - { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, - { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, - { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, - { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, - { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f2/c1690994afe461aae2d0cac62251e6802a703dec0a6c549c02ecd0de92a9/torch-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2c0d7fcfbc0c4e8bb5ebc3907cbc0c6a0da1b8f82b1fc6e14e914fa0b9baf74e", size = 80526521, upload-time = "2026-03-23T18:12:06.86Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f0/98ae802fa8c09d3149b0c8690741f3f5753c90e779bd28c9613257295945/torch-2.11.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4cf8687f4aec3900f748d553483ef40e0ac38411c3c48d0a86a438f6d7a99b18", size = 419723025, upload-time = "2026-03-23T18:11:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1e/18a9b10b4bd34f12d4e561c52b0ae7158707b8193c6cfc0aad2b48167090/torch-2.11.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1b32ceda909818a03b112006709b02be1877240c31750a8d9c6b7bf5f2d8a6e5", size = 530589207, upload-time = "2026-03-23T18:11:23.756Z" }, + { url = "https://files.pythonhosted.org/packages/35/40/2d532e8c0e23705be9d1debce5bc37b68d59a39bda7584c26fe9668076fe/torch-2.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:b3c712ae6fb8e7a949051a953fc412fe0a6940337336c3b6f905e905dac5157f", size = 114518313, upload-time = "2026-03-23T18:11:58.281Z" }, + { url = "https://files.pythonhosted.org/packages/ae/0d/98b410492609e34a155fa8b121b55c7dca229f39636851c3a9ec20edea21/torch-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7b6a60d48062809f58595509c524b88e6ddec3ebe25833d6462eeab81e5f2ce4", size = 80529712, upload-time = "2026-03-23T18:12:02.608Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/acea680005f098f79fd70c1d9d5ccc0cb4296ec2af539a0450108232fc0c/torch-2.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d91aac77f24082809d2c5a93f52a5f085032740a1ebc9252a7b052ef5a4fddc6", size = 419718178, upload-time = "2026-03-23T18:10:46.675Z" }, + { url = "https://files.pythonhosted.org/packages/8c/8b/d7be22fbec9ffee6cff31a39f8750d4b3a65d349a286cf4aec74c2375662/torch-2.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7aa2f9bbc6d4595ba72138026b2074be1233186150e9292865e04b7a63b8c67a", size = 530604548, upload-time = "2026-03-23T18:10:03.569Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bd/9912d30b68845256aabbb4a40aeefeef3c3b20db5211ccda653544ada4b6/torch-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:73e24aaf8f36ab90d95cd1761208b2eb70841c2a9ca1a3f9061b39fc5331b708", size = 114519675, upload-time = "2026-03-23T18:11:52.995Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" }, + { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115, upload-time = "2026-03-23T18:11:06.944Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279, upload-time = "2026-03-23T18:10:31.481Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047, upload-time = "2026-03-23T18:10:55.931Z" }, + { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801, upload-time = "2026-03-23T18:10:18.649Z" }, + { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382, upload-time = "2026-03-23T18:08:30.835Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509, upload-time = "2026-03-23T18:08:47.213Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842, upload-time = "2026-03-23T18:09:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574, upload-time = "2026-03-23T18:10:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324, upload-time = "2026-03-23T18:09:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026, upload-time = "2026-03-23T18:09:20.842Z" }, + { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702, upload-time = "2026-03-23T18:09:47.304Z" }, + { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442, upload-time = "2026-03-23T18:09:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385, upload-time = "2026-03-23T18:07:33.77Z" }, + { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756, upload-time = "2026-03-23T18:07:50.02Z" }, + { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300, upload-time = "2026-03-23T18:09:05.617Z" }, + { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460, upload-time = "2026-03-23T18:09:00.818Z" }, + { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835, upload-time = "2026-03-23T18:07:18.974Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405, upload-time = "2026-03-23T18:08:07.014Z" }, + { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991, upload-time = "2026-03-23T18:08:19.216Z" }, ] [[package]] name = "torchaudio" -version = "2.10.0" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "torch" }, -] wheels = [ - { url = "https://files.pythonhosted.org/packages/04/59/88ab8ebff9d91f1f1365088b30f1b9ccce07c5eeac666038a5dee5e2f9b1/torchaudio-2.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cde383582a6240c1315443df5c5638863e96b03acf1cb44a298aff07a72d373", size = 734944, upload-time = "2026-01-21T16:28:49.535Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d6/41f25f9ae9b37c191bed4cd474e403626685d2be8f7d20d011e6601fede1/torchaudio-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:cfb2ad4b7847d81931989127d803487263c8284f21156e9000daec1ac16c0831", size = 390449, upload-time = "2026-01-21T16:28:48.585Z" }, - { url = "https://files.pythonhosted.org/packages/43/ac/a14425fddd1cf56bb052a3bfd38880258008f8c3cd17f37bba55b3a88ce7/torchaudio-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:316cdb15fb37290fca89894b095d97b4dc14a90c4c61148ae5c96bb334d962cd", size = 1891070, upload-time = "2026-01-21T16:28:47.323Z" }, - { url = "https://files.pythonhosted.org/packages/6e/03/d1898db1bf7ecd47ca9b4e1b70927597d236cf721e3736d953d555901832/torchaudio-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:013079d1ba2a652184703e671b8339cbc7991f17e4ed927071fe7635f908a4a1", size = 474045, upload-time = "2026-01-21T16:28:46.191Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e7/401fe1d024bf9352371d854be6f339ad9928669e6bc8a5ba08e9dbce81cf/torchaudio-2.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bcab0e39eb18da84cba1a0c87f600abb6ce97c882200cb46e841caea106f037f", size = 736373, upload-time = "2026-01-21T16:28:41.589Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b7/c66dc34a27441d78997e20d0ffe2f5ad73db9f7b1267511be255bb94ac9b/torchaudio-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:87c841a21e82703ebd4a29170c4e60c25a2b47312dc212930087ad58965ac0c8", size = 391843, upload-time = "2026-01-21T16:28:43.093Z" }, - { url = "https://files.pythonhosted.org/packages/13/ae/a2a34a64947c4fa4a61b4c86d8f36fbcb4ebfec30fdde140267db260f96c/torchaudio-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:b2c77fb9114dd463dc805560bf55a1ac2a52e219794cc32b7b32cf2aeffd2826", size = 1894140, upload-time = "2026-01-21T16:28:35.892Z" }, - { url = "https://files.pythonhosted.org/packages/69/26/cd2aec609b4f8918e4e85e5c6a3f569bc7b5f72a7ecba3f784077102749c/torchaudio-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:4c6e9609046143b30a30183893d23ff1ce5de603dbe914b3cce5cc29f5aa5a9c", size = 474792, upload-time = "2026-01-21T16:28:45.254Z" }, - { url = "https://files.pythonhosted.org/packages/0f/36/28a6f3e857616cf7576bdbf8170e483b8c5d0a1f8d349ecb2b75921236aa/torchaudio-2.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9d0fbdbfd2f621c51d28571050d6d0c7287791034e5c7303b31480af1258f33f", size = 737144, upload-time = "2026-01-21T16:28:44.189Z" }, - { url = "https://files.pythonhosted.org/packages/ea/3f/df620439a76ece170472d41438d11a1545d5db5dc9f1eaeab8c6e055a328/torchaudio-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:42b148a0921a3721abd1f6ae098b1ec9f89703e555c4f7a0d44da87b8decbcb9", size = 391973, upload-time = "2026-01-21T16:28:39.732Z" }, - { url = "https://files.pythonhosted.org/packages/98/25/e55a30d7138f8fe56ed006df25b0a3c27681f0ec7bc9989e1778e6d559c3/torchaudio-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0e77b2956448d63790a99beed0b74ac8b8cd3a94dcdd9ad01974411078f46278", size = 1895234, upload-time = "2026-01-21T16:28:37.034Z" }, - { url = "https://files.pythonhosted.org/packages/be/a0/da53c7d20fac15f66f8838653b91162de1bf21fb40fee88cf839e4ef5174/torchaudio-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f76a01ecebf1869e1f2c50a261f1cf07e5fccb24402b4e9bbb82d6725b9c7dd", size = 475470, upload-time = "2026-01-21T16:28:40.615Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/341e7bd588355f82c5180103cb2f8070a72ab1be920ab27553a1135d4aa6/torchaudio-2.10.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:8fd38d28ee150c584d3ee3b05f39e021f0ad8a8ec8fec1f26dfe150c9db9b2f5", size = 737164, upload-time = "2026-01-21T16:28:38.354Z" }, - { url = "https://files.pythonhosted.org/packages/49/fd/831c2595c81b17141180ca11ab3c0836cc544ef13e15aa0e7b2cb619e582/torchaudio-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5bc39ff3ea341097ce1ab023dd88c9dd8ca5f96ebf48821e7d23766137bb55d7", size = 392757, upload-time = "2026-01-21T16:28:33.631Z" }, - { url = "https://files.pythonhosted.org/packages/8e/d8/405c80c57dc68ca5855bddfaae57c3d84ea7397bf1eb2aa5d59c9fa1d3a9/torchaudio-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3057c4286db5673d266124a2a10ca54e19f516772e9057f44573a7da5b85e328", size = 1897099, upload-time = "2026-01-21T16:28:24.793Z" }, - { url = "https://files.pythonhosted.org/packages/73/cf/0e48d67788c935e3b3d00e6f55a930a54a67f432e04c33ef80a38cb764fd/torchaudio-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:99e74d1901742bc10961d807fe75c0dd9496f4a4a4ff4bb317c5de4a0b6f24e6", size = 475476, upload-time = "2026-01-21T16:28:28.249Z" }, - { url = "https://files.pythonhosted.org/packages/48/29/30bcce0f17a8279b051b09250993691a828f89a03278306b23571c18df04/torchaudio-2.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6cfe98ef0ea9bee6d6297493ce67ce0c54a38d80caf6535a3ae48900fd5f3769", size = 742449, upload-time = "2026-01-21T16:28:29.556Z" }, - { url = "https://files.pythonhosted.org/packages/43/8c/653e7f67855424bf3b7cbb48335f8316f7fb02bb01a6cab38f6bf9555676/torchaudio-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:b41b254d958632dc00dc7768431cadda516c91641d798775cbb19bcd4f0d2be4", size = 393430, upload-time = "2026-01-21T16:28:34.855Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1f/f91fcb9dd47a19b720fb48042a2f6f023651948e73726e98fff60d5ed5c7/torchaudio-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:da1081d1018a1e95f5a13947402aeb037cf5ac8861219a6164df004898a96bb1", size = 1897271, upload-time = "2026-01-21T16:28:23.519Z" }, - { url = "https://files.pythonhosted.org/packages/57/27/270c26890f43838e8faa5d3e52f079bd9d9d09f9a535a11cf6b94e20ed21/torchaudio-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f1afa53146a5655258d3a86e689c6879dfe78581d9bee9ef611ace98722f86bb", size = 478966, upload-time = "2026-01-21T16:28:32.491Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5c/0e54b162bd0d1ec2f87b545553af839f906b940888d0122cdef04b965385/torchaudio-2.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1f2897fbf776d55afcb5f6d9b7bdfaea850ca7a129c8f5e4b3a4b025c431130d", size = 739544, upload-time = "2026-01-21T16:28:26.947Z" }, - { url = "https://files.pythonhosted.org/packages/57/a1/ef5571406858f4ea89c18d6ad844d21cb9858708149e6bbd9a789ee30ea5/torchaudio-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:b2d5e11a2bec08f02a4f5fb7d1902ff82d48c533a27ceedc21e6ade650cf65b3", size = 393061, upload-time = "2026-01-21T16:28:25.802Z" }, - { url = "https://files.pythonhosted.org/packages/9d/0f/a0cf0ebc6f71b1868ea056dd4cd4f1a2244b8da8bc38372a1adc984a7c1f/torchaudio-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:77f6cf11a3b61af1b0967cd642368ecd30a86d70f622b22410ae6cb42d980b72", size = 1897137, upload-time = "2026-01-21T16:28:15.366Z" }, - { url = "https://files.pythonhosted.org/packages/7f/48/98e6710a4601e190bc923c3683629c29d41fb18a818a9328515541f023ed/torchaudio-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:4711c2a86a005685ca3b5da135b2f370d81ac354e3dcb142ef45fe2c78b9c9c4", size = 475154, upload-time = "2026-01-21T16:28:22.438Z" }, - { url = "https://files.pythonhosted.org/packages/c1/9b/cd02f8add38bd98761548b0821a5e54c564117a9bbeafaf95f665ab0fd72/torchaudio-2.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13bdc1bde0c88e999699d1503304a56fc9dea6401b76bc08a5f268368129d46c", size = 742453, upload-time = "2026-01-21T16:28:20.989Z" }, - { url = "https://files.pythonhosted.org/packages/53/8a/946aa07393845b918d318b5e34b3bd0359fd27fc9fac10a85fae2bb86382/torchaudio-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ed912de8ec1b400e17a5172badcfcddc601a9cd4e02d200f3a9504fc8e54961c", size = 393434, upload-time = "2026-01-21T16:28:18.668Z" }, - { url = "https://files.pythonhosted.org/packages/e1/68/e37e8fbbae986afa80f8851e08fc017eb8ae5f7b398ee28ed92303da163e/torchaudio-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:f7aa33a8198e87949896e16ea245ea731906445becdf10130e8823c68494a94a", size = 1897289, upload-time = "2026-01-21T16:28:17.059Z" }, - { url = "https://files.pythonhosted.org/packages/5d/61/0e1f464463b85bc677036faffdfd23493aa17e8c3fc3a649abca8c019701/torchaudio-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e49f6a18a8552620c4394f8529b7551eda9312d46dfdd3500bd2be459c86aea4", size = 478968, upload-time = "2026-01-21T16:28:19.542Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d9/357eb5fe4e19a861e6fa1af4d9f535e8fa8692336e6cf436e8a21262e054/torchaudio-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ebb59c694909eccb5d61b7cc199d297692012c43286e36d92983aa7bad7586d", size = 684145, upload-time = "2026-03-23T18:13:46.671Z" }, + { url = "https://files.pythonhosted.org/packages/2a/79/90de77e73f395bba2fe477f8e82e4ae1d14d6452a706838765e850a5e80c/torchaudio-2.11.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:be7ad472acb16d16e98c005f0219b0db06a47dfe8f7b4d177062e1638f871e3b", size = 1626521, upload-time = "2026-03-23T18:13:40.98Z" }, + { url = "https://files.pythonhosted.org/packages/66/dc/5757ed7d8d11a6c14336bcb54e63980979f00005555fec80fb4aa4de5eff/torchaudio-2.11.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:5847fe2022b17c6580aeb39c8797a443411cc09edfd9183cd50ac1a3b8ccf97c", size = 1771929, upload-time = "2026-03-23T18:13:43.432Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f4/8ce2417eac66296e45b7aaa69858403fb6a52b1323f8635ec37b4b0f1fa3/torchaudio-2.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:7e2da1df4f6fe885c46db350a0dc90a0dff4b54541dff8846faa904d255e2bfe", size = 328661, upload-time = "2026-03-23T18:13:45.77Z" }, + { url = "https://files.pythonhosted.org/packages/94/77/0eec7f175d88f312296bd5b11c23bd58da37c1021f53da3db4df449ce3ee/torchaudio-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:492dd64645e9d0bb843e94f1d9a4d1e31426262ffc594fafecc1697df9df5eb9", size = 684142, upload-time = "2026-03-23T18:13:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f9/6f7ebe071b44592c85269762b55b63ab0a091b5f479f73544738f7564a1e/torchaudio-2.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:73dab4841f94d888bc7c2aed7b5547c643edc974306919fe1adfb65d57cccf4b", size = 1626527, upload-time = "2026-03-23T18:13:39.011Z" }, + { url = "https://files.pythonhosted.org/packages/ac/70/17408e0d154d0c894537a88dcbadc48e8ad3b6e1ef4a1dabda5d40245ee0/torchaudio-2.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1a07ec72fd6f26a588c39b5f029e0130d16bb40bc4221635580bf8fb18fcbc80", size = 1771930, upload-time = "2026-03-23T18:13:37.963Z" }, + { url = "https://files.pythonhosted.org/packages/c9/75/b6d03fc75b409bdaec597274d1bdd4213db716ed16f6801386b31d59c551/torchaudio-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:bb59ba4452bbbe95d75ad3ef18df9824955625f36698ce9a5998a4a9f3c1ba1d", size = 328658, upload-time = "2026-03-23T18:13:44.545Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b1/77658817acacd01a72b714440c62f419efc4d90170e704e8e7a2c0918988/torchaudio-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1cf1acc883bee9cb906a933572fed6a8a933f86ef34e9ea7d803f72317e8c1b", size = 684226, upload-time = "2026-03-23T18:13:40.023Z" }, + { url = "https://files.pythonhosted.org/packages/78/28/c7adc053039f286c2aca0038b766cbe3294e66fec6b29a820e95128f9ede/torchaudio-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:bc653defca1c16154398517a1adc98d0fb7f1dd08e58ced217558d213c2c6e29", size = 1626670, upload-time = "2026-03-23T18:13:42.162Z" }, + { url = "https://files.pythonhosted.org/packages/88/d8/d6d0f896e064aa67377484efef4911cdcc07bce2929474e1417cc0af18c2/torchaudio-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6503c0bdb29daf2e6281bb70ea2dfe2c3553b782b619eb5d73bdadd8a3f7cecf", size = 1771992, upload-time = "2026-03-23T18:13:33.188Z" }, + { url = "https://files.pythonhosted.org/packages/23/a8/941277ecc39f7a0a169d554302a1f1afd87c1d94a8aec828891916cea59a/torchaudio-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:478110f981e5d40a8d82221732c57a56c85a1d5895fb8fe646e86ee15eded3bd", size = 328663, upload-time = "2026-03-23T18:13:19.218Z" }, + { url = "https://files.pythonhosted.org/packages/fb/9e/f76fcd9877c8c78f258ee34e0fb8291fdb91e6218d582d9ca66b1e4bd4ae/torchaudio-2.11.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e3f9696a9ef1d49acc452159b052370c636406d072e9d8f10895fda87b591ea9", size = 679904, upload-time = "2026-03-23T18:13:28.329Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/249c1498ebdad3e7752866635ec0855fc0dcf898beccda5a9d2b9df8e4d0/torchaudio-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:b034d7672f1c415434f48ef17807f2cce47f29e8795338c751d4e596c9fbe8b5", size = 1618523, upload-time = "2026-03-23T18:13:15.703Z" }, + { url = "https://files.pythonhosted.org/packages/4f/98/be13fe35d9aa5c26381c0e453c828a789d15c007f8f7d08c95341d19974d/torchaudio-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1c1101c1243ef0e4063ec63298977e2d3655c15cf88d9eb0a1bd4fe2db9f47ea", size = 1771992, upload-time = "2026-03-23T18:13:35.343Z" }, + { url = "https://files.pythonhosted.org/packages/e2/8b/2bbb3dca6ff28cba0de250874d5ef4fc2822c47a934b59b3974cff3219ef/torchaudio-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:986f4df5ed17b003dc52489468601720090e65f964f8bebccf90eb45bba75744", size = 328662, upload-time = "2026-03-23T18:13:18.308Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ce/52c652d30af7d6e96c8f1735d26131e94708e3f38d852b8fa97958804dd8/torchaudio-2.11.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:bda09ea630ae7207384fb0f28c35e4f8c0d82dd6eba020b6b335ad0caa9fed49", size = 680814, upload-time = "2026-03-23T18:13:17.08Z" }, + { url = "https://files.pythonhosted.org/packages/06/95/1ad1507482e7263e556709a3f5f87fecd375a0742cdaf238806c8e72eaad/torchaudio-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:9fe3083c62e035646483a14e180d33561bdc2eed436c9ab1259c137fb7120b4a", size = 1618546, upload-time = "2026-03-23T18:13:29.686Z" }, + { url = "https://files.pythonhosted.org/packages/98/4c/480328ba07487eb9890406720304d0d460dd7a6a64098614f5aa53b662ca/torchaudio-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:13cff988697ccbad539987599f9dc672f40c417bed67570b365e4e5002bbd096", size = 1771991, upload-time = "2026-03-23T18:13:30.843Z" }, + { url = "https://files.pythonhosted.org/packages/3e/98/5d4790e2d6548768999acd34999d5aeefce8bcc23a07afaa5f03e723f557/torchaudio-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ed404c4399ad7f172c86a47c1b25293d322d1d58e26b10b0456a86cf67d37d84", size = 328661, upload-time = "2026-03-23T18:13:34.359Z" }, + { url = "https://files.pythonhosted.org/packages/39/fe/ffa618b4f0d9732d7df7a2fa2bd48657d896599bc224e5af3c70d46c546b/torchaudio-2.11.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:cc09cd1f6015b8549e7fe255fb1be5346b57e7fee06541d3f3dbb012d8c4715f", size = 679901, upload-time = "2026-03-23T18:13:25.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/54/f414d7b92dd0b3094a2409c95a97bd6c49aa0620da722a0e55462f9bd9cb/torchaudio-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:79fb3cb99169fd41bd9719647261402a164da0d105a4d81f42a3260844ec5e79", size = 1618527, upload-time = "2026-03-23T18:13:26.68Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a8/bf2e1f6ce24c990192400ae49b4acc1a0d0295b6c6a06bceecdc46ce08de/torchaudio-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:00e9f71ab9c656f0abdb40c515bd65d4658ab0ad380dee27a2efd7d51dabd3d6", size = 1771995, upload-time = "2026-03-23T18:13:23.373Z" }, + { url = "https://files.pythonhosted.org/packages/83/6f/b0efb44e0bfe8dd4d78d76ae3be280354e1fb5c8631c782785d74cd8a7b1/torchaudio-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:1424638adb8bb40087bc7b6eb103e8e4fe398210f09076f33b7b5e61501b5d66", size = 328662, upload-time = "2026-03-23T18:13:32.243Z" }, + { url = "https://files.pythonhosted.org/packages/60/84/1c792b0b700eac9a96772cfd9f96c097b17bca3234a2fde3c64b8063660d/torchaudio-2.11.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:da2725e250866da42a12934c9a6552f65a18b7187fd7a6221387f0e605fb3b96", size = 679926, upload-time = "2026-03-23T18:13:24.452Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a0/62a5842062f739239691f2e57523e0570dd06704ad987755f7644a3afa23/torchaudio-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:1be3767064364ae82705bdf2b15c1e8b41fea82c4cd04d47428a8684b634b6ed", size = 1618552, upload-time = "2026-03-23T18:13:21.09Z" }, + { url = "https://files.pythonhosted.org/packages/6d/89/c293d818f9f899db93bf291b42401c05ae29acfb2e53d5341c30ea703e62/torchaudio-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:67f6edac29ed004652c11db5c19d9debb5d835695930574f564efc8bdd061bba", size = 1771986, upload-time = "2026-03-23T18:13:22.153Z" }, + { url = "https://files.pythonhosted.org/packages/93/f7/ee5da8c03f1a3c7662c6c6a119f24a4b3e646da94be56dce3201e3a6ee9b/torchaudio-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:88fb5e29f670a33d9bac6aabb1d2734460cf6e461bde5cdc352826035851b16d", size = 328661, upload-time = "2026-03-23T18:13:20.1Z" }, ] [[package]] name = "torchvision" -version = "0.25.0" +version = "0.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -7500,34 +7528,34 @@ dependencies = [ { name = "torch" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/50/ae/cbf727421eb73f1cf907fbe5788326a08f111b3f6b6ddca15426b53fec9a/torchvision-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a95c47abb817d4e90ea1a8e57bd0d728e3e6b533b3495ae77d84d883c4d11f56", size = 1874919, upload-time = "2026-01-21T16:27:47.617Z" }, - { url = "https://files.pythonhosted.org/packages/64/68/dc7a224f606d53ea09f9a85196a3921ec3a801b0b1d17e84c73392f0c029/torchvision-0.25.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:acc339aba4a858192998c2b91f635827e40d9c469d9cf1455bafdda6e4c28ea4", size = 2343220, upload-time = "2026-01-21T16:27:44.26Z" }, - { url = "https://files.pythonhosted.org/packages/f9/fa/8cce5ca7ffd4da95193232493703d20aa06303f37b119fd23a65df4f239a/torchvision-0.25.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0d9a3f925a081dd2ebb0b791249b687c2ef2c2717d027946654607494b9b64b6", size = 8068106, upload-time = "2026-01-21T16:27:37.805Z" }, - { url = "https://files.pythonhosted.org/packages/8b/b9/a53bcf8f78f2cd89215e9ded70041765d50ef13bf301f9884ec6041a9421/torchvision-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:b57430fbe9e9b697418a395041bb615124d9c007710a2712fda6e35fb310f264", size = 3697295, upload-time = "2026-01-21T16:27:36.574Z" }, - { url = "https://files.pythonhosted.org/packages/3e/be/c704bceaf11c4f6b19d64337a34a877fcdfe3bd68160a8c9ae9bea4a35a3/torchvision-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db74a551946b75d19f9996c419a799ffdf6a223ecf17c656f90da011f1d75b20", size = 1874923, upload-time = "2026-01-21T16:27:46.574Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e9/f143cd71232430de1f547ceab840f68c55e127d72558b1061a71d0b193cd/torchvision-0.25.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f49964f96644dbac2506dffe1a0a7ec0f2bf8cf7a588c3319fed26e6329ffdf3", size = 2344808, upload-time = "2026-01-21T16:27:43.191Z" }, - { url = "https://files.pythonhosted.org/packages/43/ae/ad5d6165797de234c9658752acb4fce65b78a6a18d82efdf8367c940d8da/torchvision-0.25.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:153c0d2cbc34b7cf2da19d73450f24ba36d2b75ec9211b9962b5022fb9e4ecee", size = 8070752, upload-time = "2026-01-21T16:27:33.748Z" }, - { url = "https://files.pythonhosted.org/packages/23/19/55b28aecdc7f38df57b8eb55eb0b14a62b470ed8efeb22cdc74224df1d6a/torchvision-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:ea580ffd6094cc01914ad32f8c8118174f18974629af905cea08cb6d5d48c7b7", size = 4038722, upload-time = "2026-01-21T16:27:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/56/3a/6ea0d73f49a9bef38a1b3a92e8dd455cea58470985d25635beab93841748/torchvision-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2abe430c90b1d5e552680037d68da4eb80a5852ebb1c811b2b89d299b10573b", size = 1874920, upload-time = "2026-01-21T16:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/51/f8/c0e1ef27c66e15406fece94930e7d6feee4cb6374bbc02d945a630d6426e/torchvision-0.25.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b75deafa2dfea3e2c2a525559b04783515e3463f6e830cb71de0fb7ea36fe233", size = 2344556, upload-time = "2026-01-21T16:27:40.125Z" }, - { url = "https://files.pythonhosted.org/packages/68/2f/f24b039169db474e8688f649377de082a965fbf85daf4e46c44412f1d15a/torchvision-0.25.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f25aa9e380865b11ea6e9d99d84df86b9cc959f1a007cd966fc6f1ab2ed0e248", size = 8072351, upload-time = "2026-01-21T16:27:21.074Z" }, - { url = "https://files.pythonhosted.org/packages/ad/16/8f650c2e288977cf0f8f85184b90ee56ed170a4919347fc74ee99286ed6f/torchvision-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9c55ae8d673ab493325d1267cbd285bb94d56f99626c00ac4644de32a59ede3", size = 4303059, upload-time = "2026-01-21T16:27:11.08Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5b/1562a04a6a5a4cf8cf40016a0cdeda91ede75d6962cff7f809a85ae966a5/torchvision-0.25.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:24e11199e4d84ba9c5ee7825ebdf1cd37ce8deec225117f10243cae984ced3ec", size = 1874918, upload-time = "2026-01-21T16:27:39.02Z" }, - { url = "https://files.pythonhosted.org/packages/36/b1/3d6c42f62c272ce34fcce609bb8939bdf873dab5f1b798fd4e880255f129/torchvision-0.25.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5f271136d2d2c0b7a24c5671795c6e4fd8da4e0ea98aeb1041f62bc04c4370ef", size = 2309106, upload-time = "2026-01-21T16:27:30.624Z" }, - { url = "https://files.pythonhosted.org/packages/c7/60/59bb9c8b67cce356daeed4cb96a717caa4f69c9822f72e223a0eae7a9bd9/torchvision-0.25.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:855c0dc6d37f462482da7531c6788518baedca1e0847f3df42a911713acdfe52", size = 8071522, upload-time = "2026-01-21T16:27:29.392Z" }, - { url = "https://files.pythonhosted.org/packages/32/a5/9a9b1de0720f884ea50dbf9acb22cbe5312e51d7b8c4ac6ba9b51efd9bba/torchvision-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:cef0196be31be421f6f462d1e9da1101be7332d91984caa6f8022e6c78a5877f", size = 4321911, upload-time = "2026-01-21T16:27:35.195Z" }, - { url = "https://files.pythonhosted.org/packages/52/99/dca81ed21ebaeff2b67cc9f815a20fdaa418b69f5f9ea4c6ed71721470db/torchvision-0.25.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a8f8061284395ce31bcd460f2169013382ccf411148ceb2ee38e718e9860f5a7", size = 1896209, upload-time = "2026-01-21T16:27:32.159Z" }, - { url = "https://files.pythonhosted.org/packages/28/cc/2103149761fdb4eaed58a53e8437b2d716d48f05174fab1d9fcf1e2a2244/torchvision-0.25.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:146d02c9876858420adf41f3189fe90e3d6a409cbfa65454c09f25fb33bf7266", size = 2310735, upload-time = "2026-01-21T16:27:22.327Z" }, - { url = "https://files.pythonhosted.org/packages/76/ad/f4c985ad52ddd3b22711c588501be1b330adaeaf6850317f66751711b78c/torchvision-0.25.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c4d395cb2c4a2712f6eb93a34476cdf7aae74bb6ea2ea1917f858e96344b00aa", size = 8089557, upload-time = "2026-01-21T16:27:27.666Z" }, - { url = "https://files.pythonhosted.org/packages/63/cc/0ea68b5802e5e3c31f44b307e74947bad5a38cc655231d845534ed50ddb8/torchvision-0.25.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5e6b449e9fa7d642142c0e27c41e5a43b508d57ed8e79b7c0a0c28652da8678c", size = 4344260, upload-time = "2026-01-21T16:27:17.018Z" }, - { url = "https://files.pythonhosted.org/packages/9e/1f/fa839532660e2602b7e704d65010787c5bb296258b44fa8b9c1cd6175e7d/torchvision-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:620a236288d594dcec7634c754484542dc0a5c1b0e0b83a34bda5e91e9b7c3a1", size = 1896193, upload-time = "2026-01-21T16:27:24.785Z" }, - { url = "https://files.pythonhosted.org/packages/80/ed/d51889da7ceaf5ff7a0574fb28f9b6b223df19667265395891f81b364ab3/torchvision-0.25.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b5e7f50002a8145a98c5694a018e738c50e2972608310c7e88e1bd4c058f6ce", size = 2309331, upload-time = "2026-01-21T16:27:19.97Z" }, - { url = "https://files.pythonhosted.org/packages/90/a5/f93fcffaddd8f12f9e812256830ec9c9ca65abbf1bc369379f9c364d1ff4/torchvision-0.25.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:632db02300e83793812eee4f61ae6a2686dab10b4cfd628b620dc47747aa9d03", size = 8088713, upload-time = "2026-01-21T16:27:15.281Z" }, - { url = "https://files.pythonhosted.org/packages/1f/eb/d0096eed5690d962853213f2ee00d91478dfcb586b62dbbb449fb8abc3a6/torchvision-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:d1abd5ed030c708f5dbf4812ad5f6fbe9384b63c40d6bd79f8df41a4a759a917", size = 4325058, upload-time = "2026-01-21T16:27:26.165Z" }, - { url = "https://files.pythonhosted.org/packages/97/36/96374a4c7ab50dea9787ce987815614ccfe988a42e10ac1a2e3e5b60319a/torchvision-0.25.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad9a8a5877782944d99186e4502a614770fe906626d76e9cd32446a0ac3075f2", size = 1896207, upload-time = "2026-01-21T16:27:23.383Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e2/7abb10a867db79b226b41da419b63b69c0bd5b82438c4a4ed50e084c552f/torchvision-0.25.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:40a122c3cf4d14b651f095e0f672b688dde78632783fc5cd3d4d5e4f6a828563", size = 2310741, upload-time = "2026-01-21T16:27:18.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/e6/0927784e6ffc340b6676befde1c60260bd51641c9c574b9298d791a9cda4/torchvision-0.25.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:846890161b825b38aa85fc37fb3ba5eea74e7091ff28bab378287111483b6443", size = 8089772, upload-time = "2026-01-21T16:27:14.048Z" }, - { url = "https://files.pythonhosted.org/packages/b6/37/e7ca4ec820d434c0f23f824eb29f0676a0c3e7a118f1514f5b949c3356da/torchvision-0.25.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f07f01d27375ad89d72aa2b3f2180f07da95dd9d2e4c758e015c0acb2da72977", size = 4425879, upload-time = "2026-01-21T16:27:12.579Z" }, + { url = "https://files.pythonhosted.org/packages/74/b4/cdfee31e0402ea035135462cb0ab496e974d56fab6b4e7a1f0cbccb8cd28/torchvision-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a06d4772a8e13e772906ed736cc53ec6639e5e60554f8e5fa6ca165aabebc464", size = 1863503, upload-time = "2026-03-23T18:13:01.384Z" }, + { url = "https://files.pythonhosted.org/packages/e4/74/11fee109841e80ad14e5ca2d80bff6b10eb11b7838ff06f35bfeaa9f7251/torchvision-0.26.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:2adfbe438473236191ff077a4a9a0c767436879c89628aa97137e959b0c11a94", size = 7766423, upload-time = "2026-03-23T18:12:56.049Z" }, + { url = "https://files.pythonhosted.org/packages/5e/00/24d8c7845c3f270153fb81395a5135b2778e2538e81d14c6aea5106c689c/torchvision-0.26.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b6f9ad1ecc0eab52647298b379ee9426845f8903703e6127973f8f3d049a798b", size = 7518249, upload-time = "2026-03-23T18:12:51.743Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ed/e53cd7c0da7ae002e5e929c1796ebbe7ec0c700c29f7a0a6696497fb3d8b/torchvision-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:f13f12b3791a266de2d599cb8162925261622a037d87fc03132848343cf68f75", size = 3669784, upload-time = "2026-03-23T18:12:49.949Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bd/d552a2521bade3295b2c6e7a4a0d1022261cab7ca7011f4e2a330dbb3caa/torchvision-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55bd6ad4ae77be01ba67a410b05b51f53b0d0ee45f146eb6a0dfb9007e70ab3c", size = 1863499, upload-time = "2026-03-23T18:12:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/33/bf/21b899792b08cae7a298551c68398a79e333697479ed311b3b067aab4bdc/torchvision-0.26.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:1c55dc8affbcc0eb2060fbabbe996ae9e5839b24bb6419777f17848945a411b1", size = 7767527, upload-time = "2026-03-23T18:12:44.348Z" }, + { url = "https://files.pythonhosted.org/packages/9a/45/57bbf9e216850d065e66dd31a50f57424b607f1d878ab8956e56a1f4e36b/torchvision-0.26.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fd10b5f994c210f4f6d6761cf686f82d748554adf486cb0979770c3252868c8f", size = 7519925, upload-time = "2026-03-23T18:12:53.283Z" }, + { url = "https://files.pythonhosted.org/packages/10/58/ed8f7754299f3e91d6414b6dc09f62b3fa7c6e5d63dfe48d69ab81498a37/torchvision-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:de6424b12887ad884f39a0ee446994ae3cd3b6a00a9cafe1bead85a031132af0", size = 3983834, upload-time = "2026-03-23T18:13:00.224Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e7/56b47cc3b132aea90ccce22bcb8975dec688b002150012acc842846039d0/torchvision-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c409e1c3fdebec7a3834465086dbda8bf7680eff79abf7fd2f10c6b59520a7a4", size = 1863502, upload-time = "2026-03-23T18:12:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ec/5c31c92c08b65662fe9604a4067ae8232582805949f11ddc042cebe818ed/torchvision-0.26.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:406557718e62fdf10f5706e88d8a5ec000f872da913bf629aab9297622585547", size = 7767944, upload-time = "2026-03-23T18:12:42.805Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d8/cb6ccda1a1f35a6597645818641701207b3e8e13553e75fce5d86bac74b2/torchvision-0.26.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d61a5abb6b42a0c0c311996c2ac4b83a94418a97182c83b055a2a4ae985e05aa", size = 7522205, upload-time = "2026-03-23T18:12:54.654Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a9/c272623a0f735c35f0f6cd6dc74784d4f970e800cf063bb76687895a2ab9/torchvision-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:7993c01648e7c61d191b018e84d38fe0825c8fcb2720cd0f37caf7ba14404aa1", size = 4255155, upload-time = "2026-03-23T18:12:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/0762f77f53605d10c9477be39bb47722cc8e383bbbc2531471ce0e396c07/torchvision-0.26.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5d63dd43162691258b1b3529b9041bac7d54caa37eae0925f997108268cbf7c4", size = 1860809, upload-time = "2026-03-23T18:12:47.629Z" }, + { url = "https://files.pythonhosted.org/packages/e6/81/0b3e58d1478c660a5af4268713486b2df7203f35abd9195fea87348a5178/torchvision-0.26.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a39c7a26538c41fda453f9a9692b5ff9b35a5437db1d94f3027f6f509c160eac", size = 7727494, upload-time = "2026-03-23T18:12:46.062Z" }, + { url = "https://files.pythonhosted.org/packages/b6/dc/d9ab5d29115aa05e12e30f1397a3eeae1d88a511241dc3bce48dc4342675/torchvision-0.26.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b7e6213620bbf97742e5f79832f9e9d769e6cf0f744c5b53dad80b76db633691", size = 7521747, upload-time = "2026-03-23T18:12:36.815Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1b/f1bc86a918c5f6feab1eeff11982e2060f4704332e96185463d27855bdf5/torchvision-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:4280c35ec8cba1fcc8294fb87e136924708726864c379e4c54494797d86bc474", size = 4319880, upload-time = "2026-03-23T18:12:38.168Z" }, + { url = "https://files.pythonhosted.org/packages/66/28/b4ad0a723ed95b003454caffcc41894b34bd8379df340848cae2c33871de/torchvision-0.26.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:358fc4726d0c08615b6d83b3149854f11efb2a564ed1acb6fce882e151412d23", size = 1951973, upload-time = "2026-03-23T18:12:48.781Z" }, + { url = "https://files.pythonhosted.org/packages/71/e2/7a89096e6cf2f3336353b5338ba925e0addf9d8601920340e6bdf47e8eb3/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:3daf9cc149cf3cdcbd4df9c59dae69ffca86c6823250442c3bbfd63fc2e26c61", size = 7728679, upload-time = "2026-03-23T18:12:26.196Z" }, + { url = "https://files.pythonhosted.org/packages/69/1d/4e1eebc17d18ce080a11dcf3df3f8f717f0efdfa00983f06e8ba79259f61/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:82c3965eca27e86a316e31e4c3e5a16d353e0bcbe0ef8efa2e66502c54493c4b", size = 7609138, upload-time = "2026-03-23T18:12:35.327Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a4/f1155e943ae5b32400d7000adc81c79bb0392b16ceb33bcf13e02e48cced/torchvision-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ebc043cc5a4f0bf22e7680806dbba37ffb19e70f6953bbb44ed1a90aeb5c9bea", size = 4248202, upload-time = "2026-03-23T18:12:41.423Z" }, + { url = "https://files.pythonhosted.org/packages/7f/c8/9bffa9c7f7bdf95b2a0a2dc535c290b9f1cc580c3fb3033ab1246ffffdeb/torchvision-0.26.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:eb61804eb9dbe88c5a2a6c4da8dec1d80d2d0a6f18c999c524e32266cb1ebcd3", size = 1860813, upload-time = "2026-03-23T18:12:39.636Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ac/48f28ffd227991f2e14f4392dde7e8dc14352bb9428c1ef4a4bbf5f7ed85/torchvision-0.26.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:9a904f2131cbfadab4df828088a9f66291ad33f49ff853872aed1f86848ef776", size = 7727777, upload-time = "2026-03-23T18:12:22.549Z" }, + { url = "https://files.pythonhosted.org/packages/a4/21/a2266f7f1b0e58e624ff15fd6f01041f59182c49551ece0db9a183071329/torchvision-0.26.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:0f3e572efe62ad645017ea847e0b5e4f2f638d4e39f05bc011d1eb9ac68d4806", size = 7522174, upload-time = "2026-03-23T18:12:29.565Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ba/1666f90bc0bdd77aaa11dcc42bb9f621a9c3668819c32430452e3d404730/torchvision-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:114bec0c0e98aa4ba446f63e2fe7a2cbca37b39ac933987ee4804f65de121800", size = 4348469, upload-time = "2026-03-23T18:12:24.44Z" }, + { url = "https://files.pythonhosted.org/packages/45/8f/1f0402ac55c2ae15651ff831957d083fe70b2d12282e72612a30ba601512/torchvision-0.26.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:b7d3e295624a28b3b1769228ce1345d94cf4d390dd31136766f76f2d20f718da", size = 1860826, upload-time = "2026-03-23T18:12:34.1Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6a/18a582fe3c5ee26f49b5c9fb21ad8016b4d1c06d10178894a58653946fda/torchvision-0.26.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:7058c5878262937e876f20c25867b33724586aa4499e2853b2d52b99a5e51953", size = 7729089, upload-time = "2026-03-23T18:12:31.394Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9b/f7e119b59499edc00c55c03adc9ec3bd96144d9b81c46852c431f9c64a9a/torchvision-0.26.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:8008474855623c6ba52876589dc52df0aa66e518c25eca841445348e5f79844c", size = 7522704, upload-time = "2026-03-23T18:12:20.301Z" }, + { url = "https://files.pythonhosted.org/packages/d0/6a/09f3844c10643f6c0de5d95abc863420cfaf194c88c7dffd0ac523e2015f/torchvision-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e9d0e022c19a78552fb055d0414d47fecb4a649309b9968573daea160ba6869c", size = 4454275, upload-time = "2026-03-23T18:12:27.487Z" }, ] [[package]] @@ -7583,12 +7611,19 @@ name = "triton" version = "3.6.0" source = { registry = "https://pypi.org/simple" } wheels = [ + { url = "https://files.pythonhosted.org/packages/44/ba/b1b04f4b291a3205d95ebd24465de0e5bf010a2df27a4e58a9b5f039d8f2/triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781", size = 175972180, upload-time = "2026-01-20T16:15:53.664Z" }, { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, + { url = "https://files.pythonhosted.org/packages/0f/2c/96f92f3c60387e14cc45aed49487f3486f89ea27106c1b1376913c62abe4/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651", size = 176081190, upload-time = "2026-01-20T16:16:00.523Z" }, { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" }, + { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243, upload-time = "2026-01-20T16:16:07.857Z" }, { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" }, { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" }, { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, + { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" }, { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, + { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" }, { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, ] From 3268cb93d52eeafec9ffc33acd2cca97abae2ceb Mon Sep 17 00:00:00 2001 From: aconchillo <951761+aconchillo@users.noreply.github.com> Date: Tue, 24 Mar 2026 03:07:36 +0000 Subject: [PATCH 159/159] Update changelog for version 0.0.107 --- CHANGELOG.md | 98 +++++++++++++++++++++++++++++++++++++++ changelog/3449.changed.md | 1 - changelog/3449.fixed.md | 1 - changelog/4029.added.2.md | 1 - changelog/4029.added.md | 1 - changelog/4029.fixed.3.md | 1 - changelog/4029.fixed.md | 1 - changelog/4074.added.md | 1 - changelog/4075.fixed.md | 1 - changelog/4082.fixed.md | 1 - changelog/4083.changed.md | 1 - changelog/4090.fixed.md | 1 - changelog/4091.changed.md | 1 - changelog/4093.fixed.md | 7 --- changelog/4104.added.md | 1 - 15 files changed, 98 insertions(+), 20 deletions(-) delete mode 100644 changelog/3449.changed.md delete mode 100644 changelog/3449.fixed.md delete mode 100644 changelog/4029.added.2.md delete mode 100644 changelog/4029.added.md delete mode 100644 changelog/4029.fixed.3.md delete mode 100644 changelog/4029.fixed.md delete mode 100644 changelog/4074.added.md delete mode 100644 changelog/4075.fixed.md delete mode 100644 changelog/4082.fixed.md delete mode 100644 changelog/4083.changed.md delete mode 100644 changelog/4090.fixed.md delete mode 100644 changelog/4091.changed.md delete mode 100644 changelog/4093.fixed.md delete mode 100644 changelog/4104.added.md diff --git a/CHANGELOG.md b/CHANGELOG.md index dfd42c6c9..b96eb9876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,104 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [0.0.107] - 2026-03-23 + +### Added + +- Added `frame_order` parameter to `SyncParallelPipeline`. Set + `frame_order=FrameOrder.PIPELINE` to push synchronized output frames in + pipeline definition order (all frames from the first pipeline, then the + second, etc.) instead of the default arrival order. + (PR [#4029](https://github.com/pipecat-ai/pipecat/pull/4029)) + +- Added `sync_with_audio` field to `OutputImageRawFrame`. When set to `True`, + the output transport queues image frames with audio so they are displayed + only after all preceding audio has been sent, enabling synchronized + audio/image playback. + (PR [#4029](https://github.com/pipecat-ai/pipecat/pull/4029)) + +- Added `OpenAIResponsesLLMService`, a new LLM service that uses the OpenAI + Responses API. Supports streaming text, function calling, usage metrics, and + out-of-band inference. Works with the universal `LLMContext` and + `LLMContextAggregatorPair`. See + `examples/foundational/07-interruptible-openai-responses.py` and + `14-function-calling-openai-responses.py`. + (PR [#4074](https://github.com/pipecat-ai/pipecat/pull/4074)) + +- Added `audio_out_auto_silence` parameter to `TransportParams` (defaults to + `True`). When set to `False`, the transport waits for audio data instead of + inserting silence when the output queue is empty, which is useful for + scenarios that require uninterrupted audio playback without artificial gaps. + (PR [#4104](https://github.com/pipecat-ai/pipecat/pull/4104)) + +### Changed + +- Renamed tracing span attributes to align with OpenTelemetry GenAI semantic + conventions: `gen_ai.system` to `gen_ai.provider.name`, `system` to + `gen_ai.system_instructions`, `gen_ai.usage.cache_read_input_tokens` to + `gen_ai.usage.cache_read.input_tokens`, and + `gen_ai.usage.cache_creation_input_tokens` to + `gen_ai.usage.cache_creation.input_tokens`. + (PR [#3449](https://github.com/pipecat-ai/pipecat/pull/3449)) + +- `DeepgramSageMakerTTSService` now correctly routes audio through the base + `TTSService` audio context queue. Audio frames are delivered via + `append_to_audio_context()` instead of being pushed directly, enabling proper + ordering, interruption handling, and start/stop frame lifecycle management. + Interruptions now trigger a `Clear` message to Deepgram (flushing its text + buffer) at the right time via `on_audio_context_interrupted`. + (PR [#4083](https://github.com/pipecat-ai/pipecat/pull/4083)) + +- `GradiumTTSService` now sends a per-context `setup` message with + `client_req_id` before the first text message for each TTS context, following + Gradium's multiplexing protocol. Previously, a single setup message was sent + at connection time without a `client_req_id`, which prevented Gradium from + associating requests with their sessions when using `close_ws_on_eos=False`. + (PR [#4091](https://github.com/pipecat-ai/pipecat/pull/4091)) + +### Fixed + +- Fixed stale `system_instruction` in LLM tracing spans by reading from + `_settings.system_instruction` instead of the removed `_system_instruction` + attribute. + (PR [#3449](https://github.com/pipecat-ai/pipecat/pull/3449)) + +- Fixed `SyncParallelPipeline` breaking the Whisker debugger. + (PR [#4029](https://github.com/pipecat-ai/pipecat/pull/4029)) + +- Fixed `SyncParallelPipeline` race condition where concurrent SystemFrame + processing (e.g. from RTVI) could corrupt sink queues and cause deadlocks. + SystemFrames now take a fast path that passes them through without draining + queued output. + (PR [#4029](https://github.com/pipecat-ai/pipecat/pull/4029)) + +- Fixed TTS frame ordering so that non-system frames always arrive in correct + order relative to the `TTSStartedFrame`/`TTSAudioRawFrame`/`TTSStoppedFrame` + sequence. Previously these frames could race ahead of or behind audio context + frames, producing out-of-order output downstream. + (PR [#4075](https://github.com/pipecat-ai/pipecat/pull/4075)) + +- Fixed `SarvamTTSService` audio and error frames now route through + `append_to_audio_context()` instead of `push_frame()`, ensuring correct + behavior with audio contexts and interruptions. + (PR [#4082](https://github.com/pipecat-ai/pipecat/pull/4082)) + +- Fixed audio frame ordering and interruption handling in Fish Audio, LMNT, + Neuphonic, and Rime NonJson TTS services. These services were bypassing the + base `TTSService` audio context serialization queue by pushing audio frames + directly, which could cause out-of-order frames and broken interruptions + during speech. + (PR [#4090](https://github.com/pipecat-ai/pipecat/pull/4090)) + +- Fixed Genesys AudioHook serializer to always include the `parameters` field in + protocol messages. The AudioHook protocol requires every message to carry a + `parameters` object (even if empty), but `_create_message` omitted it when no + parameters were provided. This caused clients that validate message structure + (including the Genesys reference implementation) to reject `pong` and + parameter-less `closed` responses, breaking server sequence tracking and + preventing `outputVariables` from reaching the Architect flow. + (PR [#4093](https://github.com/pipecat-ai/pipecat/pull/4093)) + ## [0.0.106] - 2026-03-18 ### Added diff --git a/changelog/3449.changed.md b/changelog/3449.changed.md deleted file mode 100644 index d5ea2f6d7..000000000 --- a/changelog/3449.changed.md +++ /dev/null @@ -1 +0,0 @@ -- Renamed tracing span attributes to align with OpenTelemetry GenAI semantic conventions: `gen_ai.system` to `gen_ai.provider.name`, `system` to `gen_ai.system_instructions`, `gen_ai.usage.cache_read_input_tokens` to `gen_ai.usage.cache_read.input_tokens`, and `gen_ai.usage.cache_creation_input_tokens` to `gen_ai.usage.cache_creation.input_tokens`. diff --git a/changelog/3449.fixed.md b/changelog/3449.fixed.md deleted file mode 100644 index 7cf01c0cb..000000000 --- a/changelog/3449.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed stale `system_instruction` in LLM tracing spans by reading from `_settings.system_instruction` instead of the removed `_system_instruction` attribute. diff --git a/changelog/4029.added.2.md b/changelog/4029.added.2.md deleted file mode 100644 index 1ae691442..000000000 --- a/changelog/4029.added.2.md +++ /dev/null @@ -1 +0,0 @@ -- Added `frame_order` parameter to `SyncParallelPipeline`. Set `frame_order=FrameOrder.PIPELINE` to push synchronized output frames in pipeline definition order (all frames from the first pipeline, then the second, etc.) instead of the default arrival order. diff --git a/changelog/4029.added.md b/changelog/4029.added.md deleted file mode 100644 index ba3714483..000000000 --- a/changelog/4029.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `sync_with_audio` field to `OutputImageRawFrame`. When set to `True`, the output transport queues image frames with audio so they are displayed only after all preceding audio has been sent, enabling synchronized audio/image playback. diff --git a/changelog/4029.fixed.3.md b/changelog/4029.fixed.3.md deleted file mode 100644 index 3c812d590..000000000 --- a/changelog/4029.fixed.3.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `SyncParallelPipeline` breaking the Whisker debugger. diff --git a/changelog/4029.fixed.md b/changelog/4029.fixed.md deleted file mode 100644 index 57930a997..000000000 --- a/changelog/4029.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `SyncParallelPipeline` race condition where concurrent SystemFrame processing (e.g. from RTVI) could corrupt sink queues and cause deadlocks. SystemFrames now take a fast path that passes them through without draining queued output. diff --git a/changelog/4074.added.md b/changelog/4074.added.md deleted file mode 100644 index c27a8e3cf..000000000 --- a/changelog/4074.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `OpenAIResponsesLLMService`, a new LLM service that uses the OpenAI Responses API. Supports streaming text, function calling, usage metrics, and out-of-band inference. Works with the universal `LLMContext` and `LLMContextAggregatorPair`. See `examples/foundational/07-interruptible-openai-responses.py` and `14-function-calling-openai-responses.py`. diff --git a/changelog/4075.fixed.md b/changelog/4075.fixed.md deleted file mode 100644 index 97870d4bb..000000000 --- a/changelog/4075.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed TTS frame ordering so that non-system frames always arrive in correct order relative to the `TTSStartedFrame`/`TTSAudioRawFrame`/`TTSStoppedFrame` sequence. Previously these frames could race ahead of or behind audio context frames, producing out-of-order output downstream. diff --git a/changelog/4082.fixed.md b/changelog/4082.fixed.md deleted file mode 100644 index e17e84fea..000000000 --- a/changelog/4082.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed `SarvamTTSService` audio and error frames now route through `append_to_audio_context()` instead of `push_frame()`, ensuring correct behavior with audio contexts and interruptions. diff --git a/changelog/4083.changed.md b/changelog/4083.changed.md deleted file mode 100644 index d9d46957a..000000000 --- a/changelog/4083.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `DeepgramSageMakerTTSService` now correctly routes audio through the base `TTSService` audio context queue. Audio frames are delivered via `append_to_audio_context()` instead of being pushed directly, enabling proper ordering, interruption handling, and start/stop frame lifecycle management. Interruptions now trigger a `Clear` message to Deepgram (flushing its text buffer) at the right time via `on_audio_context_interrupted`. diff --git a/changelog/4090.fixed.md b/changelog/4090.fixed.md deleted file mode 100644 index ff42f09fe..000000000 --- a/changelog/4090.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed audio frame ordering and interruption handling in Fish Audio, LMNT, Neuphonic, and Rime NonJson TTS services. These services were bypassing the base `TTSService` audio context serialization queue by pushing audio frames directly, which could cause out-of-order frames and broken interruptions during speech. diff --git a/changelog/4091.changed.md b/changelog/4091.changed.md deleted file mode 100644 index a05a981a3..000000000 --- a/changelog/4091.changed.md +++ /dev/null @@ -1 +0,0 @@ -- `GradiumTTSService` now sends a per-context `setup` message with `client_req_id` before the first text message for each TTS context, following Gradium's multiplexing protocol. Previously, a single setup message was sent at connection time without a `client_req_id`, which prevented Gradium from associating requests with their sessions when using `close_ws_on_eos=False`. diff --git a/changelog/4093.fixed.md b/changelog/4093.fixed.md deleted file mode 100644 index 39fabc029..000000000 --- a/changelog/4093.fixed.md +++ /dev/null @@ -1,7 +0,0 @@ -- Fixed Genesys AudioHook serializer to always include the `parameters` field in - protocol messages. The AudioHook protocol requires every message to carry a - `parameters` object (even if empty), but `_create_message` omitted it when no - parameters were provided. This caused clients that validate message structure - (including the Genesys reference implementation) to reject `pong` and - parameter-less `closed` responses, breaking server sequence tracking and - preventing `outputVariables` from reaching the Architect flow. diff --git a/changelog/4104.added.md b/changelog/4104.added.md deleted file mode 100644 index 31c6f3873..000000000 --- a/changelog/4104.added.md +++ /dev/null @@ -1 +0,0 @@ -- Added `audio_out_auto_silence` parameter to `TransportParams` (defaults to `True`). When set to `False`, the transport waits for audio data instead of inserting silence when the output queue is empty, which is useful for scenarios that require uninterrupted audio playback without artificial gaps.