use serde::de::DeserializeOwned;
use crate::providers::chat;
use crate::util::json::parse_fenced_json;
use crate::{ChatMessage, ChatRequest};
pub(crate) async fn retry_extract_structured<T: DeserializeOwned>(
history: &[ChatMessage],
extraction_prompt: &str,
retry_prompt: &str,
params: &ChatRequest,
max_attempts: usize,
) -> anyhow::Result<T> {
let mut extraction_history = history.to_vec();
if !extraction_prompt.is_empty() {
extraction_history.push(ChatMessage::user(extraction_prompt));
}
let mut last_raw = String::new();
for _attempt in 1..=max_attempts {
let response = chat(ChatRequest {
messages: extraction_history.clone(),
allow_image_parts: false, ..params.clone()
})
.await?;
last_raw = response.text_or_empty().to_string();
if response.tool_calls.is_empty()
&& let Ok(result) = parse_fenced_json::<T>(&last_raw)
{
return Ok(result);
}
extraction_history.push(ChatMessage::assistant(last_raw.clone()));
extraction_history.push(ChatMessage::user(retry_prompt));
}
let snippet: String = last_raw.chars().take(300).collect();
anyhow::bail!(
"Failed to extract structured response after {max_attempts} attempts. Last raw: {snippet}",
)
}