1use crate::types::{FinishReason, TokenUsage, ToolDescriptor};
14
15#[derive(Debug, Clone)]
19pub struct RunStartInfo<'a> {
20 pub session_id: &'a str,
21 pub model: &'a str,
22}
23
24#[derive(Debug, Clone)]
26pub struct RunEndInfo<'a> {
27 pub session_id: &'a str,
28 pub finish_reason: FinishReason,
29 pub usage: &'a TokenUsage,
30}
31
32#[derive(Debug, Clone)]
34pub struct RunErrorInfo<'a> {
35 pub session_id: &'a str,
36 pub error: &'a str,
37}
38
39#[derive(Debug, Clone)]
41pub struct ModelRequestInfo<'a> {
42 pub session_id: &'a str,
43 pub step: u32,
44 pub model: &'a str,
45}
46
47#[derive(Debug, Clone)]
49pub struct ModelResponseInfo<'a> {
50 pub session_id: &'a str,
51 pub step: u32,
52 pub model: &'a str,
53 pub usage: &'a TokenUsage,
54}
55
56#[derive(Debug, Clone)]
58pub struct ModelErrorInfo<'a> {
59 pub session_id: &'a str,
60 pub step: u32,
61 pub model: &'a str,
62 pub error: &'a str,
63}
64
65#[derive(Debug, Clone)]
67pub struct ToolCallInfo<'a> {
68 pub session_id: &'a str,
69 pub step: u32,
70 pub tool: &'a str,
71}
72
73#[derive(Debug, Clone)]
75pub struct ToolEndInfo<'a> {
76 pub session_id: &'a str,
77 pub step: u32,
78 pub tool: &'a str,
79 pub is_error: bool,
80}
81
82#[derive(Debug, Clone)]
84pub struct ToolErrorInfo<'a> {
85 pub session_id: &'a str,
86 pub step: u32,
87 pub tool: &'a str,
88 pub error: &'a str,
89}
90
91#[derive(Debug, Clone)]
93pub struct ToolDiscoveredInfo<'a> {
94 pub tools: &'a [ToolDescriptor],
95}
96
97#[derive(Debug, Clone)]
99pub struct OutputValidationErrorInfo<'a> {
100 pub session_id: &'a str,
101 pub error: &'a str,
102}
103
104pub trait Instrumenter: Send + Sync {
110 fn on_run_start(&self, _info: &RunStartInfo<'_>) {}
112 fn on_run_end(&self, _info: &RunEndInfo<'_>) {}
114 fn on_run_error(&self, _info: &RunErrorInfo<'_>) {}
116 fn on_model_request(&self, _info: &ModelRequestInfo<'_>) {}
118 fn on_model_response(&self, _info: &ModelResponseInfo<'_>) {}
120 fn on_model_error(&self, _info: &ModelErrorInfo<'_>) {}
122 fn on_tool_call(&self, _info: &ToolCallInfo<'_>) {}
124 fn on_tool_end(&self, _info: &ToolEndInfo<'_>) {}
126 fn on_tool_error(&self, _info: &ToolErrorInfo<'_>) {}
128 fn on_tool_discovered(&self, _info: &ToolDiscoveredInfo<'_>) {}
130 fn on_output_validation_error(&self, _info: &OutputValidationErrorInfo<'_>) {}
132}
133
134#[derive(Debug, Clone, Copy, Default)]
138pub struct NoopInstrumenter;
139
140impl Instrumenter for NoopInstrumenter {}
141
142#[cfg(test)]
143mod tests {
144 use std::sync::{Arc, Mutex};
145
146 use super::*;
147
148 #[derive(Debug, Default)]
150 struct RecordingInstrumenter {
151 calls: Mutex<Vec<String>>,
152 }
153
154 impl Instrumenter for RecordingInstrumenter {
155 fn on_run_start(&self, _info: &RunStartInfo<'_>) {
156 self.calls.lock().unwrap_or_else(|p| p.into_inner()).push("run_start".into());
157 }
158 fn on_run_end(&self, _info: &RunEndInfo<'_>) {
159 self.calls.lock().unwrap_or_else(|p| p.into_inner()).push("run_end".into());
160 }
161 fn on_model_request(&self, _info: &ModelRequestInfo<'_>) {
162 self.calls.lock().unwrap_or_else(|p| p.into_inner()).push("model_request".into());
163 }
164 fn on_tool_call(&self, _info: &ToolCallInfo<'_>) {
165 self.calls.lock().unwrap_or_else(|p| p.into_inner()).push("tool_call".into());
166 }
167 }
168
169 #[test]
170 fn noop_instrumenter_is_default() {
171 let inst = NoopInstrumenter;
172 inst.on_run_start(&RunStartInfo { session_id: "s1", model: "test" });
173 }
175
176 #[test]
177 fn recording_instrumenter_tracks_calls() {
178 let inst = RecordingInstrumenter::default();
179 inst.on_run_start(&RunStartInfo { session_id: "s1", model: "test" });
180 inst.on_model_request(&ModelRequestInfo { session_id: "s1", step: 1, model: "test" });
181 inst.on_run_end(&RunEndInfo {
182 session_id: "s1",
183 finish_reason: FinishReason::Stop,
184 usage: &TokenUsage { prompt_tokens: 10, completion_tokens: 20 },
185 });
186
187 let calls = inst.calls.lock().unwrap_or_else(|p| p.into_inner());
188 assert_eq!(&*calls, &["run_start", "model_request", "run_end"]);
189 }
190
191 #[test]
192 fn instrumenter_is_object_safe() {
193 let _inst: Arc<dyn Instrumenter> = Arc::new(NoopInstrumenter);
194 }
195}