use std::future::Future;
use crate::{
ChatRequest, ChatResponse, LlmError, LlmProvider, Message, Part, Result, Role, StopReason,
ToolCall,
};
pub async fn run_tools<F, Fut>(
provider: &dyn LlmProvider,
request: &mut ChatRequest,
max_rounds: usize,
mut handler: F,
) -> Result<ChatResponse>
where
F: FnMut(ToolCall) -> Fut,
Fut: Future<Output = std::result::Result<String, String>>,
{
for _ in 0..max_rounds {
let response = provider.chat(request).await?;
request.messages.push(response.message.clone());
if response.stop_reason == StopReason::Paused {
continue;
}
let calls = response.tool_calls();
if calls.is_empty() {
return Ok(response);
}
if response.stop_reason == StopReason::MaxTokens {
request.messages.pop();
return Err(LlmError::InvalidResponse(
"the model hit max_tokens while writing a tool call; raise max_tokens".into(),
));
}
let mut results = Vec::with_capacity(calls.len());
for call in calls {
let id = call.id.clone();
let part = match handler(call).await {
Ok(content) => Part::tool_result(id, content, false),
Err(message) => Part::tool_result(id, message, true),
};
results.push(part);
}
request.messages.push(Message::new(Role::Tool, results));
}
Err(LlmError::InvalidResponse(format!(
"model did not finish within {max_rounds} tool rounds"
)))
}