is_valid() tells you whether any node is still current.
Before You Start
- Complete Quickstart to understand basic operations
- Ensure you have LLM Providers configured
- Read DataPoints for the
valid_tofield and the rest of the node schema - Use the default Ladybug graph store — currently the only backend where
close_node()can persist thevalid_tostamp (see Backend Support)
Code in Action
What Just Happened
Step 1: Remember the Original Fact
forget(everything=True) starts from a clean slate, then remember() builds the fact into the knowledge graph the same way all your data gets in — no low-level ingestion needed. Every node it creates carries a valid_to stamp (int | None, ms epoch) that defaults to None, meaning the fact is still current.
Step 2: Derive the Node Id from the Entity Name
remember() turned the sentence into entity nodes, and entity node ids are deterministic: Entity.id_for(name) applies the same normalization the ingestion pipeline uses (lowercase, spaces to underscores, apostrophes stripped) and returns the id the entity was stored under. No graph scan needed — this works the same on ten nodes or a hundred thousand.
Step 3: Close the Superseded Fact
close_node() stamps valid_to on the stored node — “now” by default — marking the fact superseded without deleting it. It returns True only if the node existed and was patched, and False otherwise (for example when the id is not in the graph). Note the import path: from cognee.tasks.storage.close_node import close_node, is_valid.
Step 4: Remember the Replacement Fact
remember() like any other data and becomes its own nodes. Supersede instead of delete: the old node stays in the graph with valid_to set, so your memory keeps the history of what used to be true.
Step 5: Check Staleness with is_valid()
is_valid(node, at_ms=None) returns True while valid_to is None (never closed) or lies strictly in the future relative to at_ms (default: now). It accepts either a DataPoint instance (reads the attribute) or a plain graph-node dict (reads the key), so it works on records read back from the graph engine, as here.
Behavior to Know About
- Node-level granularity.
valid_tolives on nodes: closing marks the whole node stale, and the edges attached to it are not stamped. - Backdating.
close_node(node_id, at_ms=...)stamps a specific ms-epoch timestamp instead of “now”, andis_valid(node, at_ms=some_past_ms)asks whether the fact was still current at that moment. - Not idempotent. Closing is last-write-wins: re-closing an already-closed node overwrites
valid_towith the new timestamp (earlier or later). Guard withis_valid()first if you need the first close to stick. - Two time axes.
valid_torecords when a fact stopped being true. It is not thetime_toonIntervalnodes (the time range anEventpoints to viaduring), which records when an event occurred — that axis belongs to Time Awareness. - Retrieval is not filtered yet. Search and graph completion neither filter nor down-weight closed nodes, so a superseded fact can still surface in results. Applying
is_valid()to what you retrieve is currently the caller’s job; retrieval-side consumption is planned as a follow-up.
Backend Support
close_node() persists valid_to through the graph adapter’s optional update_node method — see Adding a new graph database for the adapter contract.
Time Awareness
The other time axis: extract events and timestamps and run time-aware queries.
DataPoints
The building block that carries
valid_to and the rest of the node schema.