Core Concepts in TelaMentis
Understanding TelaMentis's core concepts is essential for effectively using and developing with the framework. This document outlines the fundamental building blocks of the system as implemented in Phase 1.
1. Graph Primitives
TelaMentis is centered around a property graph model, extended with temporal awareness and multi-tenancy.
Node
A Node
represents an entity or an object of interest in your knowledge graph. Examples include a person, a company, a document, or an event.
- System ID (UUID): A unique system-generated identifier for the node, managed internally by the GraphStore.
id_alias
(Optional String): A user-defined, human-readable identifier or external ID. This enables idempotent operations - if a node with the givenid_alias
exists, it will be updated; otherwise, it's created.label
(String): Defines the type or category of the node (e.g., "Person", "Organization", "Document").props
(JSON Object): A collection of key-value pairs representing the properties of the node.
Rust Implementation:
pub struct Node {
pub id_alias: Option<String>, // User-defined ID for idempotency
pub label: String, // Node type (e.g., "Person")
pub props: serde_json::Value, // Properties as JSON
}
Example Usage:
let person = Node::new("Person")
.with_id_alias("user_alice")
.with_property("name", json!("Alice Wonderland"))
.with_property("age", json!(30))
.with_property("city", json!("New York"));
TimeEdge (Bitemporal Relation)
TimeEdge
is TelaMentis's core innovation, making relations temporally aware. It tracks when a relationship was true in the real world, enabling powerful temporal queries.
from_node_id
(UUID): System ID of the source nodeto_node_id
(UUID): System ID of the target nodekind
(String): Type of the relationship (e.g., "WORKS_FOR", "KNOWS")props
(JSON Object): Properties of the relationshipvalid_from
(DateTime<Utc>): When the relationship became true in the real worldvalid_to
(Option<DateTime<Utc>>): When the relationship ceased to be true (None
= still valid)
Rust Implementation:
pub struct TimeEdge {
pub from_node_id: Uuid,
pub to_node_id: Uuid,
pub kind: String,
pub valid_from: DateTime<Utc>,
pub valid_to: Option<DateTime<Utc>>, // None = currently valid
pub props: serde_json::Value,
}
Example Usage:
let employment = TimeEdge::new(
alice_id,
acme_corp_id,
"WORKS_FOR",
"2023-01-15T09:00:00Z".parse()?, // Started working
json!({"role": "Software Engineer", "department": "Engineering"})
).with_valid_to("2024-03-01T17:00:00Z".parse()?); // Left the company
Current Implementation Status:
- ✅ Valid Time Support: Full
valid_from
/valid_to
implementation - 🔄 Transaction Time: Implicit tracking (planned for Phase 2)
2. TenantId
TelaMentis is designed for multi-tenant environments where different users or applications maintain isolated graph data.
TenantId
(String): A unique identifier for each tenant- All data (nodes, edges) logically belongs to a specific tenant
- All GraphStore operations are scoped by TenantId to ensure data isolation
Rust Implementation:
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TenantId(pub String);
impl TenantId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
Current Implementation:
- ✅ Property-Based Isolation: Default isolation using
_tenant_id
properties - 🔄 Database Isolation: Dedicated databases per tenant (planned for Phase 2)
- 🔄 Label Namespacing: Alternative isolation strategy (planned for Phase 2)
3. GraphStore Trait
The GraphStore
is the central abstraction for interacting with graph databases. All storage adapters implement this trait.
Core Operations:
#[async_trait]
pub trait GraphStore: Send + Sync {
// Node operations
async fn upsert_node(&self, tenant: &TenantId, node: Node) -> Result<Uuid, GraphError>;
async fn get_node(&self, tenant: &TenantId, id: Uuid) -> Result<Option<Node>, GraphError>;
async fn get_node_by_alias(&self, tenant: &TenantId, id_alias: &str) -> Result<Option<(Uuid, Node)>, GraphError>;
async fn delete_node(&self, tenant: &TenantId, id: Uuid) -> Result<bool, GraphError>;
// Edge operations
async fn upsert_edge(&self, tenant: &TenantId, edge: TimeEdge) -> Result<Uuid, GraphError>;
async fn delete_edge(&self, tenant: &TenantId, id: Uuid) -> Result<bool, GraphError>;
// Query operations
async fn query(&self, tenant: &TenantId, query: GraphQuery) -> Result<Vec<Path>, GraphError>;
// System operations
async fn health_check(&self) -> Result<(), GraphError>;
}
Current Implementations:
- ✅ Neo4j Adapter: Complete implementation with Cypher query translation
- 🔄 In-Memory Adapter: For testing and development (planned for Phase 2)
4. GraphQuery
GraphQuery
represents different types of queries that can be executed against the graph.
Current Query Types:
pub enum GraphQuery {
// Raw database-specific queries
Raw {
query: String,
params: HashMap<String, serde_json::Value>,
},
// Structured node queries
FindNodes {
labels: Vec<String>,
properties: HashMap<String, serde_json::Value>,
limit: Option<u32>,
},
// Structured relationship queries
FindRelationships {
from_node_id: Option<Uuid>,
to_node_id: Option<Uuid>,
relationship_types: Vec<String>,
valid_at: Option<DateTime<Utc>>, // Temporal constraint
limit: Option<u32>,
},
// Temporal queries
AsOfQuery {
base_query: Box<GraphQuery>,
as_of_time: DateTime<Utc>,
},
}
Example Usage:
// Find all Person nodes
let query = GraphQuery::FindNodes {
labels: vec!["Person".to_string()],
properties: HashMap::new(),
limit: Some(100),
};
// Find relationships valid at specific time
let temporal_query = GraphQuery::FindRelationships {
from_node_id: None,
to_node_id: None,
relationship_types: vec!["WORKS_FOR".to_string()],
valid_at: Some("2023-06-01T00:00:00Z".parse()?),
limit: None,
};
5. LLM Integration Types
TelaMentis includes first-class support for LLM-based knowledge extraction.
ExtractionContext
Input for LLM extraction operations:
pub struct ExtractionContext {
pub messages: Vec<LlmMessage>, // Conversation to extract from
pub system_prompt: Option<String>, // Custom extraction instructions
pub desired_schema: Option<String>, // JSON schema for validation
pub max_tokens: Option<u32>, // Generation limits
pub temperature: Option<f32>, // Creativity control
}
ExtractionEnvelope
Structured output from LLM extraction:
pub struct ExtractionEnvelope {
pub nodes: Vec<ExtractionNode>, // Extracted entities
pub relations: Vec<ExtractionRelation>, // Extracted relationships
pub metadata: Option<ExtractionMetadata>, // Extraction stats
}
pub struct ExtractionNode {
pub id_alias: String, // Unique identifier
pub label: String, // Entity type
pub props: serde_json::Value, // Entity properties
pub confidence: Option<f32>, // LLM confidence score
}
pub struct ExtractionRelation {
pub from_id_alias: String, // Source entity
pub to_id_alias: String, // Target entity
pub type_label: String, // Relationship type
pub props: serde_json::Value, // Relationship properties
pub valid_from: Option<DateTime<Utc>>, // Temporal validity
pub valid_to: Option<DateTime<Utc>>,
pub confidence: Option<f32>, // LLM confidence score
}
Current Implementation:
- ✅ OpenAI Connector: Complete implementation with GPT-4 support
- ✅ Schema Validation: JSON schema enforcement for LLM outputs
- ✅ Cost Tracking: Token usage and cost estimation
- 🔄 Additional Connectors: Anthropic, Gemini (planned for Phase 2)
6. Error Handling
TelaMentis uses comprehensive, typed error handling throughout the system.
Core Error Types:
#[derive(Error, Debug)]
pub enum CoreError {
#[error("Graph storage error: {0}")]
Storage(#[from] GraphError),
#[error("LLM connector error: {0}")]
Llm(#[from] LlmError),
#[error("Tenant error: {0}")]
Tenant(String),
#[error("Temporal query error: {0}")]
Temporal(String),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Internal error: {0}")]
Internal(String),
}
7. Working with the Current Implementation
Basic Node and Edge Operations
use telamentis_core::prelude::*;
// Create a tenant
let tenant = TenantId::new("my_app");
// Create nodes
let alice = Node::new("Person")
.with_id_alias("alice")
.with_property("name", json!("Alice"))
.with_property("age", json!(30));
let acme = Node::new("Company")
.with_id_alias("acme_corp")
.with_property("name", json!("Acme Corp"));
// Upsert nodes to get system IDs
let alice_id = store.upsert_node(&tenant, alice).await?;
let acme_id = store.upsert_node(&tenant, acme).await?;
// Create temporal relationship
let employment = TimeEdge::new(
alice_id,
acme_id,
"WORKS_FOR",
Utc::now(),
json!({"role": "Engineer"})
);
let edge_id = store.upsert_edge(&tenant, employment).await?;
Querying Data
// Find all Person nodes
let query = GraphQuery::FindNodes {
labels: vec!["Person".to_string()],
properties: HashMap::new(),
limit: Some(10),
};
let results = store.query(&tenant, query).await?;
// Find current employment relationships
let query = GraphQuery::FindRelationships {
from_node_id: None,
to_node_id: None,
relationship_types: vec!["WORKS_FOR".to_string()],
valid_at: Some(Utc::now()), // Only current relationships
limit: None,
};
let relationships = store.query(&tenant, query).await?;
LLM Knowledge Extraction
// Extract knowledge from text
let context = ExtractionContext {
messages: vec![LlmMessage {
role: "user".to_string(),
content: "Alice started working at Acme Corp in January 2023 as a Software Engineer.".to_string(),
}],
system_prompt: Some("Extract entities and relationships.".to_string()),
max_tokens: Some(1000),
temperature: Some(0.1),
desired_schema: None,
};
let envelope = llm_connector.extract(&tenant, context).await?;
// Process extracted data
for node in envelope.nodes {
let system_id = store.upsert_node(&tenant, Node::new(&node.label)
.with_id_alias(&node.id_alias)
.with_props(node.props)).await?;
}
8. Current Limitations and Future Enhancements
Phase 1 Limitations
- Transaction Time: Only implicit tracking (via system timestamps)
- Advanced Temporal Queries: Limited to basic "as-of" queries
- Storage Adapters: Only Neo4j implemented
- LLM Connectors: Only OpenAI implemented
- Request Pipeline: Basic implementation without plugin system
Phase 2 Enhancements (🔄 Planned)
- Full Bitemporal Support: Explicit transaction time tracking
- Advanced Temporal Reasoning: Allen's Interval Algebra queries
- Additional Adapters: In-memory, Memgraph, Anthropic, Gemini
- Request Processing Pipeline: Plugin system for request lifecycle
- Performance Optimizations: Connection pooling, query optimization
9. Best Practices
Node Design
- Use meaningful
id_alias
values for idempotency - Keep
label
values consistent (e.g., "Person", not "person" or "PERSON") - Store only essential properties in nodes; use relationships for connections
Temporal Modeling
- Use
valid_from
for when facts became true - Leave
valid_to
asNone
for ongoing relationships - Be consistent with timezone handling (always use UTC)
Multi-Tenancy
- Always scope operations by
TenantId
- Use descriptive tenant names
- Plan tenant lifecycle management
Performance
- Use batch operations for large datasets
- Index frequently queried properties
- Consider data locality for related entities
Understanding these core concepts provides the foundation for effectively using TelaMentis to build AI agents with persistent, temporal memory. The modular design ensures that as additional features are implemented in future phases, the core concepts remain stable and consistent.