diff --git a/src/pipecat/services/sarvam/stt.py b/src/pipecat/services/sarvam/stt.py index b825c0d78..ec5e42df0 100644 --- a/src/pipecat/services/sarvam/stt.py +++ b/src/pipecat/services/sarvam/stt.py @@ -78,11 +78,13 @@ class SarvamSTTService(STTService): """Configuration parameters for Sarvam STT service. Parameters: - language: Target language for transcription. Required for all models. - Defaults to "unknown" for saarika models and "en-IN" for saaras:v3. - prompt: Optional prompt to guide transcription style/context. - Only applicable to saaras:v3 models. Defaults to None. - mode: Mode of operation for saaras:v3 models. Options: transcribe, translate, + language: Target language for transcription. + - saarika:v2.5: Defaults to "unknown" (auto-detect supported) + - saaras:v2.5: Not used (auto-detects language) + - saaras:v3: Defaults to "en-IN" + prompt: Optional prompt to guide transcription/translation style/context. + Only applicable to saaras models (v2.5 and v3). Defaults to None. + mode: Mode of operation for saaras:v3 models only. Options: transcribe, translate, verbatim, translit, codemix. Defaults to "transcribe" for saaras:v3. vad_signals: Enable VAD signals in response. Defaults to None. high_vad_sensitivity: Enable high VAD (Voice Activity Detection) sensitivity. Defaults to None. @@ -111,10 +113,13 @@ class SarvamSTTService(STTService): Args: api_key: Sarvam API key for authentication. - model: Sarvam model to use for transcription. Allowed: "saarika:v2.5", "saaras:v3". + model: Sarvam model to use for transcription. Allowed values: + - "saarika:v2.5": Standard STT model + - "saaras:v2.5": STT-Translate model (auto-detects language, supports prompts) + - "saaras:v3": Advanced STT model (supports mode and prompts) sample_rate: Audio sample rate. Defaults to 16000 if not specified. input_audio_codec: Audio codec/format of the input file. Defaults to "wav". - mode: Mode of operation for saaras:v3 models. Options: transcribe, translate, + mode: Mode of operation for saaras:v3 models only. Options: transcribe, translate, verbatim, translit, codemix. Defaults to "transcribe" for saaras:v3. params: Configuration parameters for Sarvam STT service. **kwargs: Additional arguments passed to the parent STTService. @@ -125,34 +130,52 @@ class SarvamSTTService(STTService): params = params.model_copy(update={"mode": mode}) # Validate allowed models - allowed_models = {"saarika:v2.5", "saaras:v3"} + allowed_models = {"saarika:v2.5", "saaras:v3", "saaras:v2.5"} if model not in allowed_models: allowed_models_list = ", ".join(sorted(allowed_models)) raise ValueError(f"Unsupported model '{model}'. Allowed values: {allowed_models_list}.") - # Validate that saarika models don't accept prompt parameter + # Validate model-specific parameter restrictions if "saarika" in model.lower(): + # saarika models don't accept prompt or mode if params.prompt is not None: raise ValueError( f"Model '{model}' does not accept prompt parameter. " - "Prompts are only supported for saaras:v3 model." + "Prompts are only supported for saaras models (v2.5 and v3)." ) if params.mode is not None: raise ValueError( f"Model '{model}' does not accept mode parameter. " "Mode is only supported for saaras:v3 model." ) + elif model.lower() == "saaras:v2.5": + # saaras:v2.5 supports prompt but not mode + if params.mode is not None: + raise ValueError( + f"Model '{model}' does not accept mode parameter. " + "Mode is only supported for saaras:v3 model." + ) + if params.language is not None: + raise ValueError( + f"Model '{model}' does not accept language parameter. " + "saaras:v2.5 (STT-Translate) auto-detects language." + ) super().__init__(sample_rate=sample_rate, **kwargs) self.set_model_name(model) self._api_key = api_key self._language_code: Optional[Language] = params.language - # Set language string - both models require language_code + # Set language string based on model type + # - saarika:v2.5: uses language_code or defaults to "unknown" + # - saaras:v2.5: auto-detects language (no language_code needed) + # - saaras:v3: uses language_code or defaults to "en-IN" if params.language: self._language_string = language_to_sarvam_language(params.language) elif "saarika" in model.lower(): self._language_string = "unknown" + elif model.lower() == "saaras:v2.5": + self._language_string = None # STT-Translate auto-detects language elif model.lower() == "saaras:v3": self._language_string = "en-IN" else: @@ -225,7 +248,17 @@ class SarvamSTTService(STTService): Args: language: The language to use for speech recognition. + + Raises: + ValueError: If called on saaras:v2.5 model which auto-detects language. """ + # saaras:v2.5 (STT-Translate) auto-detects language + if self.model_name.lower() == "saaras:v2.5": + raise ValueError( + f"Model '{self.model_name}' does not accept language parameter. " + "saaras:v2.5 (STT-Translate) auto-detects language." + ) + logger.info(f"Switching STT language to: [{language}]") self._language_code = language self._language_string = language_to_sarvam_language(language) @@ -233,24 +266,24 @@ class SarvamSTTService(STTService): await self._connect() async def set_prompt(self, prompt: Optional[str]): - """Set the transcription prompt and reconnect. + """Set the transcription/translation prompt and reconnect. Args: - prompt: Prompt text to guide transcription style/context. + prompt: Prompt text to guide transcription/translation style/context. Pass None to clear/disable prompt. - Only applicable to saaras:v3 model. + Only applicable to saaras models (v2.5 and v3). """ # saarika models do not accept prompt parameter if "saarika" in self.model_name.lower(): if prompt is not None: raise ValueError( f"Model '{self.model_name}' does not accept prompt parameter. " - "Prompts are only supported for saaras:v3 model." + "Prompts are only supported for saaras models (v2.5 and v3)." ) # If prompt is None and it's saarika, just silently return (no-op) return - logger.info("Updating saaras:v3 prompt.") + logger.info(f"Updating {self.model_name} prompt.") self._prompt = prompt await self._disconnect() await self._connect() @@ -314,8 +347,13 @@ class SarvamSTTService(STTService): "sample_rate": self.sample_rate, } - # Both saarika and saaras:v3 use the same speech_to_text_streaming endpoint - await self._socket_client.transcribe(**method_kwargs) + # Use appropriate method based on model type + if self.model_name.lower() == "saaras:v2.5": + # STT-Translate: auto-detects input language and returns translated text + await self._socket_client.translate(**method_kwargs) + else: + # saarika:v2.5 and saaras:v3 use transcribe + await self._socket_client.transcribe(**method_kwargs) except Exception as e: yield ErrorFrame(error=f"Error sending audio to Sarvam: {e}", exception=e) @@ -331,15 +369,18 @@ class SarvamSTTService(STTService): vad_signals_str = "true" if self._vad_signals else "false" high_vad_sensitivity_str = "true" if self._high_vad_sensitivity else "false" - # Build connection parameters - both models use speech_to_text_streaming + # Build common connection parameters connect_kwargs = { "model": self.model_name, - "language_code": self._language_string, # Required for both models "vad_signals": vad_signals_str, "high_vad_sensitivity": high_vad_sensitivity_str, "sample_rate": str(self.sample_rate), } + # Add language_code for models that require it (not saaras:v2.5 which auto-detects) + if self._language_string is not None: + connect_kwargs["language_code"] = self._language_string + # Add mode for saaras:v3 only if self.model_name.lower() == "saaras:v3" and self._mode is not None: connect_kwargs["mode"] = self._mode @@ -353,17 +394,25 @@ class SarvamSTTService(STTService): pass return connect_fn(**kwargs) - # Both saarika and saaras:v3 use the same speech_to_text_streaming endpoint - self._websocket_context = _connect_with_sdk_headers( - self._sarvam_client.speech_to_text_streaming.connect, - **connect_kwargs, - ) + # Choose the appropriate endpoint based on model + if self.model_name.lower() == "saaras:v2.5": + # STT-Translate: auto-detects input language and returns translated text + self._websocket_context = _connect_with_sdk_headers( + self._sarvam_client.speech_to_text_translate_streaming.connect, + **connect_kwargs, + ) + else: + # saarika:v2.5 and saaras:v3 use speech_to_text_streaming + self._websocket_context = _connect_with_sdk_headers( + self._sarvam_client.speech_to_text_streaming.connect, + **connect_kwargs, + ) # Enter the async context manager self._socket_client = await self._websocket_context.__aenter__() - # Set prompt if provided (only for saaras:v3 model, after connection) - if self._prompt is not None and self.model_name.lower() == "saaras:v3": + # Set prompt if provided (only for saaras models, after connection) + if self._prompt is not None and "saaras" in self.model_name.lower(): await self._socket_client.set_prompt(self._prompt) # Register event handler for incoming messages