Skip to Content
These docs describe PRME 0.16.0a8, an alpha. pip install prme installs the latest stable release. To use these docs as written, run pip install prme==0.16.0a8. Changelog
GuidesMemoryClient API

MemoryClient API Reference

MemoryClient is the recommended Python interface for PRME. It’s synchronous, manages its own event loop, and works in scripts, notebooks, FastAPI apps, and any other context.

Creating a Client

from prme import MemoryClient # From a directory path (creates if needed) client = MemoryClient("./my_memories") # With explicit config from prme.config import PRMEConfig config = PRMEConfig(db_path="./custom.duckdb", vector_path="./vecs.usearch", lexical_path="./lex") client = MemoryClient(config=config)

Always use as a context manager or call close():

# Context manager (recommended) with MemoryClient("./my_memories") as client: client.store("hello", user_id="alice") # Manual lifecycle client = MemoryClient("./my_memories") try: client.store("hello", user_id="alice") finally: client.close()

store()

Store a memory node across all backends (event store, graph, vector index, lexical index).

def store( content: str, *, user_id: str, session_id: str | None = None, role: str = "user", speaker: str | None = None, node_type: NodeType = NodeType.NOTE, scope: Scope = Scope.PERSONAL, metadata: dict | None = None, confidence: float | None = None, epistemic_type: EpistemicType | None = None, source_type: SourceType | None = None, event_time: datetime | None = None, ttl_days: int | None = ..., extract: bool | None = None, ) -> str # returns event UUID

Parameters:

ParameterTypeDefaultDescription
contentstrrequiredText content to store
user_idstrrequiredOwner of this memory
session_idstr | NoneNoneGroup related memories
rolestr"user"Source role: user (the memory’s owner), participant (another human in the conversation), assistant, tool or system
speakerstr | NoneNoneName of who said it, shown by the reader context
node_typeNodeTypeNOTEType of memory node
scopeScopePERSONALVisibility scope
metadatadict | NoneNoneArbitrary key-value metadata
confidencefloat | NoneNoneOverride initial confidence (0.0-1.0)
event_timedatetime | NoneNoneWhen the event occurred (default: now)
epistemic_typeEpistemicType | NoneNoneOverride inferred assertion status
source_typeSourceType | NoneNoneOverride inferred source provenance
ttl_daysint | None...Omit for configured default; None disables expiry; an integer sets days
extractbool | NoneNoneTrue also queues the background LLM extraction ingest() runs, once the node is stored; False never does; None follows enable_store_extraction (off)

Returns: Event UUID as a string.

Example:

from prme.types import NodeType, Scope event_id = client.store( "The deployment target is AWS us-east-1.", user_id="alice", node_type=NodeType.DECISION, scope=Scope.PROJECT, metadata={"source": "architecture-review"}, )

retrieve()

Search memories using the 6-signal hybrid retrieval pipeline.

def retrieve( query: str, *, user_id: str, scope: Scope | list[Scope] | None = None, time_from: datetime | None = None, time_to: datetime | None = None, reference_time: datetime | None = None, knowledge_at: datetime | None = None, token_budget: int | None = None, ) -> RetrievalResponse

Parameters:

ParameterTypeDefaultDescription
querystrrequiredNatural language search query
user_idstrrequiredUser whose memories to search
scopeScope | list | NoneNoneFilter by scope(s)
time_fromdatetime | NoneNoneValidity window start; excludes assertions expired at or before this time (ENTITY/PREFERENCE exempt)
time_todatetime | NoneNoneValidity window end; excludes assertions that become valid after this time (ENTITY/PREFERENCE exempt)
knowledge_atdatetime | NoneNonePoint-in-time snapshot (bi-temporal)
reference_timedatetime | NoneNoneTimezone-aware clock for relative query dates and scoring; defaults to request time and is recorded in response metadata
token_budgetint | NoneNoneMax tokens for context packing
max_per_sourceint | NoneNoneMax results sharing an exact source passage and evidence set

Returns: RetrievalResponse with:

  • results — list of RetrievalCandidate objects, each with .node (MemoryNode) and .composite_score (float)
  • bundle — packed context bundle for LLM consumption
  • metadata — retrieval timing, candidate counts, backends used

Use response.bundle.render() for the exact context counted by response.bundle.tokens_used. The bundle records its tokenizer. Reconstructing text from the full node objects bypasses that budget. See rendered context and budgets.

Example:

response = client.retrieve("What tech stack are we using?", user_id="alice") for result in response.results[:5]: node = result.node print(f"[{result.composite_score:.3f}] {node.node_type.value}: {node.content}")

Deferred raw ingestion

For raw ingestion without an LLM, the synchronous client also supports durable deferred processing:

event_id = client.ingest_fast("Alice prefers dark mode", user_id="alice") result = client.process_pending(user_id="alice", budget_ms=1000) status = client.processing_status(event_id, user_id="alice")

These methods have the same status, retry, scope, and cooperative-budget semantics as the async processing API. They track new direct store() writes and raw-source indexing from both ingest_fast() and ingest(). LLM derivations use extraction_status().

ingest()

Ingest content through the LLM extraction pipeline. Automatically extracts entities, facts, relationships, preferences, and decisions.

Requires an LLM API key (OPENAI_API_KEY, ANTHROPIC_API_KEY, or Ollama).

def ingest( content: str, *, user_id: str, role: str = "user", speaker: str | None = None, session_id: str | None = None, scope: Scope = Scope.PERSONAL, ) -> str # returns event UUID

Example:

event_id = client.ingest( "I just finished migrating our auth system from JWT to session tokens.", user_id="alice", scope=Scope.PROJECT, )

ingest_batch()

Ingest multiple messages at once. Each message dict must have role and content keys, and can set speaker.

def ingest_batch( messages: list[dict], *, user_id: str, session_id: str | None = None, scope: Scope = Scope.PERSONAL, ) -> list[str] # returns list of event UUIDs

Example:

event_ids = client.ingest_batch( [ {"role": "user", "content": "Let's use Redis for caching."}, {"role": "assistant", "content": "Good call. I'll note that decision."}, ], user_id="alice", session_id="planning-session", )

reembed()

Build an embedding model’s vectors for the stored memories so the store can switch models (see switching embedding models ). It embeds with a local model, writes only vectors, and needs enable_multi_model_embeddings (off by default); without it the call raises ReembedDisabledError. embedding_status() lists the models a store holds and whether each is complete, and drop_embeddings(model) removes one that is not the active model.

def reembed( model: str | EmbeddingProvider | None = None, *, limit: int | None = None, cursor: str | None = None, dry_run: bool = False, user_id: str | None = None, batch_size: int = 64, pause_ms: int = 0, threads: int | None = None, ) -> ReembedResult
client = MemoryClient(config=PRMEConfig(enable_multi_model_embeddings=True)) preview = client.reembed("BAAI/bge-base-en-v1.5", dry_run=True) result = client.reembed("BAAI/bge-base-en-v1.5", pause_ms=50) # result.remaining == 0 when it is complete

reingest()

Re-derive one owner’s stored memories after an engine change improved how they are derived (see Re-deriving stored memories). It runs the batched extraction ingest() runs over the owner’s memories in event order, replaces the claims derived from each with new ones, and never changes the source events or raw notes. It needs enable_reingest (off by default); without it the call raises ReingestDisabledError.

def reingest( user_id: str, *, scope: Scope | str | None = None, limit: int | None = None, cursor: str | None = None, dry_run: bool = False, ) -> ReingestResult

A dry run sends no model request and changes nothing; its model_calls is the number of requests a run would send. A second run skips memories already derived under the same settings, and result.cursor continues after the last memory a capped call took.

client = MemoryClient(config=PRMEConfig(enable_reingest=True)) preview = client.reingest("alice", dry_run=True) result = client.reingest("alice", limit=500)

observe()

def observe( self, source: str | os.PathLike[str] | bytes, *, user_id: str, captured_at: datetime | None = None, source_name: str | None = None, session_id: str | None = None, metadata: dict | None = None, scope: Scope = Scope.PERSONAL, ) -> list[str]

Remember what a photo or a saved video showed (RFC-0018, phase 1). Off by default: the client’s configuration needs vision.enabled (PRME_VISION__ENABLED=true), a vision model the configured provider can reach (a local Ollama server by default) and, for video, ffmpeg. Returns the ids of the observation events, one per photo or per kept keyframe; each has role tool, the capture clock as event_time and metadata naming the source, the frame, the thumbnail and the vision model. client.get_blob(event.metadata["thumbnail"]) returns the keyframe thumbnail.

ids = client.observe("~/Pictures/receipt.jpg", user_id="alice", captured_at=datetime(2026, 10, 12, 8, 41, tzinfo=timezone.utc)) event = client.get_event(ids[0], user_id="alice") thumbnail = client.get_blob(event.metadata["thumbnail"]) # JPEG bytes

get_node()

Get a single node by ID.

def get_node(node_id: str) -> MemoryNode | None

Example:

node = client.get_node("550e8400-e29b-41d4-a716-446655440000") if node: print(f"{node.node_type.value}: {node.content}") print(f"State: {node.lifecycle_state.value}, Confidence: {node.confidence:.2f}")

query_nodes()

Query nodes with filters.

def query_nodes(**kwargs) -> list[MemoryNode]

Supported keyword arguments: user_id, node_type, lifecycle_state, limit, offset.

Example:

from prme.types import NodeType facts = client.query_nodes(user_id="alice", node_type=NodeType.FACT, limit=20) for fact in facts: print(f"[{fact.lifecycle_state.value}] {fact.content}")

organize()

Run organizer maintenance jobs (promotion, decay, deduplication, summarization, etc.).

def organize( *, user_id: str | None = None, jobs: list[str] | None = None, budget_ms: int = 5000, ) -> OrganizeResult

Parameters:

ParameterTypeDefaultDescription
user_idstr | NoneNoneScope jobs to a specific user
jobslist[str] | NoneNoneSpecific jobs to run (None = all)
budget_msint5000Time budget in milliseconds

Available jobs: promote, decay_sweep, archive, deduplicate, alias_resolve, summarize, feedback_apply, centrality_boost, tombstone_sweep, snapshot_generation, consolidate.

Example:

# Run all jobs result = client.organize(user_id="alice") print(f"Jobs run: {result.jobs_run}") # Run specific jobs result = client.organize(jobs=["promote", "deduplicate"], budget_ms=2000)

close()

Shut down the engine and background event loop. Called automatically when using a context manager.

client.close()

Integration Patterns

FastAPI

from fastapi import FastAPI from prme import MemoryClient app = FastAPI() client = MemoryClient("./memories") @app.on_event("shutdown") def shutdown(): client.close() @app.post("/remember") def remember(content: str, user_id: str): event_id = client.store(content, user_id=user_id) return {"event_id": event_id} @app.get("/recall") def recall(query: str, user_id: str): response = client.retrieve(query, user_id=user_id) return [{"content": r.node.content, "score": r.composite_score} for r in response.results]

LangChain / Agent Framework

from prme import MemoryClient client = MemoryClient("./agent_memory") def store_memory(content: str, user_id: str): """Tool for the agent to store memories.""" return client.store(content, user_id=user_id) def recall_memory(query: str, user_id: str): """Tool for the agent to recall relevant memories.""" response = client.retrieve(query, user_id=user_id) return "\n".join(r.node.content for r in response.results[:5])

Jupyter Notebook

from prme import MemoryClient client = MemoryClient("./notebook_memories") # Store during analysis client.store("Customer churn rate is 5.2% this quarter.", user_id="analyst") # Query later response = client.retrieve("What is the churn rate?", user_id="analyst") response.results[0].node.content # → "Customer churn rate is 5.2% this quarter." # Don't forget to close client.close()

Source: documentation/memory-client.md  in PRME at bd92166f.