toProductionJson method

Map<String, dynamic> toProductionJson()

Convert to JSON for Production (Railway) Matches C++ telemetry_json.cpp rac_telemetry_manager_payload_to_json()

Key differences from development:

  • Uses "id" (not "sdk_event_id")
  • Uses "timestamp" (not "event_timestamp")
  • Does NOT include modality or device_id at event level (they're at batch level)
  • All fields flattened (no 'properties' nesting)
  • No 'category' field (extra='forbid')

Implementation

Map<String, dynamic> toProductionJson() {
  final json = <String, dynamic>{
    // Required fields - match C++ exactly
    'id': id, // UUID format
    'timestamp': timestamp.toUtc().toIso8601String(),
    'event_type': type,
    'created_at': createdAt.toUtc().toIso8601String(),
    // NOTE: modality and device_id are at BATCH level in production, not here
  };

  // Helper to add non-null/non-zero values only (matches C++ add_string/add_int behavior)
  void addIfNotNull(String key, dynamic value) {
    if (value == null) return;
    // Skip zero values for integers (matches C++ add_int behavior)
    // But keep zero for doubles (confidence, real_time_factor, etc. can be 0.0)
    if (value is int && value == 0) return;
    json[key] = value;
  }

  // Helper to get value from properties
  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'));

  // Device info (included at event level in production per C++ telemetry_json.cpp:218-221)
  addIfNotNull('device', getValue('device'));
  addIfNotNull('os_version', getValue('os_version'));
  addIfNotNull('platform', getValue('platform'));
  addIfNotNull('sdk_version', getValue('sdk_version'));

  // 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 (match C++ telemetry_json.cpp:229-239)
  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('prompt_eval_time_ms', getValue('prompt_eval_time_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'));

  // STT fields (match C++ telemetry_json.cpp:242-248)
  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'));
  addIfNotNull('is_streaming', getValue('is_streaming'));
  addIfNotNull('segment_index', getValue('segment_index'));

  // TTS fields (match C++ telemetry_json.cpp:251-256)
  addIfNotNull('character_count', getValue('character_count', 'text_length'));
  addIfNotNull('characters_per_second', getValue('characters_per_second'));
  addIfNotNull('audio_size_bytes', getValue('audio_size_bytes'));
  addIfNotNull('sample_rate', getValue('sample_rate'));
  addIfNotNull('voice', getValue('voice', 'voice_id'));
  // TTS stores audio_duration_ms but backend expects output_duration_ms
  addIfNotNull('output_duration_ms', getValue('output_duration_ms', 'audio_duration_ms'));

  // Model lifecycle (match C++ telemetry_json.cpp:259-260)
  addIfNotNull('model_size_bytes', getValue('model_size_bytes'));
  addIfNotNull('archive_type', getValue('archive_type'));

  // VAD (match C++ telemetry_json.cpp:263)
  addIfNotNull('speech_duration_ms', getValue('speech_duration_ms'));

  // SDK lifecycle (match C++ telemetry_json.cpp:266)
  addIfNotNull('count', getValue('count'));

  // Storage (match C++ telemetry_json.cpp:269)
  addIfNotNull('freed_bytes', getValue('freed_bytes'));

  // Network (match C++ telemetry_json.cpp:272)
  addIfNotNull('is_online', getValue('is_online'));

  return json;
}