Merge pull request #3953 from pipecat-ai/mb/deepgram-flux-on-the-fly
Add on-the-fly Configure support for Deepgram Flux STT
This commit is contained in:
1
changelog/3953.added.md
Normal file
1
changelog/3953.added.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
- Added on-the-fly `Configure` support for `DeepgramFluxSTTService`. The `keyterm`, `eot_threshold`, `eager_eot_threshold`, and `eot_timeout_ms` settings can now be updated mid-stream via `STTUpdateSettingsFrame` without requiring a WebSocket reconnect.
|
||||||
@@ -100,6 +100,19 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
|
|||||||
context.add_message({"role": "user", "content": "Please introduce yourself to the user."})
|
context.add_message({"role": "user", "content": "Please introduce yourself to the user."})
|
||||||
await task.queue_frames([LLMRunFrame()])
|
await task.queue_frames([LLMRunFrame()])
|
||||||
|
|
||||||
|
# Update configure-able fields mid-stream (sent via Configure message,
|
||||||
|
# no reconnect needed).
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
logger.info("Updating Deepgram Flux STT settings: eot_threshold, keyterm")
|
||||||
|
await task.queue_frame(
|
||||||
|
STTUpdateSettingsFrame(
|
||||||
|
delta=DeepgramFluxSTTSettings(
|
||||||
|
eot_threshold=0.8,
|
||||||
|
keyterm=["Pipecat", "Deepgram"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
await asyncio.sleep(10)
|
await asyncio.sleep(10)
|
||||||
logger.info("Updating Deepgram Flux STT settings: language=es")
|
logger.info("Updating Deepgram Flux STT settings: language=es")
|
||||||
await task.queue_frame(
|
await task.queue_frame(
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ class FluxMessageType(str, Enum):
|
|||||||
RECEIVE_CONNECTED = "Connected"
|
RECEIVE_CONNECTED = "Connected"
|
||||||
RECEIVE_FATAL_ERROR = "Error"
|
RECEIVE_FATAL_ERROR = "Error"
|
||||||
TURN_INFO = "TurnInfo"
|
TURN_INFO = "TurnInfo"
|
||||||
|
CONFIGURE_SUCCESS = "ConfigureSuccess"
|
||||||
|
CONFIGURE_FAILURE = "ConfigureFailure"
|
||||||
|
|
||||||
|
|
||||||
class FluxEventType(str, Enum):
|
class FluxEventType(str, Enum):
|
||||||
@@ -114,6 +116,7 @@ class DeepgramFluxSTTService(WebsocketSTTService):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_settings: DeepgramFluxSTTSettings
|
_settings: DeepgramFluxSTTSettings
|
||||||
|
_CONFIGURE_FIELDS = {"keyterm", "eot_threshold", "eager_eot_threshold", "eot_timeout_ms"}
|
||||||
|
|
||||||
class InputParams(BaseModel):
|
class InputParams(BaseModel):
|
||||||
"""Configuration parameters for Deepgram Flux API.
|
"""Configuration parameters for Deepgram Flux API.
|
||||||
@@ -409,6 +412,33 @@ class DeepgramFluxSTTService(WebsocketSTTService):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.push_error(error_msg=f"Error sending closeStream: {e}", exception=e)
|
await self.push_error(error_msg=f"Error sending closeStream: {e}", exception=e)
|
||||||
|
|
||||||
|
async def _send_configure(self, fields: set[str]):
|
||||||
|
"""Send a Configure control message to update settings mid-stream.
|
||||||
|
|
||||||
|
Builds a Configure JSON message containing only the fields that changed
|
||||||
|
and sends it over the existing WebSocket connection.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fields: Set of changed field names to include in the message.
|
||||||
|
"""
|
||||||
|
message: dict[str, Any] = {"type": "Configure"}
|
||||||
|
|
||||||
|
if "keyterm" in fields:
|
||||||
|
message["keyterms"] = self._settings.keyterm
|
||||||
|
|
||||||
|
thresholds: dict[str, Any] = {}
|
||||||
|
if "eot_threshold" in fields:
|
||||||
|
thresholds["eot_threshold"] = self._settings.eot_threshold
|
||||||
|
if "eager_eot_threshold" in fields:
|
||||||
|
thresholds["eager_eot_threshold"] = self._settings.eager_eot_threshold
|
||||||
|
if "eot_timeout_ms" in fields:
|
||||||
|
thresholds["eot_timeout_ms"] = self._settings.eot_timeout_ms
|
||||||
|
if thresholds:
|
||||||
|
message["thresholds"] = thresholds
|
||||||
|
|
||||||
|
logger.debug(f"{self}: sending Configure message: {message}")
|
||||||
|
await self._websocket.send(json.dumps(message))
|
||||||
|
|
||||||
def can_generate_metrics(self) -> bool:
|
def can_generate_metrics(self) -> bool:
|
||||||
"""Check if this service can generate processing metrics.
|
"""Check if this service can generate processing metrics.
|
||||||
|
|
||||||
@@ -420,19 +450,20 @@ class DeepgramFluxSTTService(WebsocketSTTService):
|
|||||||
async def _update_settings(self, delta: DeepgramFluxSTTSettings) -> dict[str, Any]:
|
async def _update_settings(self, delta: DeepgramFluxSTTSettings) -> dict[str, Any]:
|
||||||
"""Apply a settings delta.
|
"""Apply a settings delta.
|
||||||
|
|
||||||
Settings are stored but not applied to the active connection.
|
Configure-able fields (keyterm, eot_threshold, eager_eot_threshold,
|
||||||
|
eot_timeout_ms) are sent to Deepgram via a Configure WebSocket message.
|
||||||
|
Other fields are stored but cannot be applied to the active connection.
|
||||||
"""
|
"""
|
||||||
changed = await super()._update_settings(delta)
|
changed = await super()._update_settings(delta)
|
||||||
|
|
||||||
if not changed:
|
if not changed:
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
# TODO: someday we could reconnect here to apply updated settings.
|
configure_fields = changed.keys() & self._CONFIGURE_FIELDS
|
||||||
# Code might look something like the below:
|
if configure_fields and self._websocket and self._websocket.state is State.OPEN:
|
||||||
# await self._disconnect()
|
await self._send_configure(configure_fields)
|
||||||
# await self._connect()
|
|
||||||
|
|
||||||
self._warn_unhandled_updated_settings(changed)
|
self._warn_unhandled_updated_settings(changed.keys() - self._CONFIGURE_FIELDS)
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
@@ -629,6 +660,14 @@ class DeepgramFluxSTTService(WebsocketSTTService):
|
|||||||
await self._handle_fatal_error(data)
|
await self._handle_fatal_error(data)
|
||||||
case FluxMessageType.TURN_INFO:
|
case FluxMessageType.TURN_INFO:
|
||||||
await self._handle_turn_info(data)
|
await self._handle_turn_info(data)
|
||||||
|
case FluxMessageType.CONFIGURE_SUCCESS:
|
||||||
|
logger.info(f"{self}: Configure accepted: {data}")
|
||||||
|
case FluxMessageType.CONFIGURE_FAILURE:
|
||||||
|
error_code = data.get("error_code", "unknown")
|
||||||
|
description = data.get("description", "no description")
|
||||||
|
error_msg = f"Configure rejected: [{error_code}] {description}"
|
||||||
|
logger.warning(f"{self}: {error_msg}")
|
||||||
|
await self.push_error(error_msg=error_msg)
|
||||||
|
|
||||||
async def _handle_connection_established(self):
|
async def _handle_connection_established(self):
|
||||||
"""Handle successful connection establishment to Deepgram Flux.
|
"""Handle successful connection establishment to Deepgram Flux.
|
||||||
|
|||||||
Reference in New Issue
Block a user