Skip to Content
SDKsPython

Python SDK

⏱ Running in 5 minutes — new here? Jump to the quickstart for install → first query → first agent memory, or copy the Quick start block below. pip install relata-sdk and you’re ready.

The official Python SDK for RelataDB. Sync + async surfaces, Pydantic models, fluent query builder, agent-memory client, and 12 typed v1.1 clients that mirror the server’s REST surface.

  • Source: sdks/python/ (sdks/python/relata/)
  • PyPI: relata-sdk
  • Current version: 0.2.0
  • Python: 3.11+
  • Runtime deps: httpx >= 0.27, pydantic >= 2.6

Install

pip install relata-sdk # or uv add relata-sdk

Quick start

from relata import RelataClient with RelataClient("http://localhost:9090", purpose="analytics") as client: result = client.query("SELECT * FROM Person WHERE name LIKE 'Ahmed%' LIMIT 10") for row in result: print(row)

Agent memory in three lines

Memory is a Mem0-style surface over the governed /memory/* verbs — purpose + ACL stay on by default.

from relata import Memory with Memory("http://localhost:9090", purpose="agent-notes") as m: mem_id = m.add("Alice prefers dark mode") # store hits = m.search("ui preferences", top_k=5) # recall (confidence × recency × relevance) m.forget(mem_id) # governed retract, not a hard delete

For async apps, AsyncMemory has the same surface: async with AsyncMemory(...) as m: await m.add(...) ; await m.search(...).

RelataClient — the main client

RelataClient( base_url: str, *, bearer_token: str | None = None, purpose: str | None = None, timeout: float = 30.0, tenant: str | None = None, # X-Organization-Id (multi-tenant) acting_as: str | None = None, # X-Acting-As (delegation) delegated_by: str | None = None, # X-Delegated-By headers: dict[str, str] | None = None, max_retries: int = 0, retry_backoff_secs: float = 0.5, )

Core methods

MethodReturnsDescription
query(sql, *, purpose)QueryResultExecute a SQL query
select(*cols)QueryBuilderStart a fluent query (see below)
health()HealthResponseLiveness check
status()StatusResponseProfile, role, query quota
stats()StatsEngine counts (records / states / snapshot_rows / log_leaves / tokens)
version()VersionInfoBuild info (version, commit, profile, schema_version)
ready()ReadyReport9-condition readiness report
audit_count()AuditCountResponseAudit entry count + chain validity
cluster_nodes()list[ClusterNode]List cluster nodes
close() / __enter__ / __exit__Context-manager lifecycle

Every method has an async mirror (aquery, ahealth, astats, aready, aclose, …).

Purpose is mandatory (with a default escape hatch)

Every query must declare a purpose registered in the tenant’s PurposeRegistry ("analytics", "audit", "compliance_review", …). Set a default on the client or override per-call:

client = RelataClient("http://localhost:9090", purpose="analytics") result = client.query("SELECT COUNT(*) FROM Person", purpose="audit") # override

Authentication

client = RelataClient( "https://relata.example.com", bearer_token="your-token", # required when server has RELATA_BEARER_TOKEN set purpose="analytics", )

Without a token the SDK runs as the built-in api-user principal — fine for local dev, not for production multi-tenant.

Multi-tenant + delegation headers

client = RelataClient( url, bearer_token=token, tenant="org-acme", # X-Organization-Id acting_as="user-bob", # X-Acting-As delegated_by="user-alice", # X-Delegated-By )

QueryBuilder — fluent SQL

result = ( client.select("Person") .where("nationality = 'IN'") .where("age > 25") # multiple .where() → AND .as_of("2025-01-01") # bi-temporal point-in-time .with_provenance() # PROV-O columns .order_by("name ASC") .limit(20, after="cursor") # keyset pagination .execute() )
MethodDescription
.select(*cols) / .from_(table)Projection / FROM
.where(condition)Append AND predicate (validated — SQL-injection stopgap)
.as_of(timestamp)Bi-temporal AS OF
.with_provenance()Append WITH PROVENANCE
.purpose(p)Override purpose for this query
.limit(n[, after="cursor"])LIMIT, optional keyset pagination (LIMIT n AFTER 'cursor')
.since(cursor)Incremental reads (WHERE system_from > cursor)
.offset(n)OFFSET
.order_by(*cols)ORDER BY
.paths_between(src, dst, max_hops)PATHS_BETWEEN graph operator
.match_face(probe, candidate, threshold)MATCH_FACE biometric operator
.lookup_identity(value)LOOKUP_IDENTITY operator
.hybrid_score(text)HYBRID_SCORE ranking
.raw(sql)Raw SQL escape hatch
.sql()Return assembled SQL without executing
.execute() / .aexecute()Run sync / async

v1.1 typed clients — from_client(client)

Each module inherits the parent client’s auth, tenant, and purpose context, so governance stays consistent across the surface. Every client has an async mirror (Async*).

ModuleClassSurface
relata.memoryMemory / AsyncMemoryMem0-style add / search / forget over governed /memory/*
relata.governanceGovernanceClientRules, retention (holds + WORM), breakglass, alerts, DSAR
relata.mcpMcpClient22 typed MCP tool wrappers + generic call_tool
relata.a2aA2AClientA2A tasks + LangGraph checkpoints + agent card
relata.auditAuditClientAudit entries (filtered/paginated) + signed receipts + PDF export
relata.identityIdentityClientIdentity label/uncertainty + lookup tables + ERASE SUBJECT
relata.objectsObjectClientTyped upsert + batch via /ingest?object_type=
relata.ingestIngestClientBulk NDJSON + CSV + media status
relata.vectorsVectorClientKNN + hybrid search + similar-to (SQL-backed)
relata.s3S3Clientboto3 / httpx / aiobotocore wrapper for the S3 protocol door
relata.systemSystemClientLLM config + test + jobs status
relata.streamingStreamingClientNDJSON row streams + SSE consumers (watch/alerts) + Arrow IPC
relata.tenantsTenantAdminClientTenant CRUD + quota + sharing agreements + platform admin

Example — typed client chain

from relata import RelataClient, McpClient, GovernanceClient with RelataClient(url, bearer_token=token, purpose="compliance") as client: mcp = McpClient.from_client(client) gov = GovernanceClient.from_client(client) profile = mcp.get_entity_profile("person-123", purpose="compliance") holds = gov.list_legal_holds() sigma = gov.import_sigma(open("rules.yml").read())

Response models

ModelFields
QueryResultrows, query_id, elapsed_ms, row_count — iterable, truthy-when-non-empty
HealthResponsestatus, profile, node_id
StatusResponseprofile, role, query_quota
AuditCountResponseentries, chain_valid
ClusterNodenode_id, role, url
VersionInfoversion, commit, profile, schema_version, features
Statsrecords, states, snapshot_rows, log_leaves, tokens
ReadyReportis_ready, status, reason, detail

QueryResult is iterable, indexable, and truthy:

result = client.query("SELECT * FROM Person LIMIT 10") for row in result: # iterate print(row["name"]) first = result.rows[0] # index if not result: # truthy when non-empty print("empty") print(f"{len(result)} rows in {result.elapsed_ms} ms")

Error handling

from relata.exceptions import ( PurposeError, # missing or unregistered purpose (HTTP 400) QuotaError, # per-principal cost quota exhausted (HTTP 429) AuthError, # invalid or missing bearer token (HTTP 401) ForbiddenError, # Cedar ACL denied (HTTP 403) NotFoundError, # (HTTP 404) ConflictError, # (HTTP 409) ValidationError, # (HTTP 422) RateLimitedError, # (HTTP 429 with retry_after) ConnectionError, # server unreachable ServerError, # 5xx RelataError, # base class — catch-all ) try: result = client.query("SELECT * FROM Person") except PurposeError as exc: print(f"Fix: set purpose= on client or per query. {exc}") except QuotaError as exc: print(f"Quota exhausted — reduce query scope. {exc}") except AuthError as exc: print(f"Auth failed — check bearer_token. {exc}") except ConnectionError as exc: print(f"Cannot reach server. {exc}")

Every error is RFC 7807 problem+json-aware: code, type_url, retryable, request_id are all exposed as attributes. The X-Request-ID header is auto-generated per request, and the server’s response ID is stamped on exceptions.

Async support

import asyncio from relata import RelataClient async def main(): async with RelataClient("http://localhost:9090", purpose="analytics") as client: result = await client.aquery("SELECT * FROM Person LIMIT 5") async for row in result: print(row) asyncio.run(main())

Custom object types

RelataDB is ontology-governed — unknown types are rejected on both ingest and read (fail-closed). Register custom types at runtime:

client._sync.post("/types", { "name": "AgentTask", "fields": [ {"name": "task_id", "type": "string"}, {"name": "status", "type": "string"}, ], }) client._sync.post("/ingest?object_type=AgentTask", {"task_id": "t-1", "status": "done"})

For ACL access in strict mode, grant via env var: RELATA_ACL_GRANT=AgentTask:read+write.

Agent-framework adapters

Drop-in governed memory backends for the major agent frameworks. Each adapter wraps Memory, so purpose + ACL stay on. None imports its framework, so they load with or without it installed.

FrameworkImportShape
LangChainrelata_adapters.langchain.RelataMemoryBaseMemory
LlamaIndexrelata_adapters.llamaindex.RelataMemoryBaseMemory
CrewAIrelata_adapters.crewai.RelataStorageStorage
AutoGen v0.2relata_adapters.autogen.RelataMemoryMemory (async)
AG2 (v0.4+)relata_adapters.ag2.RelataAG2MemoryMemoryProtocol
Pydantic-AIrelata_adapters.pydantic_ai.RelataMemoryBackendmemory backend
smolagentsrelata_adapters.smolagents.RelataTooltool callable
LangGraphrelata_langgraph.RelataCheckpointercheckpointer
Auto-detectrelata_adapters.registry.get_memory_adapter()picks the right class
from relata_adapters.langchain import RelataMemory mem = RelataMemory(base_url="http://localhost:9090", purpose="agent") # chain = ConversationChain(llm=..., memory=mem)

Deployment profiles

ProfileUse caseStart command
liteEmbedded / single-process / devRELATA_PROFILE=lite relata serve
serverSingle-node productionRELATA_PROFILE=server relata serve
clusterMulti-node distributed (alpha)RELATA_PROFILE=cluster relata serve

The SDK works identically across all three.

Examples

The examples/ directory ships runnable workflows:

FileDemonstrates
basic_query.pyGetting started, health check, quota check
analytics.pyFull analytics workflow: identity → cases → graph → provenance
face_search.pyMATCH_FACE operator, threshold comparison, CCTV temporal search
graph_traversal.pyPATHS_BETWEEN, temporal network shift, hub detection
audit.pyChain verification, audit summary, anomaly detection
memory_quickstart.pyMemory add / search / forget in 20 lines

License

AGPL-3.0-only. See the root LICENSE file.

Last updated on