use async_trait::async_trait;
use crate::{NamespaceId, Result};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ImportTriple {
pub subject: String,
pub predicate: String,
pub object: String,
pub valid_from: Option<String>,
pub valid_to: Option<String>,
pub confidence: f32,
pub source_memory_id: Option<String>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KgImportCounts {
pub attempted: usize,
pub added: usize,
pub skipped: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct EntityId(pub String);
impl EntityId {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Entity {
pub id: EntityId,
pub name: String,
pub entity_type: String,
pub namespace: String,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Triple {
pub id: String,
pub subject: EntityId,
pub predicate: String,
pub object: EntityId,
pub valid_from: Option<String>,
pub valid_to: Option<String>,
pub confidence: f32,
pub namespace: String,
pub source_memory_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EntityRecord {
pub entity: Option<Entity>,
pub outgoing: Vec<Triple>,
pub incoming: Vec<Triple>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KgStats {
pub entities: usize,
pub triples: usize,
}
#[async_trait]
pub trait KnowledgeGraph: Send + Sync {
#[allow(clippy::too_many_arguments)]
async fn add_triple(
&self,
ns: &NamespaceId,
subject: EntityId,
predicate: &str,
object: EntityId,
valid_from: Option<&str>,
confidence: f32,
source_memory_id: Option<&str>,
) -> Result<Triple>;
async fn query_entity(&self, ns: &NamespaceId, entity: &EntityId) -> Result<EntityRecord>;
async fn invalidate_triple(&self, ns: &NamespaceId, triple_id: &str) -> Result<()>;
async fn find_triples(
&self,
ns: &NamespaceId,
subject: Option<&EntityId>,
predicate: Option<&str>,
object: Option<&EntityId>,
) -> Result<Vec<Triple>>;
async fn kg_timeline(&self, ns: &NamespaceId, limit: usize) -> Result<Vec<Triple>>;
async fn knowledge_stats(&self, ns: &NamespaceId) -> Result<KgStats>;
async fn kg_global_stats(&self) -> Result<KgStats>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn entity_id_round_trips() {
let id = EntityId::new("Quantizon");
assert_eq!(id.as_str(), "Quantizon");
assert_eq!(id, EntityId("Quantizon".into()));
}
#[test]
fn kg_stats_defaults_to_zero() {
let s = KgStats::default();
assert_eq!(s.entities, 0);
assert_eq!(s.triples, 0);
}
}