use std::future::Future;
use std::time::Duration;
use crate::agent::request::AgentRequest;
use crate::error::{AiError, AiResult};
pub fn build_input_string(request: &AgentRequest) -> String {
let mut input = String::new();
if !request.context.is_empty() {
input.push_str("\n");
for chunk in &request.context {
let source = chunk.source.as_deref().unwrap_or("unknown");
let score = chunk
.score
.map(|s| format!("{s:.4}"))
.unwrap_or_else(|| "n/a".to_string());
input.push_str(&format!(
"\n\n{}\n\n",
chunk.id, source, score, chunk.content
));
}
input.push_str("\n\n");
}
for message in &request.messages {
match message {
super::request::AgentMessage::User(content) => {
input.push_str(&format!("User: {content}\n"));
}
super::request::AgentMessage::Assistant(content) => {
input.push_str(&format!("Assistant: {content}\n"));
}
}
}
input.push_str(&format!("User: {}", request.input));
input
}
pub fn estimate_tokens(text: &str) -> u64 {
if text.is_empty() {
return 0;
}
(text.chars().count() as f64 / 2.5).ceil() as u64
}
pub fn check_token_budget(
estimated_input_tokens: u64,
accumulated_output_chars: usize,
limit: i64,
) -> bool {
let output_estimate = (accumulated_output_chars as f64 / 2.5).ceil() as u64;
estimated_input_tokens + output_estimate > limit as u64
}
pub async fn with_retry(
max_attempts: usize,
base_delay_ms: u64,
f: F,
) -> AiResult
where
F: Fn() -> Fut,
Fut: Future