diff --git a/CHANGELOG.md b/CHANGELOG.md index fb76f2955..8b4b55c76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Using the universal `LLMContext` and associated `LLMContextAggregatorPair` is a pre-requisite for using `LLMSwitcher` to switch between LLMs at runtime. +### Changed + +- Restored `DailyTransport`'s native DTMF support using Daily's `send_dtmf()` + method instead of generated audio tones. + ### Fixed - Fixed a `AWSBedrockLLMService` crash caused by an extra `await`. diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index 5c07ed518..3830aa9c9 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -219,7 +219,34 @@ class BaseOutputTransport(FrameProcessor): pass async def write_dtmf(self, frame: OutputDTMFFrame | OutputDTMFUrgentFrame): - """Write a DTMF tone to the transport. + """Write a DTMF tone using the transport's preferred method. + + Args: + frame: The DTMF frame to write. + """ + if self._supports_native_dtmf(): + await self._write_dtmf_native(frame) + else: + await self._write_dtmf_audio(frame) + + def _supports_native_dtmf(self) -> bool: + """Override in transport implementations that support native DTMF. + + Returns: + True if the transport supports native DTMF, False otherwise. + """ + return False + + async def _write_dtmf_native(self, frame: OutputDTMFFrame | OutputDTMFUrgentFrame): + """Override in transport implementations for native DTMF. + + Args: + frame: The DTMF frame to write. + """ + raise NotImplementedError("Transport claims native DTMF support but doesn't implement it") + + async def _write_dtmf_audio(self, frame: OutputDTMFFrame | OutputDTMFUrgentFrame): + """Generate and send audio tones for DTMF. Args: frame: The DTMF frame to write. @@ -228,7 +255,6 @@ class BaseOutputTransport(FrameProcessor): dtmf_audio_frame = OutputAudioRawFrame( audio=dtmf_audio, sample_rate=self._sample_rate, num_channels=1 ) - dtmf_audio_frame.transport_destination = frame.transport_destination await self.write_audio_frame(dtmf_audio_frame) async def send_audio(self, frame: OutputAudioRawFrame): diff --git a/src/pipecat/transports/daily/transport.py b/src/pipecat/transports/daily/transport.py index 3a1a0b2fd..22bd4716d 100644 --- a/src/pipecat/transports/daily/transport.py +++ b/src/pipecat/transports/daily/transport.py @@ -1809,6 +1809,27 @@ class DailyOutputTransport(BaseOutputTransport): """ await self._client.write_video_frame(frame) + def _supports_native_dtmf(self) -> bool: + """Daily supports native DTMF via telephone events. + + Returns: + True, as Daily supports native DTMF transmission. + """ + return True + + async def _write_dtmf_native(self, frame): + """Use Daily's native send_dtmf method for telephone events. + + Args: + frame: The DTMF frame to write. + """ + await self._client.send_dtmf( + { + "sessionId": frame.transport_destination, + "tones": frame.button.value, + } + ) + class DailyTransport(BaseTransport): """Transport implementation for Daily audio and video calls.