1use std::fmt;
4use std::future::Future;
5use std::sync::Arc;
6
7use ferrin_spec::BoxFuture;
8
9use crate::generate_text::StepResult;
10use crate::stream_text::StreamEvent;
11use crate::telemetry::AbortEvent;
12use crate::telemetry::EndEvent;
13use crate::telemetry::ModelCallEndEvent;
14use crate::telemetry::ModelCallStartEvent;
15use crate::telemetry::StartEvent;
16use crate::telemetry::StepStartEvent;
17use crate::telemetry::ToolExecutionEndEvent;
18use crate::telemetry::ToolExecutionStartEvent;
19
20pub trait HookFn<E>: Send + Sync + 'static {
26 fn call(&self, event: Arc<E>) -> BoxFuture<'static, ()>;
28}
29
30impl<E, F, Fut> HookFn<E> for F
31where
32 F: Fn(Arc<E>) -> Fut + Send + Sync + 'static,
33 Fut: Future<Output = ()> + Send + 'static,
34{
35 fn call(&self, event: Arc<E>) -> BoxFuture<'static, ()> {
36 Box::pin(self(event))
37 }
38}
39
40pub type HookList<E> = Vec<Arc<dyn HookFn<E>>>;
42
43#[derive(Clone, Default)]
45pub struct Hooks {
46 pub on_start: HookList<StartEvent>,
48 pub on_step_start: HookList<StepStartEvent>,
50 pub on_language_model_call_start: HookList<ModelCallStartEvent>,
52 pub on_language_model_call_end: HookList<ModelCallEndEvent>,
54 pub on_tool_execution_start: HookList<ToolExecutionStartEvent>,
56 pub on_tool_execution_end: HookList<ToolExecutionEndEvent>,
58 pub on_step_end: HookList<StepResult>,
60 pub on_end: HookList<EndEvent>,
62 pub on_chunk: HookList<StreamEvent>,
64 pub on_abort: HookList<AbortEvent>,
66}
67
68impl Hooks {
69 #[must_use]
72 pub fn merged(mut self, other: Hooks) -> Hooks {
73 self.on_start.extend(other.on_start);
74 self.on_step_start.extend(other.on_step_start);
75 self.on_language_model_call_start
76 .extend(other.on_language_model_call_start);
77 self.on_language_model_call_end
78 .extend(other.on_language_model_call_end);
79 self.on_tool_execution_start
80 .extend(other.on_tool_execution_start);
81 self.on_tool_execution_end
82 .extend(other.on_tool_execution_end);
83 self.on_step_end.extend(other.on_step_end);
84 self.on_end.extend(other.on_end);
85 self.on_chunk.extend(other.on_chunk);
86 self.on_abort.extend(other.on_abort);
87 self
88 }
89
90 pub async fn emit<E: 'static>(list: &[Arc<dyn HookFn<E>>], event: Arc<E>) {
92 for hook in list {
93 hook.call(Arc::clone(&event)).await;
94 }
95 }
96}
97
98impl fmt::Debug for Hooks {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("Hooks")
101 .field("on_start", &self.on_start.len())
102 .field("on_step_start", &self.on_step_start.len())
103 .field(
104 "on_language_model_call_start",
105 &self.on_language_model_call_start.len(),
106 )
107 .field(
108 "on_language_model_call_end",
109 &self.on_language_model_call_end.len(),
110 )
111 .field(
112 "on_tool_execution_start",
113 &self.on_tool_execution_start.len(),
114 )
115 .field("on_tool_execution_end", &self.on_tool_execution_end.len())
116 .field("on_step_end", &self.on_step_end.len())
117 .field("on_end", &self.on_end.len())
118 .field("on_chunk", &self.on_chunk.len())
119 .field("on_abort", &self.on_abort.len())
120 .finish()
121 }
122}