use async_trait::async_trait;
use std::sync::Arc;
use crate::storage::vector::VectorSearchProvider;
use crate::types::{
Agent, AgentRelation, AgentRelationSummary, Artifact, CompilerConfig, IngestAction, Message,
MessageRole, Result, Session, SessionWithMessages, SessionWorkingSet, Span, Stance, WorkingSet,
};
#[derive(Debug, Clone)]
pub enum StorageConfig {
Sqlite {
path: String,
},
Postgres {
connection_string: String,
},
}
impl StorageConfig {
pub fn from_env(default_sqlite_path: &str) -> Self {
match std::env::var("AVOCADO_BACKEND").ok() {
None => StorageConfig::Sqlite {
path: default_sqlite_path.to_string(),
},
Some(ref s) if s == "sqlite" || s.is_empty() => StorageConfig::Sqlite {
path: default_sqlite_path.to_string(),
},
Some(ref s) if s.starts_with("sqlite:") => StorageConfig::Sqlite {
path: s.strip_prefix("sqlite:").unwrap().to_string(),
},
Some(ref s) if s.starts_with("postgres://") || s.starts_with("postgresql://") => {
StorageConfig::Postgres {
connection_string: s.clone(),
}
}
Some(s) => {
eprintln!(
"[AvocadoDB] Unknown AVOCADO_BACKEND '{}', defaulting to SQLite",
s
);
StorageConfig::Sqlite {
path: default_sqlite_path.to_string(),
}
}
}
}
}
#[async_trait]
pub trait StorageBackend: Send + Sync {
async fn get_stats(&self) -> Result<(usize, usize, usize)>;
async fn clear(&self) -> Result<()>;
async fn insert_artifact(&self, artifact: &Artifact) -> Result<()>;
async fn get_artifact(&self, artifact_id: &str) -> Result<Option<Artifact>>;
async fn get_artifact_by_path(&self, path: &str) -> Result<Option<Artifact>>;
async fn delete_artifact(&self, artifact_id: &str) -> Result<usize>;
async fn determine_ingest_action(
&self,
path: &str,
content_hash: &str,
) -> Result<IngestAction>;
async fn insert_spans(&self, spans: &[Span]) -> Result<()>;
async fn get_all_spans(&self) -> Result<Vec<Span>>;
async fn search_spans(&self, query: &str, limit: usize) -> Result<Vec<Span>>;
async fn get_vector_search(&self) -> Result<Arc<dyn VectorSearchProvider>>;
async fn invalidate_vector_index(&self);
async fn create_session(
&self,
user_id: Option<&str>,
title: Option<&str>,
) -> Result<Session>;
async fn get_session(&self, session_id: &str) -> Result<Option<Session>>;
async fn list_sessions(
&self,
user_id: Option<&str>,
limit: Option<usize>,
) -> Result<Vec<Session>>;
async fn update_session(
&self,
session_id: &str,
title: Option<&str>,
metadata: Option<&serde_json::Value>,
) -> Result<()>;
async fn delete_session(&self, session_id: &str) -> Result<()>;
async fn add_message(
&self,
session_id: &str,
role: MessageRole,
content: &str,
metadata: Option<&serde_json::Value>,
) -> Result<Message>;
async fn get_messages(
&self,
session_id: &str,
limit: Option<usize>,
) -> Result<Vec<Message>>;
async fn associate_working_set(
&self,
session_id: &str,
message_id: Option<&str>,
working_set: &WorkingSet,
query: &str,
config: &CompilerConfig,
) -> Result<SessionWorkingSet>;
async fn get_session_full(&self, session_id: &str) -> Result<Option<SessionWithMessages>>;
async fn register_agent(&self, agent: &Agent) -> Result<Agent>;
async fn get_agent(&self, agent_id: &str) -> Result<Option<Agent>>;
async fn get_agent_by_name(&self, name: &str) -> Result<Option<Agent>>;
async fn list_agents(&self) -> Result<Vec<Agent>>;
async fn add_agent_relation(
&self,
session_id: &str,
message_id: &str,
from_agent_id: &str,
target_message_id: &str,
stance: Stance,
) -> Result<AgentRelation>;
async fn get_agent_relations(&self, session_id: &str) -> Result<AgentRelationSummary>;
async fn get_session_agents(&self, session_id: &str) -> Result<Vec<Agent>>;
}