ferrin_core/telemetry/
mod.rs1use std::collections::BTreeMap;
9use std::fmt;
10use std::sync::Arc;
11
12use ferrin_spec::BoxFuture;
13use ferrin_spec::GenerateResult;
14use ferrin_spec::JsonValue;
15use ferrin_spec::StreamResult;
16use ferrin_spec::ToolCallId;
17use ferrin_spec::ToolName;
18use ferrin_tool::ToolError;
19
20use crate::error::Error;
21
22pub(crate) mod dispatcher;
23mod events;
24mod redact;
25mod redact_provider;
26pub(crate) mod spans;
27
28pub(crate) use dispatcher::TelemetryDispatcher;
29pub use events::AbortEvent;
30pub use events::EmbedEndEvent;
31pub use events::EmbedStartEvent;
32pub use events::EndEvent;
33pub use events::ErrorEvent;
34pub use events::ErrorPhase;
35pub use events::ModelCallEndEvent;
36pub use events::ModelCallStartEvent;
37pub use events::ModelIdentity;
38pub use events::RecordedInputs;
39pub use events::RerankEndEvent;
40pub use events::RerankStartEvent;
41pub use events::StartEvent;
42pub use events::StepEndEvent;
43pub use events::StepStartEvent;
44pub use events::ToolExecutionEndEvent;
45pub use events::ToolExecutionStartEvent;
46pub use events::ToolOutcome;
47pub use spans::WARNINGS_TARGET;
48
49#[derive(Debug, Clone)]
51pub struct ModelCallContext {
52 pub call_id: String,
54 pub step_number: u32,
56 pub model: ModelIdentity,
58 pub function_id: Option<String>,
60}
61
62#[derive(Debug, Clone)]
64pub struct ToolExecutionContext {
65 pub call_id: String,
67 pub tool_call_id: ToolCallId,
69 pub tool_name: ToolName,
71 pub input: Option<JsonValue>,
73}
74
75#[derive(Debug)]
77#[non_exhaustive]
78pub enum ModelCallOutcome {
79 Generate(Box<GenerateResult>),
81 Stream(Box<StreamResult>),
83}
84
85pub trait Telemetry: Send + Sync + 'static {
90 fn on_start(&self, _event: &StartEvent) {}
92 fn on_step_start(&self, _event: &StepStartEvent) {}
94 fn on_language_model_call_start(&self, _event: &ModelCallStartEvent) {}
96 fn on_language_model_call_end(&self, _event: &ModelCallEndEvent) {}
98 fn on_tool_execution_start(&self, _event: &ToolExecutionStartEvent) {}
100 fn on_tool_execution_end(&self, _event: &ToolExecutionEndEvent) {}
102 fn on_step_end(&self, _event: &StepEndEvent) {}
104 fn on_embed_start(&self, _event: &EmbedStartEvent) {}
106 fn on_embed_end(&self, _event: &EmbedEndEvent) {}
108 fn on_rerank_start(&self, _event: &RerankStartEvent) {}
110 fn on_rerank_end(&self, _event: &RerankEndEvent) {}
112 fn on_end(&self, _event: &EndEvent) {}
114 fn on_abort(&self, _event: &AbortEvent) {}
116 fn on_error(&self, _event: &ErrorEvent<'_>) {}
118
119 fn execute_language_model_call<'a>(
122 &'a self,
123 _ctx: &'a ModelCallContext,
124 call: BoxFuture<'a, Result<ModelCallOutcome, Error>>,
125 ) -> BoxFuture<'a, Result<ModelCallOutcome, Error>> {
126 call
127 }
128
129 fn execute_tool<'a>(
131 &'a self,
132 _ctx: &'a ToolExecutionContext,
133 call: BoxFuture<'a, Result<ToolOutcome, ToolError>>,
134 ) -> BoxFuture<'a, Result<ToolOutcome, ToolError>> {
135 call
136 }
137}
138
139#[derive(Clone, Default)]
141pub struct TelemetryOptions {
142 pub enabled: bool,
144 pub record_inputs: bool,
146 pub record_outputs: bool,
148 pub function_id: Option<String>,
150 pub metadata: BTreeMap<String, JsonValue>,
152 pub include_tools_context: bool,
154 pub integrations: Vec<Arc<dyn Telemetry>>,
156}
157
158impl TelemetryOptions {
159 #[must_use]
161 pub fn enabled() -> Self {
162 Self {
163 enabled: true,
164 ..Self::default()
165 }
166 }
167
168 #[must_use]
170 pub fn with_integration(mut self, integration: Arc<dyn Telemetry>) -> Self {
171 self.integrations.push(integration);
172 self
173 }
174}
175
176impl fmt::Debug for TelemetryOptions {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 f.debug_struct("TelemetryOptions")
179 .field("enabled", &self.enabled)
180 .field("record_inputs", &self.record_inputs)
181 .field("record_outputs", &self.record_outputs)
182 .field("function_id", &self.function_id)
183 .field("metadata", &self.metadata)
184 .field("include_tools_context", &self.include_tools_context)
185 .field("integrations", &self.integrations.len())
186 .finish()
187 }
188}