use crate::types::ExtractedDocument;
#[cfg(feature = "ner")]
use std::sync::Arc;
#[cfg(feature = "classification")]
use crate::ClassificationLabel;
#[cfg(feature = "ner")]
use crate::types::entity::{Entity, EntityCategory};
#[cfg(feature = "ner")]
pub struct NerEnrichmentConfig {
pub backend: Arc<dyn crate::text::ner::NerBackend>,
pub categories: Vec<EntityCategory>,
}
#[cfg(feature = "classification")]
pub struct ClassificationEnrichmentConfig {
pub config: crate::core::config::PageClassificationConfig,
}
#[cfg(feature = "classification")]
pub struct ChunkClassificationEnrichmentConfig {
pub config: crate::core::config::ChunkClassificationConfig,
}
#[cfg(feature = "captioning")]
pub struct CaptioningEnrichmentConfig {
pub config: crate::core::config::LlmConfig,
pub custom_prompt: Option<String>,
}
#[derive(Default)]
pub struct EnrichmentConfig {
#[cfg(feature = "ner")]
pub ner: Option<NerEnrichmentConfig>,
#[cfg(feature = "classification")]
pub classification: Option<ClassificationEnrichmentConfig>,
#[cfg(feature = "classification")]
pub chunk_classification: Option<ChunkClassificationEnrichmentConfig>,
#[cfg(feature = "captioning")]
pub captioning: Option<CaptioningEnrichmentConfig>,
#[cfg(feature = "transcription-types")]
pub transcription: Option<crate::core::config::TranscriptionConfig>,
}
pub struct EnrichedResult {
pub extraction: ExtractedDocument,
#[cfg(feature = "ner")]
pub entities: Option<Vec<Entity>>,
#[cfg(feature = "classification")]
pub classification: Option<Vec<ClassificationLabel>>,
#[cfg(feature = "captioning")]
pub captions: Option<Vec<String>>,
}
#[cfg_attr(alef, alef(skip))]
#[cfg_attr(not(feature = "classification"), allow(unused_mut))]
pub async fn enrich(mut extraction: ExtractedDocument, config: &EnrichmentConfig) -> crate::Result<EnrichedResult> {
#[cfg(not(any(
feature = "transcription-types",
feature = "classification",
feature = "ner",
feature = "captioning",
)))]
let _ = config;
#[cfg(feature = "transcription-types")]
if config.transcription.is_some() {
return Err(crate::XbergError::Other(
"transcription backend not yet implemented; set config.transcription = None to skip".into(),
));
}
#[cfg(feature = "classification")]
let classification = if let Some(ref cfg) = config.classification {
let pages: Vec<&str> = match extraction.pages.as_deref() {
Some(pages) => pages.iter().map(|p| p.content.as_str()).collect(),
None => vec![extraction.content.as_str()],
};
Some(crate::text::classification::classify_document(&pages, &cfg.config).await?)
} else {
None
};
#[cfg(feature = "classification")]
if let Some(ref cfg) = config.chunk_classification {
crate::text::classification::classify_chunks(&mut extraction, &cfg.config).await?;
}
#[cfg(feature = "ner")]
let entities = if let Some(ref cfg) = config.ner {
Some(crate::text::ner::detect_entities(&extraction.content, cfg.backend.as_ref(), &cfg.categories).await?)
} else {
None
};
#[cfg(feature = "captioning")]
let captions = if let Some(ref cfg) = config.captioning {
match extraction.images.as_deref() {
None | Some([]) => Some(Vec::new()),
Some(images) => {
let mut out = Vec::with_capacity(images.len());
for image in images {
let caption = if image.data.is_empty() {
String::new()
} else {
crate::captioning::caption_image(&image.data, &cfg.config, cfg.custom_prompt.as_deref()).await?
};
out.push(caption);
}
Some(out)
}
}
} else {
None
};
Ok(EnrichedResult {
extraction,
#[cfg(feature = "ner")]
entities,
#[cfg(feature = "classification")]
classification,
#[cfg(feature = "captioning")]
captions,
})
}