How MemPalace Handles Temporal Entity-Relationship Queries with Time Filtering
The MemPalace knowledge graph implements temporal entity-relationship queries with time filtering by storing ISO-8601 validity intervals in SQLite and using normalized text comparisons to resolve "as-of" moments.
The MemPalace open-source project maintains a fully offline knowledge graph capable of answering temporal entity-relationship queries with time filtering without external databases. By embedding valid_from and valid_to columns directly into the SQLite triples table and normalizing all temporal inputs to canonical UTC strings, the system determines historical truth using lexical comparisons against stored intervals.
Schema Design for Temporal Facts
In mempalace/knowledge_graph.py, the knowledge graph persists relationships (triples) in a SQLite table that includes two optional temporal columns: valid_from and valid_to. These columns hold ISO-8601 dates or canonical UTC datetimes, allowing every fact to be scoped to a specific time interval. A NULL value in valid_to indicates the fact remains current indefinitely.
Input Validation and Normalization
Before any temporal value reaches the database, the system validates and normalizes it through sanitize_iso_temporal in mempalace/config.py (lines 38-53). This function guarantees that only fully-qualified dates (YYYY-MM-DD) or UTC timestamps (YYYY-MM-DDTHH:MM:SSZ) are accepted, rejecting ambiguous or malformed inputs at the API boundary.
Both add_triple and invalidate methods invoke this sanitizer, ensuring that all temporal boundaries stored in the graph are comparable using simple text collation.
Converting Dates to Comparable Keys
Because SQLite stores timestamps as plain text, the graph must create comparison keys that enable correct interval logic. The helpers _temporal_start_key and _temporal_end_key in mempalace/knowledge_graph.py (lines 56-66) handle this conversion:
- Date-only values expand to the start of day (
...T00:00:00Z) or end of day (...T23:59:59Z) - Full timestamps pass through with UTC normalization
This expansion ensures that a query for "2025-06-15" matches facts valid during that entire day, not just exact timestamp matches.
SQL-Level Temporal Filtering
The private function _temporal_filter_sql generates a SQL fragment that safely compares stored valid_from/valid_to against the requested "as-of" instant. It leverages _sql_temporal_start_expr and _sql_temporal_end_expr (lines 106-126) to transform column values into comparable UTC strings on the fly.
The generated clause follows this pattern:
AND (t.valid_from IS NULL OR <start_expr> <= ?)
AND (t.valid_to IS NULL OR <end_expr> >= ?)
The two placeholders receive the same normalized "as-of" key, returning only facts where the query moment falls inside the validity interval.
Executing Time-Filtered Queries
The public entry point query_entity in mempalace/knowledge_graph.py (lines 64-71) accepts three arguments:
name: The entity to look upas_of: Optional ISO-8601 timestamp triggering the temporal filterdirection:"outgoing","incoming", or"both"to control edge direction
When as_of is provided, query_entity incorporates the SQL fragment from _temporal_filter_sql and returns a list of dictionaries containing the relationship, its validity period, and a boolean current flag (True when valid_to is NULL).
Managing Fact Lifecycles
Beyond querying, the graph provides explicit lifecycle management:
add_triple: Inserts new facts with optional validity windows, callingsanitize_iso_temporaland rejecting inverted intervals (wherevalid_fromexceedsvalid_to)invalidate: Setsvalid_toto a supplied "ended" timestamp using the same normalization logic, effectively closing a fact without deleting historical data
Complete Implementation Example
The following example demonstrates initializing the graph, adding temporally-scoped facts, querying with time filters, and invalidating obsolete relationships:
from mempalace.knowledge_graph import KnowledgeGraph
# -------------------------------------------------
# 1️⃣ Initialise the graph (creates ~/.mempalace/… if missing)
kg = KnowledgeGraph()
# -------------------------------------------------
# 2️⃣ Add some facts with temporal scopes
kg.add_triple(
subject="Max",
predicate="child_of",
obj="Alice",
valid_from="2015-04-01", # date‑only → start of day
)
kg.add_triple(
subject="Max",
predicate="does",
obj="swimming",
valid_from="2025-01-01T12:00:00Z", # explicit UTC timestamp
valid_to="2026-01-01T00:00:00Z", # expires after a year
)
# -------------------------------------------------
# 3️⃣ Query “what was true about Max in June 2025?”
facts_june = kg.query_entity("Max", as_of="2025-06-15")
print("June 2025 facts:", facts_june)
# → Returns the “child_of” fact (still open) and the “does‑swimming” fact
# because the query instant falls between its valid_from/valid_to.
# -------------------------------------------------
# 4️⃣ Query “what was true about Max in February 2027?”
facts_feb = kg.query_entity("Max", as_of="2027-02-01")
print("Feb 2027 facts:", facts_feb)
# → Only the “child_of” fact remains; the swimming fact has expired.
# -------------------------------------------------
# 5️⃣ Invalidate a fact (e.g., Max stops swimming)
kg.invalidate("Max", "does", "swimming", ended="2026-02-15")
# -------------------------------------------------
# 6️⃣ Retrieve the full timeline for Max (chronological order)
timeline = kg.timeline(entity_name="Max")
for entry in timeline:
print(entry)
Summary
- MemPalace stores temporal metadata in
valid_fromandvalid_tocolumns using ISO-8601 format in SQLite - Input sanitization via
sanitize_iso_temporalinmempalace/config.pyensures all dates are comparable UTC strings - Comparison keys generated by
_temporal_start_keyand_temporal_end_keyconvert date-only values to day boundaries - SQL filtering uses
_temporal_filter_sqlwith text comparisons to resolve "as-of" queries without external databases query_entityserves as the primary interface for temporal entity-relationship queries with time filtering, returning validity intervals and current status
Frequently Asked Questions
What date formats does MemPalace accept for temporal queries?
MemPalace accepts fully-qualified ISO-8601 dates (YYYY-MM-DD) or UTC timestamps (YYYY-MM-DDTHH:MM:SSZ) through the as_of parameter. The sanitize_iso_temporal function in mempalace/config.py rejects ambiguous formats or missing timezone indicators, ensuring consistent UTC normalization before storage or comparison.
How does the knowledge graph handle facts without an end date?
Facts with valid_to set to NULL are considered perpetually current. The SQL generated by _temporal_filter_sql explicitly checks t.valid_to IS NULL in the WHERE clause, allowing these open-ended facts to match any "as-of" query that occurs after their valid_from timestamp. The query_entity result includes a current boolean flag set to True for such records.
Can I query for relationships that were valid during an entire date range?
The current implementation in mempalace/knowledge_graph.py supports point-in-time queries via the as_of parameter rather than range-overlaps. To find facts covering a duration, you would execute multiple point queries or post-filter results from timeline to verify that valid_from precedes your range start and valid_to exceeds your range end.
How does MemPalace handle timezone-aware timestamps?
All temporal inputs are normalized to canonical UTC representations by sanitize_iso_temporal. The system stores and compares timestamps as UTC strings ending with Z, ensuring that temporal entity-relationship queries with time filtering return consistent results regardless of the client's local timezone.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →