Skip to main content

adk_telemetry/
events.rs

1//! Content event emitter for opt-in prompt/completion capture.
2//!
3//! Emits span events containing prompt or completion text when
4//! [`SemconvConfig::capture_content`](crate::config::SemconvConfig::capture_content)
5//! is enabled.
6
7use tracing::Span;
8
9use crate::config::SemconvConfig;
10
11/// Emits opt-in content events on the current span.
12///
13/// Only emits when [`SemconvConfig::capture_content`] is `true`.
14/// When content capture is disabled (the default), these methods are no-ops.
15///
16/// # Example
17/// ```
18/// use adk_telemetry::config::SemconvConfig;
19/// use adk_telemetry::events::ContentEventEmitter;
20///
21/// let config = SemconvConfig { capture_content: true };
22/// ContentEventEmitter::emit_prompt(&config, "What is the weather?");
23/// ContentEventEmitter::emit_completion(&config, "The weather is sunny.");
24/// ```
25pub struct ContentEventEmitter;
26
27impl ContentEventEmitter {
28    /// Emit a `gen_ai.content.prompt` span event with the prompt text.
29    ///
30    /// Only emits when `config.capture_content` is `true`.
31    pub fn emit_prompt(config: &SemconvConfig, prompt: &str) {
32        if !config.capture_content {
33            return;
34        }
35        let span = Span::current();
36        if span.is_none() {
37            tracing::debug!("content event emitter: no active span for prompt event");
38            return;
39        }
40        span.in_scope(|| {
41            tracing::info!(event_name = "gen_ai.content.prompt", content = prompt);
42        });
43    }
44
45    /// Emit a `gen_ai.content.completion` span event with the completion text.
46    ///
47    /// Only emits when `config.capture_content` is `true`.
48    pub fn emit_completion(config: &SemconvConfig, completion: &str) {
49        if !config.capture_content {
50            return;
51        }
52        let span = Span::current();
53        if span.is_none() {
54            tracing::debug!("content event emitter: no active span for completion event");
55            return;
56        }
57        span.in_scope(|| {
58            tracing::info!(event_name = "gen_ai.content.completion", content = completion);
59        });
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_emit_prompt_disabled_is_noop() {
69        let config = SemconvConfig::default();
70        // Should not panic even without an active span
71        ContentEventEmitter::emit_prompt(&config, "test prompt");
72    }
73
74    #[test]
75    fn test_emit_completion_disabled_is_noop() {
76        let config = SemconvConfig::default();
77        // Should not panic even without an active span
78        ContentEventEmitter::emit_completion(&config, "test completion");
79    }
80}