1#[cfg(feature = "telemetry")]
6use rig_tap::{EventKind, emit_kind};
7
8#[cfg(feature = "telemetry")]
9tokio::task_local! {
10 pub static CURRENT_SESSION: String;
12}
13
14pub async fn with_session<F, Fut>(session_id: String, f: F) -> Fut::Output
16where
17 F: FnOnce() -> Fut,
18 Fut: std::future::Future,
19{
20 #[cfg(feature = "telemetry")]
21 {
22 CURRENT_SESSION.scope(session_id, f()).await
23 }
24 #[cfg(not(feature = "telemetry"))]
25 {
26 let _ = session_id;
27 f().await
28 }
29}
30
31pub fn current_session() -> String {
33 #[cfg(feature = "telemetry")]
34 {
35 CURRENT_SESSION
36 .try_with(|s| s.clone())
37 .unwrap_or_else(|_| "unknown".to_string())
38 }
39 #[cfg(not(feature = "telemetry"))]
40 {
41 "unknown".to_string()
42 }
43}
44
45#[cfg_attr(not(feature = "telemetry"), allow(unused_variables))]
56pub fn record_prompt(
57 model: &str,
58 tokens_in: u64,
59 tokens_out: u64,
60 cached_in: u64,
61 reasoning: u64,
62 duration_ms: u64,
63) {
64 #[cfg(feature = "telemetry")]
65 emit_kind(
66 current_session(),
67 EventKind::PromptCompleted {
68 model: model.to_string(),
69 tokens_in: Some(tokens_in),
70 tokens_out: Some(tokens_out),
71 cached_tokens_in: Some(cached_in),
72 reasoning_tokens: Some(reasoning),
73 cost_usd: None,
74 finish_reason: None,
75 response_id: None,
76 previous_response_id: None,
77 time_to_first_token_ms: None,
78 duration_ms: Some(duration_ms),
79 },
80 );
81}
82
83pub async fn record_tool_call<F, Fut, T, E>(
85 #[allow(unused_variables)] tool_name: &'static str,
86 #[allow(unused_variables)] args: &str,
87 f: F,
88) -> Result<T, E>
89where
90 F: FnOnce() -> Fut,
91 Fut: std::future::Future<Output = Result<T, E>>,
92 T: serde::Serialize,
93 E: std::fmt::Display,
94{
95 #[cfg(feature = "telemetry")]
96 let start = std::time::Instant::now();
97 #[cfg(feature = "telemetry")]
98 let call_id = format!("{tool_name}-{}", uuid::Uuid::new_v4());
99 #[cfg(feature = "telemetry")]
100 let session = current_session();
101
102 #[cfg(feature = "telemetry")]
103 emit_kind(
104 &session,
105 EventKind::ToolInvoked {
106 tool_name: tool_name.to_string(),
107 provider_call_id: None,
108 call_id: call_id.clone(),
109 args_json: args.to_string(),
110 truncated: false,
111 },
112 );
113
114 let res = f().await;
115
116 #[cfg(feature = "telemetry")]
117 match &res {
118 Ok(val) => {
119 let result_str = serde_json::to_string(val).unwrap_or_else(|_| "{}".to_string());
120 emit_kind(
121 &session,
122 EventKind::ToolCompleted {
123 tool_name: tool_name.to_string(),
124 provider_call_id: None,
125 call_id,
126 result: result_str,
127 truncated: false,
128 duration_ms: Some(start.elapsed().as_millis() as u64),
129 },
130 );
131 }
132 Err(err) => {
133 emit_kind(
134 &session,
135 EventKind::ToolFailed {
136 tool_name: tool_name.to_string(),
137 call_id,
138 error_class: rig_tap::ErrorClass::Unknown,
139 message: err.to_string(),
140 },
141 );
142 }
143 }
144
145 res
146}