From 7b777219645b73ea28d778932e0d8f3b8cc963f1 Mon Sep 17 00:00:00 2001 From: Sabiha Khan <87858386+chewwbaka@users.noreply.github.com> Date: Fri, 6 Mar 2026 13:31:43 +0530 Subject: [PATCH] fix: add cloudonix call hangup strategy (#181) * fix: add cloudonix call hangup strategy * chore: upgrade pipecat --- api/services/pipecat/transport_setup.py | 3 + .../providers/cloudonix_call_strategies.py | 76 +++++++++++++++++++ pipecat | 2 +- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 api/services/telephony/providers/cloudonix_call_strategies.py diff --git a/api/services/pipecat/transport_setup.py b/api/services/pipecat/transport_setup.py index c7b56ff..77023ef 100644 --- a/api/services/pipecat/transport_setup.py +++ b/api/services/pipecat/transport_setup.py @@ -10,6 +10,7 @@ from api.services.telephony.providers.ari_call_strategies import ( ARIBridgeSwapStrategy, ARIHangupStrategy, ) +from api.services.telephony.providers.cloudonix_call_strategies import CloudonixHangupStrategy from api.services.telephony.providers.twilio_call_strategies import ( TwilioConferenceStrategy, TwilioHangupStrategy, @@ -131,11 +132,13 @@ async def create_cloudonix_transport( from pipecat.serializers.cloudonix import CloudonixFrameSerializer + hangup_strategy = CloudonixHangupStrategy() serializer = CloudonixFrameSerializer( call_id=call_id, stream_sid=stream_sid, domain_id=domain_id, bearer_token=bearer_token, + hangup_strategy=hangup_strategy, ) return FastAPIWebsocketTransport( diff --git a/api/services/telephony/providers/cloudonix_call_strategies.py b/api/services/telephony/providers/cloudonix_call_strategies.py new file mode 100644 index 0000000..86a2c9a --- /dev/null +++ b/api/services/telephony/providers/cloudonix_call_strategies.py @@ -0,0 +1,76 @@ +"""Cloudonix-specific call operation strategies.""" + +from typing import Any, Dict + +from loguru import logger + +from pipecat.serializers.call_strategies import HangupStrategy + + +class CloudonixHangupStrategy(HangupStrategy): + """Implements hangup for Cloudonix calls.""" + + async def execute_hangup(self, context: Dict[str, Any]) -> bool: + """Terminate a Cloudonix session via REST API. + + Note: CloudonixFrameSerializer inherits TwilioFrameSerializer and maps + Cloudonix params to Twilio-compatible keys when building the context: + call_id -> call_sid + domain_id -> account_sid + bearer_token -> auth_token + """ + try: + import aiohttp + + call_id = context.get("call_sid") or context.get("call_id") + domain_id = context.get("account_sid") or context.get("domain_id") + bearer_token = context.get("auth_token") or context.get("bearer_token") + + if not call_id or not domain_id or not bearer_token: + missing = [ + k + for k, v in { + "call_id": call_id, + "domain_id": domain_id, + "bearer_token": bearer_token, + }.items() + if not v + ] + logger.warning( + f"Cannot hang up Cloudonix call: missing required parameters: {', '.join(missing)}" + ) + return False + + endpoint = f"https://api.cloudonix.io/customers/self/domains/{domain_id}/sessions/{call_id}" + headers = { + "Authorization": f"Bearer {bearer_token}", + "Content-Type": "application/json", + } + + logger.info(f"Terminating Cloudonix call {call_id} via DELETE {endpoint}") + + async with aiohttp.ClientSession() as session: + async with session.delete(endpoint, headers=headers) as response: + status = response.status + response_text = await response.text() + + if status in (200, 204, 404): + logger.info( + f"Successfully terminated Cloudonix session {call_id} " + f"(HTTP {status})" + ) + return True + else: + logger.warning( + f"Unexpected response terminating Cloudonix session {call_id}: " + f"HTTP {status}, Response: {response_text}" + ) + return False + + except Exception as e: + logger.error( + f"Error terminating Cloudonix call " + f"{context.get('call_sid') or context.get('call_id')}: {e}", + exc_info=True, + ) + return False diff --git a/pipecat b/pipecat index 26e335f..a1d3906 160000 --- a/pipecat +++ b/pipecat @@ -1 +1 @@ -Subproject commit 26e335fbfdf792d54ad37da414998762f111e231 +Subproject commit a1d39061b2829a66d1bdd03b6e0525746d382057