Module agents.metrics.analytics
Classes
class AnalyticsClient (session_id: str | None = None)-
Expand source code
class AnalyticsClient: """Client for sending analytics data to external endpoints""" _instance: Optional["AnalyticsClient"] = None _initialized: bool = False def __new__(cls, *args, **kwargs) -> "AnalyticsClient": if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self, session_id: Optional[str] = None): if self._initialized: return self.session_id = session_id self.base_url = "https://api.videosdk.live" self._initialized = True self.turn_count = 0 self.metrics_options = None def configure(self, metrics_options: Any, signaling_base_url: Optional[str] = None) -> None: """Configure analytics client with metrics options and signaling base URL""" self.metrics_options = metrics_options self.turn_count = 0 if signaling_base_url: host = signaling_base_url.rstrip("/") if not host.startswith(("http://", "https://")): host = f"https://{host}" self.base_url = host def set_session_id(self, session_id: str) -> None: """Set the session ID for analytics tracking""" self.session_id = session_id async def send_interaction_analytics( self, interaction_data: Dict[str, Any] ) -> None: """Route analytics to the appropriate endpoint(s)""" self.turn_count += 1 metrics_enabled = getattr(self.metrics_options, "enabled", True) if self.metrics_options else True custom_url = getattr(self.metrics_options, "export_url", None) if self.metrics_options else None custom_headers = getattr(self.metrics_options, "export_headers", None) if self.metrics_options else None tasks = [] if self.turn_count == 1: tasks.append(self._send_to_default_endpoint(interaction_data)) if metrics_enabled: if custom_url: tasks.append(self._send_to_custom_endpoint(custom_url, custom_headers, interaction_data)) elif self.turn_count > 1: tasks.append(self._send_to_default_endpoint(interaction_data)) if tasks: await asyncio.gather(*tasks, return_exceptions=True) async def _send_to_default_endpoint(self, interaction_data: Dict[str, Any]) -> None: """Send turn analytics to the default VideoSDK API endpoint""" session_id_from_payload = interaction_data.get("sessionId") current_session_id = self.session_id or session_id_from_payload data = {"data": [interaction_data]} if not current_session_id: logger.error("Failed sending session data : No session ID") return auth_token = os.getenv("VIDEOSDK_AUTH_TOKEN") if not auth_token: logger.error("Failed sending session data : No auth token") return url = f"{self.base_url}/v2/sessions/{current_session_id}/agent-analytics" headers = {"Authorization": f"{auth_token}", "Content-Type": "application/json"} await self._execute_request(url, headers, data, "default endpoint") async def _send_to_custom_endpoint( self, custom_url: str, custom_headers: Optional[Dict[str, str]], interaction_data: Dict[str, Any] ) -> None: """Send turn analytics to a custom endpoint""" data = {"data": [interaction_data]} headers = {"Content-Type": "application/json"} if custom_headers: headers.update(custom_headers) await self._execute_request(custom_url, headers, data, "custom endpoint") async def _execute_request(self, url: str, headers: Dict[str, str], data: Dict[str, Any], endpoint_name: str) -> None: """Execute the HTTP POST request to send analytics data""" try: async with aiohttp.ClientSession() as session: async with session.post(url, json=data, headers=headers) as response: if response.status == 200: logger.info(f"Analytics sent successfully to {endpoint_name}") else: response_text = await response.text() logger.error( f"Failed to send analytics to {endpoint_name}: HTTP {response.status}" ) logger.error(f"Response content: {response_text}") except Exception as e: logger.error(f"Error sending analytics to {endpoint_name}: {e}") def send_interaction_analytics_safe(self, interaction_data: Dict[str, Any]) -> None: """ Safely send turn analytics without blocking. Creates a task if event loop is running, otherwise ignores. """ try: asyncio.create_task(self.send_interaction_analytics(interaction_data)) pass except RuntimeError: passClient for sending analytics data to external endpoints
Methods
def configure(self, metrics_options: Any, signaling_base_url: str | None = None) ‑> None-
Expand source code
def configure(self, metrics_options: Any, signaling_base_url: Optional[str] = None) -> None: """Configure analytics client with metrics options and signaling base URL""" self.metrics_options = metrics_options self.turn_count = 0 if signaling_base_url: host = signaling_base_url.rstrip("/") if not host.startswith(("http://", "https://")): host = f"https://{host}" self.base_url = hostConfigure analytics client with metrics options and signaling base URL
async def send_interaction_analytics(self, interaction_data: Dict[str, Any]) ‑> None-
Expand source code
async def send_interaction_analytics( self, interaction_data: Dict[str, Any] ) -> None: """Route analytics to the appropriate endpoint(s)""" self.turn_count += 1 metrics_enabled = getattr(self.metrics_options, "enabled", True) if self.metrics_options else True custom_url = getattr(self.metrics_options, "export_url", None) if self.metrics_options else None custom_headers = getattr(self.metrics_options, "export_headers", None) if self.metrics_options else None tasks = [] if self.turn_count == 1: tasks.append(self._send_to_default_endpoint(interaction_data)) if metrics_enabled: if custom_url: tasks.append(self._send_to_custom_endpoint(custom_url, custom_headers, interaction_data)) elif self.turn_count > 1: tasks.append(self._send_to_default_endpoint(interaction_data)) if tasks: await asyncio.gather(*tasks, return_exceptions=True)Route analytics to the appropriate endpoint(s)
def send_interaction_analytics_safe(self, interaction_data: Dict[str, Any]) ‑> None-
Expand source code
def send_interaction_analytics_safe(self, interaction_data: Dict[str, Any]) -> None: """ Safely send turn analytics without blocking. Creates a task if event loop is running, otherwise ignores. """ try: asyncio.create_task(self.send_interaction_analytics(interaction_data)) pass except RuntimeError: passSafely send turn analytics without blocking. Creates a task if event loop is running, otherwise ignores.
def set_session_id(self, session_id: str) ‑> None-
Expand source code
def set_session_id(self, session_id: str) -> None: """Set the session ID for analytics tracking""" self.session_id = session_idSet the session ID for analytics tracking