diff --git a/docs/concepts/vendors.md b/docs/concepts/vendors.md index 9ece26f..0d5113b 100644 --- a/docs/concepts/vendors.md +++ b/docs/concepts/vendors.md @@ -68,6 +68,8 @@ Used with `agent.with_tts()`. Each TTS vendor produces audio at a specific sampl | `MiniMaxTTS` | MiniMax | `model` for supported Agora-managed global models; `key`, `group_id`, `model`, `voice_id`, `url` for BYOK | — | | `GenericTTS` | Generic OpenAI-compatible TTS over HTTP(S) | `url` | Configurable | | `DeepgramTTS` | Deepgram | `api_key`, `model` | Configurable | +| `GradiumTTS` | Gradium | `api_key` | Configurable | +| `MistralTTS` | Mistral | `api_key` | — | | `SarvamTTS` | Sarvam | `api_key` | — | | `XaiTTS` | xAI | `api_key`, `language` | Configurable | diff --git a/docs/guides/regional-routing.md b/docs/guides/regional-routing.md index ab4c2c3..23bea9b 100644 --- a/docs/guides/regional-routing.md +++ b/docs/guides/regional-routing.md @@ -40,7 +40,7 @@ If you omit `with_stt()`, AgentKit uses `FengmingSTT` by default for `Area.CN` c | Client area | STT classes | LLM classes | TTS classes | Avatar classes | |---|---|---|---|---| -| `Area.US`, `Area.EU`, `Area.AP` | `DeepgramSTT`, `SpeechmaticsSTT`, `MicrosoftSTT`, `OpenAISTT`, `GoogleSTT`, `AmazonSTT`, `AssemblyAISTT`, `AresSTT`, `SarvamSTT`, `XaiSTT` | `OpenAI`, `AzureOpenAI`, `Anthropic`, `Gemini`, `Groq`, `VertexAILLM`, `AmazonBedrock`, `Dify`, `CustomLLM` | `ElevenLabsTTS`, `MicrosoftTTS`, `OpenAITTS`, `CartesiaTTS`, `GoogleTTS`, `AmazonTTS`, `DeepgramTTS`, `HumeAITTS`, `RimeTTS`, `FishAudioTTS`, `MiniMaxTTS`, `MurfTTS`, `SarvamTTS`, `GenericTTS`, `XaiTTS` | `LiveAvatarAvatar`, `HeyGenAvatar`, `AkoolAvatar`, `AnamAvatar`, `GenericAvatar` | +| `Area.US`, `Area.EU`, `Area.AP` | `DeepgramSTT`, `SpeechmaticsSTT`, `MicrosoftSTT`, `OpenAISTT`, `GoogleSTT`, `AmazonSTT`, `AssemblyAISTT`, `AresSTT`, `SarvamSTT`, `XaiSTT` | `OpenAI`, `AzureOpenAI`, `Anthropic`, `Gemini`, `Groq`, `VertexAILLM`, `AmazonBedrock`, `Dify`, `CustomLLM` | `ElevenLabsTTS`, `MicrosoftTTS`, `OpenAITTS`, `CartesiaTTS`, `GoogleTTS`, `AmazonTTS`, `DeepgramTTS`, `GradiumTTS`, `MistralTTS`, `HumeAITTS`, `RimeTTS`, `FishAudioTTS`, `MiniMaxTTS`, `MurfTTS`, `SarvamTTS`, `GenericTTS`, `XaiTTS` | `LiveAvatarAvatar`, `HeyGenAvatar`, `AkoolAvatar`, `AnamAvatar`, `GenericAvatar` | | `Area.CN` | `FengmingSTT`, `TencentSTT`, `MicrosoftCNSTT`, `XfyunSTT`, `XfyunBigModelSTT`, `XfyunDialectSTT` | `AliyunLLM`, `BytedanceLLM`, `DeepSeekLLM`, `TencentLLM` | `MiniMaxCNTTS`, `TencentTTS`, `BytedanceTTS`, `MicrosoftCNTTS`, `CosyVoiceTTS`, `BytedanceDuplexTTS`, `StepFunTTS`, `GenericTTS` | `SenseTimeAvatar`, `SpatiusAvatar` | Global client example: diff --git a/docs/reference/vendors.md b/docs/reference/vendors.md index 73c17a8..ac61d9a 100644 --- a/docs/reference/vendors.md +++ b/docs/reference/vendors.md @@ -19,7 +19,7 @@ Construct vendors directly from `agora_agent`, then bind a client with `Agent(cl | Area | STT classes | LLM classes | TTS classes | Avatar classes | |---|---|---|---|---| -| `Area.US`, `Area.EU`, `Area.AP` | `DeepgramSTT`, `SpeechmaticsSTT`, `MicrosoftSTT`, `OpenAISTT`, `GoogleSTT`, `AmazonSTT`, `AssemblyAISTT`, `AresSTT`, `SarvamSTT`, `XaiSTT` | `OpenAI`, `AzureOpenAI`, `Anthropic`, `Gemini`, `Groq`, `VertexAILLM`, `AmazonBedrock`, `Dify`, `CustomLLM` | `ElevenLabsTTS`, `MicrosoftTTS`, `OpenAITTS`, `CartesiaTTS`, `GoogleTTS`, `AmazonTTS`, `DeepgramTTS`, `HumeAITTS`, `RimeTTS`, `FishAudioTTS`, `MiniMaxTTS`, `MurfTTS`, `SarvamTTS`, `GenericTTS`, `XaiTTS` | `LiveAvatarAvatar`, `HeyGenAvatar`, `AkoolAvatar`, `AnamAvatar`, `GenericAvatar` | +| `Area.US`, `Area.EU`, `Area.AP` | `DeepgramSTT`, `SpeechmaticsSTT`, `MicrosoftSTT`, `OpenAISTT`, `GoogleSTT`, `AmazonSTT`, `AssemblyAISTT`, `AresSTT`, `SarvamSTT`, `XaiSTT` | `OpenAI`, `AzureOpenAI`, `Anthropic`, `Gemini`, `Groq`, `VertexAILLM`, `AmazonBedrock`, `Dify`, `CustomLLM` | `ElevenLabsTTS`, `MicrosoftTTS`, `OpenAITTS`, `CartesiaTTS`, `GoogleTTS`, `AmazonTTS`, `DeepgramTTS`, `GradiumTTS`, `MistralTTS`, `HumeAITTS`, `RimeTTS`, `FishAudioTTS`, `MiniMaxTTS`, `MurfTTS`, `SarvamTTS`, `GenericTTS`, `XaiTTS` | `LiveAvatarAvatar`, `HeyGenAvatar`, `AkoolAvatar`, `AnamAvatar`, `GenericAvatar` | | `Area.CN` | `FengmingSTT`, `TencentSTT`, `MicrosoftCNSTT`, `XfyunSTT`, `XfyunBigModelSTT`, `XfyunDialectSTT` | `AliyunLLM`, `BytedanceLLM`, `DeepSeekLLM`, `TencentLLM` | `MiniMaxCNTTS`, `TencentTTS`, `BytedanceTTS`, `MicrosoftCNTTS`, `CosyVoiceTTS`, `BytedanceDuplexTTS`, `StepFunTTS`, `GenericTTS` | `SenseTimeAvatar`, `SpatiusAvatar` | Global example: @@ -303,6 +303,18 @@ The SDK also includes named helpers for the remaining Agora-supported LLM provid | `additional_params` | `Dict[str, Any]` | No | `None` | Additional Deepgram TTS parameters, flattened into `params` | | `skip_patterns` | `List[int]` | No | `None` | Skip patterns | +### `GradiumTTS` + +| Parameter | Type | Required | Default | Description | +|---|---|---|---|---| +| `api_key` | `str` | Yes | — | Gradium API key | +| `url` | `str` | No | `None` | WebSocket endpoint for streaming TTS output | +| `model_name` | `str` | No | `None` | Gradium TTS model name (e.g., `default`) | +| `voice_id` | `str` | No | `None` | Gradium voice identifier | +| `sample_rate` | `int` | No | `None` | Audio sample rate in Hz | +| `additional_params` | `Dict[str, Any]` | No | `None` | Additional Gradium TTS parameters, flattened into `params` | +| `skip_patterns` | `List[int]` | No | `None` | Skip patterns | + ### `HumeAITTS` | Parameter | Type | Required | Default | Description | @@ -389,6 +401,16 @@ AgentKit serializes `credential_mode` at the top level of the Rime TTS configura `key` and `group_id` are required together for BYOK. `url` is optional. In both BYOK and managed modes, exactly one of `voice_id` or `timber_weights` must be provided. Without `key`, `model` must be one of the supported Agora-managed MiniMax models. +### `MistralTTS` + +| Parameter | Type | Required | Default | Description | +|---|---|---|---|---| +| `api_key` | `str` | Yes | — | Mistral API key | +| `model` | `str` | No | `None` | Mistral TTS model name (e.g., `voxtral-mini-tts-2603`) | +| `voice` | `str` | No | `None` | Mistral voice identifier | +| `additional_params` | `Dict[str, Any]` | No | `None` | Additional Mistral TTS parameters, flattened into `params` | +| `skip_patterns` | `List[int]` | No | `None` | Skip patterns | + ### `MurfTTS` | Parameter | Type | Required | Default | Description | diff --git a/src/agora_agent/agentkit/__init__.py b/src/agora_agent/agentkit/__init__.py index 3ab9d95..7ed3577 100644 --- a/src/agora_agent/agentkit/__init__.py +++ b/src/agora_agent/agentkit/__init__.py @@ -175,6 +175,7 @@ GenericAvatar, GoogleSTT, GoogleTTS, + GradiumTTS, Groq, HeyGenAvatar, HumeAITTS, @@ -185,6 +186,7 @@ MicrosoftSTT, MicrosoftTTS, MiniMaxTTS, + MistralTTS, MurfTTS, OpenAI, OpenAIRealtime, @@ -393,12 +395,14 @@ "CartesiaTTS", "DeepgramTTS", "GoogleTTS", + "GradiumTTS", "AmazonTTS", "HumeAITTS", "RimeTTS", "FishAudioTTS", "MiniMaxTTS", "MiniMaxCNTTS", + "MistralTTS", "TencentTTS", "BytedanceTTS", "CosyVoiceTTS", diff --git a/src/agora_agent/agentkit/regional_agent.py b/src/agora_agent/agentkit/regional_agent.py index 9010816..d71a7b6 100644 --- a/src/agora_agent/agentkit/regional_agent.py +++ b/src/agora_agent/agentkit/regional_agent.py @@ -54,9 +54,11 @@ ElevenLabsTTS, FishAudioTTS, GoogleTTS, + GradiumTTS, HumeAITTS, MicrosoftTTS, MiniMaxTTS, + MistralTTS, MurfTTS, OpenAITTS, GenericTTS, @@ -99,6 +101,8 @@ GenericTTS, XaiTTS, DeepgramTTS, + GradiumTTS, + MistralTTS, ] GlobalLLM = typing.Union[ OpenAI, diff --git a/src/agora_agent/agentkit/vendors/__init__.py b/src/agora_agent/agentkit/vendors/__init__.py index 72960de..9bd07fa 100644 --- a/src/agora_agent/agentkit/vendors/__init__.py +++ b/src/agora_agent/agentkit/vendors/__init__.py @@ -54,9 +54,11 @@ ElevenLabsTTS, FishAudioTTS, GoogleTTS, + GradiumTTS, HumeAITTS, MicrosoftTTS, MiniMaxTTS, + MistralTTS, MurfTTS, OpenAITTS, GenericTTS, @@ -92,12 +94,14 @@ "CartesiaTTS", "DeepgramTTS", "GoogleTTS", + "GradiumTTS", "AmazonTTS", "HumeAITTS", "RimeTTS", "FishAudioTTS", "MiniMaxTTS", "MiniMaxCNTTS", + "MistralTTS", "MurfTTS", "SarvamTTS", "GenericTTS", diff --git a/src/agora_agent/agentkit/vendors/catalog.py b/src/agora_agent/agentkit/vendors/catalog.py index bffe1c8..f44b94e 100644 --- a/src/agora_agent/agentkit/vendors/catalog.py +++ b/src/agora_agent/agentkit/vendors/catalog.py @@ -35,9 +35,11 @@ ElevenLabsTTS, FishAudioTTS, GoogleTTS, + GradiumTTS, HumeAITTS, MicrosoftTTS, MiniMaxTTS, + MistralTTS, MurfTTS, OpenAITTS, GenericTTS, @@ -102,6 +104,8 @@ def __init__( "generic": GenericTTS, "xai": XaiTTS, "deepgram": DeepgramTTS, + "gradium": GradiumTTS, + "mistral": MistralTTS, }, avatar={ "akool": AkoolAvatar, diff --git a/src/agora_agent/agentkit/vendors/namespaces.py b/src/agora_agent/agentkit/vendors/namespaces.py index f71d5a1..1f85e09 100644 --- a/src/agora_agent/agentkit/vendors/namespaces.py +++ b/src/agora_agent/agentkit/vendors/namespaces.py @@ -32,9 +32,11 @@ ElevenLabsTTS, FishAudioTTS, GoogleTTS, + GradiumTTS, HumeAITTS, MicrosoftTTS, MiniMaxTTS, + MistralTTS, MurfTTS, OpenAITTS, GenericTTS, @@ -101,6 +103,8 @@ class GlobalTTSVendors: generic = GenericTTS xai = XaiTTS deepgram = DeepgramTTS + gradium = GradiumTTS + mistral = MistralTTS class CNTTSVendors: diff --git a/src/agora_agent/agentkit/vendors/region.py b/src/agora_agent/agentkit/vendors/region.py index d1bdff9..1ade190 100644 --- a/src/agora_agent/agentkit/vendors/region.py +++ b/src/agora_agent/agentkit/vendors/region.py @@ -56,6 +56,8 @@ "generic", "xai", "deepgram", + "gradium", + "mistral", ) CN_LLM_VENDORS: typing.Tuple[str, ...] = ( diff --git a/src/agora_agent/agentkit/vendors/tts.py b/src/agora_agent/agentkit/vendors/tts.py index 3a0c256..bb99e04 100644 --- a/src/agora_agent/agentkit/vendors/tts.py +++ b/src/agora_agent/agentkit/vendors/tts.py @@ -257,6 +257,35 @@ def to_config(self) -> Dict[str, Any]: return result +class GradiumTTS(BaseTTS): + model_config = ConfigDict(extra="forbid") + + api_key: str = Field(..., description="Gradium API key") + url: Optional[str] = Field(default=None, description="WebSocket endpoint for streaming TTS output") + model_name: Optional[str] = Field(default=None, description="Gradium TTS model name") + voice_id: Optional[str] = Field(default=None, description="Gradium voice identifier") + sample_rate: Optional[int] = Field(default=None, description="Audio sample rate in Hz") + additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional Gradium TTS parameters") + skip_patterns: Optional[List[int]] = Field(default=None) + + def to_config(self) -> Dict[str, Any]: + params: Dict[str, Any] = dict(self.additional_params or {}) + params["api_key"] = self.api_key + if self.url is not None: + params["url"] = self.url + if self.model_name is not None: + params["model_name"] = self.model_name + if self.voice_id is not None: + params["voice_id"] = self.voice_id + if self.sample_rate is not None: + params["sample_rate"] = self.sample_rate + + result: Dict[str, Any] = {"vendor": "gradium", "params": params} + if self.skip_patterns is not None: + result["skip_patterns"] = self.skip_patterns + return result + + class HumeAITTS(BaseTTS): model_config = ConfigDict(extra="forbid") @@ -455,6 +484,29 @@ def to_config(self) -> Dict[str, Any]: return result +class MistralTTS(BaseTTS): + model_config = ConfigDict(extra="forbid") + + api_key: str = Field(..., description="Mistral API key") + model: Optional[str] = Field(default=None, description="Mistral TTS model name") + voice: Optional[str] = Field(default=None, description="Mistral voice identifier") + additional_params: Optional[Dict[str, Any]] = Field(default=None, description="Additional Mistral TTS parameters") + skip_patterns: Optional[List[int]] = Field(default=None) + + def to_config(self) -> Dict[str, Any]: + params: Dict[str, Any] = dict(self.additional_params or {}) + params["api_key"] = self.api_key + if self.model is not None: + params["model"] = self.model + if self.voice is not None: + params["voice"] = self.voice + + result: Dict[str, Any] = {"vendor": "mistral", "params": params} + if self.skip_patterns is not None: + result["skip_patterns"] = self.skip_patterns + return result + + class SarvamTTS(BaseTTS): model_config = ConfigDict(extra="forbid") diff --git a/tests/custom/test_regional_vendors.py b/tests/custom/test_regional_vendors.py index 9a67642..0a3ea66 100644 --- a/tests/custom/test_regional_vendors.py +++ b/tests/custom/test_regional_vendors.py @@ -6,8 +6,10 @@ Area, DeepgramSTT, GenericTTS, + GradiumTTS, MiniMaxCNTTS, MiniMaxTTS, + MistralTTS, OpenAI, SpatiusAvatar, TencentSTT, @@ -15,6 +17,8 @@ XaiTTS, XaiGrok, ) +from agora_agent.agentkit.vendors.catalog import GLOBAL_VENDOR_NAMESPACE +from agora_agent.agentkit.vendors.namespaces import GlobalTTSVendors from agora_agent.agentkit.vendors.region import ( CN_ASR_VENDORS, CN_AVATAR_VENDORS, @@ -194,6 +198,25 @@ def test_xai_asr_and_tts_are_classified_as_global_vendors() -> None: assert global_agent.tts is not None and global_agent.tts["vendor"] == "xai" +@pytest.mark.parametrize( + ("vendor_name", "tts"), + [ + ("gradium", GradiumTTS(api_key="gradium-key")), + ("mistral", MistralTTS(api_key="mistral-key")), + ], +) +def test_new_tts_are_classified_as_global_vendors(vendor_name: str, tts) -> None: + assert vendor_name not in CN_TTS_VENDORS + assert vendor_name in GLOBAL_TTS_VENDORS + assert GLOBAL_VENDOR_NAMESPACE.tts[vendor_name] is type(tts) + assert getattr(GlobalTTSVendors, vendor_name) is type(tts) + + global_agent = Agent(client=_client(Area.US)).with_tts(tts) + + assert global_agent.__class__.__name__ == "GlobalAgent" + assert global_agent.tts is not None and global_agent.tts["vendor"] == vendor_name + + def test_xai_grok_remains_mllm_vendor() -> None: agent = Agent(client=_client(Area.US)).with_mllm(XaiGrok(api_key="xai-key")) diff --git a/tests/custom/test_root_exports.py b/tests/custom/test_root_exports.py index 2cc3f5f..819edd0 100644 --- a/tests/custom/test_root_exports.py +++ b/tests/custom/test_root_exports.py @@ -11,6 +11,8 @@ def test_root_exports_match_agentkit_for_common_symbols() -> None: "GlobalAgent", "RegionalAgent", "DeepgramSTT", + "GradiumTTS", + "MistralTTS", "MiniMaxCNTTS", "TencentSTT", "OpenAI", @@ -42,6 +44,8 @@ def test_unknown_root_export_raises_attribute_error() -> None: def test_dir_includes_agentkit_vendor_exports() -> None: assert "DeepgramSTT" in dir(agora_agent) + assert "GradiumTTS" in dir(agora_agent) + assert "MistralTTS" in dir(agora_agent) assert "MiniMaxCNTTS" in dir(agora_agent) assert "TencentSTT" in dir(agora_agent) assert "CNAgent" in dir(agora_agent) @@ -51,6 +55,8 @@ def test_dir_includes_agentkit_vendor_exports() -> None: def test_all_includes_agentkit_vendor_exports() -> None: assert "CredentialMode" in agora_agent.__all__ assert "DeepgramSTT" in agora_agent.__all__ + assert "GradiumTTS" in agora_agent.__all__ + assert "MistralTTS" in agora_agent.__all__ assert "MiniMaxCNTTS" in agora_agent.__all__ assert "TencentSTT" in agora_agent.__all__ assert "OpenAI" in agora_agent.__all__ diff --git a/tests/custom/test_tts_vendors.py b/tests/custom/test_tts_vendors.py index ce99753..8737548 100644 --- a/tests/custom/test_tts_vendors.py +++ b/tests/custom/test_tts_vendors.py @@ -1,6 +1,23 @@ import pytest -from agora_agent import AmazonTTS, CartesiaTTS, CredentialMode, DeepgramTTS, ElevenLabsTTS, FishAudioTTS, GoogleTTS, HumeAITTS, MicrosoftTTS, MiniMaxTTS, MurfTTS, OpenAITTS, RimeTTS, SarvamTTS +from agora_agent import ( + AmazonTTS, + CartesiaTTS, + CredentialMode, + DeepgramTTS, + ElevenLabsTTS, + FishAudioTTS, + GoogleTTS, + GradiumTTS, + HumeAITTS, + MicrosoftTTS, + MiniMaxTTS, + MistralTTS, + MurfTTS, + OpenAITTS, + RimeTTS, + SarvamTTS, +) from agora_agent.agents.types.start_agents_request_properties import StartAgentsRequestProperties from agora_agent.core.jsonable_encoder import jsonable_encoder from agora_agent.core.pydantic_utilities import parse_obj_as @@ -61,6 +78,34 @@ def test_tts_vendor_params_match_generated_core_shapes() -> None: "encoding": "linear16", } + assert GradiumTTS( + api_key="gradium-key", + url="wss://api.gradium.ai/api/speech/tts", + model_name="default", + voice_id="voice", + sample_rate=16000, + additional_params={"api_key": "ignored-key", "custom_gain": 0.5}, + ).to_config()["params"] == { + "api_key": "gradium-key", + "url": "wss://api.gradium.ai/api/speech/tts", + "model_name": "default", + "voice_id": "voice", + "sample_rate": 16000, + "custom_gain": 0.5, + } + + assert MistralTTS( + api_key="mistral-key", + model="voxtral-mini-tts-2603", + voice="voice", + additional_params={"api_key": "ignored-key", "speed": 1.1}, + ).to_config()["params"] == { + "api_key": "mistral-key", + "model": "voxtral-mini-tts-2603", + "voice": "voice", + "speed": 1.1, + } + assert OpenAITTS(api_key="openai-key", voice="coral", model="gpt-4o-mini-tts", base_url="https://api.openai.com/v1", instructions="speak clearly").to_config()["params"] == { "voice": "coral", "api_key": "openai-key", @@ -170,6 +215,28 @@ def test_tts_managed_mode_validation_matches_core_shapes() -> None: ) +def test_new_global_tts_optional_fields_and_skip_patterns() -> None: + assert GradiumTTS(api_key="gradium-key", skip_patterns=[1, 2]).to_config() == { + "vendor": "gradium", + "params": {"api_key": "gradium-key"}, + "skip_patterns": [1, 2], + } + assert MistralTTS(api_key="mistral-key", skip_patterns=[3]).to_config() == { + "vendor": "mistral", + "params": {"api_key": "mistral-key"}, + "skip_patterns": [3], + } + + assert GradiumTTS(api_key="gradium-key", sample_rate=16000).resolved_sample_rate == 16000 + assert MistralTTS(api_key="mistral-key").resolved_sample_rate is None + + +@pytest.mark.parametrize("tts_class", [GradiumTTS, MistralTTS]) +def test_new_global_tts_requires_api_key(tts_class) -> None: + with pytest.raises(Exception, match="api_key"): + tts_class() + + def test_rime_tts_managed_credential_mode_params() -> None: config = RimeTTS( credential_mode=CredentialMode.MANAGED, @@ -281,6 +348,42 @@ def test_tts_wire_serialization_applies_fern_aliases() -> None: }, } + gradium_config = GradiumTTS( + api_key="gradium-key", + model_name="default", + voice_id="voice", + sample_rate=16000, + additional_params={"custom_gain": 0.5}, + ).to_config() + gradium_wire = jsonable_encoder(parse_obj_as(StartAgentsRequestProperties, {**_BASE, "tts": gradium_config})) + assert gradium_wire["tts"] == { + "vendor": "gradium", + "params": { + "api_key": "gradium-key", + "model_name": "default", + "voiceId": "voice", + "sample_rate": 16000, + "custom_gain": 0.5, + }, + } + + mistral_config = MistralTTS( + api_key="mistral-key", + model="voxtral-mini-tts-2603", + voice="voice", + additional_params={"speed": 1.1}, + ).to_config() + mistral_wire = jsonable_encoder(parse_obj_as(StartAgentsRequestProperties, {**_BASE, "tts": mistral_config})) + assert mistral_wire["tts"] == { + "vendor": "mistral", + "params": { + "api_key": "mistral-key", + "model": "voxtral-mini-tts-2603", + "voice": "voice", + "speed": 1.1, + }, + } + murf_config = MurfTTS(key="murf-key", voice_id="Ariana").to_config() assert "voiceId" in murf_config["params"] murf_wire = jsonable_encoder(parse_obj_as(StartAgentsRequestProperties, {**_BASE, "tts": murf_config}))