mod commit;
mod cosine;
mod edge;
mod enrich;
mod error;
mod extraction;
mod forget;
mod inspect;
mod memory;
mod resolve;
mod synthesis;
pub use commit::{CommitContext, CommitError};
pub use enrich::{
DEFAULT_ENRICHMENT_DEPTH, GraphContext, GraphEntity, GraphRelationship, MAX_ENRICHMENT_DEPTH,
};
pub use edge::{
CardinalityPolicy, Edge, EdgeCatalog, EdgeError, EdgeResolution, EdgeResolver, ExistingEdge, NaiveAppendResolver,
RelationCardinality, TemporalEdgeResolver,
};
pub use error::GraphError;
pub use extraction::{
DEFAULT_TRIPLE_PROMPT, LlmExtractor, TRIPLE_REPLY_MAX_CHARS, Triple, TripleExtractor, TripleSet,
};
pub use inspect::{
DEFAULT_INSPECTION_LIMIT, GraphEdge, GraphNode, GraphSnapshot, MAX_INSPECTION_LIMIT,
};
pub use memory::InMemoryGraphStore;
pub use resolve::{
EmbeddingEntityResolver, EntityCatalog, EntityResolver, EntityVector, ExactStringResolver, InMemoryEntityCatalog,
Resolution, ResolveError, MIN_ENTITY_SIMILARITY,
};
pub use synthesis::{
EmbeddingSynthesizer, MIN_CORROBORATION_SIMILARITY, PassthroughSynthesizer, SemanticFact, SynthesisError,
Synthesizer,
};
#[cfg(feature = "knowledge-graph")]
mod falkor;
#[cfg(feature = "knowledge-graph")]
mod falkor_catalog;
#[cfg(feature = "knowledge-graph")]
mod staging;
#[cfg(feature = "knowledge-graph")]
pub use falkor::FalkorGraphStore;
#[cfg(feature = "knowledge-graph")]
pub use falkor_catalog::{FalkorEdgeCatalog, FalkorEntityCatalog};
#[cfg(feature = "knowledge-graph")]
pub use staging::TripleStaging;
use std::collections::HashMap;
use std::future::Future;
pub const DEFAULT_GRAPH_NAME: &str = "memoir";
pub type GraphRow = Vec<(String, String)>;
pub type GraphRows = Vec<GraphRow>;
#[derive(Clone, Debug, PartialEq)]
pub enum GraphParam {
Str(String),
Int(i64),
Float(f64),
}
impl GraphParam {
#[must_use]
pub fn to_cypher_literal(&self) -> String {
match self {
Self::Str(s) => format!("'{}'", s.replace('\\', "\\\\").replace('\'', "\\'")),
Self::Int(n) => n.to_string(),
Self::Float(f) => f.to_string(),
}
}
}
impl From<String> for GraphParam {
fn from(value: String) -> Self {
Self::Str(value)
}
}
impl From<&str> for GraphParam {
fn from(value: &str) -> Self {
Self::Str(value.to_string())
}
}
pub trait GraphStore: Send + Sync + 'static {
fn ensure_graph(&self) -> impl Future<Output = Result<(), GraphError>> + Send;
fn query(
&self,
cypher: &str,
params: &HashMap<String, GraphParam>,
) -> impl Future<Output = Result<GraphRows, GraphError>> + Send;
fn forget_pids(&self, pids: &[&str]) -> impl Future<Output = Result<(), GraphError>> + Send {
forget::forget_pids(self, pids)
}
fn forget_scope(&self, scope: &crate::memory::Scope) -> impl Future<Output = Result<(), GraphError>> + Send {
forget::forget_scope(self, scope)
}
fn commit_triples<EM, ER, EdgeR>(
&self,
embedder: &EM,
entities: &ER,
edges: &EdgeR,
ctx: &CommitContext,
triples: &TripleSet,
) -> impl Future<Output = Result<usize, CommitError>> + Send
where
EM: crate::embedding::EmbeddingModel,
ER: EntityResolver,
EdgeR: EdgeResolver,
{
commit::commit_triples(self, embedder, entities, edges, ctx, triples)
}
fn neighbors(
&self,
seed_pids: &[&str],
scope: &crate::memory::Scope,
depth: usize,
) -> impl Future<Output = Result<GraphContext, GraphError>> + Send {
enrich::neighbors(self, seed_pids, scope, depth)
}
fn inspect_scope(
&self,
agent_id: Option<&str>,
org_id: Option<&str>,
user_id: Option<&str>,
limit: usize,
) -> impl Future<Output = Result<GraphSnapshot, GraphError>> + Send {
inspect::inspect_scope(self, agent_id, org_id, user_id, limit)
}
}
#[cfg(test)]
mod tests {
use super::GraphParam;
#[test]
fn should_quote_string_param_as_cypher_literal() {
assert_eq!(GraphParam::Str("Alice".to_string()).to_cypher_literal(), "'Alice'");
}
#[test]
fn should_render_int_param_bare() {
assert_eq!(GraphParam::Int(500).to_cypher_literal(), "500");
}
#[test]
fn should_render_float_param_bare() {
assert_eq!(GraphParam::Float(0.85).to_cypher_literal(), "0.85");
}
#[test]
fn should_escape_embedded_single_quote_in_string_param() {
assert_eq!(GraphParam::Str("O'Brien".to_string()).to_cypher_literal(), r"'O\'Brien'");
}
#[test]
fn should_escape_backslash_before_quote_in_string_param() {
assert_eq!(GraphParam::Str(r"a\b".to_string()).to_cypher_literal(), r"'a\\b'");
}
#[test]
fn should_not_let_injection_break_out_of_string_literal() {
let injection = r#"x"}) DETACH DELETE n //"#;
let rendered = GraphParam::Str(injection.to_string()).to_cypher_literal();
assert!(rendered.starts_with('\''));
assert!(rendered.ends_with('\''));
}
}