mirror of
https://github.com/perstarkse/minne.git
synced 2026-06-30 18:11:34 +02:00
fix: leaner error handling by boxing large variants
This commit is contained in:
+56
-6
@@ -9,7 +9,7 @@ use crate::storage::types::file_info::FileError;
|
|||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum EmbeddingError {
|
pub enum EmbeddingError {
|
||||||
#[error("openai error: {0}")]
|
#[error("openai error: {0}")]
|
||||||
OpenAI(#[from] OpenAIError),
|
OpenAI(Box<OpenAIError>),
|
||||||
#[error("fastembed error: {0}")]
|
#[error("fastembed error: {0}")]
|
||||||
FastEmbed(String),
|
FastEmbed(String),
|
||||||
#[error("task join error: {0}")]
|
#[error("task join error: {0}")]
|
||||||
@@ -24,6 +24,12 @@ pub enum EmbeddingError {
|
|||||||
UnknownModel(String),
|
UnknownModel(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<OpenAIError> for EmbeddingError {
|
||||||
|
fn from(err: OpenAIError) -> Self {
|
||||||
|
Self::OpenAI(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EmbeddingError {
|
impl EmbeddingError {
|
||||||
pub(crate) fn fastembed(err: impl std::fmt::Display) -> Self {
|
pub(crate) fn fastembed(err: impl std::fmt::Display) -> Self {
|
||||||
Self::FastEmbed(err.to_string())
|
Self::FastEmbed(err.to_string())
|
||||||
@@ -39,9 +45,9 @@ impl EmbeddingError {
|
|||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum AppError {
|
pub enum AppError {
|
||||||
#[error("database error: {0}")]
|
#[error("database error: {0}")]
|
||||||
Database(#[from] surrealdb::Error),
|
Database(Box<surrealdb::Error>),
|
||||||
#[error("openai error: {0}")]
|
#[error("openai error: {0}")]
|
||||||
OpenAI(#[from] OpenAIError),
|
OpenAI(Box<OpenAIError>),
|
||||||
#[error("embedding error: {0}")]
|
#[error("embedding error: {0}")]
|
||||||
Embedding(#[from] EmbeddingError),
|
Embedding(#[from] EmbeddingError),
|
||||||
#[error("file error: {0}")]
|
#[error("file error: {0}")]
|
||||||
@@ -61,17 +67,47 @@ pub enum AppError {
|
|||||||
#[error("io error: {0}")]
|
#[error("io error: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
#[error("reqwest error: {0}")]
|
#[error("reqwest error: {0}")]
|
||||||
Reqwest(#[from] reqwest::Error),
|
Reqwest(Box<reqwest::Error>),
|
||||||
#[error("storage error: {0}")]
|
#[error("storage error: {0}")]
|
||||||
Storage(#[from] object_store::Error),
|
Storage(Box<object_store::Error>),
|
||||||
#[error("ingestion processing error: {0}")]
|
#[error("ingestion processing error: {0}")]
|
||||||
Processing(String),
|
Processing(String),
|
||||||
#[error("dom smoothie error: {0}")]
|
#[error("dom smoothie error: {0}")]
|
||||||
DomSmoothie(#[from] dom_smoothie::ReadabilityError),
|
DomSmoothie(Box<dom_smoothie::ReadabilityError>),
|
||||||
#[error("internal service error: {0}")]
|
#[error("internal service error: {0}")]
|
||||||
InternalError(String),
|
InternalError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<surrealdb::Error> for AppError {
|
||||||
|
fn from(err: surrealdb::Error) -> Self {
|
||||||
|
Self::Database(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OpenAIError> for AppError {
|
||||||
|
fn from(err: OpenAIError) -> Self {
|
||||||
|
Self::OpenAI(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::Error> for AppError {
|
||||||
|
fn from(err: reqwest::Error) -> Self {
|
||||||
|
Self::Reqwest(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<object_store::Error> for AppError {
|
||||||
|
fn from(err: object_store::Error) -> Self {
|
||||||
|
Self::Storage(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<dom_smoothie::ReadabilityError> for AppError {
|
||||||
|
fn from(err: dom_smoothie::ReadabilityError) -> Self {
|
||||||
|
Self::DomSmoothie(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AppError {
|
impl AppError {
|
||||||
/// Builds an [`AppError::InternalError`] from a displayable message.
|
/// Builds an [`AppError::InternalError`] from a displayable message.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -79,3 +115,17 @@ impl AppError {
|
|||||||
Self::InternalError(msg.to_string())
|
Self::InternalError(msg.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::AppError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn app_error_is_reasonably_sized() {
|
||||||
|
assert!(
|
||||||
|
std::mem::size_of::<AppError>() <= 64,
|
||||||
|
"AppError is {} bytes",
|
||||||
|
std::mem::size_of::<AppError>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ pub enum FileError {
|
|||||||
|
|
||||||
/// Database operation on the file record failed.
|
/// Database operation on the file record failed.
|
||||||
#[error("surrealdb error: {0}")]
|
#[error("surrealdb error: {0}")]
|
||||||
SurrealError(#[from] surrealdb::Error),
|
SurrealError(Box<surrealdb::Error>),
|
||||||
|
|
||||||
/// Failed to persist the temporary file to its final location.
|
/// Failed to persist the temporary file to its final location.
|
||||||
#[error("failed to persist file: {0}")]
|
#[error("failed to persist file: {0}")]
|
||||||
@@ -48,7 +48,19 @@ pub enum FileError {
|
|||||||
|
|
||||||
/// The underlying object store operation failed.
|
/// The underlying object store operation failed.
|
||||||
#[error("object store error: {0}")]
|
#[error("object store error: {0}")]
|
||||||
ObjectStore(#[from] ObjectStoreError),
|
ObjectStore(Box<ObjectStoreError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<surrealdb::Error> for FileError {
|
||||||
|
fn from(err: surrealdb::Error) -> Self {
|
||||||
|
Self::SurrealError(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ObjectStoreError> for FileError {
|
||||||
|
fn from(err: ObjectStoreError) -> Self {
|
||||||
|
Self::ObjectStore(Box::new(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stored_object!(FileInfo, "file", {
|
stored_object!(FileInfo, "file", {
|
||||||
@@ -163,7 +175,7 @@ impl FileInfo {
|
|||||||
match db_client.get_item::<FileInfo>(id).await {
|
match db_client.get_item::<FileInfo>(id).await {
|
||||||
Ok(Some(file_info)) => Ok(file_info),
|
Ok(Some(file_info)) => Ok(file_info),
|
||||||
Ok(None) => Err(FileError::FileNotFound(id.to_string())),
|
Ok(None) => Err(FileError::FileNotFound(id.to_string())),
|
||||||
Err(e) => Err(FileError::SurrealError(e)),
|
Err(e) => Err(FileError::from(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +245,7 @@ impl FileInfo {
|
|||||||
if let Ok(existing) = Self::get_by_sha(&sha256, user_id, db_client).await {
|
if let Ok(existing) = Self::get_by_sha(&sha256, user_id, db_client).await {
|
||||||
return Ok(existing);
|
return Ok(existing);
|
||||||
}
|
}
|
||||||
Err(FileError::SurrealError(e))
|
Err(FileError::from(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +275,7 @@ impl FileInfo {
|
|||||||
storage
|
storage
|
||||||
.delete_prefix(&parent_prefix)
|
.delete_prefix(&parent_prefix)
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Storage)?;
|
.map_err(AppError::from)?;
|
||||||
info!(
|
info!(
|
||||||
"Removed object prefix {} and its contents via StorageManager",
|
"Removed object prefix {} and its contents via StorageManager",
|
||||||
parent_prefix
|
parent_prefix
|
||||||
@@ -286,7 +298,7 @@ impl FileInfo {
|
|||||||
&self,
|
&self,
|
||||||
storage: &StorageManager,
|
storage: &StorageManager,
|
||||||
) -> Result<bytes::Bytes, AppError> {
|
) -> Result<bytes::Bytes, AppError> {
|
||||||
storage.get(&self.path).await.map_err(AppError::Storage)
|
storage.get(&self.path).await.map_err(AppError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Persist bytes to storage using StorageManager.
|
/// Persist bytes to storage using StorageManager.
|
||||||
|
|||||||
@@ -183,9 +183,9 @@ impl KnowledgeEntity {
|
|||||||
.bind(("sources", source_ids.to_vec()))
|
.bind(("sources", source_ids.to_vec()))
|
||||||
.bind(("user_id", user_id.to_owned()))
|
.bind(("user_id", user_id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.take(0)
|
.take(0)
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(entities)
|
Ok(entities)
|
||||||
}
|
}
|
||||||
@@ -203,9 +203,9 @@ impl KnowledgeEntity {
|
|||||||
.bind(("table", Self::table_name()))
|
.bind(("table", Self::table_name()))
|
||||||
.bind(("source_id", source_id.to_owned()))
|
.bind(("source_id", source_id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -248,9 +248,9 @@ impl KnowledgeEntity {
|
|||||||
.bind(("entity", entity))
|
.bind(("entity", entity))
|
||||||
.bind(("emb", emb))
|
.bind(("emb", emb))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -289,11 +289,11 @@ impl KnowledgeEntity {
|
|||||||
.bind(("embedding", query_embedding))
|
.bind(("embedding", query_embedding))
|
||||||
.bind(("user_id", user_id.to_string()))
|
.bind(("user_id", user_id.to_string()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
response = response.check().map_err(AppError::Database)?;
|
response = response.check().map_err(AppError::from)?;
|
||||||
|
|
||||||
let rows: Vec<Row> = response.take::<Vec<Row>>(0).map_err(AppError::Database)?;
|
let rows: Vec<Row> = response.take::<Vec<Row>>(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -321,7 +321,7 @@ impl KnowledgeEntity {
|
|||||||
let entity: KnowledgeEntity = db_client
|
let entity: KnowledgeEntity = db_client
|
||||||
.get_item(id)
|
.get_item(id)
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.ok_or_else(|| AppError::NotFound(format!("entity {id} not found")))?;
|
.ok_or_else(|| AppError::NotFound(format!("entity {id} not found")))?;
|
||||||
|
|
||||||
let settings = SystemSettings::get_current(db_client).await?;
|
let settings = SystemSettings::get_current(db_client).await?;
|
||||||
@@ -355,9 +355,9 @@ impl KnowledgeEntity {
|
|||||||
.bind(("emb", emb))
|
.bind(("emb", emb))
|
||||||
.bind(("description", description.to_string()))
|
.bind(("description", description.to_string()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -453,9 +453,9 @@ impl KnowledgeEntity {
|
|||||||
KnowledgeEntityEmbedding::table_name()
|
KnowledgeEntityEmbedding::table_name()
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
db.client
|
db.client
|
||||||
.query(format!(
|
.query(format!(
|
||||||
@@ -463,9 +463,9 @@ impl KnowledgeEntity {
|
|||||||
KnowledgeEntityEmbedding::table_name()
|
KnowledgeEntityEmbedding::table_name()
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
// Perform DB updates in a single transaction
|
// Perform DB updates in a single transaction
|
||||||
info!("Applying embedding updates in a transaction...");
|
info!("Applying embedding updates in a transaction...");
|
||||||
@@ -504,9 +504,9 @@ impl KnowledgeEntity {
|
|||||||
db.client
|
db.client
|
||||||
.query(transaction_query)
|
.query(transaction_query)
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
info!("Re-embedding process for knowledge entities completed successfully.");
|
info!("Re-embedding process for knowledge entities completed successfully.");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ impl KnowledgeEntityEmbedding {
|
|||||||
dimension,
|
dimension,
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = db.client.query(query).await.map_err(AppError::Database)?;
|
let res = db.client.query(query).await.map_err(AppError::from)?;
|
||||||
res.check().map_err(AppError::Database)?;
|
res.check().map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,8 @@ impl KnowledgeEntityEmbedding {
|
|||||||
.query(query)
|
.query(query)
|
||||||
.bind(("entity_id", entity_id.clone()))
|
.bind(("entity_id", entity_id.clone()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
let embeddings: Vec<Self> = result.take(0).map_err(AppError::Database)?;
|
let embeddings: Vec<Self> = result.take(0).map_err(AppError::from)?;
|
||||||
Ok(embeddings.into_iter().next())
|
Ok(embeddings.into_iter().next())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +101,8 @@ impl KnowledgeEntityEmbedding {
|
|||||||
.query(query)
|
.query(query)
|
||||||
.bind(("entity_ids", entity_ids.to_vec()))
|
.bind(("entity_ids", entity_ids.to_vec()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
let embeddings: Vec<Self> = result.take(0).map_err(AppError::Database)?;
|
let embeddings: Vec<Self> = result.take(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(embeddings
|
Ok(embeddings
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -123,9 +123,9 @@ impl KnowledgeEntityEmbedding {
|
|||||||
.query(query)
|
.query(query)
|
||||||
.bind(("entity_id", entity_id.clone()))
|
.bind(("entity_id", entity_id.clone()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,9 +142,9 @@ impl KnowledgeEntityEmbedding {
|
|||||||
.query(query)
|
.query(query)
|
||||||
.bind(("source_id", source_id.to_owned()))
|
.bind(("source_id", source_id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ impl KnowledgeRelationship {
|
|||||||
.bind(("source_id", self.metadata.source_id.clone()))
|
.bind(("source_id", self.metadata.source_id.clone()))
|
||||||
.bind(("relationship_type", self.metadata.relationship_type.clone()))
|
.bind(("relationship_type", self.metadata.relationship_type.clone()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -89,9 +89,9 @@ impl KnowledgeRelationship {
|
|||||||
.bind(("source_id", source_id.to_owned()))
|
.bind(("source_id", source_id.to_owned()))
|
||||||
.bind(("user_id", user_id.to_owned()))
|
.bind(("user_id", user_id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -109,9 +109,9 @@ impl KnowledgeRelationship {
|
|||||||
.bind(("id", id.to_owned()))
|
.bind(("id", id.to_owned()))
|
||||||
.bind(("user_id", user_id.to_owned()))
|
.bind(("user_id", user_id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
let deleted: Vec<KnowledgeRelationship> =
|
let deleted: Vec<KnowledgeRelationship> =
|
||||||
delete_result.take(0).map_err(AppError::Database)?;
|
delete_result.take(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
if !deleted.is_empty() {
|
if !deleted.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -122,9 +122,9 @@ impl KnowledgeRelationship {
|
|||||||
.query("SELECT * FROM type::thing('relates_to', $id)")
|
.query("SELECT * FROM type::thing('relates_to', $id)")
|
||||||
.bind(("id", id.to_owned()))
|
.bind(("id", id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
let existing: Option<KnowledgeRelationship> =
|
let existing: Option<KnowledgeRelationship> =
|
||||||
exists_result.take(0).map_err(AppError::Database)?;
|
exists_result.take(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
if existing.is_some() {
|
if existing.is_some() {
|
||||||
Err(AppError::Auth(
|
Err(AppError::Auth(
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ impl TextChunk {
|
|||||||
.bind(("source_id", source_id.to_owned()))
|
.bind(("source_id", source_id.to_owned()))
|
||||||
.bind(("table", Self::table_name()))
|
.bind(("table", Self::table_name()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -97,9 +97,9 @@ impl TextChunk {
|
|||||||
.bind(("chunk", chunk))
|
.bind(("chunk", chunk))
|
||||||
.bind(("emb", emb))
|
.bind(("emb", emb))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -140,11 +140,11 @@ impl TextChunk {
|
|||||||
.bind(("embedding", query_embedding))
|
.bind(("embedding", query_embedding))
|
||||||
.bind(("user_id", user_id.to_string()))
|
.bind(("user_id", user_id.to_string()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
response = response.check().map_err(AppError::Database)?;
|
response = response.check().map_err(AppError::from)?;
|
||||||
|
|
||||||
let rows: Vec<Row> = response.take::<Vec<Row>>(0).map_err(AppError::Database)?;
|
let rows: Vec<Row> = response.take::<Vec<Row>>(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -208,11 +208,11 @@ impl TextChunk {
|
|||||||
.bind(("user_id", user_id.to_owned()))
|
.bind(("user_id", user_id.to_owned()))
|
||||||
.bind(("limit", limit))
|
.bind(("limit", limit))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
response = response.check().map_err(AppError::Database)?;
|
response = response.check().map_err(AppError::from)?;
|
||||||
|
|
||||||
let rows: Vec<Row> = response.take::<Vec<Row>>(0).map_err(AppError::Database)?;
|
let rows: Vec<Row> = response.take::<Vec<Row>>(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -314,16 +314,16 @@ impl TextChunk {
|
|||||||
TextChunkEmbedding::table_name()
|
TextChunkEmbedding::table_name()
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
db.client
|
db.client
|
||||||
.query(format!("DELETE FROM {};", TextChunkEmbedding::table_name()))
|
.query(format!("DELETE FROM {};", TextChunkEmbedding::table_name()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
// Perform DB updates in a single transaction against the embedding table
|
// Perform DB updates in a single transaction against the embedding table
|
||||||
info!("Applying embedding updates in a transaction...");
|
info!("Applying embedding updates in a transaction...");
|
||||||
@@ -366,9 +366,9 @@ impl TextChunk {
|
|||||||
db.client
|
db.client
|
||||||
.query(transaction_query)
|
.query(transaction_query)
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
info!("Re-embedding process for text chunks completed successfully.");
|
info!("Re-embedding process for text chunks completed successfully.");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ impl TextChunkEmbedding {
|
|||||||
dimension,
|
dimension,
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = db.client.query(query).await.map_err(AppError::Database)?;
|
let res = db.client.query(query).await.map_err(AppError::from)?;
|
||||||
res.check().map_err(AppError::Database)?;
|
res.check().map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -85,9 +85,9 @@ impl TextChunkEmbedding {
|
|||||||
.query(query)
|
.query(query)
|
||||||
.bind(("chunk_id", chunk_id.clone()))
|
.bind(("chunk_id", chunk_id.clone()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
let embeddings: Vec<Self> = result.take(0).map_err(AppError::Database)?;
|
let embeddings: Vec<Self> = result.take(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(embeddings.into_iter().next())
|
Ok(embeddings.into_iter().next())
|
||||||
}
|
}
|
||||||
@@ -106,9 +106,9 @@ impl TextChunkEmbedding {
|
|||||||
.query(query)
|
.query(query)
|
||||||
.bind(("chunk_id", chunk_id.clone()))
|
.bind(("chunk_id", chunk_id.clone()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -129,9 +129,9 @@ impl TextChunkEmbedding {
|
|||||||
.query(query)
|
.query(query)
|
||||||
.bind(("source_id", source_id.to_owned()))
|
.bind(("source_id", source_id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.check()
|
.check()
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ impl TextContent {
|
|||||||
surrealdb::Datetime::from(now),
|
surrealdb::Datetime::from(now),
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
if updated.is_none() {
|
if updated.is_none() {
|
||||||
return Err(AppError::NotFound(format!("text content {id} not found")));
|
return Err(AppError::NotFound(format!("text content {id} not found")));
|
||||||
@@ -138,10 +138,10 @@ impl TextContent {
|
|||||||
.bind(("file_id", file_id.to_owned()))
|
.bind(("file_id", file_id.to_owned()))
|
||||||
.bind(("exclude_id", exclude_id.to_owned()))
|
.bind(("exclude_id", exclude_id.to_owned()))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
let existing: Option<surrealdb::sql::Thing> =
|
let existing: Option<surrealdb::sql::Thing> =
|
||||||
response.take(0).map_err(AppError::Database)?;
|
response.take(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(existing.is_some())
|
Ok(existing.is_some())
|
||||||
}
|
}
|
||||||
@@ -193,9 +193,9 @@ impl TextContent {
|
|||||||
.bind(("user_id", user_id.to_owned()))
|
.bind(("user_id", user_id.to_owned()))
|
||||||
.bind(("limit", limit))
|
.bind(("limit", limit))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?
|
.map_err(AppError::from)?
|
||||||
.take(0)
|
.take(0)
|
||||||
.map_err(AppError::Database)
|
.map_err(AppError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a fallback display label for a source id when no matching content row exists.
|
/// Builds a fallback display label for a source id when no matching content row exists.
|
||||||
@@ -239,9 +239,9 @@ impl TextContent {
|
|||||||
.bind(("user_id", user_id.to_owned()))
|
.bind(("user_id", user_id.to_owned()))
|
||||||
.bind(("record_ids", record_ids))
|
.bind(("record_ids", record_ids))
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
let contents: Vec<SourceLabelRow> = response.take(0).map_err(AppError::Database)?;
|
let contents: Vec<SourceLabelRow> = response.take(0).map_err(AppError::from)?;
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
source_id_count = source_ids.len(),
|
source_id_count = source_ids.len(),
|
||||||
|
|||||||
@@ -688,7 +688,7 @@ impl User {
|
|||||||
|
|
||||||
db.delete_item::<IngestionTask>(id)
|
db.delete_item::<IngestionTask>(id)
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Database)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
--leading-snug: 1.375;
|
--leading-snug: 1.375;
|
||||||
--leading-relaxed: 1.625;
|
--leading-relaxed: 1.625;
|
||||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||||
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
--default-transition-duration: 150ms;
|
--default-transition-duration: 150ms;
|
||||||
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
@@ -284,6 +285,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.drawer-open {
|
||||||
|
> .drawer-side {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
> .drawer-toggle {
|
||||||
|
display: none;
|
||||||
|
& ~ .drawer-side {
|
||||||
|
pointer-events: auto;
|
||||||
|
visibility: visible;
|
||||||
|
position: sticky;
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
overscroll-behavior: auto;
|
||||||
|
opacity: 100%;
|
||||||
|
& > .drawer-overlay {
|
||||||
|
cursor: default;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
& > *:not(.drawer-overlay) {
|
||||||
|
translate: 0%;
|
||||||
|
[dir="rtl"] & {
|
||||||
|
translate: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:checked ~ .drawer-side {
|
||||||
|
pointer-events: auto;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.drawer-toggle {
|
.drawer-toggle {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
height: calc(0.25rem * 0);
|
height: calc(0.25rem * 0);
|
||||||
@@ -1042,6 +1074,22 @@
|
|||||||
grid-row-start: 1;
|
grid-row-start: 1;
|
||||||
min-width: calc(0.25rem * 0);
|
min-width: calc(0.25rem * 0);
|
||||||
}
|
}
|
||||||
|
.chat-image {
|
||||||
|
grid-row: span 2 / span 2;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
.chat-footer {
|
||||||
|
grid-row-start: 3;
|
||||||
|
display: flex;
|
||||||
|
gap: calc(0.25rem * 1);
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
|
.chat-header {
|
||||||
|
grid-row-start: 1;
|
||||||
|
display: flex;
|
||||||
|
gap: calc(0.25rem * 1);
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
@@ -1748,6 +1796,9 @@
|
|||||||
.w-10 {
|
.w-10 {
|
||||||
width: calc(var(--spacing) * 10);
|
width: calc(var(--spacing) * 10);
|
||||||
}
|
}
|
||||||
|
.w-11 {
|
||||||
|
width: calc(var(--spacing) * 11);
|
||||||
|
}
|
||||||
.w-11\/12 {
|
.w-11\/12 {
|
||||||
width: calc(11/12 * 100%);
|
width: calc(11/12 * 100%);
|
||||||
}
|
}
|
||||||
@@ -1811,6 +1862,9 @@
|
|||||||
.flex-none {
|
.flex-none {
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
|
.flex-shrink {
|
||||||
|
flex-shrink: 1;
|
||||||
|
}
|
||||||
.flex-shrink-0 {
|
.flex-shrink-0 {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@@ -1823,6 +1877,13 @@
|
|||||||
.grow {
|
.grow {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
.border-collapse {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
.-translate-y-1 {
|
||||||
|
--tw-translate-y: calc(var(--spacing) * -1);
|
||||||
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
|
}
|
||||||
.-translate-y-1\/2 {
|
.-translate-y-1\/2 {
|
||||||
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
|
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
@@ -1895,6 +1956,9 @@
|
|||||||
.justify-start {
|
.justify-start {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
.gap-0 {
|
||||||
|
gap: calc(var(--spacing) * 0);
|
||||||
|
}
|
||||||
.gap-0\.5 {
|
.gap-0\.5 {
|
||||||
gap: calc(var(--spacing) * 0.5);
|
gap: calc(var(--spacing) * 0.5);
|
||||||
}
|
}
|
||||||
@@ -2027,6 +2091,9 @@
|
|||||||
.border-base-200 {
|
.border-base-200 {
|
||||||
border-color: var(--color-base-200);
|
border-color: var(--color-base-200);
|
||||||
}
|
}
|
||||||
|
.border-base-content {
|
||||||
|
border-color: var(--color-base-content);
|
||||||
|
}
|
||||||
.border-base-content\/10 {
|
.border-base-content\/10 {
|
||||||
border-color: var(--color-base-content);
|
border-color: var(--color-base-content);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
@@ -2063,6 +2130,9 @@
|
|||||||
.bg-transparent {
|
.bg-transparent {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
.bg-warning {
|
||||||
|
background-color: var(--color-warning);
|
||||||
|
}
|
||||||
.bg-warning\/10 {
|
.bg-warning\/10 {
|
||||||
background-color: var(--color-warning);
|
background-color: var(--color-warning);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
@@ -2081,6 +2151,9 @@
|
|||||||
.loading-spinner {
|
.loading-spinner {
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");
|
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.mask-repeat {
|
||||||
|
mask-repeat: repeat;
|
||||||
|
}
|
||||||
.fill-current {
|
.fill-current {
|
||||||
fill: currentcolor;
|
fill: currentcolor;
|
||||||
}
|
}
|
||||||
@@ -2111,6 +2184,9 @@
|
|||||||
.p-8 {
|
.p-8 {
|
||||||
padding: calc(var(--spacing) * 8);
|
padding: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
|
.px-1 {
|
||||||
|
padding-inline: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
.px-1\.5 {
|
.px-1\.5 {
|
||||||
padding-inline: calc(var(--spacing) * 1.5);
|
padding-inline: calc(var(--spacing) * 1.5);
|
||||||
}
|
}
|
||||||
@@ -2265,6 +2341,9 @@
|
|||||||
--tw-tracking: var(--tracking-widest);
|
--tw-tracking: var(--tracking-widest);
|
||||||
letter-spacing: var(--tracking-widest);
|
letter-spacing: var(--tracking-widest);
|
||||||
}
|
}
|
||||||
|
.text-wrap {
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
.break-words {
|
.break-words {
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
@@ -2331,6 +2410,17 @@
|
|||||||
.italic {
|
.italic {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
.underline {
|
||||||
|
text-decoration-line: underline;
|
||||||
|
}
|
||||||
|
.swap-active {
|
||||||
|
.swap-off {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
.swap-on {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
.opacity-0 {
|
.opacity-0 {
|
||||||
opacity: 0%;
|
opacity: 0%;
|
||||||
}
|
}
|
||||||
@@ -2424,6 +2514,10 @@
|
|||||||
--tw-duration: 300ms;
|
--tw-duration: 300ms;
|
||||||
transition-duration: 300ms;
|
transition-duration: 300ms;
|
||||||
}
|
}
|
||||||
|
.ease-in-out {
|
||||||
|
--tw-ease: var(--ease-in-out);
|
||||||
|
transition-timing-function: var(--ease-in-out);
|
||||||
|
}
|
||||||
.ease-out {
|
.ease-out {
|
||||||
--tw-ease: var(--ease-out);
|
--tw-ease: var(--ease-out);
|
||||||
transition-timing-function: var(--ease-out);
|
transition-timing-function: var(--ease-out);
|
||||||
|
|||||||
@@ -233,8 +233,7 @@ fn plan_embedding_settings_update(
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|value| !value.is_empty())
|
.filter(|value| !value.is_empty())
|
||||||
.map(ToOwned::to_owned)
|
.map_or_else(|| current.embedding_model.clone(), ToOwned::to_owned);
|
||||||
.unwrap_or_else(|| current.embedding_model.clone());
|
|
||||||
|
|
||||||
if !is_valid_fastembed_model_code(&embedding_model) {
|
if !is_valid_fastembed_model_code(&embedding_model) {
|
||||||
return Err(AppError::Validation(format!(
|
return Err(AppError::Validation(format!(
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ pub async fn extract_text_from_file(
|
|||||||
let file_bytes = storage
|
let file_bytes = storage
|
||||||
.get(&file_info.path)
|
.get(&file_info.path)
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::Storage)?;
|
.map_err(AppError::from)?;
|
||||||
let local_path = resolve_existing_local_path(storage, &file_info.path).await;
|
let local_path = resolve_existing_local_path(storage, &file_info.path).await;
|
||||||
|
|
||||||
match file_info.mime_type.as_str() {
|
match file_info.mime_type.as_str() {
|
||||||
|
|||||||
Reference in New Issue
Block a user