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;
24pub(crate) mod spans;
25
26pub(crate) use dispatcher::TelemetryDispatcher;
27pub use events::AbortEvent;
28pub use events::EmbedEndEvent;
29pub use events::EmbedStartEvent;
30pub use events::EndEvent;
31pub use events::ErrorEvent;
32pub use events::ErrorPhase;
33pub use events::ModelCallEndEvent;
34pub use events::ModelCallStartEvent;
35pub use events::ModelIdentity;
36pub use events::RecordedInputs;
37pub use events::RerankEndEvent;
38pub use events::RerankStartEvent;
39pub use events::StartEvent;
40pub use events::StepEndEvent;
41pub use events::StepStartEvent;
42pub use events::ToolExecutionEndEvent;
43pub use events::ToolExecutionStartEvent;
44pub use events::ToolOutcome;
45pub use spans::WARNINGS_TARGET;
46
47#[derive(Debug, Clone)]
49pub struct ModelCallContext {
50 pub call_id: String,
52 pub step_number: u32,
54 pub model: ModelIdentity,
56 pub function_id: Option<String>,
58}
59
60#[derive(Debug, Clone)]
62pub struct ToolExecutionContext {
63 pub call_id: String,
65 pub tool_call_id: ToolCallId,
67 pub tool_name: ToolName,
69 pub input: Option<JsonValue>,
71}
72
73#[derive(Debug)]
75#[non_exhaustive]
76pub enum ModelCallOutcome {
77 Generate(Box<GenerateResult>),
79 Stream(Box<StreamResult>),
81}
82
83pub trait Telemetry: Send + Sync + 'static {
88 fn on_start(&self, _event: &StartEvent) {}
90 fn on_step_start(&self, _event: &StepStartEvent) {}
92 fn on_language_model_call_start(&self, _event: &ModelCallStartEvent) {}
94 fn on_language_model_call_end(&self, _event: &ModelCallEndEvent) {}
96 fn on_tool_execution_start(&self, _event: &ToolExecutionStartEvent) {}
98 fn on_tool_execution_end(&self, _event: &ToolExecutionEndEvent) {}
100 fn on_step_end(&self, _event: &StepEndEvent) {}
102 fn on_embed_start(&self, _event: &EmbedStartEvent) {}
104 fn on_embed_end(&self, _event: &EmbedEndEvent) {}
106 fn on_rerank_start(&self, _event: &RerankStartEvent) {}
108 fn on_rerank_end(&self, _event: &RerankEndEvent) {}
110 fn on_end(&self, _event: &EndEvent) {}
112 fn on_abort(&self, _event: &AbortEvent) {}
114 fn on_error(&self, _event: &ErrorEvent<'_>) {}
116
117 fn execute_language_model_call<'a>(
120 &'a self,
121 _ctx: &'a ModelCallContext,
122 call: BoxFuture<'a, Result<ModelCallOutcome, Error>>,
123 ) -> BoxFuture<'a, Result<ModelCallOutcome, Error>> {
124 call
125 }
126
127 fn execute_tool<'a>(
129 &'a self,
130 _ctx: &'a ToolExecutionContext,
131 call: BoxFuture<'a, Result<ToolOutcome, ToolError>>,
132 ) -> BoxFuture<'a, Result<ToolOutcome, ToolError>> {
133 call
134 }
135}
136
137#[derive(Clone, Default)]
139pub struct TelemetryOptions {
140 pub enabled: bool,
142 pub record_inputs: bool,
144 pub record_outputs: bool,
146 pub function_id: Option<String>,
148 pub metadata: BTreeMap<String, JsonValue>,
150 pub include_tools_context: bool,
152 pub integrations: Vec<Arc<dyn Telemetry>>,
154}
155
156impl TelemetryOptions {
157 #[must_use]
159 pub fn enabled() -> Self {
160 Self {
161 enabled: true,
162 ..Self::default()
163 }
164 }
165
166 #[must_use]
168 pub fn with_integration(mut self, integration: Arc<dyn Telemetry>) -> Self {
169 self.integrations.push(integration);
170 self
171 }
172}
173
174impl fmt::Debug for TelemetryOptions {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 f.debug_struct("TelemetryOptions")
177 .field("enabled", &self.enabled)
178 .field("record_inputs", &self.record_inputs)
179 .field("record_outputs", &self.record_outputs)
180 .field("function_id", &self.function_id)
181 .field("metadata", &self.metadata)
182 .field("include_tools_context", &self.include_tools_context)
183 .field("integrations", &self.integrations.len())
184 .finish()
185 }
186}