use anyhow::Result;
use std::{future::Future, pin::Pin};
use dyn_clone::DynClone;
use swiftide_core::chat_completion::{
ChatCompletionRequest, ChatCompletionResponse, ChatMessage, ToolCall, ToolOutput,
errors::ToolError,
};
use crate::{Agent, errors::AgentError, state::StopReason};
pub trait BeforeAllFn:
for<'a> Fn(&'a mut Agent) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(BeforeAllFn);
pub trait AfterEachFn:
for<'a> Fn(&'a mut Agent) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(AfterEachFn);
pub trait BeforeCompletionFn:
for<'a> Fn(
&'a mut Agent,
&mut ChatCompletionRequest<'_>,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(BeforeCompletionFn);
pub trait AfterCompletionFn:
for<'a> Fn(
&'a mut Agent,
&mut ChatCompletionResponse,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(AfterCompletionFn);
pub trait AfterToolFn:
for<'tool> Fn(
&'tool mut Agent,
&ToolCall,
&'tool mut Result<ToolOutput, ToolError>,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'tool>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(AfterToolFn);
pub trait BeforeToolFn:
for<'a> Fn(&'a mut Agent, &ToolCall) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(BeforeToolFn);
pub trait MessageHookFn:
for<'a> Fn(&'a Agent, &mut ChatMessage) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(MessageHookFn);
pub trait OnStartFn:
for<'a> Fn(&'a mut Agent) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(OnStartFn);
pub trait OnStopFn:
for<'a> Fn(
&'a mut Agent,
StopReason,
Option<&AgentError>,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(OnStopFn);
pub trait OnStreamFn:
for<'a> Fn(
&'a Agent,
&ChatCompletionResponse,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
dyn_clone::clone_trait_object!(OnStreamFn);
#[derive(Clone, strum_macros::EnumDiscriminants, strum_macros::Display)]
#[strum_discriminants(name(HookTypes), derive(strum_macros::Display))]
pub enum Hook {
BeforeAll(Box<dyn BeforeAllFn>),
BeforeCompletion(Box<dyn BeforeCompletionFn>),
AfterCompletion(Box<dyn AfterCompletionFn>),
BeforeTool(Box<dyn BeforeToolFn>),
AfterTool(Box<dyn AfterToolFn>),
AfterEach(Box<dyn AfterEachFn>),
OnNewMessage(Box<dyn MessageHookFn>),
OnStart(Box<dyn OnStartFn>),
OnStop(Box<dyn OnStopFn>),
OnStream(Box<dyn OnStreamFn>),
}
impl<F> BeforeAllFn for F where
F: for<'a> Fn(&'a mut Agent) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> AfterEachFn for F where
F: for<'a> Fn(&'a mut Agent) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> BeforeCompletionFn for F where
F: for<'a> Fn(
&'a mut Agent,
&mut ChatCompletionRequest<'_>,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> AfterCompletionFn for F where
F: for<'a> Fn(
&'a mut Agent,
&mut ChatCompletionResponse,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> BeforeToolFn for F where
F: for<'a> Fn(
&'a mut Agent,
&ToolCall,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> AfterToolFn for F where
F: for<'tool> Fn(
&'tool mut Agent,
&ToolCall,
&'tool mut Result<ToolOutput, ToolError>,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'tool>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> MessageHookFn for F where
F: for<'a> Fn(
&'a Agent,
&mut ChatMessage,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> OnStartFn for F where
F: for<'a> Fn(&'a mut Agent) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> OnStopFn for F where
F: for<'a> Fn(
&'a mut Agent,
StopReason,
Option<&AgentError>,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
impl<F> OnStreamFn for F where
F: for<'a> Fn(
&'a Agent,
&ChatCompletionResponse,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
+ Send
+ Sync
+ DynClone
{
}
#[cfg(test)]
mod tests {
use crate::Agent;
#[test]
fn test_hooks_compile_sync_and_async() {
Agent::builder()
.before_all(|_| Box::pin(async { Ok(()) }))
.on_start(|_| Box::pin(async { Ok(()) }))
.before_completion(|_, _| Box::pin(async { Ok(()) }))
.before_tool(|_, _| Box::pin(async { Ok(()) }))
.after_tool(|_, _, _| Box::pin(async { Ok(()) }))
.after_completion(|_, _| Box::pin(async { Ok(()) }));
}
}