Skip to content

Quickstart (Python)

This guide shows how to open an engine, store facts with remember, read them back with recall, and observe what Contested looks like. The same flow is shown in Rust on the Rust quickstart.

Install from PyPI:

Terminal window
pip install mempill
# or: uv add mempill
quickstart.py
from mempill import open_in_memory, history, remember, recall, RememberOptions
engine = open_in_memory()
agent = "my-agent"

Use mempill.open(path) instead to persist facts across process restarts.

Step 2 — Remember two facts with non-overlapping time windows

Section titled “Step 2 — Remember two facts with non-overlapping time windows”
# Berlin valid [2020-01-01, 2024-12-31] → covered the past
remember(
engine, agent, "user", "city", "Berlin",
RememberOptions(valid_from="2020-01-01", valid_until="2024-12-31"),
)
# Munich valid [2025-01-01, ∞) → covers NOW (2026), so it wins
remember(
engine, agent, "user", "city", "Munich",
RememberOptions(valid_from="2025-01-01"),
)

Dates are lenient: "2026", "2026-06", "2026-06-01", and RFC 3339 are all accepted. Natural-language parsing ("next month", "last Tuesday") is the host’s job — mempill stays LLM-free and expects unambiguous date strings.

Step 3 — Recall: Munich wins (succession, not conflict)

Section titled “Step 3 — Recall: Munich wins (succession, not conflict)”
result = recall(engine, agent, "user", "city")
assert result.as_str() == "Munich"
assert not result.is_contested()
print(f"city={result.as_str()!r} is_contested={result.is_contested()}")
# city='Munich' is_contested=False

Because the two time windows do not overlap, the engine can resolve a single winner via valid-time succession — no conflict, no human needed.

history() returns every claim for a subject-line, ordered oldest→newest. Each entry carries a .status of "Current" or "Superseded". .current() is guaranteed to agree with recall(). The History object is iterable.

h = history(engine, agent, "user", "city")
for e in h:
print(e.value, e.status)
# Berlin Superseded
# Munich Current
# current() agrees with recall()
assert h.current().value == recall(engine, agent, "user", "city").as_str()
assert not h.is_empty()

Note: there is no valid_at parameter yet — point-in-time valid-time as-of queries are planned for v0.3. For the full history API see Query Patterns.

Step 5 — Contested: two timeless facts for the same line

Section titled “Step 5 — Contested: two timeless facts for the same line”
remember(engine, agent, "acme", "ceo", "Alice")
remember(engine, agent, "acme", "ceo", "Bob")
ceo = recall(engine, agent, "acme", "ceo")
assert ceo.is_contested()
assert ceo.value is None # Contested: no winner — use candidates
assert len(ceo.candidates) == 2
print(f"is_contested={ceo.is_contested()} candidates={[c.value for c in ceo.candidates]}")
# is_contested=True candidates=['Alice', 'Bob']

recall never silently picks a winner. When facts genuinely conflict, is_contested() is True, value is None, and candidates lists every preserved claim. The agent decides what to do next: surface to a human, request oracle adjudication, or accept the ambiguity.

Attribute Resolved Contested NoBelief
as_str() "Munich" None None
is_contested() False True False
len(candidates) 0 2+ 0

To resolve a Contested belief you can:

  1. Call engine.reconcile(...) to let the oracle re-adjudicate.
  2. Supply a new remember with a tighter valid-time window that makes one claim a successor.
  3. Surface to a human via the MCP adapter’s submit_adjudication tool.

Most users only need remember / recall. If you need fine-grained provenance, confidence weights, or explicit cardinality, you can call the underlying dict API directly.

Full DTO — for advanced provenance control
import mempill
from mempill import ProvenanceLabel, Disposition
engine = mempill.open_in_memory()
# Write a claim with explicit provenance, confidence, and criticality.
resp = engine.ingest_claim({
"agent_id": "my-agent",
"subject": "user",
"predicate": "city",
"value": "Berlin",
"provenance": ProvenanceLabel.external_user_asserted(),
"cardinality": "Functional",
"confidence": {"value_confidence": 0.95, "valid_time_confidence": 0.0},
"criticality": "Medium",
"derived_from": [],
})
print(resp["claim_ref"], resp["disposition"])
# <uuid> CommittedCheap
assert resp["disposition"] == Disposition.CommittedCheap
# Read back the belief.
result = engine.query_memory({
"agent_id": "my-agent",
"subject": "user",
"predicate": "city",
})
print(result["belief"]["status"]) # TimingUncertain
print(result["belief"]["primary"]["fact"]["value"]) # Berlin

See the API Reference for the full IngestClaimRequest field table and the 12-state disposition model.