use std::fmt;
use std::future::Future;
use std::panic::AssertUnwindSafe;
use std::panic::catch_unwind;
use std::sync::Arc;
use ferrin_spec::BoxFuture;
use futures_util::FutureExt;
use futures_util::future::join_all;
use crate::generate_text::StepResult;
use crate::stream_text::StreamEvent;
use crate::telemetry::AbortEvent;
use crate::telemetry::EndEvent;
use crate::telemetry::ModelCallEndEvent;
use crate::telemetry::ModelCallStartEvent;
use crate::telemetry::StartEvent;
use crate::telemetry::StepStartEvent;
use crate::telemetry::ToolExecutionEndEvent;
use crate::telemetry::ToolExecutionStartEvent;
pub trait HookFn<E>: Send + Sync + 'static {
fn call(&self, event: Arc<E>) -> BoxFuture<'static, ()>;
}
impl<E, F, Fut> HookFn<E> for F
where
F: Fn(Arc<E>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
fn call(&self, event: Arc<E>) -> BoxFuture<'static, ()> {
Box::pin(self(event))
}
}
pub type HookList<E> = Vec<Arc<dyn HookFn<E>>>;
#[derive(Clone, Default)]
pub struct Hooks {
pub on_start: HookList<StartEvent>,
pub on_step_start: HookList<StepStartEvent>,
pub on_language_model_call_start: HookList<ModelCallStartEvent>,
pub on_language_model_call_end: HookList<ModelCallEndEvent>,
pub on_tool_execution_start: HookList<ToolExecutionStartEvent>,
pub on_tool_execution_end: HookList<ToolExecutionEndEvent>,
pub on_step_end: HookList<StepResult>,
pub on_end: HookList<EndEvent>,
pub on_chunk: HookList<StreamEvent>,
pub on_abort: HookList<AbortEvent>,
}
impl Hooks {
#[must_use]
pub fn merged(mut self, other: Hooks) -> Hooks {
self.on_start.extend(other.on_start);
self.on_step_start.extend(other.on_step_start);
self.on_language_model_call_start
.extend(other.on_language_model_call_start);
self.on_language_model_call_end
.extend(other.on_language_model_call_end);
self.on_tool_execution_start
.extend(other.on_tool_execution_start);
self.on_tool_execution_end
.extend(other.on_tool_execution_end);
self.on_step_end.extend(other.on_step_end);
self.on_end.extend(other.on_end);
self.on_chunk.extend(other.on_chunk);
self.on_abort.extend(other.on_abort);
self
}
pub async fn emit<E: 'static>(list: &[Arc<dyn HookFn<E>>], event: Arc<E>) {
let futures = list.iter().filter_map(|hook| {
catch_unwind(AssertUnwindSafe(|| hook.call(Arc::clone(&event))))
.ok()
.map(|future| AssertUnwindSafe(future).catch_unwind())
});
let _ = join_all(futures).await;
}
}
impl fmt::Debug for Hooks {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Hooks")
.field("on_start", &self.on_start.len())
.field("on_step_start", &self.on_step_start.len())
.field(
"on_language_model_call_start",
&self.on_language_model_call_start.len(),
)
.field(
"on_language_model_call_end",
&self.on_language_model_call_end.len(),
)
.field(
"on_tool_execution_start",
&self.on_tool_execution_start.len(),
)
.field("on_tool_execution_end", &self.on_tool_execution_end.len())
.field("on_step_end", &self.on_step_end.len())
.field("on_end", &self.on_end.len())
.field("on_chunk", &self.on_chunk.len())
.field("on_abort", &self.on_abort.len())
.finish()
}
}