1use tracing::Span;
6
7pub fn agent_run_span(agent_name: &str, invocation_id: &str) -> Span {
21 tracing::info_span!(
22 "agent.run",
23 agent.name = agent_name,
24 invocation.id = invocation_id,
25 otel.kind = "internal"
26 )
27}
28
29pub fn model_call_span(model_name: &str) -> Span {
42 tracing::info_span!("model.call", model.name = model_name, otel.kind = "client")
43}
44
45pub fn llm_generate_span(provider: &str, model_name: &str, stream: bool) -> Span {
63 tracing::info_span!(
64 "gen_ai.generate",
65 gen_ai.system = %provider,
66 gen_ai.request.model = %model_name,
67 gen_ai.request.stream = stream,
68 gen_ai.usage.input_tokens = tracing::field::Empty,
69 gen_ai.usage.output_tokens = tracing::field::Empty,
70 gen_ai.usage.total_tokens = tracing::field::Empty,
71 gen_ai.usage.cache_read_tokens = tracing::field::Empty,
72 gen_ai.usage.cache_creation_tokens = tracing::field::Empty,
73 gen_ai.usage.thinking_tokens = tracing::field::Empty,
74 gen_ai.usage.audio_input_tokens = tracing::field::Empty,
75 gen_ai.usage.audio_output_tokens = tracing::field::Empty,
76 otel.kind = "client",
77 )
78}
79
80#[derive(Debug, Clone, Default)]
97pub struct LlmUsage {
98 pub input_tokens: i32,
100 pub output_tokens: i32,
102 pub total_tokens: i32,
104 pub cache_read_tokens: Option<i32>,
106 pub cache_creation_tokens: Option<i32>,
108 pub thinking_tokens: Option<i32>,
110 pub audio_input_tokens: Option<i32>,
112 pub audio_output_tokens: Option<i32>,
114}
115
116pub fn record_llm_usage(usage: &LlmUsage) {
138 let span = Span::current();
139 span.record("gen_ai.usage.input_tokens", i64::from(usage.input_tokens));
140 span.record("gen_ai.usage.output_tokens", i64::from(usage.output_tokens));
141 span.record("gen_ai.usage.total_tokens", i64::from(usage.total_tokens));
142 if let Some(v) = usage.cache_read_tokens {
143 span.record("gen_ai.usage.cache_read_tokens", i64::from(v));
144 }
145 if let Some(v) = usage.cache_creation_tokens {
146 span.record("gen_ai.usage.cache_creation_tokens", i64::from(v));
147 }
148 if let Some(v) = usage.thinking_tokens {
149 span.record("gen_ai.usage.thinking_tokens", i64::from(v));
150 }
151 if let Some(v) = usage.audio_input_tokens {
152 span.record("gen_ai.usage.audio_input_tokens", i64::from(v));
153 }
154 if let Some(v) = usage.audio_output_tokens {
155 span.record("gen_ai.usage.audio_output_tokens", i64::from(v));
156 }
157}
158
159pub fn tool_execute_span(tool_name: &str) -> Span {
172 tracing::info_span!("tool.execute", tool.name = tool_name, otel.kind = "internal")
173}
174
175pub fn team_run_span(team_name: &str, invocation_id: &str, coordinator: &str) -> Span {
177 tracing::info_span!(
178 "team.run",
179 team.name = team_name,
180 team.coordinator = coordinator,
181 invocation.id = invocation_id,
182 team.status = tracing::field::Empty,
183 otel.kind = "internal"
184 )
185}
186
187pub fn team_run_span_with_context(
190 team_name: &str,
191 invocation_id: &str,
192 session_id: &str,
193 coordinator: &str,
194) -> Span {
195 tracing::info_span!(
196 "team.run",
197 team.name = team_name,
198 team.coordinator = coordinator,
199 invocation.id = invocation_id,
200 "gcp.vertex.agent.invocation_id" = invocation_id,
201 "gcp.vertex.agent.session_id" = session_id,
202 "gcp.vertex.agent.event_id" = %format!("{invocation_id}:team"),
203 "gen_ai.conversation.id" = session_id,
204 team.status = tracing::field::Empty,
205 otel.kind = "internal"
206 )
207}
208
209pub fn team_member_span(team_name: &str, member: &str, invocation_id: &str) -> Span {
211 tracing::info_span!(
212 "team.member.run",
213 team.name = team_name,
214 team.member = member,
215 invocation.id = invocation_id,
216 team.status = tracing::field::Empty,
217 otel.kind = "internal"
218 )
219}
220
221pub fn team_member_span_with_context(
224 team_name: &str,
225 member: &str,
226 invocation_id: &str,
227 session_id: &str,
228) -> Span {
229 tracing::info_span!(
230 "team.member.run",
231 team.name = team_name,
232 team.member = member,
233 invocation.id = invocation_id,
234 "gcp.vertex.agent.invocation_id" = invocation_id,
235 "gcp.vertex.agent.session_id" = session_id,
236 "gcp.vertex.agent.event_id" = %format!("{invocation_id}:team-member:{member}"),
237 "gen_ai.conversation.id" = session_id,
238 team.status = tracing::field::Empty,
239 otel.kind = "internal"
240 )
241}
242
243pub fn team_relationship_span(
245 team_name: &str,
246 from: &str,
247 to: &str,
248 kind: &str,
249 edge_id: &str,
250) -> Span {
251 tracing::info_span!(
252 "team.relationship.execute",
253 team.name = team_name,
254 team.relationship.from = from,
255 team.relationship.to = to,
256 team.relationship.kind = kind,
257 team.edge.id = edge_id,
258 team.status = tracing::field::Empty,
259 team.duration_ms = tracing::field::Empty,
260 otel.kind = "internal"
261 )
262}
263
264pub fn team_relationship_span_with_context(
269 team_name: &str,
270 from: &str,
271 to: &str,
272 kind: &str,
273 edge_id: &str,
274 invocation_id: &str,
275 session_id: &str,
276) -> Span {
277 tracing::info_span!(
278 "team.relationship.execute",
279 team.name = team_name,
280 team.relationship.from = from,
281 team.relationship.to = to,
282 team.relationship.kind = kind,
283 team.edge.id = edge_id,
284 "gcp.vertex.agent.invocation_id" = invocation_id,
285 "gcp.vertex.agent.session_id" = session_id,
286 "gcp.vertex.agent.event_id" = edge_id,
287 "gen_ai.conversation.id" = session_id,
288 team.status = tracing::field::Empty,
289 team.duration_ms = tracing::field::Empty,
290 otel.kind = "internal"
291 )
292}
293
294pub fn callback_span(callback_type: &str) -> Span {
307 tracing::debug_span!(
308 "callback",
309 callback.type = callback_type,
310 )
311}
312
313pub fn add_context_attributes(user_id: &str, session_id: &str) {
319 let span = Span::current();
320 span.record("user.id", user_id);
321 span.record("session.id", session_id);
322}