pub mod embeddings;
pub mod vectordb;
use std::sync::Arc;
use async_trait::async_trait;
use serde_json::{Map, Value};
use crate::config::KnowledgeSettings;
use crate::error::Result;
use crate::tools::knowledge_tool::{KnowledgeHit, KnowledgeProvider};
use embeddings::Embedder;
use vectordb::{KnowledgeEntry, SearchResult, VectorStore};
#[derive(Debug)]
pub struct KnowledgeBase {
store: VectorStore,
config: KnowledgeSettings,
}
impl KnowledgeBase {
pub async fn new(config: &KnowledgeSettings) -> Result<Self> {
let model_name = config.embedding_model.clone();
let embedder = tokio::task::spawn_blocking(move || Embedder::new(&model_name))
.await
.map_err(|e| {
crate::error::AgentError::KnowledgeError(format!("Embedder init panicked: {}", e))
})??;
let embedder = Arc::new(embedder);
let store = VectorStore::new(&config.db_path, embedder, &config.default_collection).await?;
store.create_collection(&config.default_collection).await?;
Ok(Self {
store,
config: config.clone(),
})
}
pub async fn query_existing(&self, query: &str) -> Result<Vec<SearchResult>> {
let limit = self.config.max_results;
self.store
.search(&self.config.default_collection, query, limit)
.await
}
pub async fn store_results(
&self,
collection: &str,
entries: Vec<KnowledgeEntry>,
) -> Result<()> {
self.store.insert(collection, entries).await
}
pub async fn create_collection(&self, name: &str) -> Result<()> {
self.store.create_collection(name).await
}
pub async fn list_collections(&self) -> Result<Vec<String>> {
self.store.list_collections().await
}
}
#[async_trait]
impl KnowledgeProvider for KnowledgeBase {
async fn query(
&self,
query: &str,
collection: &str,
limit: usize,
) -> std::result::Result<Vec<KnowledgeHit>, String> {
let results = self
.store
.search(collection, query, limit)
.await
.map_err(|e| format!("Knowledge query failed: {}", e))?;
let hits: Vec<KnowledgeHit> = results
.into_iter()
.filter(|r| {
r.score >= self.config.similarity_threshold
})
.map(|r| KnowledgeHit {
text: r.text,
score: r.score,
metadata: if r.metadata.is_null() {
None
} else if let Value::Object(map) = r.metadata {
Some(map)
} else {
let mut map = Map::new();
map.insert("value".into(), r.metadata);
Some(map)
},
})
.collect();
Ok(hits)
}
}