pub mod chunk_classifier;
pub mod page_classifier;
pub use chunk_classifier::classify_chunks;
pub use page_classifier::{classify_pages, classify_text};
pub async fn classify_document(
pages: &[&str],
config: &crate::core::config::PageClassificationConfig,
) -> crate::Result<Vec<crate::ClassificationLabel>> {
if config.labels.is_empty() {
return Err(crate::XbergError::validation(
"PageClassificationConfig.labels must contain at least one entry",
));
}
if pages.is_empty() {
return Ok(Vec::new());
}
let ctx = page_classifier::ClassifyContext::new(config);
let mut all_labels: Vec<crate::ClassificationLabel> = Vec::new();
let mut label_counts: std::collections::HashMap<String, (f32, u32)> = std::collections::HashMap::new();
for page_text in pages {
if page_text.is_empty() {
continue;
}
let (labels, _usage) = page_classifier::classify_one(page_text, &ctx, config).await?;
for label in labels {
all_labels.push(label.clone());
let count = label_counts.entry(label.label).or_insert((0.0, 0));
if let Some(conf) = label.confidence {
count.0 += conf;
}
count.1 += 1;
}
}
if config.multi_label {
all_labels.sort_by(|a, b| a.label.cmp(&b.label));
all_labels.dedup_by(|a, b| a.label == b.label);
Ok(all_labels)
} else {
if all_labels.is_empty() {
return Ok(Vec::new());
}
let best = all_labels.into_iter().max_by(|a, b| {
let a_score = a.confidence.unwrap_or(0.0);
let b_score = b.confidence.unwrap_or(0.0);
a_score.partial_cmp(&b_score).unwrap_or(std::cmp::Ordering::Equal)
});
Ok(best.into_iter().collect())
}
}