use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum Origin {
Direct,
Agent,
Baseline,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ChurnKind {
Add,
Remove,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EmbedderLoadStatus {
Ok,
Slow,
Failed,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchHitTrace {
pub tool_id: String,
pub score: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchStage {
pub name: String,
pub took_ms: u64,
pub top_score: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SkillHitTrace {
pub skill_id: String,
pub score: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FactHitTrace {
pub fact_id: String,
pub score: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FactInjectReason {
Never,
Evicted,
Mutated,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum TraceEvent {
Search {
query: String,
origin: Origin,
top_k: u32,
hits: Vec<SearchHitTrace>,
stages: Vec<SearchStage>,
took_ms: u64,
},
IndexChurn {
kind: ChurnKind,
tool_id: String,
},
SkillSearch {
query: String,
origin: Origin,
top_k: u32,
hits: Vec<SkillHitTrace>,
stages: Vec<SearchStage>,
took_ms: u64,
},
SkillChurn {
kind: ChurnKind,
skill_id: String,
},
SkillInvoke {
skill_id: String,
took_ms: u64,
},
FactSearch {
query: String,
origin: Origin,
top_k: u32,
hits: Vec<FactHitTrace>,
stages: Vec<SearchStage>,
took_ms: u64,
},
FactChurn {
kind: ChurnKind,
fact_id: String,
},
FactInject {
fact_id: String,
reason: FactInjectReason,
},
FactInjectSkip {
fact_id: String,
},
FactSnapshot {
fact_id: String,
},
InvokeStart {
tool_id: String,
args_size_bytes: u64,
},
InvokeEnd {
tool_id: String,
took_ms: u64,
},
InvokeError {
tool_id: String,
took_ms: u64,
error: String,
},
GatewaySearch {
query: String,
origin: Origin,
top_k: u32,
hits: u32,
took_ms: u64,
},
GatewayInvoke {
tool_id: String,
took_ms: u64,
},
GatewayError {
tool_id: String,
error: String,
},
UpstreamRegister {
server: String,
transport: String,
tool_count: u32,
},
UpstreamInvoke {
server: String,
tool_id: String,
took_ms: u64,
},
UpstreamError {
server: String,
tool_id: String,
error: String,
},
AuthRefresh {
upstream: String,
ok: bool,
},
AuthNeeds {
upstream: String,
},
AuthFlowStart {
upstream: String,
},
AuthFlowEnd {
upstream: String,
ok: bool,
},
EventsDropped {
dropped_count: u64,
reason: String,
window_start_ts: u64,
window_end_ts: u64,
},
EmbedderLoad {
model: String,
status: EmbedderLoadStatus,
took_ms: u64,
reason: Option<String>,
},
EmbedderDownload {
model: String,
bytes: u64,
},
EmbedderModelMismatch {
built: String,
active: String,
},
UsageModelMismatch {
built: String,
active: String,
dim_mismatch: bool,
},
UsageBoost {
intent: Option<String>,
similarity: f64,
support: u32,
promoted: u32,
#[serde(default)]
dropped: u32,
},
EmbedderPoolingAssumed {
model: String,
pooling: String,
},
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TraceEventContext {
pub event_id: Option<String>,
pub invocation_id: Option<String>,
pub catalog_version: Option<String>,
pub environment: Option<String>,
pub end_user_id: Option<String>,
pub trace_id: Option<String>,
pub span_id: Option<String>,
}
impl TraceEventContext {
pub fn new_invocation() -> Self {
Self {
invocation_id: Some(ulid::Ulid::new().to_string()),
..Self::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TraceEnvelope {
pub v: u32,
#[serde(default)]
pub event_id: String,
pub ts: u64,
pub session_id: String,
#[serde(default)]
pub source_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub invocation_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub catalog_version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trace_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub span_id: Option<String>,
#[serde(flatten)]
pub event: TraceEvent,
}