use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
use crate::worker::{PlaywrightWorker, ScrapeOptions};
use url::Url;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SemanticDocument {
pub name: String,
pub url: String,
pub extension: String,
pub summary: String,
pub category: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SemanticExtractionResult {
pub documents: Vec<SemanticDocument>,
}
const DOC_EXTRACT_PROMPT: &str = r#"
Extrae una lista de todos los documentos, archivos, reportes, decretos o leyes descargables mencionados en el texto.
Para cada documento, extrae obligatoriamente los siguientes campos:
- name: El título real, limpio y descriptivo del documento (ej. "Ley Orgánica del Ambiente").
- url: El enlace o URL de descarga mencionado (debe ser el enlace exacto provisto en el texto).
- extension: La extensión del archivo en minúsculas (pdf, xlsx, csv, doc, etc.).
- summary: Un resumen de una sola frase sobre el contenido del documento.
- category: Categoría (ej. "Ley", "Reglamento", "Decreto", "Estadística", "Estudio").
Formato de salida estricto (JSON):
{
"documents": [
{
"name": "Nombre descriptivo",
"url": "https://enlace-de-descarga",
"extension": "pdf",
"summary": "Resumen del contenido...",
"category": "Ley"
}
]
}
Si no se menciona ningún documento descargable, responde estrictamente con {"documents": []}.
"#;
pub fn extract_semantic_documents(
worker: &mut PlaywrightWorker,
compressed_text: &str,
base_url: &str,
) -> Result<Vec<SemanticDocument>> {
let options = ScrapeOptions {
deep_scan: false,
semantic_embeddings: None,
text: Some(compressed_text.to_string()),
prompt: Some(DOC_EXTRACT_PROMPT.to_string()),
};
let response = worker.scrape("extract", Some(options))?;
if response.status != "success" {
return Err(anyhow!(
"El extractor semántico falló: {}",
response.message.unwrap_or_else(|| "Error desconocido".to_string())
));
}
let raw_data = response
.data
.ok_or_else(|| anyhow!("El worker no devolvió datos estructurados"))?;
let mut extraction: SemanticExtractionResult = serde_json::from_value(raw_data)
.map_err(|e| anyhow!("Error al deserializar la extracción semántica de documentos: {}", e))?;
let base = Url::parse(base_url)?;
for doc in &mut extraction.documents {
if let Ok(full_url) = base.join(&doc.url) {
doc.url = full_url.to_string();
}
}
Ok(extraction.documents)
}