use async_trait::async_trait;
use serde_json::Value;
use super::Hooks;
use super::context::RunContext;
use crate::chat::ChatResponse;
use crate::error::Error;
use crate::message::Message;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum LogLevel {
Trace,
Debug,
#[default]
Info,
Warn,
}
macro_rules! log_at_level {
($level:expr, $($arg:tt)*) => {
match $level {
LogLevel::Trace => tracing::trace!($($arg)*),
LogLevel::Debug => tracing::debug!($($arg)*),
LogLevel::Info => tracing::info!($($arg)*),
LogLevel::Warn => tracing::warn!($($arg)*),
}
};
}
#[derive(Debug, Clone, Copy, Default)]
pub struct LoggingHooks {
level: LogLevel,
}
impl LoggingHooks {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub const fn with_level(level: LogLevel) -> Self {
Self { level }
}
}
#[async_trait]
impl Hooks for LoggingHooks {
async fn on_agent_start(&self, ctx: &RunContext, agent_name: &str) {
log_at_level!(
self.level,
agent = agent_name,
step = ctx.step(),
"Agent started"
);
}
async fn on_agent_end(&self, ctx: &RunContext, agent_name: &str, output: &Value) {
let usage = ctx.usage();
log_at_level!(self.level,
agent = agent_name,
step = ctx.step(),
input_tokens = usage.input_tokens,
output_tokens = usage.output_tokens,
total_tokens = usage.total_tokens,
output = %output,
"Agent completed"
);
}
async fn on_llm_start(
&self,
ctx: &RunContext,
agent_name: &str,
_system_prompt: Option<&str>,
messages: &[Message],
) {
log_at_level!(
self.level,
agent = agent_name,
step = ctx.step(),
message_count = messages.len(),
"LLM request started"
);
}
async fn on_llm_end(&self, ctx: &RunContext, agent_name: &str, response: &ChatResponse) {
let model = response.model.as_deref().unwrap_or("unknown");
let usage_str = response
.usage
.map_or_else(|| "none".to_owned(), |u| u.to_string());
log_at_level!(self.level,
agent = agent_name,
step = ctx.step(),
model = model,
usage = %usage_str,
stop_reason = ?response.stop_reason,
"LLM request completed"
);
}
async fn on_tool_start(&self, ctx: &RunContext, agent_name: &str, tool_name: &str) {
log_at_level!(
self.level,
agent = agent_name,
step = ctx.step(),
tool = tool_name,
"Tool execution started"
);
}
async fn on_tool_end(&self, ctx: &RunContext, agent_name: &str, tool_name: &str, result: &str) {
log_at_level!(
self.level,
agent = agent_name,
step = ctx.step(),
tool = tool_name,
result_len = result.len(),
"Tool execution completed"
);
}
async fn on_error(&self, ctx: &RunContext, agent_name: &str, error: &Error) {
tracing::warn!(
agent = agent_name,
step = ctx.step(),
error = %error,
"Agent error"
);
}
}