use crate::audit::AuditEntry;
use crate::context::ContextEntry;
use crate::conversations::{Conversation, Message};
use crate::db::{AgentDB, DbStats};
use crate::error::Result;
use crate::fts::FtsResult;
use crate::hybrid::{HybridQuery, HybridResult, TriModalQuery, TriModalResult};
use crate::labels::DataLabel;
use crate::memory::{TraversalOptions, TraversalResult};
use crate::prompts::PromptTemplate;
use crate::tools::{Tool, ToolCall};
use crate::traces::Trace;
use crate::vectors::{
BatchEntry, Collection, DistanceMetric, SearchOptions, SearchResult, VectorEntry,
};
use crate::workflows::Workflow;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::task;
#[derive(Clone)]
pub struct AsyncAgentDB {
inner: Arc<AgentDB>,
}
impl AsyncAgentDB {
pub async fn open(path: &str) -> Result<Self> {
let path = path.to_string();
let db = task::spawn_blocking(move || AgentDB::open(&path))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))??;
Ok(Self {
inner: Arc::new(db),
})
}
pub async fn execute(&self, sql: &str) -> Result<usize> {
let db = self.inner.clone();
let sql = sql.to_string();
task::spawn_blocking(move || db.execute(&sql))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn execute_batch(&self, sql: &str) -> Result<()> {
let db = self.inner.clone();
let sql = sql.to_string();
task::spawn_blocking(move || db.execute_batch(&sql))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn query_json(&self, sql: &str) -> Result<Vec<Value>> {
let db = self.inner.clone();
let sql = sql.to_string();
task::spawn_blocking(move || db.query_json(&sql))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn query_json_params(&self, sql: &str, params: Vec<String>) -> Result<Vec<Value>> {
let db = self.inner.clone();
let sql = sql.to_string();
task::spawn_blocking(move || {
let param_refs: Vec<&dyn rusqlite::ToSql> =
params.iter().map(|s| s as &dyn rusqlite::ToSql).collect();
db.query_json_params(&sql, ¶m_refs)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn stats(&self) -> Result<DbStats> {
let db = self.inner.clone();
task::spawn_blocking(move || db.stats())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub fn vectors(&self) -> AsyncVectorStore {
AsyncVectorStore {
inner: self.inner.clone(),
}
}
pub fn memory(&self) -> AsyncMemoryGraph {
AsyncMemoryGraph {
inner: self.inner.clone(),
}
}
pub fn fts(&self) -> AsyncFullTextStore {
AsyncFullTextStore {
inner: self.inner.clone(),
}
}
pub fn conversations(&self) -> AsyncConversationStore {
AsyncConversationStore {
inner: self.inner.clone(),
}
}
pub fn workflows(&self) -> AsyncWorkflowStore {
AsyncWorkflowStore {
inner: self.inner.clone(),
}
}
pub fn traces(&self) -> AsyncTraceStore {
AsyncTraceStore {
inner: self.inner.clone(),
}
}
pub fn tools(&self) -> AsyncToolStore {
AsyncToolStore {
inner: self.inner.clone(),
}
}
pub fn audit(&self) -> AsyncAuditStore {
AsyncAuditStore {
inner: self.inner.clone(),
}
}
pub fn context(&self) -> AsyncContextStore {
AsyncContextStore {
inner: self.inner.clone(),
}
}
pub fn prompts(&self) -> AsyncPromptStore {
AsyncPromptStore {
inner: self.inner.clone(),
}
}
pub fn labels(&self) -> AsyncLabelStore {
AsyncLabelStore {
inner: self.inner.clone(),
}
}
pub async fn tri_modal_query(&self, query: TriModalQuery) -> Result<Vec<TriModalResult>> {
let db = self.inner.clone();
task::spawn_blocking(move || db.tri_modal_query(&query))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn hybrid_query(
&self,
anchor_node: &str,
embedding: Vec<f32>,
collection: &str,
graph_depth: usize,
top_k: usize,
alpha: f64,
filter: Option<Value>,
) -> Result<Vec<HybridResult>> {
let db = self.inner.clone();
let anchor = anchor_node.to_string();
let col = collection.to_string();
task::spawn_blocking(move || {
let q = HybridQuery {
anchor_node: &anchor,
embedding: &embedding,
collection: &col,
graph_depth,
top_k,
alpha,
filter,
};
db.hybrid_query(q)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn close(self) -> Result<()> {
let db = Arc::try_unwrap(self.inner).map_err(|arc| {
crate::error::AgentDbError::InvalidArgument(format!(
"AsyncAgentDB::close called while {} other reference(s) exist",
Arc::strong_count(&arc) - 1
))
})?;
task::spawn_blocking(move || db.close())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncVectorStore {
inner: Arc<AgentDB>,
}
impl AsyncVectorStore {
pub async fn collection(&self, name: &str, dim: usize) -> Result<AsyncCollection> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || {
db.vectors()
.collection(&name, dim)
.map(|c| AsyncCollection { inner: Arc::new(c) })
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn collection_with_metric(
&self,
name: &str,
dim: usize,
metric: DistanceMetric,
) -> Result<AsyncCollection> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || {
db.vectors()
.collection_with_metric(&name, dim, metric)
.map(|c| AsyncCollection { inner: Arc::new(c) })
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn list_collections(&self) -> Result<Vec<(String, usize, i64)>> {
let db = self.inner.clone();
task::spawn_blocking(move || db.vectors().list_collections())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn drop_collection(&self, name: &str) -> Result<()> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || db.vectors().drop_collection(&name))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
#[derive(Clone)]
pub struct AsyncCollection {
inner: Arc<Collection>,
}
impl AsyncCollection {
pub async fn upsert(&self, entry: VectorEntry) -> Result<()> {
let col = self.inner.clone();
task::spawn_blocking(move || col.upsert(entry))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn upsert_batch(&self, entries: Vec<BatchEntry>) -> Result<usize> {
let col = self.inner.clone();
task::spawn_blocking(move || col.upsert_batch(entries))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn search(
&self,
query: Vec<f32>,
options: SearchOptions,
) -> Result<Vec<SearchResult>> {
let col = self.inner.clone();
task::spawn_blocking(move || col.search(&query, options))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn count(&self) -> Result<i64> {
let col = self.inner.clone();
task::spawn_blocking(move || col.count())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn reindex(&self) -> Result<()> {
let col = self.inner.clone();
task::spawn_blocking(move || col.reindex())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn delete(&self, id: &str) -> Result<()> {
let col = self.inner.clone();
let id = id.to_string();
task::spawn_blocking(move || col.delete(&id))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn upsert_with_text(&self, entry: VectorEntry, text: String) -> Result<()> {
let col = self.inner.clone();
task::spawn_blocking(move || col.upsert_with_text(entry, &text))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncMemoryGraph {
inner: Arc<AgentDB>,
}
impl AsyncMemoryGraph {
pub async fn add_node(&self, id: &str, kind: &str, data: Option<Value>) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
let kind = kind.to_string();
task::spawn_blocking(move || db.memory().add_node(&id, &kind, data))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn add_edge(&self, src: &str, dst: &str, relation: &str, weight: f64) -> Result<()> {
let db = self.inner.clone();
let src = src.to_string();
let dst = dst.to_string();
let relation = relation.to_string();
task::spawn_blocking(move || db.memory().add_edge(&src, &dst, &relation, weight))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn neighbors(
&self,
node_id: &str,
opts: TraversalOptions,
) -> Result<Vec<TraversalResult>> {
let db = self.inner.clone();
let node_id = node_id.to_string();
task::spawn_blocking(move || db.memory().neighbors(&node_id, opts))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_node(&self, id: &str) -> Result<crate::memory::Node> {
let db = self.inner.clone();
let id = id.to_string();
task::spawn_blocking(move || db.memory().get_node(&id))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn delete_node(&self, id: &str) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
task::spawn_blocking(move || db.memory().delete_node(&id))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn delete_edge(&self, src: &str, dst: &str, relation: &str) -> Result<()> {
let db = self.inner.clone();
let src = src.to_string();
let dst = dst.to_string();
let relation = relation.to_string();
task::spawn_blocking(move || db.memory().delete_edge(&src, &dst, &relation))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn nodes_by_kind(&self, kind: &str) -> Result<Vec<crate::memory::Node>> {
let db = self.inner.clone();
let kind = kind.to_string();
task::spawn_blocking(move || db.memory().nodes_by_kind(&kind))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncFullTextStore {
inner: Arc<AgentDB>,
}
impl AsyncFullTextStore {
pub async fn index_text(
&self,
collection: &str,
id: &str,
collection_id: &str,
text: &str,
) -> Result<()> {
let db = self.inner.clone();
let collection = collection.to_string();
let id = id.to_string();
let collection_id = collection_id.to_string();
let text = text.to_string();
task::spawn_blocking(move || db.fts().index_text(&collection, &id, &collection_id, &text))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn search(
&self,
collection: &str,
query: &str,
top_k: usize,
) -> Result<Vec<FtsResult>> {
let db = self.inner.clone();
let collection = collection.to_string();
let query = query.to_string();
task::spawn_blocking(move || db.fts().search(&collection, &query, top_k))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn delete_text(&self, collection: &str, id: &str) -> Result<()> {
let db = self.inner.clone();
let collection = collection.to_string();
let id = id.to_string();
task::spawn_blocking(move || db.fts().delete_text(&collection, &id))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn optimize(&self, collection: &str) -> Result<()> {
let db = self.inner.clone();
let collection = collection.to_string();
task::spawn_blocking(move || db.fts().optimize(&collection))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncConversationStore {
inner: Arc<AgentDB>,
}
impl AsyncConversationStore {
pub async fn create_conversation(
&self,
id: &str,
title: Option<&str>,
metadata: Option<Value>,
) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
let title = title.map(|s| s.to_string());
task::spawn_blocking(move || {
db.conversations()
.create_conversation(&id, title.as_deref(), metadata)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn add_message(
&self,
conversation_id: &str,
role: &str,
content: &str,
metadata: Option<Value>,
) -> Result<String> {
let db = self.inner.clone();
let cid = conversation_id.to_string();
let role = role.to_string();
let content = content.to_string();
task::spawn_blocking(move || {
db.conversations()
.add_message(&cid, &role, &content, metadata)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_messages(
&self,
conversation_id: &str,
limit: Option<usize>,
) -> Result<Vec<Message>> {
let db = self.inner.clone();
let cid = conversation_id.to_string();
task::spawn_blocking(move || db.conversations().get_messages(&cid, limit))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn list_conversations(&self) -> Result<Vec<Conversation>> {
let db = self.inner.clone();
task::spawn_blocking(move || db.conversations().list_conversations())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn delete_conversation(&self, id: &str) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
task::spawn_blocking(move || db.conversations().delete_conversation(&id))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn search_messages(
&self,
query: &str,
top_k: usize,
conversation_id: Option<&str>,
) -> Result<Vec<crate::conversations::MessageSearchResult>> {
let db = self.inner.clone();
let q = query.to_string();
let cid = conversation_id.map(|s| s.to_string());
task::spawn_blocking(move || {
db.conversations()
.search_messages(&q, top_k, cid.as_deref())
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncWorkflowStore {
inner: Arc<AgentDB>,
}
impl AsyncWorkflowStore {
pub async fn create_workflow(
&self,
id: &str,
name: &str,
input: Option<Value>,
metadata: Option<Value>,
) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
let name = name.to_string();
task::spawn_blocking(move || db.workflows().create_workflow(&id, &name, input, metadata))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn add_step(
&self,
workflow_id: &str,
name: &str,
input: Option<Value>,
) -> Result<String> {
let db = self.inner.clone();
let wid = workflow_id.to_string();
let name = name.to_string();
task::spawn_blocking(move || db.workflows().add_step(&wid, &name, input))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn update_step(
&self,
step_id: &str,
status: &str,
output: Option<Value>,
error: Option<&str>,
) -> Result<()> {
let db = self.inner.clone();
let sid = step_id.to_string();
let status = status.to_string();
let error = error.map(|s| s.to_string());
task::spawn_blocking(move || {
db.workflows()
.update_step(&sid, &status, output, error.as_deref())
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn complete_workflow(&self, id: &str, output: Option<Value>) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
task::spawn_blocking(move || db.workflows().complete_workflow(&id, output))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn fail_workflow(&self, id: &str, error: Option<&str>) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
let error = error.map(|s| s.to_string());
task::spawn_blocking(move || db.workflows().fail_workflow(&id, error.as_deref()))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_workflow(&self, id: &str) -> Result<Workflow> {
let db = self.inner.clone();
let id = id.to_string();
task::spawn_blocking(move || db.workflows().get_workflow(&id))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn list_workflows(&self, status_filter: Option<&str>) -> Result<Vec<Workflow>> {
let db = self.inner.clone();
let status = status_filter.map(|s| s.to_string());
task::spawn_blocking(move || db.workflows().list_workflows(status.as_deref()))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncTraceStore {
inner: Arc<AgentDB>,
}
impl AsyncTraceStore {
pub async fn add_trace(
&self,
session_id: Option<&str>,
parent_id: Option<&str>,
trace_type: &str,
content: &str,
metadata: Option<Value>,
) -> Result<String> {
let db = self.inner.clone();
let sid = session_id.map(|s| s.to_string());
let pid = parent_id.map(|s| s.to_string());
let tt = trace_type.to_string();
let content = content.to_string();
task::spawn_blocking(move || {
db.traces()
.add_trace(sid.as_deref(), pid.as_deref(), &tt, &content, metadata)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_traces(
&self,
session_id: &str,
limit: Option<usize>,
offset: Option<usize>,
) -> Result<Vec<Trace>> {
let db = self.inner.clone();
let sid = session_id.to_string();
task::spawn_blocking(move || db.traces().get_traces(&sid, limit, offset))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_trace_tree(&self, root_id: &str) -> Result<Vec<Trace>> {
let db = self.inner.clone();
let rid = root_id.to_string();
task::spawn_blocking(move || db.traces().get_trace_tree(&rid))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncToolStore {
inner: Arc<AgentDB>,
}
impl AsyncToolStore {
pub async fn register_tool(
&self,
name: &str,
description: Option<&str>,
parameters_schema: Option<Value>,
version: Option<&str>,
) -> Result<String> {
let db = self.inner.clone();
let name = name.to_string();
let desc = description.map(|s| s.to_string());
let ver = version.map(|s| s.to_string());
task::spawn_blocking(move || {
db.tools()
.register_tool(&name, desc.as_deref(), parameters_schema, ver.as_deref())
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_tool(&self, name: &str) -> Result<Tool> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || db.tools().get_tool(&name))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn list_tools(&self) -> Result<Vec<Tool>> {
let db = self.inner.clone();
task::spawn_blocking(move || db.tools().list_tools())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn delete_tool(&self, name: &str) -> Result<()> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || db.tools().delete_tool(&name))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn log_tool_call(
&self,
session_id: Option<&str>,
tool_name: &str,
arguments: Option<Value>,
result: Option<Value>,
error: Option<&str>,
latency_ms: Option<i64>,
) -> Result<String> {
let db = self.inner.clone();
let sid = session_id.map(|s| s.to_string());
let name = tool_name.to_string();
let err = error.map(|s| s.to_string());
task::spawn_blocking(move || {
db.tools()
.log_tool_call(sid.as_deref(), &name, arguments, result, err.as_deref(), latency_ms)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_tool_calls(
&self,
session_id: Option<&str>,
tool_name: Option<&str>,
limit: Option<usize>,
) -> Result<Vec<ToolCall>> {
let db = self.inner.clone();
let sid = session_id.map(|s| s.to_string());
let name = tool_name.map(|s| s.to_string());
task::spawn_blocking(move || {
db.tools()
.get_tool_calls(sid.as_deref(), name.as_deref(), limit)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncAuditStore {
inner: Arc<AgentDB>,
}
impl AsyncAuditStore {
#[allow(clippy::too_many_arguments)]
pub async fn log(
&self,
actor: Option<&str>,
action: &str,
table_name: &str,
record_id: &str,
old_value: Option<Value>,
new_value: Option<Value>,
reason: Option<&str>,
) -> Result<String> {
let db = self.inner.clone();
let actor = actor.map(|s| s.to_string());
let action = action.to_string();
let table = table_name.to_string();
let record = record_id.to_string();
let reason = reason.map(|s| s.to_string());
task::spawn_blocking(move || {
db.audit().log(
actor.as_deref(),
&action,
&table,
&record,
old_value,
new_value,
reason.as_deref(),
)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn query_by_record(
&self,
table_name: &str,
record_id: &str,
limit: Option<usize>,
) -> Result<Vec<AuditEntry>> {
let db = self.inner.clone();
let table = table_name.to_string();
let record = record_id.to_string();
task::spawn_blocking(move || db.audit().query_by_record(&table, &record, limit))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn query_by_actor(&self, actor: &str, limit: Option<usize>) -> Result<Vec<AuditEntry>> {
let db = self.inner.clone();
let actor = actor.to_string();
task::spawn_blocking(move || db.audit().query_by_actor(&actor, limit))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn query_recent(&self, limit: Option<usize>) -> Result<Vec<AuditEntry>> {
let db = self.inner.clone();
task::spawn_blocking(move || db.audit().query_recent(limit))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncContextStore {
inner: Arc<AgentDB>,
}
impl AsyncContextStore {
#[allow(clippy::too_many_arguments)]
pub async fn add_entry(
&self,
session_id: &str,
source_type: &str,
source_id: &str,
content_preview: Option<&str>,
token_count: i64,
relevance_score: f64,
priority: i64,
) -> Result<String> {
let db = self.inner.clone();
let sid = session_id.to_string();
let st = source_type.to_string();
let si = source_id.to_string();
let cp = content_preview.map(|s| s.to_string());
task::spawn_blocking(move || {
db.context()
.add_entry(&sid, &st, &si, cp.as_deref(), token_count, relevance_score, priority)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn build_window(
&self,
session_id: &str,
max_tokens: i64,
) -> Result<Vec<ContextEntry>> {
let db = self.inner.clone();
let sid = session_id.to_string();
task::spawn_blocking(move || db.context().build_window(&sid, max_tokens))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_entries(&self, session_id: &str) -> Result<Vec<ContextEntry>> {
let db = self.inner.clone();
let sid = session_id.to_string();
task::spawn_blocking(move || db.context().get_entries(&sid))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn clear_session(&self, session_id: &str) -> Result<()> {
let db = self.inner.clone();
let sid = session_id.to_string();
task::spawn_blocking(move || db.context().clear_session(&sid))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn remove_entry(&self, id: &str) -> Result<()> {
let db = self.inner.clone();
let id = id.to_string();
task::spawn_blocking(move || db.context().remove_entry(&id))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncPromptStore {
inner: Arc<AgentDB>,
}
impl AsyncPromptStore {
pub async fn create_template(
&self,
name: &str,
template: &str,
model_hint: Option<&str>,
max_tokens: Option<i64>,
metadata: Option<Value>,
) -> Result<String> {
let db = self.inner.clone();
let name = name.to_string();
let tmpl = template.to_string();
let hint = model_hint.map(|s| s.to_string());
task::spawn_blocking(move || {
db.prompts()
.create_template(&name, &tmpl, hint.as_deref(), max_tokens, metadata)
})
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_template(&self, name: &str) -> Result<PromptTemplate> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || db.prompts().get_template(&name))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn list_templates(&self) -> Result<Vec<PromptTemplate>> {
let db = self.inner.clone();
task::spawn_blocking(move || db.prompts().list_templates())
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn render(&self, name: &str, vars: HashMap<String, String>) -> Result<String> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || db.prompts().render(&name, &vars))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn delete_template(&self, name: &str) -> Result<()> {
let db = self.inner.clone();
let name = name.to_string();
task::spawn_blocking(move || db.prompts().delete_template(&name))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}
pub struct AsyncLabelStore {
inner: Arc<AgentDB>,
}
impl AsyncLabelStore {
pub async fn tag(
&self,
table_name: &str,
record_id: &str,
label: &str,
tagged_by: Option<&str>,
) -> Result<()> {
let db = self.inner.clone();
let table = table_name.to_string();
let record = record_id.to_string();
let lbl = label.to_string();
let by = tagged_by.map(|s| s.to_string());
task::spawn_blocking(move || db.labels().tag(&table, &record, &lbl, by.as_deref()))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn untag(&self, table_name: &str, record_id: &str, label: &str) -> Result<()> {
let db = self.inner.clone();
let table = table_name.to_string();
let record = record_id.to_string();
let lbl = label.to_string();
task::spawn_blocking(move || db.labels().untag(&table, &record, &lbl))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn get_labels(&self, table_name: &str, record_id: &str) -> Result<Vec<DataLabel>> {
let db = self.inner.clone();
let table = table_name.to_string();
let record = record_id.to_string();
task::spawn_blocking(move || db.labels().get_labels(&table, &record))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn has_label(
&self,
table_name: &str,
record_id: &str,
label: &str,
) -> Result<bool> {
let db = self.inner.clone();
let table = table_name.to_string();
let record = record_id.to_string();
let lbl = label.to_string();
task::spawn_blocking(move || db.labels().has_label(&table, &record, &lbl))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn find_by_label(&self, label: &str, limit: Option<usize>) -> Result<Vec<DataLabel>> {
let db = self.inner.clone();
let lbl = label.to_string();
task::spawn_blocking(move || db.labels().find_by_label(&lbl, limit))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
pub async fn clear_record(&self, table_name: &str, record_id: &str) -> Result<()> {
let db = self.inner.clone();
let table = table_name.to_string();
let record = record_id.to_string();
task::spawn_blocking(move || db.labels().clear_record(&table, &record))
.await
.map_err(|e| crate::error::AgentDbError::InvalidArgument(e.to_string()))?
}
}