2026-03-16 15:04:08 +05:30
|
|
|
from typing import Optional, TypedDict
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
import openai
|
2026-03-06 16:49:14 +05:30
|
|
|
from deepgram import DeepgramClient
|
2025-09-09 14:37:32 +05:30
|
|
|
from groq import Groq
|
|
|
|
|
|
|
|
|
|
# try:
|
|
|
|
|
# from pyneuphonic import Neuphonic
|
|
|
|
|
# except ImportError:
|
|
|
|
|
# Neuphonic = None
|
2026-06-12 14:55:30 +05:30
|
|
|
from api.schemas.ai_model_configuration import (
|
2026-06-09 16:30:03 +05:30
|
|
|
EffectiveAIModelConfiguration,
|
2025-09-09 14:37:32 +05:30
|
|
|
)
|
|
|
|
|
from api.services.configuration.registry import ServiceConfig, ServiceProviders
|
2026-03-16 15:04:08 +05:30
|
|
|
from api.services.mps_service_key_client import mps_service_key_client
|
2026-05-27 13:07:45 +05:30
|
|
|
from api.utils.url_security import validate_user_configured_service_url
|
2025-09-09 14:37:32 +05:30
|
|
|
|
2026-03-27 00:07:38 +05:30
|
|
|
AuthContext = TypedDict(
|
|
|
|
|
"AuthContext",
|
|
|
|
|
{"organization_id": Optional[int], "created_by": Optional[str]},
|
|
|
|
|
total=False,
|
|
|
|
|
)
|
|
|
|
|
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
class APIKeyStatus(TypedDict):
|
|
|
|
|
model: str
|
|
|
|
|
message: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class APIKeyStatusResponse(TypedDict):
|
|
|
|
|
status: list[APIKeyStatus]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserConfigurationValidator:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self._validator_map = {
|
|
|
|
|
ServiceProviders.OPENAI.value: self._check_openai_api_key,
|
|
|
|
|
ServiceProviders.DEEPGRAM.value: self._check_deepgram_api_key,
|
|
|
|
|
ServiceProviders.GROQ.value: self._check_groq_api_key,
|
2026-02-09 13:31:32 +05:30
|
|
|
ServiceProviders.OPENROUTER.value: self._check_openrouter_api_key,
|
2025-09-09 14:37:32 +05:30
|
|
|
ServiceProviders.ELEVENLABS.value: self._validate_elevenlabs_api_key,
|
|
|
|
|
ServiceProviders.GOOGLE.value: self._check_google_api_key,
|
|
|
|
|
ServiceProviders.AZURE.value: self._check_azure_api_key,
|
2026-06-02 12:50:00 +05:30
|
|
|
ServiceProviders.AZURE_SPEECH.value: self._check_azure_speech_api_key,
|
2025-09-09 14:37:32 +05:30
|
|
|
ServiceProviders.CARTESIA.value: self._check_cartesia_api_key,
|
|
|
|
|
ServiceProviders.DOGRAH.value: self._check_dograh_api_key,
|
2025-12-25 15:05:53 +05:30
|
|
|
ServiceProviders.SARVAM.value: self._check_sarvam_api_key,
|
2026-01-08 18:03:26 +05:30
|
|
|
ServiceProviders.SPEECHMATICS.value: self._check_speechmatics_api_key,
|
2026-03-24 15:24:07 +08:00
|
|
|
ServiceProviders.CAMB.value: self._check_camb_api_key,
|
2026-03-19 15:06:59 +05:30
|
|
|
ServiceProviders.AWS_BEDROCK.value: self._check_aws_bedrock_api_key,
|
2026-03-31 21:42:03 +05:30
|
|
|
ServiceProviders.SPEACHES.value: self._check_speaches_api_key,
|
2026-06-15 22:56:01 +05:30
|
|
|
ServiceProviders.HUGGINGFACE.value: self._check_huggingface_api_key,
|
2026-05-22 18:04:59 +05:30
|
|
|
ServiceProviders.GOOGLE_VERTEX.value: self._check_google_vertex_llm_api_key,
|
2026-03-31 21:42:03 +05:30
|
|
|
ServiceProviders.OPENAI_REALTIME.value: self._check_openai_api_key,
|
2026-05-22 18:04:59 +05:30
|
|
|
ServiceProviders.GROK_REALTIME.value: self._check_grok_realtime_api_key,
|
2026-05-23 12:51:55 +05:30
|
|
|
ServiceProviders.ULTRAVOX_REALTIME.value: self._check_ultravox_realtime_api_key,
|
2026-03-31 21:42:03 +05:30
|
|
|
ServiceProviders.GOOGLE_REALTIME.value: self._check_google_api_key,
|
2026-05-16 18:05:23 +05:30
|
|
|
ServiceProviders.GOOGLE_VERTEX_REALTIME.value: self._check_google_vertex_realtime_api_key,
|
2026-06-02 12:50:00 +05:30
|
|
|
ServiceProviders.AZURE_REALTIME.value: self._check_azure_realtime_api_key,
|
2026-04-03 07:10:37 +05:30
|
|
|
ServiceProviders.ASSEMBLYAI.value: self._check_assemblyai_api_key,
|
2026-04-04 14:47:48 +05:30
|
|
|
ServiceProviders.GLADIA.value: self._check_gladia_api_key,
|
2026-04-07 14:05:47 +05:30
|
|
|
ServiceProviders.RIME.value: self._check_rime_api_key,
|
2026-05-22 15:39:41 +08:00
|
|
|
ServiceProviders.MINIMAX.value: self._check_minimax_api_key,
|
feat: add Smallest AI TTS and STT provider integration (#444)
* feat: add Smallest AI TTS and STT provider integration
Integrates Smallest AI's Waves (TTS) and Pulse (STT) APIs as selectable
providers in the Dograh platform. Dograh's pipecat fork already contains
the pipecat-level service implementations; this wires them into the API
configuration registry and service factory.
- Added `SMALLEST = "smallest"` to `ServiceProviders` enum
- Registered `SmallestAITTSConfiguration` (lightning-v3.1/v2, voices,
language, speed) and `SmallestAISTTConfiguration` (pulse model, 30+
languages) Pydantic config classes with the TTS/STT registries
- Added factory branches in `create_tts_service` and `create_stt_service`
routing to `SmallestTTSService` and `SmallestSTTService` from pipecat
* fix: update Smallest AI models to v4 naming convention
- TTS: rename lightning-v3.1 → lightning_v3.1, add lightning_v3.1_pro, drop deprecated lightning-v2
- STT: keep pulse only (pulse-pro is not a streaming model)
* fix: change default TTS voice from emily to sophia for lightning_v3.1
emily is not a verified lightning_v3.1 voice; sophia is the pipecat
SmallestTTSService default and confirmed to work with the standard pool.
* fix: replace 9 invalid lightning_v3.1 voice IDs with verified ones
jasmine, james, michael, aria, lara, asel, sarah, rishi, deepika do not
exist in the lightning_v3.1 voice catalog. Replaced with avery, liam,
lucas, olivia, freya, devansh, maya, dhruv, maithili — all verified
against the API.
* fix: smallest ai config validation and tts model compatibility
* chore: ruff fix
* chore: updated smallest ai schema in openapi.json
---------
Co-authored-by: Sabiha Khan <sabihak89@gmail.com>
Co-authored-by: Sabiha Khan <87858386+chewwbaka@users.noreply.github.com>
2026-06-17 12:55:53 +05:30
|
|
|
ServiceProviders.SMALLEST.value: self._check_smallest_api_key,
|
2025-09-09 14:37:32 +05:30
|
|
|
}
|
|
|
|
|
|
2026-03-27 00:07:38 +05:30
|
|
|
async def validate(
|
|
|
|
|
self,
|
2026-06-09 16:30:03 +05:30
|
|
|
configuration: EffectiveAIModelConfiguration,
|
2026-03-27 00:07:38 +05:30
|
|
|
organization_id: Optional[int] = None,
|
|
|
|
|
created_by: Optional[str] = None,
|
|
|
|
|
) -> APIKeyStatusResponse:
|
|
|
|
|
self._auth_context: AuthContext = {
|
|
|
|
|
"organization_id": organization_id,
|
|
|
|
|
"created_by": created_by,
|
|
|
|
|
}
|
2025-09-09 14:37:32 +05:30
|
|
|
status_list = []
|
|
|
|
|
|
|
|
|
|
status_list.extend(self._validate_service(configuration.llm, "llm"))
|
2026-03-31 21:42:03 +05:30
|
|
|
if configuration.is_realtime:
|
|
|
|
|
status_list.extend(
|
|
|
|
|
self._validate_service(
|
|
|
|
|
configuration.realtime, "realtime", required=True
|
|
|
|
|
)
|
|
|
|
|
)
|
2026-06-12 14:55:30 +05:30
|
|
|
else:
|
|
|
|
|
status_list.extend(self._validate_service(configuration.stt, "stt"))
|
|
|
|
|
status_list.extend(self._validate_service(configuration.tts, "tts"))
|
|
|
|
|
# Embeddings is optional - only validate if configured
|
|
|
|
|
status_list.extend(
|
|
|
|
|
self._validate_service(
|
|
|
|
|
configuration.embeddings, "embeddings", required=False
|
|
|
|
|
)
|
|
|
|
|
)
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
if status_list:
|
|
|
|
|
raise ValueError(status_list)
|
|
|
|
|
|
|
|
|
|
return {"status": [{"model": "all", "message": "ok"}]}
|
|
|
|
|
|
|
|
|
|
def _validate_service(
|
2026-01-17 14:37:03 +05:30
|
|
|
self,
|
|
|
|
|
service_config: Optional[ServiceConfig],
|
|
|
|
|
service_name: str,
|
|
|
|
|
required: bool = True,
|
2025-09-09 14:37:32 +05:30
|
|
|
) -> list[APIKeyStatus]:
|
|
|
|
|
"""Validate a service configuration and return any error statuses."""
|
|
|
|
|
if not service_config:
|
2026-01-17 14:37:03 +05:30
|
|
|
if required:
|
|
|
|
|
return [{"model": service_name, "message": "API key is missing"}]
|
|
|
|
|
return [] # Optional service not configured is OK
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
provider = service_config.provider
|
2026-03-19 15:06:59 +05:30
|
|
|
|
2026-05-27 13:07:45 +05:30
|
|
|
for url_field in ("base_url", "endpoint"):
|
|
|
|
|
url = getattr(service_config, url_field, None)
|
|
|
|
|
if url:
|
|
|
|
|
try:
|
|
|
|
|
validate_user_configured_service_url(
|
|
|
|
|
url,
|
|
|
|
|
field_name=url_field,
|
|
|
|
|
)
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
return [{"model": service_name, "message": str(e)}]
|
|
|
|
|
|
2026-03-31 21:42:03 +05:30
|
|
|
# Speaches doesn't require an API key
|
|
|
|
|
if provider == ServiceProviders.SPEACHES.value:
|
2026-03-24 17:50:45 +05:30
|
|
|
try:
|
2026-03-31 21:42:03 +05:30
|
|
|
if not self._check_speaches_api_key(provider, service_config):
|
2026-03-24 17:50:45 +05:30
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"model": service_name,
|
|
|
|
|
"message": f"Invalid {provider} configuration",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
return [{"model": service_name, "message": str(e)}]
|
|
|
|
|
return []
|
|
|
|
|
|
2026-05-16 18:05:23 +05:30
|
|
|
# Vertex Realtime uses service-account credentials (or ADC) instead of api_key
|
|
|
|
|
if provider == ServiceProviders.GOOGLE_VERTEX_REALTIME.value:
|
|
|
|
|
try:
|
|
|
|
|
if not self._check_google_vertex_realtime_api_key(
|
|
|
|
|
provider, service_config
|
|
|
|
|
):
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"model": service_name,
|
|
|
|
|
"message": f"Invalid {provider} configuration",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
return [{"model": service_name, "message": str(e)}]
|
|
|
|
|
return []
|
|
|
|
|
|
2026-05-22 18:04:59 +05:30
|
|
|
# Vertex LLM uses service-account credentials (or ADC) instead of api_key
|
|
|
|
|
if provider == ServiceProviders.GOOGLE_VERTEX.value:
|
|
|
|
|
try:
|
|
|
|
|
if not self._check_google_vertex_llm_api_key(provider, service_config):
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"model": service_name,
|
|
|
|
|
"message": f"Invalid {provider} configuration",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
return [{"model": service_name, "message": str(e)}]
|
|
|
|
|
return []
|
|
|
|
|
|
2026-03-19 15:06:59 +05:30
|
|
|
# AWS Bedrock uses AWS credentials instead of api_key
|
|
|
|
|
if provider == ServiceProviders.AWS_BEDROCK.value:
|
|
|
|
|
try:
|
|
|
|
|
if not self._check_aws_bedrock_api_key(provider, service_config):
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"model": service_name,
|
|
|
|
|
"message": f"Invalid {provider} credentials",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
return [{"model": service_name, "message": str(e)}]
|
|
|
|
|
return []
|
|
|
|
|
|
2026-05-22 15:39:41 +08:00
|
|
|
# MiniMax TTS requires a group_id alongside the API key.
|
|
|
|
|
# LLM configs don't expose group_id, so only check when the field exists.
|
|
|
|
|
if provider == ServiceProviders.MINIMAX.value and hasattr(
|
|
|
|
|
service_config, "group_id"
|
|
|
|
|
):
|
|
|
|
|
if not getattr(service_config, "group_id", None):
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"model": service_name,
|
|
|
|
|
"message": "group_id is required for MiniMax TTS",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
2025-09-09 14:37:32 +05:30
|
|
|
api_key = service_config.api_key
|
|
|
|
|
|
2026-03-16 15:04:08 +05:30
|
|
|
try:
|
2026-05-27 13:07:45 +05:30
|
|
|
if not self._check_api_key(provider, api_key, service_config):
|
2026-03-16 15:04:08 +05:30
|
|
|
return [
|
2026-05-27 13:07:45 +05:30
|
|
|
{
|
|
|
|
|
"model": service_name,
|
|
|
|
|
"message": (
|
|
|
|
|
f"Invalid {provider} API key. Please verify your API key is "
|
|
|
|
|
f"correct, has not expired, and has the required permissions."
|
|
|
|
|
),
|
|
|
|
|
}
|
2026-03-16 15:04:08 +05:30
|
|
|
]
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
return [{"model": service_name, "message": str(e)}]
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
|
2026-05-27 13:07:45 +05:30
|
|
|
def _check_api_key(
|
|
|
|
|
self,
|
|
|
|
|
provider: str,
|
|
|
|
|
api_key: str,
|
|
|
|
|
service_config: Optional[ServiceConfig] = None,
|
|
|
|
|
) -> bool:
|
2025-09-09 14:37:32 +05:30
|
|
|
"""Check if an API key for a provider is valid."""
|
|
|
|
|
validator = self._validator_map.get(provider)
|
|
|
|
|
if not validator:
|
|
|
|
|
return False
|
|
|
|
|
|
2026-05-27 13:07:45 +05:30
|
|
|
if provider in (
|
|
|
|
|
ServiceProviders.OPENAI.value,
|
|
|
|
|
ServiceProviders.OPENAI_REALTIME.value,
|
|
|
|
|
):
|
|
|
|
|
return validator(provider, api_key, service_config)
|
2025-09-09 14:37:32 +05:30
|
|
|
return validator(provider, api_key)
|
|
|
|
|
|
2026-05-27 13:07:45 +05:30
|
|
|
def _check_openai_api_key(
|
|
|
|
|
self, model: str, api_key: str, service_config: Optional[ServiceConfig] = None
|
|
|
|
|
) -> bool:
|
|
|
|
|
client_kwargs: dict[str, str] = {"api_key": api_key}
|
|
|
|
|
base_url = getattr(service_config, "base_url", None) if service_config else None
|
|
|
|
|
if base_url:
|
|
|
|
|
client_kwargs["base_url"] = base_url
|
|
|
|
|
client = openai.OpenAI(**client_kwargs)
|
2025-09-09 14:37:32 +05:30
|
|
|
try:
|
|
|
|
|
client.models.list()
|
2026-03-16 15:04:08 +05:30
|
|
|
return True
|
2025-09-09 14:37:32 +05:30
|
|
|
except openai.AuthenticationError:
|
2026-05-27 13:07:45 +05:30
|
|
|
if base_url and "openai.com" not in base_url:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"Invalid OpenAI API key. The key was rejected by the API at {base_url}. "
|
|
|
|
|
"Please check that your API key is correct and has not been revoked."
|
|
|
|
|
)
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Invalid OpenAI API key. The key was rejected by the OpenAI API. "
|
|
|
|
|
"Please check that your API key is correct and has not been revoked. "
|
|
|
|
|
"You can verify your keys at https://platform.openai.com/api-keys."
|
|
|
|
|
)
|
|
|
|
|
except openai.APIConnectionError:
|
|
|
|
|
if base_url:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"Could not connect to the OpenAI-compatible API at {base_url}. "
|
|
|
|
|
"Please verify that the base_url is correct and reachable, and try again."
|
|
|
|
|
)
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Could not connect to the OpenAI API. Please check your network connection "
|
|
|
|
|
"and try again."
|
|
|
|
|
)
|
|
|
|
|
except openai.APIError:
|
|
|
|
|
if base_url:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"The OpenAI-compatible API at {base_url} returned an error while "
|
|
|
|
|
"validating the API key. Please verify that the base_url is correct, "
|
|
|
|
|
"the service is available, and the API key is valid."
|
|
|
|
|
)
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"The OpenAI API returned an error while validating the API key. "
|
|
|
|
|
"Please try again later."
|
|
|
|
|
)
|
|
|
|
|
except Exception:
|
|
|
|
|
if base_url:
|
|
|
|
|
raise ValueError(
|
|
|
|
|
f"Failed to validate the OpenAI API key using the API at {base_url}. "
|
|
|
|
|
"Please verify that the base_url is correct and reachable, and that the "
|
|
|
|
|
"API key is valid."
|
|
|
|
|
)
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Failed to validate the OpenAI API key. Please try again later."
|
|
|
|
|
)
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
def _check_deepgram_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
try:
|
2026-03-06 16:49:14 +05:30
|
|
|
deepgram = DeepgramClient(api_key=api_key)
|
|
|
|
|
deepgram.manage.v1.projects.list()
|
2026-03-16 15:04:08 +05:30
|
|
|
return True
|
2026-03-06 16:49:14 +05:30
|
|
|
except Exception:
|
2026-05-27 13:07:45 +05:30
|
|
|
raise ValueError(
|
|
|
|
|
"Invalid Deepgram API key. The key was rejected by the Deepgram API. "
|
|
|
|
|
"Please check that your API key is correct and active. "
|
|
|
|
|
"You can verify your keys at https://console.deepgram.com/."
|
|
|
|
|
)
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
def _check_groq_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
client = Groq(api_key=api_key)
|
|
|
|
|
try:
|
|
|
|
|
client.models.list()
|
2026-03-16 15:04:08 +05:30
|
|
|
return True
|
2025-09-09 14:37:32 +05:30
|
|
|
except Exception:
|
2026-05-27 13:07:45 +05:30
|
|
|
raise ValueError(
|
|
|
|
|
"Invalid Groq API key. The key was rejected by the Groq API. "
|
|
|
|
|
"Please check that your API key is correct and active. "
|
|
|
|
|
"You can verify your keys at https://console.groq.com/keys."
|
|
|
|
|
)
|
2025-09-09 14:37:32 +05:30
|
|
|
|
|
|
|
|
def _validate_elevenlabs_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_google_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_azure_api_key(self, model: str, api_key: str) -> bool:
|
2026-06-02 12:50:00 +05:30
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_azure_speech_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_azure_realtime_api_key(self, model: str, api_key: str) -> bool:
|
2025-09-09 14:37:32 +05:30
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_cartesia_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_dograh_api_key(self, model: str, api_key: str) -> bool:
|
2026-03-16 15:04:08 +05:30
|
|
|
if api_key.startswith("dgr"):
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"You provided a Dograh API key (dgr...) instead of a service key. "
|
|
|
|
|
"Please use a service key (mps...)."
|
|
|
|
|
)
|
2026-03-27 00:07:38 +05:30
|
|
|
auth = getattr(self, "_auth_context", {})
|
|
|
|
|
return mps_service_key_client.validate_service_key(
|
|
|
|
|
api_key,
|
|
|
|
|
organization_id=auth.get("organization_id"),
|
|
|
|
|
created_by=auth.get("created_by"),
|
|
|
|
|
)
|
2025-09-09 14:37:32 +05:30
|
|
|
|
2025-12-25 15:05:53 +05:30
|
|
|
def _check_sarvam_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
2026-01-12 12:47:32 +05:30
|
|
|
|
2026-02-09 13:31:32 +05:30
|
|
|
def _check_openrouter_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
|
|
|
|
|
2026-05-22 18:04:59 +05:30
|
|
|
def _check_grok_realtime_api_key(self, model: str, api_key: str) -> bool:
|
2026-05-23 12:51:55 +05:30
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_ultravox_realtime_api_key(self, model: str, api_key: str) -> bool:
|
2026-05-22 18:04:59 +05:30
|
|
|
return True
|
|
|
|
|
|
2026-01-08 18:03:26 +05:30
|
|
|
def _check_speechmatics_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
2026-03-19 15:06:59 +05:30
|
|
|
|
2026-03-24 15:24:07 +08:00
|
|
|
def _check_camb_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
2026-03-24 17:50:45 +05:30
|
|
|
|
2026-03-31 21:42:03 +05:30
|
|
|
def _check_speaches_api_key(self, model: str, service_config) -> bool:
|
2026-03-24 17:50:45 +05:30
|
|
|
if not getattr(service_config, "base_url", None):
|
2026-03-31 21:42:03 +05:30
|
|
|
raise ValueError("base_url is required for Speaches services")
|
2026-03-24 17:50:45 +05:30
|
|
|
return True
|
|
|
|
|
|
2026-06-15 22:56:01 +05:30
|
|
|
def _check_huggingface_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
if not api_key.startswith("hf_"):
|
|
|
|
|
raise ValueError(
|
|
|
|
|
"Invalid Hugging Face API token format. Use a token that starts with "
|
|
|
|
|
"'hf_' and has Inference Providers permission."
|
|
|
|
|
)
|
|
|
|
|
return True
|
|
|
|
|
|
2026-05-16 18:05:23 +05:30
|
|
|
def _check_google_vertex_realtime_api_key(self, model: str, service_config) -> bool:
|
|
|
|
|
if not getattr(service_config, "project_id", None):
|
|
|
|
|
raise ValueError("project_id is required for Google Vertex Realtime")
|
|
|
|
|
if not getattr(service_config, "location", None):
|
|
|
|
|
raise ValueError("location is required for Google Vertex Realtime")
|
|
|
|
|
return True
|
|
|
|
|
|
2026-05-22 18:04:59 +05:30
|
|
|
def _check_google_vertex_llm_api_key(self, model: str, service_config) -> bool:
|
|
|
|
|
if not getattr(service_config, "project_id", None):
|
|
|
|
|
raise ValueError("project_id is required for Google Vertex")
|
|
|
|
|
if not getattr(service_config, "location", None):
|
|
|
|
|
raise ValueError("location is required for Google Vertex")
|
|
|
|
|
return True
|
|
|
|
|
|
2026-03-19 15:06:59 +05:30
|
|
|
def _check_aws_bedrock_api_key(self, model: str, service_config) -> bool:
|
|
|
|
|
if not service_config.aws_access_key or not service_config.aws_secret_key:
|
|
|
|
|
raise ValueError("AWS access key and secret key are required for Bedrock")
|
|
|
|
|
return True
|
2026-04-03 07:10:37 +05:30
|
|
|
|
|
|
|
|
def _check_assemblyai_api_key(self, model: str, service_config) -> bool:
|
|
|
|
|
return True
|
2026-04-04 14:47:48 +05:30
|
|
|
|
|
|
|
|
def _check_gladia_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
2026-04-07 14:05:47 +05:30
|
|
|
|
|
|
|
|
def _check_rime_api_key(self, model: str, api_key: str) -> bool:
|
|
|
|
|
return True
|
2026-05-22 15:39:41 +08:00
|
|
|
|
|
|
|
|
def _check_minimax_api_key(self, model: str, api_key: str) -> bool:
|
feat: add Smallest AI TTS and STT provider integration (#444)
* feat: add Smallest AI TTS and STT provider integration
Integrates Smallest AI's Waves (TTS) and Pulse (STT) APIs as selectable
providers in the Dograh platform. Dograh's pipecat fork already contains
the pipecat-level service implementations; this wires them into the API
configuration registry and service factory.
- Added `SMALLEST = "smallest"` to `ServiceProviders` enum
- Registered `SmallestAITTSConfiguration` (lightning-v3.1/v2, voices,
language, speed) and `SmallestAISTTConfiguration` (pulse model, 30+
languages) Pydantic config classes with the TTS/STT registries
- Added factory branches in `create_tts_service` and `create_stt_service`
routing to `SmallestTTSService` and `SmallestSTTService` from pipecat
* fix: update Smallest AI models to v4 naming convention
- TTS: rename lightning-v3.1 → lightning_v3.1, add lightning_v3.1_pro, drop deprecated lightning-v2
- STT: keep pulse only (pulse-pro is not a streaming model)
* fix: change default TTS voice from emily to sophia for lightning_v3.1
emily is not a verified lightning_v3.1 voice; sophia is the pipecat
SmallestTTSService default and confirmed to work with the standard pool.
* fix: replace 9 invalid lightning_v3.1 voice IDs with verified ones
jasmine, james, michael, aria, lara, asel, sarah, rishi, deepika do not
exist in the lightning_v3.1 voice catalog. Replaced with avery, liam,
lucas, olivia, freya, devansh, maya, dhruv, maithili — all verified
against the API.
* fix: smallest ai config validation and tts model compatibility
* chore: ruff fix
* chore: updated smallest ai schema in openapi.json
---------
Co-authored-by: Sabiha Khan <sabihak89@gmail.com>
Co-authored-by: Sabiha Khan <87858386+chewwbaka@users.noreply.github.com>
2026-06-17 12:55:53 +05:30
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def _check_smallest_api_key(self, model: str, api_key: str) -> bool:
|
2026-05-22 15:39:41 +08:00
|
|
|
return True
|