hirn_engine/observability/
diagnostics.rs1use std::fmt;
8use std::time::Duration;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct QueryId(ulid::Ulid);
13
14impl QueryId {
15 pub fn new() -> Self {
17 Self(ulid::Ulid::new())
18 }
19}
20
21impl fmt::Display for QueryId {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(f, "{}", self.0)
24 }
25}
26
27impl Default for QueryId {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33#[derive(Debug, Clone, Default)]
35pub struct QueryDiagnostics {
36 pub query_id: Option<QueryId>,
38 pub authorize_us: Option<u64>,
40 pub embed_ms: Option<f64>,
42 pub optimize_ms: Option<f64>,
44 pub physical_plan_ms: Option<f64>,
46 pub execute_plan_ms: Option<f64>,
48 pub vector_search_ms: Option<f64>,
50 pub graph_expand_ms: Option<f64>,
52 pub rerank_ms: Option<f64>,
54 pub neural_rerank_ms: Option<f64>,
56 pub decode_ms: Option<f64>,
60 pub assemble_ms: Option<f64>,
63 pub total_ms: Option<f64>,
65 pub records_scanned: Option<usize>,
67 pub records_returned: Option<usize>,
69 pub threshold_filtered_count: Option<usize>,
71 pub competitive_inhibition_count: Option<usize>,
73 pub truncated_by_limit_count: Option<usize>,
75 pub raw_text_redacted_results: Option<usize>,
77 pub multivector_fallback_count: Option<usize>,
79 pub neural_rerank_fallback_count: Option<usize>,
81}
82
83impl QueryDiagnostics {
84 #[must_use]
85 pub fn advanced_retrieval_fallback_summary(&self) -> Option<String> {
86 let mut parts = Vec::new();
87
88 if let Some(count) = self.multivector_fallback_count.filter(|count| *count > 0) {
89 parts.push(format!("multivector_fallback_count={count}"));
90 }
91 if let Some(count) = self.neural_rerank_fallback_count.filter(|count| *count > 0) {
92 parts.push(format!("neural_rerank_fallback_count={count}"));
93 }
94
95 (!parts.is_empty()).then(|| parts.join(", "))
96 }
97}
98
99impl fmt::Display for QueryDiagnostics {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 if let Some(id) = &self.query_id {
102 writeln!(f, "Query ID: {id}")?;
103 }
104 writeln!(f, "Stage Timings:")?;
105 if let Some(v) = self.authorize_us {
106 writeln!(f, " authorize: {:.3}ms", v as f64 / 1000.0)?;
107 }
108 if let Some(v) = self.embed_ms {
109 writeln!(f, " embed_query: {v:.3}ms")?;
110 }
111 if let Some(v) = self.optimize_ms {
112 writeln!(f, " optimize: {v:.3}ms")?;
113 }
114 if let Some(v) = self.physical_plan_ms {
115 writeln!(f, " physical_plan: {v:.3}ms")?;
116 }
117 if let Some(v) = self.execute_plan_ms {
118 writeln!(f, " execute_plan: {v:.3}ms")?;
119 }
120 if let Some(v) = self.vector_search_ms {
121 writeln!(f, " vector_search: {v:.3}ms")?;
122 }
123 if let Some(v) = self.graph_expand_ms {
124 writeln!(f, " graph_expand: {v:.3}ms")?;
125 }
126 if let Some(v) = self.rerank_ms {
127 writeln!(f, " rerank: {v:.3}ms")?;
128 }
129 if let Some(v) = self.neural_rerank_ms {
130 writeln!(f, " neural_rerank: {v:.3}ms")?;
131 }
132 if let Some(v) = self.decode_ms {
133 writeln!(f, " decode: {v:.3}ms")?;
134 }
135 if let Some(v) = self.assemble_ms {
136 writeln!(f, " assemble: {v:.3}ms")?;
137 }
138 if let Some(v) = self.total_ms {
139 writeln!(f, " total: {v:.3}ms")?;
140 }
141 if self.records_scanned.is_some() || self.records_returned.is_some() {
142 writeln!(f, "Row Counts:")?;
143 if let Some(v) = self.records_scanned {
144 writeln!(f, " scanned: {v}")?;
145 }
146 if let Some(v) = self.records_returned {
147 writeln!(f, " returned: {v}")?;
148 }
149 }
150 if self.threshold_filtered_count.is_some()
151 || self.competitive_inhibition_count.is_some()
152 || self.truncated_by_limit_count.is_some()
153 || self.raw_text_redacted_results.is_some()
154 {
155 writeln!(f, "Suppression:")?;
156 if let Some(v) = self.threshold_filtered_count {
157 writeln!(f, " threshold_filtered: {v}")?;
158 }
159 if let Some(v) = self.competitive_inhibition_count {
160 writeln!(f, " competitively_inhibited: {v}")?;
161 }
162 if let Some(v) = self.truncated_by_limit_count {
163 writeln!(f, " truncated_by_limit: {v}")?;
164 }
165 if let Some(v) = self.raw_text_redacted_results {
166 writeln!(f, " raw_text_redacted: {v}")?;
167 }
168 }
169 if self.multivector_fallback_count.is_some() || self.neural_rerank_fallback_count.is_some()
170 {
171 writeln!(f, "Fallbacks:")?;
172 if let Some(v) = self.multivector_fallback_count {
173 writeln!(f, " multivector: {v}")?;
174 }
175 if let Some(v) = self.neural_rerank_fallback_count {
176 writeln!(f, " neural_rerank: {v}")?;
177 }
178 }
179 Ok(())
180 }
181}
182
183pub(crate) fn duration_ms(d: Duration) -> f64 {
185 d.as_secs_f64() * 1000.0
186}