fix: vobiz pipeline cleanup

This commit is contained in:
Sabiha Khan 2025-12-25 15:19:35 +05:30
parent e83f3a36d2
commit 182d8a27db
5 changed files with 233 additions and 2 deletions

View file

@ -11,6 +11,7 @@ from loguru import logger
from api.enums import WorkflowRunMode
from api.services.telephony.base import CallInitiationResult, TelephonyProvider
from api.services.telephony.vobiz_api_client import VobizApiClient
from api.utils.tunnel import TunnelURLProvider
if TYPE_CHECKING:
@ -45,6 +46,9 @@ class VobizProvider(TelephonyProvider):
self.from_numbers = [self.from_numbers]
self.base_url = "https://api.vobiz.ai/api"
# API client for centralized api calls
self._api_client = VobizApiClient(self.auth_id, self.auth_token) if self.validate_config() else None
async def initiate_call(
self,
@ -138,7 +142,7 @@ class VobizProvider(TelephonyProvider):
return CallInitiationResult(
call_id=call_id,
status="queued", # Vobiz returns "message": "call fired"
provider_metadata={},
provider_metadata={"call_id": call_id},
raw_response=response_data,
)
@ -267,6 +271,20 @@ class VobizProvider(TelephonyProvider):
logger.error(f"Exception fetching Vobiz call cost: {e}")
return {"cost_usd": 0.0, "duration": 0, "status": "error", "error": str(e)}
async def stop_audio_stream(self, call_id: str, stream_id: Optional[str] = None) -> Dict[str, Any]:
"""Stop Vobiz audio stream(s) using the centralized API client."""
if not self._api_client:
return {"success": False, "error": "Vobiz api client missing in provider implementation"}
return await self._api_client.stop_audio_stream(call_id, stream_id)
async def hangup_call(self, call_id: str) -> Dict[str, Any]:
"""Hangup Vobiz call using the centralized API client."""
if not self._api_client:
return {"success": False, "error": "Vobiz api client missing in provider implementation"}
return await self._api_client.hangup_call(call_id)
def parse_status_callback(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Parse Vobiz status callback data into generic format.
@ -338,4 +356,5 @@ class VobizProvider(TelephonyProvider):
logger.error(
f"[run {workflow_run_id}] Error in Vobiz WebSocket handler: {e}"
)
raise

View file

@ -0,0 +1,117 @@
"""
Vobiz API client for call and stream management.
"""
import aiohttp
from typing import Dict, Any, Optional
class VobizApiClient:
"""Centralized client for Vobiz API operations."""
def __init__(self, auth_id: str, auth_token: str):
"""Initialize the Vobiz API client.
Args:
auth_id: Vobiz Account ID
auth_token: Vobiz Auth Token
"""
self.auth_id = auth_id
self.auth_token = auth_token
self.base_url = "https://api.vobiz.ai/api"
@property
def headers(self) -> Dict[str, str]:
"""Get authentication headers for Vobiz API."""
return {
"X-Auth-ID": self.auth_id,
"X-Auth-Token": self.auth_token,
}
async def stop_audio_stream(self, call_id: str, stream_id: Optional[str] = None) -> Dict[str, Any]:
"""Stop Vobiz audio stream(s) for a call.
Args:
call_id: The Vobiz call_uuid
stream_id: Optional specific stream ID. If teh stream ID is not available, stops all streams for the call.
Returns:
Dict containing the API response
"""
if stream_id:
# Stop specific stream
endpoint = f"{self.base_url}/v1/Account/{self.auth_id}/Call/{call_id}/Stream/{stream_id}/"
else:
# Stop all streams for the call
endpoint = f"{self.base_url}/v1/Account/{self.auth_id}/Call/{call_id}/Stream/"
try:
async with aiohttp.ClientSession() as session:
async with session.delete(endpoint, headers=self.headers) as response:
response_text = await response.text()
response_data = {}
try:
response_data = await response.json() if response_text else {}
except:
# If JSON parsing fails, include raw text
response_data = {"raw_text": response_text}
return {
"status_code": response.status,
"response_body": response_data,
"raw_text": response_text
}
except Exception as e:
return {"exception": str(e)}
async def hangup_call(self, call_id: str) -> Dict[str, Any]:
"""Hang up a Vobiz call.
Args:
call_id: The Vobiz call_uuid
Returns:
Dict containing the API response
"""
endpoint = f"{self.base_url}/v1/Account/{self.auth_id}/Call/{call_id}/"
try:
async with aiohttp.ClientSession() as session:
async with session.delete(endpoint, headers=self.headers) as response:
response_text = await response.text()
response_data = {}
try:
response_data = await response.json() if response_text else {}
except:
# If JSON parsing fails, include raw text
response_data = {"raw_text": response_text}
return {
"status_code": response.status,
"response_body": response_data,
"raw_text": response_text
}
except Exception as e:
return {"exception": str(e)}
async def stop_streams_and_hangup(self, call_id: str, stream_id: Optional[str] = None) -> tuple[Dict[str, Any], Dict[str, Any]]:
"""Stop audio streams and hang up call in the same order.
Args:
call_id: The Vobiz call_uuid
stream_id: Optional specific stream ID. If None, stops all streams for the call.
Returns:
Tuple of (stream_result, hangup_result)
"""
# Step 1: Stop audio streams
stream_result = await self.stop_audio_stream(call_id, stream_id)
# Step 2: Hang up the call
hangup_result = await self.hangup_call(call_id)
return stream_result, hangup_result