fix #14170 fix #13893 fix #13673 fix #13543 fix #13308 fix #7607 #### PR Dependency Tree * **PR #14247** * **PR #14248** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Integrations panel in Account Settings to link/unlink calendar providers. * Collapsible settings wrapper for improved layout. * **Improvements** * Calendar system reworked: per-account calendar groups, simplified toggles with explicit Save, richer event display (multi-dot date indicators), improved event time/title handling across journal views. * **Localization** * Added calendar keys: save-error, no-journal, no-calendar; removed legacy duplicate-error keys. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
86 lines
2.3 KiB
Rust
86 lines
2.3 KiB
Rust
use std::{collections::HashMap, io::Cursor};
|
|
|
|
use serde_json::Value;
|
|
|
|
/**
|
|
* modified from https://github.com/Abraxas-365/langchain-rust/tree/v4.6.0/src/document_loaders
|
|
*/
|
|
use super::*;
|
|
#[derive(Debug, Clone)]
|
|
pub struct HtmlLoader<R> {
|
|
html: R,
|
|
url: Url,
|
|
}
|
|
|
|
impl HtmlLoader<Cursor<Vec<u8>>> {
|
|
pub fn from_string<S: Into<String>>(input: S, url: Url) -> Self {
|
|
let input = input.into();
|
|
let reader = Cursor::new(input.into_bytes());
|
|
Self::new(reader, url)
|
|
}
|
|
}
|
|
|
|
impl<R: Read> HtmlLoader<R> {
|
|
pub fn new(html: R, url: Url) -> Self {
|
|
Self { html, url }
|
|
}
|
|
}
|
|
|
|
impl<R: Read + Send + Sync + 'static> Loader for HtmlLoader<R> {
|
|
fn load(mut self) -> LoaderResult<Vec<Document>> {
|
|
let cleaned_html = readability::extractor::extract(&mut self.html, &self.url)?;
|
|
let doc = Document::new(format!("{}\n{}", cleaned_html.title, cleaned_html.text))
|
|
.with_metadata(HashMap::from([("source".to_string(), Value::from(self.url.as_str()))]));
|
|
|
|
Ok(vec![doc])
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_html_loader() {
|
|
let input = "<p>Hello world!</p>";
|
|
|
|
let html_loader = HtmlLoader::new(input.as_bytes(), Url::parse("https://example.com/").unwrap());
|
|
|
|
let documents = html_loader.load().unwrap();
|
|
|
|
let expected = "\nHello world!";
|
|
|
|
assert_eq!(documents.len(), 1);
|
|
assert_eq!(
|
|
documents[0].metadata.get("source").unwrap(),
|
|
&Value::from("https://example.com/")
|
|
);
|
|
assert_eq!(documents[0].page_content, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn test_html_load_from_path() {
|
|
let buffer = include_bytes!("../../../fixtures/sample.html");
|
|
let html_loader = HtmlLoader::new(Cursor::new(buffer), Url::parse("https://example.com/").unwrap());
|
|
|
|
let documents = html_loader.load().unwrap();
|
|
|
|
let expected = [
|
|
"Example Domain",
|
|
"",
|
|
" This domain is for use in illustrative examples in documents. You may",
|
|
" use this domain in literature without prior coordination or asking for",
|
|
" permission.",
|
|
" More information...",
|
|
]
|
|
.join("\n");
|
|
|
|
assert_eq!(documents.len(), 1);
|
|
assert_eq!(
|
|
documents[0].metadata.get("source").unwrap(),
|
|
&Value::from("https://example.com/")
|
|
);
|
|
assert_eq!(documents[0].page_content, expected);
|
|
}
|
|
}
|