use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ToolName {
Search,
Recall,
Embed,
GraphQuery,
LlmComplete,
LlmStream,
ConfigLoad,
SecretRead,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum FeatureName {
SmartRecall,
GraphTraversal,
HybridSearch,
TokenEstimation,
SafetyClassification,
OutputSanitization,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ProviderCategory {
CloudFirstParty,
CloudThirdParty,
Local,
Proxy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TelemetryEvent {
ToolInvoked {
tool: ToolName,
},
ToolCompleted {
tool: ToolName,
duration_ms: u64,
success: bool,
},
SessionStarted {
session_id: String,
},
SessionEnded {
session_id: String,
turns: u32,
tokens_used: u64,
},
Error {
class: FailureClass,
},
FeatureUsed {
feature: FeatureName,
},
ProviderRouted {
category: ProviderCategory,
},
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum FailureClass {
Network,
Auth,
RateLimit,
Validation,
Storage,
Timeout,
Internal,
Unknown,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn event_serializes_no_pii() {
let event = TelemetryEvent::ToolInvoked {
tool: ToolName::Search,
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("Search"));
assert!(!json.contains("password"));
assert!(!json.contains("email"));
}
#[test]
fn failure_class_roundtrip() {
let class = FailureClass::RateLimit;
let json = serde_json::to_string(&class).unwrap();
let back: FailureClass = serde_json::from_str(&json).unwrap();
assert_eq!(back, FailureClass::RateLimit);
}
#[test]
fn session_event() {
let event = TelemetryEvent::SessionStarted {
session_id: "abc-123".into(),
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("abc-123"));
}
}