Skip to Content
SDKsRust

Rust 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. cargo add relata-sdk and you’re ready.

The native Rust SDK for RelataDB. Async RelataClient over gRPC, with Arrow RecordBatch results and true zero-copy Arrow Flight IPC streaming. Ships a high-level Memory client (mirrors Python relata.Memory) over the governed /memory/* REST verbs.

  • Source: crates/relata-sdk-rust/
  • Crate: relata-sdk-rust (workspace member)
  • Async runtime: Tokio
  • Wire protocol: gRPC (tonic + prost) — ADR-073
  • Arrow: arrow, arrow-array, arrow-schema, arrow-flight

Unlike the Python / TypeScript / Go SDKs which speak HTTP, the native Rust SDK speaks gRPC for query/ingest/admin, and uses Arrow Flight for zero-copy columnar streaming. The high-level Memory client is HTTP (the /memory/* verbs are HTTP-only).

Quick start

use relata_sdk_rust::{RelataClient, QueryRequest}; #[tokio::main] async fn main() { let mut client = RelataClient::connect("http://localhost:50051").await.unwrap(); let resp = client.query(QueryRequest { purpose: "investigation".to_owned(), sql: "SELECT * FROM Person LIMIT 5".to_owned(), }).await.unwrap(); println!("{} rows", resp.row_count); for row in &resp.rows { println!("{:?}", row); } }

RelataClient

A connected client holds three tonic sub-clients (query, ingest, admin) sharing one HTTP/2 channel. All methods are async.

pub struct RelataClient { /* ... */ } impl RelataClient { pub async fn connect(endpoint: impl Into<String>) -> Result<Self, tonic::transport::Error>; pub fn endpoint(&self) -> &str; pub async fn query(&mut self, req: QueryRequest) -> Result<QueryResponse, tonic::Status>; pub async fn ingest(&mut self, batch: IngestBatch) -> Result<IngestResponse, tonic::Status>; pub async fn query_arrow(&mut self, sql: &str, purpose: &str) -> Result<Vec<RecordBatch>, SdkError>; pub async fn query_flight( flight_endpoint: impl Into<String>, sql: impl Into<String>, bearer: &str, ) -> Result<Vec<RecordBatch>, SdkError>; pub async fn health(&mut self) -> Result<HealthStatus, tonic::Status>; }

Public types

QueryRequest

Every query must declare a purpose registered in the tenant’s PurposeRegistry (SPECS §5.22.4):

pub struct QueryRequest { pub purpose: String, pub sql: String, }

QueryResponse

pub struct QueryResponse { pub columns: Vec<String>, pub row_count: usize, pub rows: Vec<HashMap<String, String>>, // JSON-decoded }

IngestBatch / IngestResponse

pub struct IngestBatch { pub object_type: String, pub purpose: String, // ingest also requires a purpose pub rows_json: Vec<String>, // one JSON object per row } pub struct IngestResponse { pub rows_accepted: i64, pub errors: Vec<String>, }

HealthStatus

pub struct HealthStatus { pub status: String, // "ok" or error pub profile: String, // "lite" | "server" | "cluster" pub node_id: String, }

Arrow result paths

Two ways to get Arrow RecordBatches back — pick the one that matches your throughput needs:

1. query_arrow — gRPC stream → RecordBatch

Calls the ExecuteStream RPC. The server chunks rows as RowBatch messages (up to 1000 rows each); each batch is converted to an Arrow RecordBatch with one Utf8 column per field. All column values are serialised to strings (same as the JSON path).

let batches = client.query_arrow("SELECT * FROM Person", "analytics").await?; for batch in &batches { println!("{} rows, {} cols", batch.num_rows(), batch.num_columns()); }

2. query_flight — zero-copy Arrow Flight DoGet (#958)

True zero-copy Arrow Flight IPC streaming — no JSON intermediate. The Flight door is distinct from the gRPC endpoint; enable server-side with RELATA_FLIGHT_ENABLE=true (default port 8815).

use relata_sdk_rust::RelataClient; let batches = RelataClient::query_flight( "http://localhost:8815", // Flight door "PURPOSE 'analytics' SELECT * FROM Person", "", // bearer, "" in open dev mode ).await?;

High-level Memory client

relata_sdk_rust::memory::Memory is a thin convenience layer over the governed /memory/* REST verbs — mirrors the Python relata.Memory. Mem0-style add / search / forget. Governance stays on: a non-empty purpose is required on every call, and forget is a retention-policy retract, not a hard delete.

use relata_sdk_rust::memory::Memory; #[tokio::main] async fn main() { let m = Memory::new("http://localhost:9090", "agent-notes").unwrap(); let id = m.add("Alice prefers dark mode").await.unwrap(); let hits = m.search("ui preferences", 5).await.unwrap(); m.forget(&id).await.unwrap(); }

The native RelataClient speaks gRPC; the memory verbs are HTTP, so Memory carries its own reqwest transport.

Errors

pub enum SdkError { Grpc(tonic::Status), Arrow(arrow_schema::ArrowError), MalformedResponse(String), }

Implements Display + std::error::Error. Conversions provided from tonic::Status and arrow_schema::ArrowError.

Examples

See crates/relata-sdk-rust/examples/ for runnable workflows.

License

AGPL-3.0-only — see the root LICENSE file.

Last updated on