toProductionJson method
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;
}