toSupabaseJson method

Map<String, dynamic> toSupabaseJson({
  1. required String deviceId,
  2. required String sdkVersion,
  3. required String platform,
})

Convert to JSON for Supabase (development) Uses column names expected by Supabase telemetry_events table Schema matches C++ telemetry_json.cpp rac_telemetry_manager_payload_to_json()

Only includes non-null values to match C++ behavior (add_string skips null). Events are sent one at a time, so each can have different keys.

Implementation

Map<String, dynamic> toSupabaseJson({
  required String deviceId,
  required String sdkVersion,
  required String platform,
}) {
  final json = <String, dynamic>{
    // Required fields (Supabase-specific key names)
    'sdk_event_id': id,
    'event_type': type,
    'event_timestamp': timestamp.toUtc().toIso8601String(),
    'created_at': createdAt.toUtc().toIso8601String(),
    // Development-only fields
    'modality': category.value,
    'device_id': deviceId,
    // Device info
    'platform': platform,
    'sdk_version': sdkVersion,
  };

  // Helper to add non-null values only (matches C++ add_string/add_int behavior)
  void addIfNotNull(String supabaseKey, dynamic value) {
    if (value != null) {
      json[supabaseKey] = value;
    }
  }

  // Helper to get value from properties with fallback key
  dynamic getValue(String key, [String? fallbackKey]) {
    return properties[key] ?? (fallbackKey != null ? properties[fallbackKey] : null);
  }

  // Session tracking
  addIfNotNull('session_id', getValue('session_id'));

  // Model info
  addIfNotNull('model_id', getValue('model_id'));
  addIfNotNull('model_name', getValue('model_name'));
  addIfNotNull('framework', getValue('framework'));

  // Common metrics
  addIfNotNull(
      'processing_time_ms',
      getValue('processing_time_ms') ??
          getValue('load_time_ms') ??
          getValue('latency_ms') ??
          getValue('download_time_ms'));
  addIfNotNull('success', getValue('success'));
  addIfNotNull('error_message', getValue('error_message'));
  addIfNotNull('error_code', getValue('error_code'));

  // LLM fields
  addIfNotNull('input_tokens', getValue('input_tokens', 'prompt_tokens'));
  addIfNotNull('output_tokens', getValue('output_tokens', 'completion_tokens'));
  addIfNotNull('total_tokens', getValue('total_tokens'));
  addIfNotNull('tokens_per_second', getValue('tokens_per_second'));
  addIfNotNull('time_to_first_token_ms', getValue('time_to_first_token_ms'));
  addIfNotNull('generation_time_ms', getValue('generation_time_ms'));
  addIfNotNull('context_length', getValue('context_length'));
  addIfNotNull('temperature', getValue('temperature'));
  addIfNotNull('max_tokens', getValue('max_tokens'));
  addIfNotNull('is_streaming', getValue('is_streaming'));

  // STT fields
  addIfNotNull('audio_duration_ms', getValue('audio_duration_ms'));
  addIfNotNull('real_time_factor', getValue('real_time_factor'));
  addIfNotNull('word_count', getValue('word_count'));
  addIfNotNull('confidence', getValue('confidence'));
  addIfNotNull('language', getValue('language'));

  // TTS fields
  addIfNotNull('character_count', getValue('character_count', 'text_length'));
  addIfNotNull('voice', getValue('voice', 'voice_id'));
  addIfNotNull('sample_rate', getValue('sample_rate'));
  addIfNotNull('characters_per_second', getValue('characters_per_second'));
  // TTS uses output_duration_ms in Supabase (same as audio_duration_ms)
  addIfNotNull('output_duration_ms', getValue('audio_duration_ms'));
  addIfNotNull('audio_size_bytes', getValue('audio_size_bytes'));

  return json;
}