use crate::core::config::LlmConfig;
use crate::types::LlmUsage;
#[allow(clippy::field_reassign_with_default)]
#[cfg_attr(alef, alef(skip))]
pub async fn complete_text(
llm_config: &LlmConfig,
prompt: &str,
source: &str,
) -> crate::Result<(String, Option<LlmUsage>)> {
use liter_llm::LlmClient;
let client = super::client::create_client(llm_config)?;
let mut request = liter_llm::ChatCompletionRequest::default();
request.model = llm_config.model.clone();
request.messages = vec![liter_llm::Message::User(liter_llm::UserMessage {
content: liter_llm::UserContent::Text(prompt.to_string()),
name: None,
})];
request.temperature = llm_config.temperature;
request.max_tokens = llm_config.max_tokens;
let response = client
.chat(request)
.await
.map_err(|e| crate::XbergError::parsing(format!("LLM text completion request failed ({source}): {e}")))?;
let usage = super::usage::extract_usage_from_chat(&response, source);
let text = response
.choices
.first()
.and_then(|c| c.message.content.as_ref().and_then(|m| m.as_text()))
.map(|s| s.trim().to_string())
.ok_or_else(|| {
crate::XbergError::parsing(format!(
"LLM text completion ({source}) returned no content (model={}, {} choices)",
llm_config.model,
response.choices.len()
))
})?;
Ok((text, usage))
}