use super::config::LlmApiConfig;
pub fn call_haiku_api(prompt: &str, config: &LlmApiConfig, thinking_mode: bool) -> Result<(String, Option<String>), String> {
let client = reqwest::blocking::Client::new();
let mut body = serde_json::json!({
"model": config.haiku_model,
"max_tokens": if thinking_mode { 8192 } else { 1024 },
"messages": [
{"role": "user", "content": prompt}
]
});
if thinking_mode {
body["thinking"] = serde_json::json!({
"type": "enabled",
"budget_tokens": 4096
});
}
let url = format!("{}/v1/messages", config.base_url.trim_end_matches('/'));
let resp = client
.post(&url)
.header("x-api-key", &config.auth_token)
.header("anthropic-version", "2023-06-01")
.header("Content-Type", "application/json")
.json(&body)
.send()
.map_err(|e| format!("API request failed: {}", e))?;
if !resp.status().is_success() {
let status = resp.status();
let text = resp.text().unwrap_or_default();
return Err(format!("API returned {}: {}", status, text));
}
let json: serde_json::Value = resp
.json()
.map_err(|e| format!("Failed to parse API response: {}", e))?;
let blocks = json["content"].as_array();
let text = blocks
.and_then(|bs| {
let parts: Vec<String> = bs.iter()
.filter(|b| b["type"].as_str() == Some("text"))
.filter_map(|b| b["text"].as_str())
.map(|s| s.to_string())
.collect();
if parts.is_empty() { None } else { Some(parts.join("\n")) }
})
.or_else(|| {
json["choices"][0]["message"]["content"]
.as_str()
.map(|s| s.to_string())
})
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "(empty response)".to_string());
let thinking = if thinking_mode {
let anthropic_thinking = blocks.and_then(|bs| {
let parts: Vec<String> = bs.iter()
.filter(|b| b["type"].as_str() == Some("thinking"))
.filter_map(|b| b["thinking"].as_str())
.map(|s| s.to_string())
.collect();
if parts.is_empty() { None } else { Some(parts.join("\n\n")) }
});
let openai_thinking = json["choices"][0]["message"]["reasoning_content"]
.as_str()
.map(|s| s.to_string());
let alt_thinking = json["choices"][0]["message"]["reasoning"]
.as_str()
.map(|s| s.to_string());
anthropic_thinking
.or(openai_thinking)
.or(alt_thinking)
} else {
None
};
Ok((text, thinking))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_call_haiku_api_error_on_bad_url() {
let config = LlmApiConfig {
auth_token: "bad-token".into(),
base_url: "https://invalid.example.com".into(),
haiku_model: "test-model".into(),
};
let result = call_haiku_api("hello", &config, false);
assert!(result.is_err());
}
}