From 803ea9d8bcfbfa6f18e3590328383cf2551979b1 Mon Sep 17 00:00:00 2001 From: Adrian Cowham Date: Thu, 27 Feb 2025 12:31:02 -0800 Subject: [PATCH] update the canonical client so that the audio recording is optional as long as there is a transcript --- examples/canonical-metrics/bot.py | 2 +- src/pipecat/services/canonical.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/examples/canonical-metrics/bot.py b/examples/canonical-metrics/bot.py index aa376c290..646636dc1 100644 --- a/examples/canonical-metrics/bot.py +++ b/examples/canonical-metrics/bot.py @@ -113,8 +113,8 @@ async def main(): llm, tts, transport.output(), - audio_buffer_processor, # captures audio into a buffer canonical, # uploads audio buffer to Canonical AI for metrics + audio_buffer_processor, # captures audio into a buffer context_aggregator.assistant(), ] ) diff --git a/src/pipecat/services/canonical.py b/src/pipecat/services/canonical.py index 1c268ab22..7b62273d1 100644 --- a/src/pipecat/services/canonical.py +++ b/src/pipecat/services/canonical.py @@ -62,17 +62,21 @@ class CanonicalMetricsService(AIService): self, *, aiohttp_session: aiohttp.ClientSession, - audio_buffer_processor: AudioBufferProcessor, call_id: str, assistant: str, api_key: str, api_url: str = "https://voiceapp.canonical.chat/api/v1", assistant_speaks_first: bool = True, output_dir: str = "recordings", + audio_buffer_processor: Optional[AudioBufferProcessor] = None, context: Optional[OpenAILLMContext] = None, **kwargs, ): super().__init__(**kwargs) + # Validate that at least one of audio_buffer_processor or context is provided + if audio_buffer_processor is None and context is None: + raise ValueError("At least one of audio_buffer_processor or context must be specified") + self._aiohttp_session = aiohttp_session self._audio_buffer_processor = audio_buffer_processor self._api_key = api_key @@ -85,16 +89,36 @@ class CanonicalMetricsService(AIService): async def stop(self, frame: EndFrame): await super().stop(frame) - await self._process_audio() + await self._process_completion() async def cancel(self, frame: CancelFrame): await super().cancel(frame) - await self._process_audio() + await self._process_completion() async def process_frame(self, frame: Frame, direction: FrameDirection): await super().process_frame(frame, direction) await self.push_frame(frame, direction) + async def _process_completion(self): + if self._audio_buffer_processor is not None: + await self._process_audio() + elif self._context is not None: + await self._process_transcript() + + async def _process_transcript(self): + params = { + "callId": self._call_id, + "assistant": {"id": self._assistant, "speaksFirst": self._assistant_speaks_first}, + "transcript": self._context.messages, + } + response = await self._aiohttp_session.post( + f"{self._api_url}/call", + headers=self._request_headers(), + json=params, + ) + if not response.ok: + logger.error(f"Failed to process transcript: {await response.text()}") + async def _process_audio(self): audio_buffer_processor = self._audio_buffer_processor