use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::{
CodememError, Edge, GraphNode, MemoryNode, NodeKind, RawGraphMetrics, RelationshipType,
Repository, Session, UnresolvedRefData,
};
#[derive(Debug, Clone)]
pub struct PendingUnresolvedRef {
pub id: String,
pub source_node: String,
pub target_name: String,
pub namespace: String,
pub file_path: String,
pub line: usize,
pub ref_kind: String,
pub package_hint: Option<String>,
}
pub trait VectorBackend: Send + Sync {
fn insert(&mut self, id: &str, embedding: &[f32]) -> Result<(), CodememError>;
fn insert_batch(&mut self, items: &[(String, Vec<f32>)]) -> Result<(), CodememError>;
fn search(&self, query: &[f32], k: usize) -> Result<Vec<(String, f32)>, CodememError>;
fn remove(&mut self, id: &str) -> Result<bool, CodememError>;
fn save(&self, path: &std::path::Path) -> Result<(), CodememError>;
fn load(&mut self, path: &std::path::Path) -> Result<(), CodememError>;
fn stats(&self) -> VectorStats;
fn needs_compaction(&self) -> bool {
false
}
fn ghost_count(&self) -> usize {
0
}
fn rebuild_from_entries(
&mut self,
_entries: &[(String, Vec<f32>)],
) -> Result<(), CodememError> {
Ok(())
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VectorStats {
pub count: usize,
pub dimensions: usize,
pub metric: String,
pub memory_bytes: usize,
}
pub trait GraphBackend: Send + Sync {
fn add_node(&mut self, node: GraphNode) -> Result<(), CodememError>;
fn get_node(&self, id: &str) -> Result<Option<GraphNode>, CodememError>;
fn remove_node(&mut self, id: &str) -> Result<bool, CodememError>;
fn add_edge(&mut self, edge: Edge) -> Result<(), CodememError>;
fn get_edges(&self, node_id: &str) -> Result<Vec<Edge>, CodememError>;
fn remove_edge(&mut self, id: &str) -> Result<bool, CodememError>;
fn bfs(&self, start_id: &str, max_depth: usize) -> Result<Vec<GraphNode>, CodememError>;
fn dfs(&self, start_id: &str, max_depth: usize) -> Result<Vec<GraphNode>, CodememError>;
fn bfs_filtered(
&self,
start_id: &str,
max_depth: usize,
exclude_kinds: &[NodeKind],
include_relationships: Option<&[RelationshipType]>,
) -> Result<Vec<GraphNode>, CodememError>;
fn dfs_filtered(
&self,
start_id: &str,
max_depth: usize,
exclude_kinds: &[NodeKind],
include_relationships: Option<&[RelationshipType]>,
) -> Result<Vec<GraphNode>, CodememError>;
fn shortest_path(&self, from: &str, to: &str) -> Result<Vec<String>, CodememError>;
fn stats(&self) -> GraphStats;
fn get_all_nodes(&self) -> Vec<GraphNode> {
Vec::new()
}
fn get_node_ref(&self, _id: &str) -> Option<&GraphNode> {
None
}
fn get_edges_ref(&self, _node_id: &str) -> Vec<&Edge> {
Vec::new()
}
fn node_count(&self) -> usize {
self.stats().node_count
}
fn edge_count(&self) -> usize {
self.stats().edge_count
}
fn recompute_centrality(&mut self) {}
fn recompute_centrality_with_options(&mut self, _include_betweenness: bool) {}
fn recompute_centrality_for_namespace(&mut self, _namespace: &str) {}
fn ensure_betweenness_computed(&mut self) {}
fn compute_centrality(&mut self) {}
fn get_pagerank(&self, _node_id: &str) -> f64 {
0.0
}
fn get_betweenness(&self, _node_id: &str) -> f64 {
0.0
}
fn raw_graph_metrics_for_memory(&self, _memory_id: &str) -> Option<RawGraphMetrics> {
None
}
fn connected_components(&self) -> Vec<Vec<String>> {
Vec::new()
}
fn strongly_connected_components(&self) -> Vec<Vec<String>> {
Vec::new()
}
fn pagerank(&self, _damping: f64, _iterations: usize, _tolerance: f64) -> HashMap<String, f64> {
HashMap::new()
}
fn pagerank_for_namespace(
&self,
_namespace: &str,
_damping: f64,
_iterations: usize,
_tolerance: f64,
) -> HashMap<String, f64> {
panic!("pagerank_for_namespace must be implemented; default fallback violates isolation guarantee");
}
fn louvain_communities(&self, _resolution: f64) -> Vec<Vec<String>> {
Vec::new()
}
fn topological_layers(&self) -> Vec<Vec<String>> {
Vec::new()
}
fn louvain_with_assignment(&self, resolution: f64) -> HashMap<String, usize> {
let communities = self.louvain_communities(resolution);
let mut assignment = HashMap::new();
for (idx, community) in communities.into_iter().enumerate() {
for node_id in community {
assignment.insert(node_id, idx);
}
}
assignment
}
fn subgraph_top_n(
&self,
_n: usize,
_namespace: Option<&str>,
_kinds: Option<&[NodeKind]>,
) -> (Vec<GraphNode>, Vec<Edge>) {
(Vec::new(), Vec::new())
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GraphStats {
pub node_count: usize,
pub edge_count: usize,
pub node_kind_counts: HashMap<String, usize>,
pub relationship_type_counts: HashMap<String, usize>,
}
pub trait EmbeddingProvider: Send + Sync {
fn dimensions(&self) -> usize;
fn embed(&self, text: &str) -> Result<Vec<f32>, crate::CodememError>;
fn embed_batch(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>, crate::CodememError> {
texts.iter().map(|t| self.embed(t)).collect()
}
fn name(&self) -> &str;
fn cache_stats(&self) -> (usize, usize) {
(0, 0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageStats {
pub memory_count: usize,
pub embedding_count: usize,
pub node_count: usize,
pub edge_count: usize,
}
#[derive(Debug, Clone)]
pub struct ConsolidationLogEntry {
pub cycle_type: String,
pub run_at: i64,
pub affected_count: usize,
}
pub trait StorageBackend: Send + Sync {
fn insert_memory(&self, memory: &MemoryNode) -> Result<(), CodememError>;
fn get_memory(&self, id: &str) -> Result<Option<MemoryNode>, CodememError>;
fn get_memory_no_touch(&self, id: &str) -> Result<Option<MemoryNode>, CodememError> {
self.get_memory(id)
}
fn get_memories_batch(&self, ids: &[&str]) -> Result<Vec<MemoryNode>, CodememError>;
fn update_memory(
&self,
id: &str,
content: &str,
importance: Option<f64>,
) -> Result<(), CodememError>;
fn delete_memory(&self, id: &str) -> Result<bool, CodememError>;
fn delete_memory_cascade(&self, id: &str) -> Result<bool, CodememError> {
let deleted = self.delete_memory(id)?;
if deleted {
let _ = self.delete_graph_edges_for_node(id);
let _ = self.delete_graph_node(id);
let _ = self.delete_embedding(id);
}
Ok(deleted)
}
fn delete_memories_batch_cascade(&self, ids: &[&str]) -> Result<usize, CodememError> {
let mut count = 0;
for id in ids {
if self.delete_memory_cascade(id)? {
count += 1;
}
}
Ok(count)
}
fn delete_expired_memories(&self) -> Result<usize, CodememError>;
fn expire_memories_for_file(&self, file_path: &str) -> Result<usize, CodememError>;
fn list_memory_ids(&self) -> Result<Vec<String>, CodememError>;
fn list_memory_ids_for_namespace(&self, namespace: &str) -> Result<Vec<String>, CodememError>;
fn find_memory_ids_by_tag(
&self,
tag: &str,
namespace: Option<&str>,
exclude_id: &str,
) -> Result<Vec<String>, CodememError>;
fn list_namespaces(&self) -> Result<Vec<String>, CodememError>;
fn memory_count(&self) -> Result<usize, CodememError>;
fn store_embedding(&self, memory_id: &str, embedding: &[f32]) -> Result<(), CodememError>;
fn get_embedding(&self, memory_id: &str) -> Result<Option<Vec<f32>>, CodememError>;
fn delete_embedding(&self, memory_id: &str) -> Result<bool, CodememError>;
fn list_all_embeddings(&self) -> Result<Vec<(String, Vec<f32>)>, CodememError>;
fn insert_graph_node(&self, node: &GraphNode) -> Result<(), CodememError>;
fn get_graph_node(&self, id: &str) -> Result<Option<GraphNode>, CodememError>;
fn delete_graph_node(&self, id: &str) -> Result<bool, CodememError>;
fn all_graph_nodes(&self) -> Result<Vec<GraphNode>, CodememError>;
fn insert_graph_edge(&self, edge: &Edge) -> Result<(), CodememError>;
fn get_edges_for_node(&self, node_id: &str) -> Result<Vec<Edge>, CodememError>;
fn all_graph_edges(&self) -> Result<Vec<Edge>, CodememError>;
fn delete_graph_edge(&self, edge_id: &str) -> Result<bool, CodememError> {
let _ = edge_id;
Ok(false)
}
fn delete_graph_edges_for_node(&self, node_id: &str) -> Result<usize, CodememError>;
fn delete_graph_nodes_by_prefix(&self, prefix: &str) -> Result<usize, CodememError>;
fn start_session(&self, id: &str, namespace: Option<&str>) -> Result<(), CodememError>;
fn end_session(&self, id: &str, summary: Option<&str>) -> Result<(), CodememError>;
fn list_sessions(
&self,
namespace: Option<&str>,
limit: usize,
) -> Result<Vec<Session>, CodememError>;
fn insert_consolidation_log(
&self,
cycle_type: &str,
affected_count: usize,
) -> Result<(), CodememError>;
fn last_consolidation_runs(&self) -> Result<Vec<ConsolidationLogEntry>, CodememError>;
fn get_repeated_searches(
&self,
min_count: usize,
namespace: Option<&str>,
) -> Result<Vec<(String, usize, Vec<String>)>, CodememError>;
fn get_file_hotspots(
&self,
min_count: usize,
namespace: Option<&str>,
) -> Result<Vec<(String, usize, Vec<String>)>, CodememError>;
fn get_tool_usage_stats(
&self,
namespace: Option<&str>,
) -> Result<Vec<(String, usize)>, CodememError>;
fn get_decision_chains(
&self,
min_count: usize,
namespace: Option<&str>,
) -> Result<Vec<(String, usize, Vec<String>)>, CodememError>;
fn decay_stale_memories(
&self,
threshold_ts: i64,
decay_factor: f64,
) -> Result<usize, CodememError>;
fn list_memories_for_creative(
&self,
) -> Result<Vec<(String, String, Vec<String>)>, CodememError>;
fn find_hash_duplicates(&self) -> Result<Vec<(String, String, f64)>, CodememError>;
fn find_forgettable(&self, importance_threshold: f64) -> Result<Vec<String>, CodememError>;
fn insert_memories_batch(&self, memories: &[MemoryNode]) -> Result<(), CodememError> {
for memory in memories {
self.insert_memory(memory)?;
}
Ok(())
}
fn store_embeddings_batch(&self, items: &[(&str, &[f32])]) -> Result<(), CodememError> {
for (id, embedding) in items {
self.store_embedding(id, embedding)?;
}
Ok(())
}
fn insert_graph_nodes_batch(&self, nodes: &[GraphNode]) -> Result<(), CodememError> {
for node in nodes {
self.insert_graph_node(node)?;
}
Ok(())
}
fn insert_graph_edges_batch(&self, edges: &[Edge]) -> Result<(), CodememError> {
for edge in edges {
self.insert_graph_edge(edge)?;
}
Ok(())
}
fn find_unembedded_memories(&self) -> Result<Vec<(String, String)>, CodememError>;
fn search_graph_nodes(
&self,
query: &str,
namespace: Option<&str>,
limit: usize,
) -> Result<Vec<GraphNode>, CodememError>;
fn list_memories_by_tag(
&self,
tag: &str,
namespace: Option<&str>,
limit: usize,
) -> Result<Vec<MemoryNode>, CodememError>;
fn list_memories_filtered(
&self,
namespace: Option<&str>,
memory_type: Option<&str>,
) -> Result<Vec<MemoryNode>, CodememError>;
fn get_stale_memories_for_decay(
&self,
threshold_ts: i64,
) -> Result<Vec<(String, f64, u32, i64)>, CodememError>;
fn batch_update_importance(&self, updates: &[(String, f64)]) -> Result<usize, CodememError>;
fn session_count(&self, namespace: Option<&str>) -> Result<usize, CodememError>;
fn load_file_hashes(&self, namespace: &str) -> Result<HashMap<String, String>, CodememError>;
fn save_file_hashes(
&self,
namespace: &str,
hashes: &HashMap<String, String>,
) -> Result<(), CodememError>;
fn record_session_activity(
&self,
session_id: &str,
tool_name: &str,
file_path: Option<&str>,
directory: Option<&str>,
pattern: Option<&str>,
) -> Result<(), CodememError>;
fn get_session_activity_summary(
&self,
session_id: &str,
) -> Result<crate::SessionActivitySummary, CodememError>;
fn get_session_hot_directories(
&self,
session_id: &str,
limit: usize,
) -> Result<Vec<(String, usize)>, CodememError>;
fn has_auto_insight(&self, session_id: &str, dedup_tag: &str) -> Result<bool, CodememError>;
fn count_directory_reads(
&self,
session_id: &str,
directory: &str,
) -> Result<usize, CodememError>;
fn was_file_read_in_session(
&self,
session_id: &str,
file_path: &str,
) -> Result<bool, CodememError>;
fn count_search_pattern_in_session(
&self,
session_id: &str,
pattern: &str,
) -> Result<usize, CodememError>;
fn list_repos(&self) -> Result<Vec<Repository>, CodememError>;
fn add_repo(&self, repo: &Repository) -> Result<(), CodememError>;
fn get_repo(&self, id: &str) -> Result<Option<Repository>, CodememError>;
fn remove_repo(&self, id: &str) -> Result<bool, CodememError>;
fn update_repo_status(
&self,
id: &str,
status: &str,
indexed_at: Option<&str>,
) -> Result<(), CodememError>;
fn stats(&self) -> Result<StorageStats, CodememError>;
fn begin_transaction(&self) -> Result<(), CodememError> {
Ok(())
}
fn commit_transaction(&self) -> Result<(), CodememError> {
Ok(())
}
fn rollback_transaction(&self) -> Result<(), CodememError> {
Ok(())
}
fn graph_edges_for_namespace_with_cross(
&self,
_namespace: &str,
_include_cross_namespace: bool,
) -> Result<Vec<Edge>, CodememError> {
Ok(Vec::new())
}
fn upsert_package_registry(
&self,
_package_name: &str,
_namespace: &str,
_version: &str,
_manifest: &str,
) -> Result<(), CodememError> {
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn store_unresolved_ref(
&self,
_source_qualified_name: &str,
_target_name: &str,
_source_namespace: &str,
_file_path: &str,
_line: usize,
_ref_kind: &str,
_package_hint: Option<&str>,
) -> Result<(), CodememError> {
Ok(())
}
fn store_unresolved_refs_batch(
&self,
refs: &[UnresolvedRefData],
) -> Result<usize, CodememError> {
let mut count = 0;
for r in refs {
self.store_unresolved_ref(
&r.source_qualified_name,
&r.target_name,
&r.namespace,
&r.file_path,
r.line,
&r.ref_kind,
r.package_hint.as_deref(),
)?;
count += 1;
}
Ok(count)
}
fn list_registered_packages(&self) -> Result<Vec<(String, String, String)>, CodememError> {
Ok(Vec::new())
}
fn list_pending_unresolved_refs(&self) -> Result<Vec<PendingUnresolvedRef>, CodememError> {
Ok(Vec::new())
}
fn delete_unresolved_ref(&self, _id: &str) -> Result<(), CodememError> {
Ok(())
}
fn count_unresolved_refs(&self, _namespace: &str) -> Result<usize, CodememError> {
Ok(0)
}
fn list_registered_packages_for_namespace(
&self,
_namespace: &str,
) -> Result<Vec<(String, String, String)>, CodememError> {
Ok(Vec::new())
}
fn store_api_endpoint(
&self,
_method: &str,
_path: &str,
_handler_symbol: &str,
_namespace: &str,
) -> Result<(), CodememError> {
Ok(())
}
fn store_api_client_call(
&self,
_library: &str,
_method: Option<&str>,
_caller_symbol: &str,
_namespace: &str,
) -> Result<(), CodememError> {
Ok(())
}
fn list_api_endpoints(
&self,
_namespace: &str,
) -> Result<Vec<(String, String, String, String)>, CodememError> {
Ok(Vec::new())
}
fn store_event_channel(
&self,
channel: &str,
direction: &str,
protocol: &str,
handler: &str,
namespace: &str,
description: &str,
) -> Result<(), CodememError> {
let _ = (
channel,
direction,
protocol,
handler,
namespace,
description,
);
Ok(())
}
#[allow(clippy::type_complexity)]
fn list_event_channels(
&self,
namespace: &str,
) -> Result<Vec<(String, String, String, String, String)>, CodememError> {
let _ = namespace;
Ok(Vec::new())
}
#[allow(clippy::type_complexity)]
fn list_all_event_channels(
&self,
) -> Result<Vec<(String, String, String, String, String, String)>, CodememError> {
Ok(Vec::new())
}
fn get_namespace_root(&self, _namespace: &str) -> Result<Option<String>, CodememError> {
Ok(None)
}
fn set_namespace_root(&self, _namespace: &str, _root_path: &str) -> Result<(), CodememError> {
Ok(())
}
}