use std::collections::BTreeMap;
use std::fmt;
use std::sync::Arc;
use ferrin_spec::BoxFuture;
use ferrin_spec::GenerateResult;
use ferrin_spec::JsonValue;
use ferrin_spec::StreamResult;
use ferrin_spec::ToolCallId;
use ferrin_spec::ToolName;
use ferrin_tool::ToolError;
use crate::error::Error;
pub(crate) mod dispatcher;
mod events;
mod redact;
mod redact_provider;
pub(crate) mod spans;
pub(crate) use dispatcher::TelemetryDispatcher;
pub use events::AbortEvent;
pub use events::EmbedEndEvent;
pub use events::EmbedStartEvent;
pub use events::EndEvent;
pub use events::ErrorEvent;
pub use events::ErrorPhase;
pub use events::ModelCallEndEvent;
pub use events::ModelCallStartEvent;
pub use events::ModelIdentity;
pub use events::RecordedInputs;
pub use events::RerankEndEvent;
pub use events::RerankStartEvent;
pub use events::StartEvent;
pub use events::StepEndEvent;
pub use events::StepStartEvent;
pub use events::ToolExecutionEndEvent;
pub use events::ToolExecutionStartEvent;
pub use events::ToolOutcome;
pub use spans::WARNINGS_TARGET;
#[derive(Debug, Clone)]
pub struct ModelCallContext {
pub call_id: String,
pub step_number: u32,
pub model: ModelIdentity,
pub function_id: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ToolExecutionContext {
pub call_id: String,
pub tool_call_id: ToolCallId,
pub tool_name: ToolName,
pub input: Option<JsonValue>,
pub record_outputs: bool,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ModelCallOutcome {
Generate(Box<GenerateResult>),
Stream(Box<StreamResult>),
}
macro_rules! callback {
($name:ident, $event:ty, $doc:literal) => {
#[doc = $doc]
fn $name<'a>(&'a self, _event: &'a $event) -> BoxFuture<'a, ()> {
Box::pin(async {})
}
};
}
pub trait Telemetry: Send + Sync + 'static {
callback!(on_start, StartEvent, "A call started.");
callback!(on_step_start, StepStartEvent, "A step started.");
callback!(
on_language_model_call_start,
ModelCallStartEvent,
"A model call is about to be made."
);
callback!(
on_language_model_call_end,
ModelCallEndEvent,
"A model call finished."
);
callback!(
on_tool_execution_start,
ToolExecutionStartEvent,
"A tool execution started."
);
callback!(
on_tool_execution_end,
ToolExecutionEndEvent,
"A tool execution finished."
);
callback!(on_step_end, StepEndEvent, "A step finished.");
callback!(
on_embed_start,
EmbedStartEvent,
"An embedding call started."
);
callback!(on_embed_end, EmbedEndEvent, "An embedding call finished.");
callback!(on_rerank_start, RerankStartEvent, "A rerank call started.");
callback!(on_rerank_end, RerankEndEvent, "A rerank call finished.");
callback!(
on_embed_operation_start,
crate::embed::EmbedCallStartEvent,
"An embedding operation started before its attempts."
);
callback!(
on_embed_operation_end,
crate::embed::EmbedCallEndEvent,
"An embedding operation completed all its attempts."
);
callback!(
on_rerank_operation_start,
crate::rerank::RerankCallStartEvent,
"A reranking operation started before its attempts."
);
callback!(
on_rerank_operation_end,
crate::rerank::RerankCallEndEvent,
"A reranking operation completed all its attempts."
);
callback!(on_end, EndEvent, "A call finished.");
callback!(on_abort, AbortEvent, "A streaming call was aborted.");
callback!(on_error, ErrorEvent<'_>, "An error occurred.");
fn execute_language_model_call<'a>(
&'a self,
_ctx: &'a ModelCallContext,
call: BoxFuture<'a, Result<ModelCallOutcome, Error>>,
) -> BoxFuture<'a, Result<ModelCallOutcome, Error>> {
call
}
fn execute_tool<'a>(
&'a self,
_ctx: &'a ToolExecutionContext,
call: BoxFuture<'a, Result<ToolOutcome, ToolError>>,
) -> BoxFuture<'a, Result<ToolOutcome, ToolError>> {
call
}
}
#[derive(Clone, Default)]
pub struct TelemetryOptions {
pub enabled: bool,
pub record_inputs: bool,
pub record_outputs: bool,
pub function_id: Option<String>,
pub metadata: BTreeMap<String, JsonValue>,
pub include_runtime_context: bool,
pub include_tools_context: bool,
pub integrations: Vec<Arc<dyn Telemetry>>,
}
impl TelemetryOptions {
#[must_use]
pub fn enabled() -> Self {
Self {
enabled: true,
..Self::default()
}
}
#[must_use]
pub fn with_integration(mut self, integration: Arc<dyn Telemetry>) -> Self {
self.integrations.push(integration);
self
}
}
impl fmt::Debug for TelemetryOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TelemetryOptions")
.field("enabled", &self.enabled)
.field("record_inputs", &self.record_inputs)
.field("record_outputs", &self.record_outputs)
.field("function_id", &self.function_id)
.field("metadata", &self.metadata)
.field("include_runtime_context", &self.include_runtime_context)
.field("include_tools_context", &self.include_tools_context)
.field("integrations", &self.integrations.len())
.finish()
}
}