RememberStackremember.dev/docs

Python SDK

The remember package is the Python client for RememberStack. You use it to store documents, wait until they are processed, and ask the memory what it knows.

This page lists every public name the package exports. For the fields inside a result (facts, claims, validity windows, negatives), see Result types.

Install

pip install remember

The package needs Python 3.12 or newer. It depends only on httpx, pydantic and pydantic-settings; it does not contain the engine. To run the engine, see Install with Docker Compose.

pip install remember also installs the remember command. See CLI.

The client is synchronous. There is no async client yet (What is not built yet).

Public names

remember.__all__ contains these names:

NameKindSection
ClientclassClient
RememberClientalias of ClientClient
MemoryClientclassMemoryClient
resolve_connectionfunctionConnecting
MemoryApiErrorexceptionErrors
RateLimitedexceptionErrors
StoredKeyRefusedexceptionErrors
PipelineDeadLetteredexceptionErrors
ConnectorNotFoundErrorexceptionErrors
CapabilityReadinessmodelModels
ClaimValidPrecisionenumModels
ConnectorCreatemodelModels
ConnectorDescriptormodelModels
ContextBundleV2modelModels
DocumentDeletionmodelModels
DocumentPagemodelModels
DocumentSummarymodelModels
DocumentVersionSummarymodelModels
EnvelopemodelModels
IngestedVersionmodelModels
PipelineReadinessReportmodelModels
PipelineStageReadinessmodelModels
QueryResultDictdict subclassModels
ReadinessRequirementsmodelModels
TemporalMatchenumModels
ToolDescriptormodelModels
VersionPipelineReadinessmodelModels
__version__stringThe installed package version, for example "0.17.0".

remember.credentials.CredentialError, raised when the credential file is unreadable or not in the current format, lives outside __all__. See Errors.

Which client to use

You want toUse
Store and retrieve memory (the usual case)Client (same as RememberClient)
The same, without the file-path ingest shortcutMemoryClient

Client is a subclass of MemoryClient. Every method listed under MemoryClient is available on Client.

Connecting

Client and MemoryClient resolve their connection the way the CLI does, with one function, remember.resolve_connection(). Each setting is resolved on its own; first match wins:

Setting1. Argument2. Environment3. Credential file4. Otherwise
Keyapi_keyREMEMBER_API_KEYkeyno key
Addressbase_urlREMEMBER_API_URLapi_urlhttp://127.0.0.1:8000
ProjectprojectREMEMBER_PROJECTdefault_projectnone

The credential file is the one the CLI writes (CLI: credential file). It is read only when a setting is not given as an argument or in the environment.

A key read from the credential file is only sent to the address stored beside it. When base_url or REMEMBER_API_URL names another address, the first request raises StoredKeyRefused; pass the key explicitly to use it there. A key you pass as an argument or in REMEMBER_API_KEY is sent wherever you point it.

export REMEMBER_API_URL=http://localhost:8000
export REMEMBER_API_KEY=<the key your engine accepts, if any>
import remember
 
with remember.Client() as memory:
    print(memory.deployment_build_info().build_revision)

Client

class remember.Client(MemoryClient)
remember.RememberClient = remember.Client

The client for one deployment. It talks directly to the deployment's own address.

Constructor

Client(
    *,
    api_key: str | None = None,
    base_url: str | None = None,
    project: str | None = None,
    timeout: float = 30.0,
    client: httpx.Client | None = None,
    transport: httpx.BaseTransport | None = None,
)

All parameters are keyword-only.

ParameterMeaning
api_keyThe API key. A bare key or a full Bearer … value.
base_urlThe deployment address.
projectWhich project to use, by id or name, when the key covers several.
timeoutSeconds per HTTP request. Default 30.
clientAn httpx.Client you built yourself, with its own address and headers. It cannot be combined with any other parameter, and nothing is resolved. The SDK does not close a client you pass in.
transportAn httpx transport to use instead of the network (for tests or a proxy).

How each setting is chosen: Connecting. Creating a client makes no network call.

Raises:

  • ValueError when client is combined with another parameter.
  • ValueError when the key is empty, is the bare word Bearer, or contains a line break.
  • remember.credentials.CredentialError when the credential file has to be read and is unreadable or not in the current format.

Client.ingest

Same signature and behaviour as MemoryClient.ingest, with one difference: a str source is always treated as a file path. A path that does not exist raises FileNotFoundError (on MemoryClient it raises ValueError).

Client.ingest_file

Client.ingest_file(
    file_path: str | Path,
    *,
    filename: str | None = None,
    mime: str | None = None,
    title: str | None = None,
    source_kind: str | None = None,
    source_ref: str | None = None,
    source_modified_at: datetime | None = None,
    versioning_mode: Literal["snapshot", "living"] = "snapshot",
    source_version_ref: str | None = None,
) -> IngestedVersion

Reads one file and ingests it. It calls ingest(file_path, …) with the same arguments.

version = memory.ingest_file("notes/billing-migration-kickoff.md")

Context manager

with remember.Client() as memory: returns the client and closes its HTTP connection pool when the block ends. Outside a with block, call memory.close() yourself. close() closes only a connection pool the SDK created; a client= you injected stays open.

MemoryClient

class remember.MemoryClient

The typed client every other memory client builds on.

Constructor

MemoryClient takes the same parameters as Client and resolves its connection the same way (Connecting).

MemoryClient supports with and close() the same way Client does.

Method summary

MethodHTTP routeReturns
ingestPOST /ingestIngestedVersion
pipeline_readinessPOST /readinessPipelineReadinessReport
wait_for_readinessPOST /readiness, repeatedPipelineReadinessReport
deployment_build_infoGET /deploymentDeploymentBuildInfo
list_documentsGET /documentsDocumentPage
delete_documentDELETE /documents/{doc_id}DocumentDeletion
list_operationsGET /operationstuple[ToolDescriptor, ...]
run_operationPOST /operations/{name}Envelope or ContextBundleV2
facts_contextPOST /operations/facts_contextEnvelope
claims_and_sources_contextPOST /operations/claims_and_sources_contextEnvelope
combined_contextPOST /operations/combined_contextContextBundleV2
resolve_entityPOST /operations/resolve_entityEnvelope
resolveGET /resolveEnvelope
lookup_relationsGET /lookup/relationsEnvelope
lookup_observationsGET /lookup/observationsEnvelope
transcript_relationGET /transcript/relation/{id}Envelope
hydrate_relationGET /hydrate/relation/{id}Envelope
search_claimsGET /search/claimsEnvelope
search_chunksGET /search/chunksEnvelope
adjacent_chunksGET /chunks/{id}/adjacentEnvelope
graph_neighborhoodPOST /graph/neighborhoodEnvelope
graph_pathPOST /graph/pathEnvelope
graph_citation_pathPOST /graph/citation-pathEnvelope
query_sqlPOST /query/sqldict
open_queryPOST /query/sqlQueryResultDict
explain_sqlPOST /query/sql/explaindict
explain_queryPOST /query/sql/explainQueryResultDict
describe_query_spaceGET /query/spacedict
search_query_spaceGET /query/space/searchlist[dict]
list_saved_queriesGET /query/savedlist[dict]
describe_saved_queryGET /query/saved/{namespace}/{name}dict
run_saved_queryPOST /query/saved/{namespace}/{name}/rundict
call_open_queryone of the /query/* routesobject
Connector methods/connectors…not served

Every method raises MemoryApiError when the deployment answers with an error status, when the network fails (status_code 0), or when a success response does not match the expected shape (status_code 200). Methods also raise ValueError for the client-side checks listed with each one.

The HTTP routes are documented in the HTTP API reference.

Store

ingest

MemoryClient.ingest(
    source: bytes | Path | str | None = None,
    *,
    content: bytes | None = None,
    filename: str | None = None,
    mime: str | None = None,
    title: str | None = None,
    source_kind: str | None = None,
    source_ref: str | None = None,
    source_modified_at: datetime | None = None,
    versioning_mode: Literal["snapshot", "living"] = "snapshot",
    source_version_ref: str | None = None,
) -> IngestedVersion

Uploads one document. The call returns as soon as the engine has stored the bytes; processing runs afterwards. Wait with wait_for_readiness before you expect the content in results.

ParameterMeaning
sourceA Path or str file path (read from disk), or bytes.
contentRaw bytes. Wins over source when both are given; source then only supplies the filename.
filenameRequired for bytes. Defaults to the file's name for a path.
mimeMedia type; an explicit value always wins. Defaults to the type of the extension of the file's real name (for a path) or of filename (for bytes): .md is text/markdown, .pdf is application/pdf, on every Python installation; the full table is in Ingest files. An extension outside that table takes the type your Python installation's MIME database gives it, or application/octet-stream if it has none. The engine converts only media types it has a converter for; see File formats and converters.
titleA human title for the document.
source_kind, source_refThe document's stable identity: what kind of source it is and its id within that kind. Always together. Ingesting again with the same pair creates a new version of the same document when the bytes differ. See Documents, versions and sources.
source_modified_atWhen the source itself last changed. Must be timezone-aware UTC. Requires source_kind/source_ref.
versioning_mode"snapshot" (default) or "living". "living" requires source_kind/source_ref. See Updating a source.
source_version_refThe source system's own revision label. Requires source_kind/source_ref.

Raises ValueError when:

  • only one of source_kind and source_ref is given;
  • source_modified_at, source_version_ref or versioning_mode="living" is given without source_kind/source_ref;
  • source_modified_at is naive or not UTC;
  • a str path is not a file (MemoryClient only);
  • neither source nor content is given;
  • bytes are given without filename.

Reading a path can raise OSError.

Returns IngestedVersion. created is False when these exact bytes were already stored for this document; no new processing starts.

from datetime import UTC, datetime
from pathlib import Path
 
version = memory.ingest(
    Path("specs/billing-migration.md"),
    title="Billing migration spec",
    source_kind="notes",
    source_ref="specs/billing-migration.md",
    source_modified_at=datetime(2026, 9, 21, 14, 0, tzinfo=UTC),
)
print(version.doc_id, version.version_id, version.created)

pipeline_readiness

MemoryClient.pipeline_readiness(
    *, version_ids: tuple[UUID, ...], require: ReadinessRequirements
) -> PipelineReadinessReport

Asks, once, whether the given versions have finished processing and whether the capabilities you name are ready. require must name all four capabilities (pipeline, p1, live_graph, p3) as True or False.

Raises ValueError when version_ids is empty.

report = memory.pipeline_readiness(
    version_ids=(version.version_id,),
    require=remember.ReadinessRequirements(
        pipeline=True, p1=True, live_graph=True, p3=False
    ),
)
for v in report.versions:
    print(v.version_id, v.ready, [(s.stage, s.status) for s in v.stages])

What each capability means: The pipeline and readiness.

wait_for_readiness

MemoryClient.wait_for_readiness(
    version_ids: Sequence[str | UUID],
    *,
    timeout: float = 1800.0,
    poll_interval: float = 15.0,
    require_p3: bool = False,
) -> PipelineReadinessReport

Calls pipeline_readiness at once and then every poll_interval seconds until the report says ready, requiring pipeline, p1 and live_graph (and p3 when require_p3=True). Returns the ready report. The defaults, 30 minutes and 15 seconds, are starting points sized for single documents; raise timeout for bulk loads.

A stage that is failed has a retry scheduled, so the method keeps polling. A stage that is dead_letter has used all its attempts and never becomes ready, so the method stops at once.

Raises:

  • PipelineDeadLettered when any stage of any listed version is dead_letter;
  • TimeoutError when timeout seconds pass first;
  • ValueError when an id is not a UUID.

See Wait until a document is queryable.

memory.wait_for_readiness([version.version_id])

deployment_build_info

MemoryClient.deployment_build_info() -> DeploymentBuildInfo

Returns which engine build and which model bindings the deployment is serving: build_revision (str), model_bindings (dict of str to str), document_binding_generation (str or None), and tools (dict of MCP memory tool name to tool version). The class is remember.models.DeploymentBuildInfo; it is not in __all__.

info = memory.deployment_build_info()
print(info.build_revision, info.model_bindings.get("claim_extraction"))

list_documents

MemoryClient.list_documents(
    *,
    limit: int = 50,
    cursor: str | None = None,
    status: Literal["ingesting", "converting", "structuring", "ready", "failed"] | None = None,
) -> DocumentPage

Returns one page of the deployment's documents, newest document first, as a DocumentPage. Pass the returned cursor to read the next page; it is None on the last page. status keeps only documents whose newest version has that status. limit is 1 to 200; the deployment answers 422 outside that range. Deleted documents are not listed. See GET /documents.

cursor = None
while True:
    page = memory.list_documents(cursor=cursor)
    for document in page.documents:
        print(document.doc_id, document.title, document.serving)
    if page.cursor is None:
        break
    cursor = page.cursor

delete_document

MemoryClient.delete_document(*, doc_id: UUID | str) -> DocumentDeletion

Removes one document, every version of it, from the memory and returns a DocumentDeletion with doc_id, deleted_at, claims_retired, relations_closed and observations_closed. Its claims stop counting as evidence, and facts that only it supported are closed with a recorded retraction. The claims and the stored original are kept as history. Needs a write credential. See DELETE /documents/{doc_id}.

Raises ValueError before sending anything when doc_id is not a UUID, and MemoryApiError with status_code 404 and detail "document_not_found" when the document does not exist or is already deleted.

from remember import MemoryApiError
 
try:
    deletion = memory.delete_document(doc_id=version.doc_id)
    print(f"{deletion.claims_retired} claims retired")
except MemoryApiError as error:
    if error.status_code != 404:
        raise

Assured operations

The four assured operations are the recommended way to ask the memory a question. Their full contracts are in Assured operations.

list_operations

MemoryClient.list_operations() -> tuple[ToolDescriptor, ...]

Returns the descriptors of the deployment's operations: resolve_entity, claims_and_sources_context, facts_context, combined_context. Each descriptor carries the operation's input schema.

for op in memory.list_operations():
    print(op.name, op.version, op.input_schema.get("required"))

run_operation

MemoryClient.run_operation(
    *, name: str, arguments: Mapping[str, object] | None = None
) -> Envelope | ContextBundleV2

Runs one operation by name with its JSON arguments. Returns a ContextBundleV2 when the response is a combined bundle, otherwise an Envelope. Use it for arguments the convenience methods below do not expose (k, evidence_per_fact, candidate_k, entity_ids on claims_and_sources_context).

The engine validates arguments and answers with an error (raised as MemoryApiError) for an unknown name, a missing required argument, an unknown argument or a value outside its bounds.

result = memory.run_operation(
    name="facts_context",
    arguments={"query": "Who owns the billing migration?", "k": 25},
)

facts_context

MemoryClient.facts_context(
    query: str,
    *,
    time: Mapping[str, object] | None = None,
    hops: int | None = None,
    predicate: str | None = None,
    entity_ids: Sequence[str | UUID] | None = None,
) -> Envelope

Returns the facts the memory holds true that match query, with their evidence.

ArgumentConstraint (checked by the engine)
query1 to 8,192 characters
time{"mode": "current"} (default), {"mode": "at", "at": "<ISO date-time>"}, {"mode": "overlap", "from": "…", "to": "…"} or {"mode": "history"}
hops1 or 2; default 1
predicate1 to 200 characters
entity_ids1 to 19 unique entity UUIDs
facts = memory.facts_context(
    "billing migration owner", time={"mode": "history"}
)
for fact in facts.facts:
    print(fact.label, fact.validity.valid_from, fact.validity.valid_until)

claims_and_sources_context

MemoryClient.claims_and_sources_context(query: str) -> Envelope

Returns what sources said about query (claims) and the matching source passages (chunks). query is 1 to 8,192 characters.

said = memory.claims_and_sources_context("Why was the cutover date moved?")
for claim in said.evidence:
    print(claim.claim_text, claim.asserted_at, claim.document_title)

combined_context

MemoryClient.combined_context(
    query: str, *, time: Mapping[str, object] | None = None
) -> ContextBundleV2

Runs claims_and_sources_context and facts_context together and returns both results in one bundle: bundle.claims_and_sources and bundle.facts. time takes the same values as in facts_context.

bundle = memory.combined_context("What did Ravi decide about invoicing?")
print(len(bundle.facts.facts), len(bundle.claims_and_sources.evidence))

resolve_entity

MemoryClient.resolve_entity(name: str) -> Envelope

Returns the ranked candidate entities for a name. It never picks one silently. name must be at least one character.

people = memory.resolve_entity("Dana")
for candidate in people.entities:
    print(candidate.entity_id, candidate.canonical_name)
 
dana_id = people.entities[0].entity_id   # reused by the examples below

Entities and facts

resolve

MemoryClient.resolve(
    *, name: str, context_entity_ids: tuple[UUID, ...] = ()
) -> Envelope

Resolves a name, using up to 8 already-known entities as context to rank the candidates. More than 8 is refused by the engine.

memory.resolve(name="the migration", context_entity_ids=(dana_id,))

lookup_relations

MemoryClient.lookup_relations(
    *,
    subject_entity_id: UUID | None = None,
    predicate: str | None = None,
    object_entity_id: UUID | None = None,
    valid_at: datetime | None = None,
    k: int = 50,
) -> Envelope

Returns the relations (facts between two entities) that match the pattern. With valid_at, returns the relations that were valid at that instant; without it, the current ones. At most k (1–400) come back; the envelope's truncation says when more matched.

memory.lookup_relations(subject_entity_id=dana_id, predicate="leads")

lookup_observations

MemoryClient.lookup_observations(
    *, entity_id: UUID, property_query: str | None = None, k: int = 10
) -> Envelope

Returns the current observations (facts about one entity), optionally narrowed by property_query text, at most k.

memory.lookup_observations(entity_id=dana_id, property_query="role")

transcript_relation

MemoryClient.transcript_relation(*, relation_id: UUID) -> Envelope

Returns the recorded decisions that shaped one relation (the transcript field of the envelope).

hydrate_relation

MemoryClient.hydrate_relation(*, relation_id: UUID) -> Envelope

Returns one relation with its evidence and the source documents behind it.

relation = next(f for f in facts.facts if f.kind == "relation")
cited = memory.hydrate_relation(relation_id=relation.fact_id)
print([s.title for s in cited.sources])

search_claims

MemoryClient.search_claims(
    *, query: str, k: int = 10, channel: Literal["semantic", "bm25"] = "semantic"
) -> Envelope

Searches claims by meaning ("semantic") or by keywords ("bm25"). The result is evidence, not facts. The engine accepts k from 1 to 400.

search_chunks

MemoryClient.search_chunks(
    *, query: str, k: int = 10, channel: Literal["semantic", "bm25"] = "semantic"
) -> Envelope

Searches source passages. Same arguments and bounds as search_claims.

hits = memory.search_chunks(query="invoice numbering", k=5, channel="bm25")
for chunk in hits.chunks:
    print(chunk.chunk_id, chunk.chunk_text[:80])

adjacent_chunks

MemoryClient.adjacent_chunks(*, chunk_id: UUID | str, window: int = 1) -> Envelope

Returns the passages immediately before and after one chunk in the same document version, in document order. window is 1 or 2.

Raises ValueError when window is outside 1–2 or chunk_id is not a UUID.

around = memory.adjacent_chunks(chunk_id=hits.chunks[0].chunk_id, window=2)

Graph

Details and limits: Graph.

graph_neighborhood

MemoryClient.graph_neighborhood(
    *,
    entity_id: UUID,
    hops: int = 2,
    predicates: tuple[str, ...] = (),
    valid_at: datetime | None = None,
    believed_at: datetime | None = None,
    limit: int = 500,
    continuation: str | None = None,
    include_paths: bool = False,
) -> Envelope

Returns the entities and relations around one entity. The engine accepts hops 1–4, limit 1–500 and at most 100 predicates. Pass the envelope's truncation.continuation back as continuation to read the next page.

around_dana = memory.graph_neighborhood(entity_id=dana_id, hops=1)
print(len(around_dana.nodes), len(around_dana.edges))

graph_path

MemoryClient.graph_path(
    *,
    from_entity_id: UUID,
    to_entity_id: UUID,
    max_hops: int = 4,
    predicates: tuple[str, ...] = (),
    valid_at: datetime | None = None,
    believed_at: datetime | None = None,
) -> Envelope

Returns the shortest paths between two entities. The engine accepts max_hops 1–6.

graph_citation_path

MemoryClient.graph_citation_path(
    *, from_doc_id: UUID, to_doc_id: UUID, max_hops: int = 6
) -> Envelope

Returns citation paths from one document to another. max_hops 1–6.

SQL and saved queries

SQL queries run over the query space (memory_v1): prepared, read-only views and functions. The engine parses every statement and validates it against the query space before it runs; anything outside it is rejected. See Query space memory_v1 and Explore memory with SQL. Failures carry a code on MemoryApiError.code (for example relation_not_allowed, statement_timeout); the list is in Errors.

query_sql

MemoryClient.query_sql(
    *,
    sql: str,
    parameters: list[object] | tuple[object, ...] = (),
    max_rows: int | None = None,
) -> dict[str, object]

Runs one statement with positional parameters ($1, $2, …). Returns the QueryResult/v1 object as a dict. max_rows must be 0 or more.

open_query

MemoryClient.open_query(
    sql: str, *, parameters: Sequence[object] = (), max_rows: int | None = None
) -> QueryResultDict

The same call, returning a QueryResultDict with .rows, .columns and .truncated attributes.

res = memory.open_query(
    "SELECT predicate, count(*) AS n FROM facts_current GROUP BY 1 ORDER BY 2 DESC LIMIT $1",
    parameters=[10],
)
for row in res.rows:
    print(row)

explain_sql

MemoryClient.explain_sql(
    *, sql: str, parameters: list[object] | tuple[object, ...] = ()
) -> dict[str, object]

Checks and plans a statement without running it. Returns QueryResult/v1 as a dict.

explain_query

MemoryClient.explain_query(
    sql: str, *, parameters: Sequence[object] = ()
) -> QueryResultDict

explain_sql returning a QueryResultDict.

describe_query_space

MemoryClient.describe_query_space(
    *, pattern: str | None = None, include_examples: bool = False
) -> dict[str, object]

Returns the query space's views, functions, comments and limits. pattern is a shell-style filter over view names.

space = memory.describe_query_space(pattern="facts_*")

search_query_space

MemoryClient.search_query_space(*, query: str, k: int = 10) -> list[dict[str, object]]

Searches the query space's own documentation (never your data). Each hit has kind, name, score, purpose, tags. The engine accepts k 1–25.

list_saved_queries

MemoryClient.list_saved_queries(
    *, namespace: str | None = None, status: str | None = None
) -> list[dict[str, object]]

Lists saved queries. Without status, only active versions are listed.

describe_saved_query

MemoryClient.describe_saved_query(
    *, namespace: str, name: str, version: int | None = None
) -> dict[str, object]

Returns one saved query's SQL, parameters and declared columns. namespace and name must match ^[a-z][a-z0-9_]*$, otherwise ValueError.

run_saved_query

MemoryClient.run_saved_query(
    *,
    namespace: str,
    name: str,
    parameters: list[object] | tuple[object, ...] = (),
    version: int | None = None,
    max_rows: int | None = None,
) -> dict[str, object]

Runs one active saved query. Same identifier rule as describe_saved_query. Returns QueryResult/v1 as a dict. See Saved queries.

call_open_query

MemoryClient.call_open_query(*, name: str, arguments: Mapping[str, object]) -> object

Runs one of the seven SQL query tools by its MCP tool name (query_sql, explain_sql, describe_query_space, search_query_space, list_saved_queries, describe_saved_query, run_saved_query) with MCP tool arguments. The remember mcp server uses it. Arguments are checked strictly: unknown keys, wrong types and out-of-range numbers are refused.

Connector methods

connectors(), add_connector(*, connector), pause_connector(*, connector_id) and connector_status(*, connector_id) exist in the SDK, but no deployment serves the /connectors routes today; every call raises MemoryApiError with status_code 404. See What is not built yet.

Errors

RuntimeError
├── MemoryApiError                 every memory call
│   ├── RateLimited                429 from the deployment's admission limits
│   └── StoredKeyRefused           the stored key may not go to that address
└── PipelineDeadLettered           wait_for_readiness
Exception
└── ConnectorNotFoundError         exported, not raised today
ValueError
└── CredentialError                remember.credentials (the credential file)

MemoryApiError

Raised by every memory-client method when a request fails.

AttributeMeaning
status_codeHTTP status. 0 means the request never got an answer (connection refused, timeout, DNS). 200 means the answer did not match the expected shape.
detailThe error text from the deployment.
codeA machine code, set for SQL and saved-query routes (for example parse_error, quota_exceeded, saved_query_not_found) and for a 429 (rate_limited or concurrency_limited); otherwise None.
responseNot set by the SDK (None).

str(error) reads API <status_code>: <detail>.

try:
    memory.query_sql(sql="SELECT * FROM pg_catalog.pg_class")
except remember.MemoryApiError as error:
    print(error.status_code, error.code)   # 422 relation_not_allowed

Status codes and their meaning: Errors and status codes.

RateLimited

Raised when the deployment refuses a request with 429 because a key or the deployment has reached its request-rate or in-flight limit. code is rate_limited or concurrency_limited; retry_after is the Retry-After the deployment sent, in seconds (a float), or None. The client does not retry by itself.

import time
 
try:
    memory.facts_context("Who owns the billing migration?")
except remember.RateLimited as error:
    time.sleep(error.retry_after or 1)

StoredKeyRefused

Raised on the first request when the key came from the credential file and the address came from base_url or REMEMBER_API_URL and is not an address that key may be sent to (Connecting). Pass the key as api_key or in REMEMBER_API_KEY to use it there.

PipelineDeadLettered

Raised by wait_for_readiness when a stage of a version it waits on is dead_letter: the stage used all its retry attempts and will not finish by waiting.

AttributeMeaning
dead_letteredEvery dead-lettered stage found, as (version_id, stage, status) tuples.
reportThe PipelineReadinessReport that showed them.

str(error) names each version and stage. On a self-hosted deployment the operator can replay the work after fixing the cause. See When a version fails.

CredentialError

remember.credentials.CredentialError (a ValueError) is raised when the credential file cannot be read or written safely: it is a symlink, it is readable by group or others, it is not a version-2 file, or the lock cannot be taken. remember setup --self-hosted replaces the file.

ConnectorNotFoundError

Exported for the connector methods; nothing raises it today.

Models

Field-level detail for results is in Result types. The models below are Pydantic models (frozen) unless noted.

NameWhat it is
EnvelopeThe result of every retrieval call: grain, temporal_scope, entities, facts, evidence, chunks, sources, graph fields, truncation, negative, freshness and more.
ContextBundleV2The result of combined_context: contract ("ContextBundle/v2"), claims_and_sources (an evidence Envelope) and facts (a fact Envelope).
IngestedVersionThe result of ingest: deployment_id, doc_id, version_id (UUIDs), content_hash (str), created (bool), and the settings in force: mime (str), title (str or None), versioning_mode ("snapshot" or "living").
DocumentPageThe result of list_documents: documents (tuple of DocumentSummary) and cursor.
DocumentSummaryOne document: doc_id, title, source_kind, source_uri, first_seen_at, latest (a DocumentVersionSummary), serving.
DocumentVersionSummaryversion_id, version_no, status, ingested_at, error.
DocumentDeletionThe result of delete_document: doc_id, deleted_at, claims_retired, relations_closed, observations_closed.
ReadinessRequirementsThe capabilities a readiness check must confirm: pipeline, p1, live_graph, p3, all required booleans.
PipelineReadinessReportready, versions (tuple of VersionPipelineReadiness), capabilities (dict of capability name to CapabilityReadiness), document_binding_generation, model_bindings, build_revision.
VersionPipelineReadinessversion_id, ready, stages (tuple of PipelineStageReadiness).
PipelineStageReadinessstage, component_version, status (missing, pending, running, succeeded, failed, dead_letter, skipped), finished_at, defer_reason (why waiting work waits: no_route, budget, scheduled, retry_backoff, or None).
CapabilityReadinessrequired, ready, checked_at, reason, version, built_at, published_at.
ToolDescriptorOne operation's descriptor: name, description, input_schema, result_schema, result_contract, output_grain, answer_intent, mutates, version, implementation_plan_hash.
QueryResultDictA dict holding a QueryResult/v1 response, with three read-only attributes: rows (list of rows; each row is a list of column values in column order), columns (list of {"name", "type", "nullable"} dicts) and truncated (bool).
ClaimValidPrecisionString enum: how narrow a stated validity window is. unknown, instant, day, month, quarter, year, open.
TemporalMatchString enum: whether a fact confirmed or only possible overlapped the query window.
ConnectorCreate, ConnectorDescriptorConnector configuration models. No deployment serves connectors today. ConnectorCreate refuses a configuration key that looks like a secret (token, password, api_key …) and asks for credential_ref instead.

Note

QueryResultDict.rows is annotated as a list of dicts, but each row arrives as a list of values. Pair it with columns to get names: [dict(zip([c["name"] for c in res.columns], row)) for row in res.rows].