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 rememberThe 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:
| Name | Kind | Section |
|---|---|---|
Client | class | Client |
RememberClient | alias of Client | Client |
MemoryClient | class | MemoryClient |
resolve_connection | function | Connecting |
MemoryApiError | exception | Errors |
RateLimited | exception | Errors |
StoredKeyRefused | exception | Errors |
PipelineDeadLettered | exception | Errors |
ConnectorNotFoundError | exception | Errors |
CapabilityReadiness | model | Models |
ClaimValidPrecision | enum | Models |
ConnectorCreate | model | Models |
ConnectorDescriptor | model | Models |
ContextBundleV2 | model | Models |
DocumentDeletion | model | Models |
DocumentPage | model | Models |
DocumentSummary | model | Models |
DocumentVersionSummary | model | Models |
Envelope | model | Models |
IngestedVersion | model | Models |
PipelineReadinessReport | model | Models |
PipelineStageReadiness | model | Models |
QueryResultDict | dict subclass | Models |
ReadinessRequirements | model | Models |
TemporalMatch | enum | Models |
ToolDescriptor | model | Models |
VersionPipelineReadiness | model | Models |
__version__ | string | The 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 to | Use |
|---|---|
| Store and retrieve memory (the usual case) | Client (same as RememberClient) |
The same, without the file-path ingest shortcut | MemoryClient |
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:
| Setting | 1. Argument | 2. Environment | 3. Credential file | 4. Otherwise |
|---|---|---|---|---|
| Key | api_key | REMEMBER_API_KEY | key | no key |
| Address | base_url | REMEMBER_API_URL | api_url | http://127.0.0.1:8000 |
| Project | project | REMEMBER_PROJECT | default_project | none |
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.ClientThe 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.
| Parameter | Meaning |
|---|---|
api_key | The API key. A bare key or a full Bearer … value. |
base_url | The deployment address. |
project | Which project to use, by id or name, when the key covers several. |
timeout | Seconds per HTTP request. Default 30. |
client | An 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. |
transport | An 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:
ValueErrorwhenclientis combined with another parameter.ValueErrorwhen the key is empty, is the bare wordBearer, or contains a line break.remember.credentials.CredentialErrorwhen 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,
) -> IngestedVersionReads 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.MemoryClientThe 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
| Method | HTTP route | Returns |
|---|---|---|
ingest | POST /ingest | IngestedVersion |
pipeline_readiness | POST /readiness | PipelineReadinessReport |
wait_for_readiness | POST /readiness, repeated | PipelineReadinessReport |
deployment_build_info | GET /deployment | DeploymentBuildInfo |
list_documents | GET /documents | DocumentPage |
delete_document | DELETE /documents/{doc_id} | DocumentDeletion |
list_operations | GET /operations | tuple[ToolDescriptor, ...] |
run_operation | POST /operations/{name} | Envelope or ContextBundleV2 |
facts_context | POST /operations/facts_context | Envelope |
claims_and_sources_context | POST /operations/claims_and_sources_context | Envelope |
combined_context | POST /operations/combined_context | ContextBundleV2 |
resolve_entity | POST /operations/resolve_entity | Envelope |
resolve | GET /resolve | Envelope |
lookup_relations | GET /lookup/relations | Envelope |
lookup_observations | GET /lookup/observations | Envelope |
transcript_relation | GET /transcript/relation/{id} | Envelope |
hydrate_relation | GET /hydrate/relation/{id} | Envelope |
search_claims | GET /search/claims | Envelope |
search_chunks | GET /search/chunks | Envelope |
adjacent_chunks | GET /chunks/{id}/adjacent | Envelope |
graph_neighborhood | POST /graph/neighborhood | Envelope |
graph_path | POST /graph/path | Envelope |
graph_citation_path | POST /graph/citation-path | Envelope |
query_sql | POST /query/sql | dict |
open_query | POST /query/sql | QueryResultDict |
explain_sql | POST /query/sql/explain | dict |
explain_query | POST /query/sql/explain | QueryResultDict |
describe_query_space | GET /query/space | dict |
search_query_space | GET /query/space/search | list[dict] |
list_saved_queries | GET /query/saved | list[dict] |
describe_saved_query | GET /query/saved/{namespace}/{name} | dict |
run_saved_query | POST /query/saved/{namespace}/{name}/run | dict |
call_open_query | one of the /query/* routes | object |
| 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,
) -> IngestedVersionUploads 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.
| Parameter | Meaning |
|---|---|
source | A Path or str file path (read from disk), or bytes. |
content | Raw bytes. Wins over source when both are given; source then only supplies the filename. |
filename | Required for bytes. Defaults to the file's name for a path. |
mime | Media 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. |
title | A human title for the document. |
source_kind, source_ref | The 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_at | When 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_ref | The source system's own revision label. Requires source_kind/source_ref. |
Raises ValueError when:
- only one of
source_kindandsource_refis given; source_modified_at,source_version_reforversioning_mode="living"is given withoutsource_kind/source_ref;source_modified_atis naive or not UTC;- a
strpath is not a file (MemoryClientonly); - neither
sourcenorcontentis 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
) -> PipelineReadinessReportAsks, 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,
) -> PipelineReadinessReportCalls 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:
PipelineDeadLetteredwhen any stage of any listed version isdead_letter;TimeoutErrorwhentimeoutseconds pass first;ValueErrorwhen 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() -> DeploymentBuildInfoReturns 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,
) -> DocumentPageReturns 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.cursordelete_document
MemoryClient.delete_document(*, doc_id: UUID | str) -> DocumentDeletionRemoves 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:
raiseAssured 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 | ContextBundleV2Runs 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,
) -> EnvelopeReturns the facts the memory holds true that match query, with their
evidence.
| Argument | Constraint (checked by the engine) |
|---|---|
query | 1 to 8,192 characters |
time | {"mode": "current"} (default), {"mode": "at", "at": "<ISO date-time>"}, {"mode": "overlap", "from": "…", "to": "…"} or {"mode": "history"} |
hops | 1 or 2; default 1 |
predicate | 1 to 200 characters |
entity_ids | 1 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) -> EnvelopeReturns 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
) -> ContextBundleV2Runs 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) -> EnvelopeReturns 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 belowEntities and facts
resolve
MemoryClient.resolve(
*, name: str, context_entity_ids: tuple[UUID, ...] = ()
) -> EnvelopeResolves 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,
) -> EnvelopeReturns 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
) -> EnvelopeReturns 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) -> EnvelopeReturns the recorded decisions that shaped one relation (the transcript
field of the envelope).
hydrate_relation
MemoryClient.hydrate_relation(*, relation_id: UUID) -> EnvelopeReturns 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
search_claims
MemoryClient.search_claims(
*, query: str, k: int = 10, channel: Literal["semantic", "bm25"] = "semantic"
) -> EnvelopeSearches 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"
) -> EnvelopeSearches 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) -> EnvelopeReturns 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,
) -> EnvelopeReturns 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,
) -> EnvelopeReturns 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
) -> EnvelopeReturns 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
) -> QueryResultDictThe 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] = ()
) -> QueryResultDictexplain_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]) -> objectRuns 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.
| Attribute | Meaning |
|---|---|
status_code | HTTP status. 0 means the request never got an answer (connection refused, timeout, DNS). 200 means the answer did not match the expected shape. |
detail | The error text from the deployment. |
code | A 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. |
response | Not 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_allowedStatus 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.
| Attribute | Meaning |
|---|---|
dead_lettered | Every dead-lettered stage found, as (version_id, stage, status) tuples. |
report | The 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.
| Name | What it is |
|---|---|
Envelope | The result of every retrieval call: grain, temporal_scope, entities, facts, evidence, chunks, sources, graph fields, truncation, negative, freshness and more. |
ContextBundleV2 | The result of combined_context: contract ("ContextBundle/v2"), claims_and_sources (an evidence Envelope) and facts (a fact Envelope). |
IngestedVersion | The 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"). |
DocumentPage | The result of list_documents: documents (tuple of DocumentSummary) and cursor. |
DocumentSummary | One document: doc_id, title, source_kind, source_uri, first_seen_at, latest (a DocumentVersionSummary), serving. |
DocumentVersionSummary | version_id, version_no, status, ingested_at, error. |
DocumentDeletion | The result of delete_document: doc_id, deleted_at, claims_retired, relations_closed, observations_closed. |
ReadinessRequirements | The capabilities a readiness check must confirm: pipeline, p1, live_graph, p3, all required booleans. |
PipelineReadinessReport | ready, versions (tuple of VersionPipelineReadiness), capabilities (dict of capability name to CapabilityReadiness), document_binding_generation, model_bindings, build_revision. |
VersionPipelineReadiness | version_id, ready, stages (tuple of PipelineStageReadiness). |
PipelineStageReadiness | stage, 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). |
CapabilityReadiness | required, ready, checked_at, reason, version, built_at, published_at. |
ToolDescriptor | One operation's descriptor: name, description, input_schema, result_schema, result_contract, output_grain, answer_intent, mutates, version, implementation_plan_hash. |
QueryResultDict | A 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). |
ClaimValidPrecision | String enum: how narrow a stated validity window is. unknown, instant, day, month, quarter, year, open. |
TemporalMatch | String enum: whether a fact confirmed or only possible overlapped the query window. |
ConnectorCreate, ConnectorDescriptor | Connector 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].