use async_trait::async_trait;
use chrono::Utc;
use crate::budget::BudgetChecker;
use crate::cancellation::CancellationToken;
use crate::capability::Capability;
use crate::context::ContextAssembler;
use crate::driver::{DriverRegistry, ModelSpec, Usage};
use crate::error::Result;
use crate::event::{EventData, EventListener, EventRequest};
use crate::event_log::EventLog;
use crate::extensions::Extensions;
use crate::hooks::{PostToolExecHook, PreToolUseHook};
use crate::id::{EventId, SessionId, TurnId};
use crate::message::Message;
use crate::replay::message_from_event_data;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct TurnResult {
pub turn_id: TurnId,
pub response: String,
pub iterations: usize,
pub tool_calls: usize,
pub usage: Usage,
}
pub struct TurnHost<'a> {
pub session_id: SessionId,
pub system_prompt: &'a str,
pub model: &'a ModelSpec,
pub capabilities: &'a [Arc<dyn Capability>],
pub drivers: &'a DriverRegistry,
pub listeners: &'a [Arc<dyn EventListener>],
pub max_iterations: usize,
pub log: &'a dyn EventLog,
pub messages: &'a mut Vec<Message>,
pub cancellation: CancellationToken,
pub pre_tool_hooks: &'a [Arc<dyn PreToolUseHook>],
pub post_tool_hooks: &'a [Arc<dyn PostToolExecHook>],
pub budget_checker: Option<Arc<dyn BudgetChecker>>,
pub context_assembler: &'a dyn ContextAssembler,
pub extensions: &'a Extensions,
}
impl TurnHost<'_> {
pub async fn record(&mut self, turn_id: TurnId, effects: Vec<EventData>) -> Result<()> {
for data in effects {
let request = EventRequest::with_turn(self.session_id, turn_id, data);
let event = if request.data.is_ephemeral() {
request.into_ephemeral_event(EventId::new(), Utc::now())
} else {
if let Some(message) = message_from_event_data(&request.data) {
self.messages.push(message);
}
self.log.append(request).await?
};
for listener in self.listeners {
listener.on_event(&event).await;
}
}
Ok(())
}
}
#[async_trait]
pub trait TurnExecutor: Send + Sync {
async fn run_turn(&self, host: &mut TurnHost<'_>, input: Message) -> Result<TurnResult>;
}