/** * Performance tracking utilities for client-side metrics (React Native) * * This module provides functionality to track performance metrics and send them * to Datadog via the backend API for aggregation. */ import { Platform } from 'react-native' import DeviceInfo from 'react-native-device-info' import type { ApiClient } from '../hooks/use_api_client' const appName = DeviceInfo.getApplicationName() interface PerformanceMetric { name: string value: number unit: 'milliseconds' | 'seconds' | 'count' } interface PendingMessageMetric { idempotentKey: string startTime: number } const pendingMetrics = new Map() /** * Start tracking message creation performance */ export function startMessageCreationTracking(idempotentKey: string): void { if (!idempotentKey) return const startTime = Date.now() pendingMetrics.set(`message:${idempotentKey}`, { idempotentKey, startTime }) pendingMetrics.set(`conversation:${idempotentKey}`, { idempotentKey, startTime }) cleanupStaleMetrics() } /** * Complete message creation tracking when the message.created event is received */ export function completeMessageCreationTracking({ apiClient, idempotentKey, }: { apiClient: ApiClient idempotentKey: string }): void { if (!idempotentKey) return const duration = durationForIdempotentKey(`message:${idempotentKey}`) if (!duration) return sendPerformanceMetric(apiClient, { name: 'chat.message.creation.message_roundtrip_time', value: duration, unit: 'milliseconds', }) } /** * Complete message creation tracking when the conversation.updated event is received */ export function completeMessageCreationConversationTracking({ apiClient, idempotentKey, }: { apiClient: ApiClient idempotentKey: string }): void { if (!idempotentKey) return const duration = durationForIdempotentKey(`conversation:${idempotentKey}`) if (!duration) return sendPerformanceMetric(apiClient, { name: 'chat.message.creation.conversation_roundtrip_time', value: duration, unit: 'milliseconds', }) } function durationForIdempotentKey(idempotentKey: string): number | undefined { const metric = pendingMetrics.get(idempotentKey) if (!metric) return undefined pendingMetrics.delete(idempotentKey) const endTime = Date.now() return endTime - metric.startTime } /** * Send performance metric to backend for Datadog submission */ async function sendPerformanceMetric( apiClient: ApiClient, metric: PerformanceMetric ): Promise { if (__DEV__) return try { await apiClient.chat.post({ url: `/me/metrics`, data: { data: { type: 'Metric', attributes: { metric: { name: metric.name, value: metric.value, unit: metric.unit, platform: Platform.OS, app: appName, }, }, }, }, }) } catch (_e) { // Ignore errors } } /** * Clean up any stale metrics (older than 5 minutes) * This prevents memory leaks if messages fail to complete for any reason */ function cleanupStaleMetrics(): void { const now = Date.now() const staleThreshold = 5 * 60 * 1000 // 5 minutes in milliseconds for (const [key, metric] of pendingMetrics.entries()) { if (now - metric.startTime > staleThreshold) { pendingMetrics.delete(key) } } }