Skip to main content

Client

Struct Client 

Source
pub struct Client { /* private fields */ }
Expand description

High-level facade composing the embedder, store, and vector index.

Constructed via Client::builder. Cheap to clone — internally backed by Arc so multiple call sites can share one configured Memoir instance.

Implementations§

Source§

impl Client

Source

pub async fn new( database_url: String, qdrant: String, schema: Option<String>, system_prompt: Option<String>, collection: Option<String>, extraction_llm: Option<LlmConfig>, contradiction_llm: Option<LlmConfig>, categorize_model: Option<NliConfig>, ) -> Result<Client, ClientError>

Builds a Client from Postgres and Qdrant connection strings.

memoir-core owns its own connection pool. The pool’s search_path is pinned to the configured schema so memoir-core’s tables and migration ledger never collide with the consumer’s other Postgres state. The consumer never sees a sea_orm::DatabaseConnection — this is a deliberate boundary so the library can manage its own connection lifecycle (search_path, pool sizing, future read-replica routing) without each consumer reinventing the same plumbing.

LLM providers are configured per LlmRole via the extraction_llm and contradiction_llm setters. A role left unconfigured produces a registry with no entry for that role; downstream call sites (e.g. the extraction worker stage) skip gracefully when their preferred role is absent.

§Examples
use memoir_core::client::Client;
use memoir_core::llm::LlmConfig;

let client = Client::builder()
    .database_url("postgres://postgres:postgres@localhost:54321/my_app")
    .qdrant("http://localhost:6334")
    .schema("memoir")
    .extraction_llm(LlmConfig::ollama("http://localhost:11434", "llama3.2"))
    .build()
    .await?;
§Errors

Returns ClientError::Database when the pool cannot connect, ClientError::Embedding if the embedder fails to initialize, ClientError::Vector if ensure_collection fails on Qdrant, and ClientError::Llm if a configured provider can’t be constructed (e.g. malformed URL or api-key rejected by rig).

Source

pub fn builder() -> ClientBuilder

Builds a Client from Postgres and Qdrant connection strings.

memoir-core owns its own connection pool. The pool’s search_path is pinned to the configured schema so memoir-core’s tables and migration ledger never collide with the consumer’s other Postgres state. The consumer never sees a sea_orm::DatabaseConnection — this is a deliberate boundary so the library can manage its own connection lifecycle (search_path, pool sizing, future read-replica routing) without each consumer reinventing the same plumbing.

LLM providers are configured per LlmRole via the extraction_llm and contradiction_llm setters. A role left unconfigured produces a registry with no entry for that role; downstream call sites (e.g. the extraction worker stage) skip gracefully when their preferred role is absent.

§Examples
use memoir_core::client::Client;
use memoir_core::llm::LlmConfig;

let client = Client::builder()
    .database_url("postgres://postgres:postgres@localhost:54321/my_app")
    .qdrant("http://localhost:6334")
    .schema("memoir")
    .extraction_llm(LlmConfig::ollama("http://localhost:11434", "llama3.2"))
    .build()
    .await?;
§Errors

Returns ClientError::Database when the pool cannot connect, ClientError::Embedding if the embedder fails to initialize, ClientError::Vector if ensure_collection fails on Qdrant, and ClientError::Llm if a configured provider can’t be constructed (e.g. malformed URL or api-key rejected by rig).

Source§

impl Client

Source

pub async fn migrate(&self) -> Result<(), ClientError>

Applies memoir-core’s migrations in the configured Postgres schema.

Idempotent — safe to call on every startup. Creates the schema if missing and applies all pending migrations.

§Errors

Returns ClientError::Migration if the schema name is invalid or if any migration fails to apply.

Source

pub fn schema(&self) -> &str

Returns the Postgres schema this client writes its tables into.

Source

pub fn system_prompt(&self) -> Option<&str>

Returns the configured system-prompt section, if any.

Source

pub fn collection_name(&self) -> &str

Returns the Qdrant collection name configured for vector storage.

Source

pub fn embedder(&self) -> &OnnxEmbedding

Returns the embedding model used by this client.

Source

pub fn store(&self) -> &PostgresStore

Returns the source-of-truth store used by this client.

Source

pub fn index(&self) -> &QdrantIndex

Returns the vector index used by this client.

Source

pub fn llms(&self) -> &LlmRegistry

Returns the registry of LLM providers configured on this client.

Source

pub fn remember( &self, prompt: impl Into<String>, scope: Scope, ) -> RememberBuilder<'_>

Writes prompt as an episodic memory under scope.

Returns a per-call builder; await it to persist the row and enqueue its embed (and, if extraction is configured, extract) job. The returned Memory reflects the source-of-truth row; its vector index entry is pending until the worker drains the embed job. Use Client::search to retrieve memories — remember is write-only.

Attach optional JSON metadata via RememberBuilder::metadata; without it the column defaults to {}. Attach a parsed event-time via RememberBuilder::event_at when the content references a specific moment (e.g. “the deployment happened Friday”); without it, the memory has no event-time and is excluded from event-time range filters at search time.

§Examples
let written = client
    .remember("hello", scope)
    .metadata(serde_json::json!({ "source": "chat" }))
    .await?;
println!("wrote pid={}", written.pid);
§Errors

Returns ClientError::Store wrapping a database failure when the row cannot be inserted, and ClientError::Jobs when the embed or extract job cannot be enqueued.

Source

pub fn edit(&self, pid: impl Into<String>) -> EditBuilder<'_>

Mutates an existing memory in place — a correction, not a supersession.

Returns a per-call builder; await it to apply the patch. Use this when the original row was wrong and the caller is overwriting it (typo, misheard utterance, wrong parsed date). When the original was true at the time but new information now obsoletes it, use the contradiction path that calls MemoryStore::supersede instead — edit discards the old text, supersede preserves it.

created_at is preserved; updated_at bumps automatically via the database trigger. Content changes flip the row’s vector-index state back to pending and enqueue a re-embed job, so the row drops out of search hits until the worker drains the queue — same lifecycle as a fresh remember(). See EditBuilder for the builder methods.

§Errors

Returns ClientError::Store wrapping crate::store::StoreError::NotFound when no memory matches pid, crate::store::StoreError::UnsupportedEdit when the target row’s kind does not support in-place edits (today: every non-Episodic kind), ClientError::ReservedMetadataKey when metadata contains a reserved payload key, and ClientError::Jobs when the re-embed job cannot be enqueued.

Source

pub fn search( &self, query: impl Into<String>, scope: Scope, ) -> SearchBuilder<'_>

Searches indexed memories in scope by vector similarity to query.

Returns a per-call builder; await it to embed the query, run the vector search, and assemble the matching Memory rows. The kind toggles on the builder filter retrieval. See SearchBuilder for builder methods.

Only memories whose vector index entry has reached indexed are eligible. Rows still in pending (recently written via Client::remember, not yet drained by the worker) are filtered out — they can still be inspected by pid via Client::recall.

§Examples
let memories = client.search("hello", scope).limit(5).await?;
for m in memories.list() {
    println!("{}", m.content);
}
§Errors

Returns ClientError::Embedding if the query cannot be embedded, ClientError::Vector if the vector index search fails, and ClientError::Store wrapping a database failure when the matched pids cannot be hydrated to full rows.

Source

pub fn timeline(&self, scope: Scope) -> TimelineBuilder<'_>

Returns memories in scope ordered chronologically — the event log.

Postgres-only read; no embedding, no Qdrant. Includes superseded rows by default (this is the audit view). Default order is newest-first, default limit is crate::store::DEFAULT_TIMELINE_LIMIT. See TimelineBuilder for the builder methods.

§Errors

Returns ClientError::Store wrapping crate::store::StoreError::InvalidScope when a Scope target has empty fields, or wrapping crate::store::StoreError::Database for database failures.

Source

pub fn query(&self, query: impl Into<String>, scope: Scope) -> QueryBuilder<'_>

Retrieves memories in scope ranked by hybrid cosine-and-recency, as a prompt-shaped MemoryContext.

Mirrors Client::search’s candidate-retrieval primitives but re-ranks the top-K candidates by combining vector similarity with recency before returning. Default strategy is RankingStrategy::default_hybrid; override via QueryBuilder::ranking. The default strategy’s parameter values are explicitly allowed to drift pre-1.0 — callers depending on a specific ranking must pass an explicit RankingStrategy::Hybrid { .. }.

Returns a MemoryContext suitable for dropping into a system prompt via Display. See MemoryContext for the rendering shape and the staleness caveat for cached output.

§Errors

Returns ClientError::Embedding if the query cannot be embedded, ClientError::Vector if the vector index search fails, and ClientError::Store wrapping a database failure when the matched pids cannot be hydrated to full rows.

Source

pub fn recall_as_of( &self, scope: Scope, as_of: impl Into<DateTime<FixedOffset>>, ) -> RecallAsOfBuilder<'_>

Returns memoir’s state of knowledge in scope as of as_of.

A memory is included when it was written on or before as_of and was not yet superseded then. Pure Postgres read; no Qdrant, no embedder. Newest-first by created_at, default limit crate::store::DEFAULT_TIMELINE_LIMIT. See RecallAsOfBuilder for the builder methods.

§Errors

Returns ClientError::Store wrapping crate::store::StoreError::InvalidScope when a Scope target has empty fields, or wrapping crate::store::StoreError::Database for database failures.

Source

pub async fn recall(&self, pid: &str) -> Result<Memory, ClientError>

Looks up a single memory by its public id, at any lifecycle state.

Returns the memory regardless of whether its vector index entry is pending, indexed, or failed — callers using this for direct lookups see the source-of-truth row from Postgres.

No scope check is performed: any caller holding a pid can retrieve the corresponding memory. The library treats its caller as the trust boundary; service-mode callers (epic 0007) gate access via their own auth layer.

§Examples
let memory = client.recall("AbCdEfGhIjKlMnOpQrStU").await?;
println!("{}", memory.content);
§Errors

Returns ClientError::Store wrapping crate::store::StoreError::NotFound when no memory matches pid, and ClientError::Store wrapping crate::store::StoreError::Database for database failures.

Source

pub async fn forget( &self, target: ForgetTarget, ) -> Result<Vec<String>, ClientError>

Deletes one memory by pid, or every memory matching a scope tuple.

The Postgres delete is authoritative — returned pids reflect what was actually removed from the source of truth. The Qdrant delete is best-effort: on failure the source-of-truth row is already gone and the orphaned vector becomes the reconciliation sweep’s problem (ticket 0012). Failure does not propagate; it emits memoir.forget.index_delete_failed at WARN.

Returns the pids that were deleted. An empty vector means the target matched no rows — not an error.

§Examples
let deleted = client.forget(ForgetTarget::Pid("abc123".to_string())).await?;
let cleared = client.forget(ForgetTarget::Scope(scope)).await?;
§Errors

Returns ClientError::Store wrapping crate::store::StoreError::InvalidScope when a Scope target has any empty field, and ClientError::Store wrapping crate::store::StoreError::Database for database failures.

Source

pub async fn reject(&self, pid: &str) -> Result<(), ClientError>

Rejects a memory: a wrong extraction the user corrected (epic 0011).

Marks the row retirement_reason = 'rejected' and evicts its vector, so it disappears from every read and can no longer pollute search or reprocessing. The row is kept (not deleted) — it is the reprocess “don’t re-derive this” guard and counts toward the extraction-accuracy metric. Rejection is the extraction-error case; for a source that merely changed, use Self::mark_stale.

§Errors

Returns ClientError::Store wrapping crate::store::StoreError::NotFound when no memory matches pid, or crate::store::StoreError::Database for database failures. A vector-eviction failure is logged at WARN (reconciliation cleans the orphan) and does not fail the call once the row is marked.

Source

pub async fn mark_stale(&self, pid: &str) -> Result<(), ClientError>

Marks a memory stale: its episodic source changed (epic 0011).

Marks the row retirement_reason = 'stale' and evicts its vector. Like Self::reject the row is hidden everywhere and kept, but stale is NOT an extraction error (the model was right; the source moved), so it does not count against the accuracy metric.

§Errors

See Self::reject.

Source

pub fn feedback(&self, pid: impl Into<String>) -> FeedbackBuilder<'_>

Corrects a wrong extraction by teaching, not editing (epic 0011).

pid is the wrong semantic memory the user saw in recall. Awaiting the returned builder enqueues a reprocess of that fact’s episodic source: the derived rows are retired as rejected and re-derived with the correction in context, so a corrected fact replaces the wrong one. The user never hand-writes a semantic row — semantic memory stays always-derived. Fire-and-forget: returns once the job is enqueued.

To correct the episodic record itself, use Self::edit; that is a different correction (the source changed, not a wrong extraction). See FeedbackBuilder for the builder methods.

§Errors

Returns ClientError::Store wrapping crate::store::StoreError::NotFound when no memory matches pid, ClientError::NotCorrectable when the target is not a semantic row or has no episodic source, and ClientError::Jobs when the reprocess job cannot be enqueued.

Source

pub fn reconcile(&self) -> ReconcileBuilder<'_>

Runs reconciliation: retries failed rows and cleans Qdrant orphans.

Returns a per-call builder. Awaiting it runs the configured passes and returns a ReconcileSummary. Both passes run by default; narrow with ReconcileBuilder::only_retry_failed / ReconcileBuilder::only_clean_orphans. Idempotent: running against a clean store does nothing and exits cleanly.

§Examples
let summary = client.reconcile().await?;
let _ = summary;
Source

pub fn spawn_worker(&self) -> WorkerBuilder<'_>

Configures the background queue worker; call .start().await to launch.

Returns a per-call builder. The worker polls the memory_jobs queue, dispatches each row to its stage handler (embed in ticket 0007, extract in ticket 0006), and runs lease recovery when the queue is idle. Cooperative shutdown via WorkerHandle::shutdown.

§Examples
let worker = client.spawn_worker().start().await?;
// ... server runs ...
worker.shutdown().await;
Source

pub async fn failed_jobs( &self, limit: usize, ) -> Result<Vec<FailedJob>, ClientError>

Lists failed jobs newest-first, capped at limit.

Returns metadata only (id, kind, source pid, attempts, failure reason, last update); content from the referenced memory is NOT included. Operators who need to inspect the memory’s content can follow up with Self::recall against the source pid.

§Errors

Returns ClientError::Jobs wrapping any database failure.

Source

pub async fn retry_job(&self, id: i64) -> Result<(), ClientError>

Retries one failed job, clearing the attempt counter.

The attempt counter is reset to zero on operator-initiated retry: a human has decided prior failures shouldn’t count against the new attempt budget. Reconciliation-driven retries leave the counter alone (see Self::reconcile).

§Errors

Returns ClientError::Jobs wrapping crate::jobs::JobsError::NotFound when no failed job matches id, or wrapping a database failure.

Source

pub fn retry_failed_jobs(&self) -> RetryBuilder<'_>

Configures a bulk retry. Awaiting the returned builder runs it.

See RetryBuilder for filter and dry-run options. Returns the number of affected (or for dry_run, would-affect) rows.

Source

pub async fn delete_failed_job(&self, id: i64) -> Result<(), ClientError>

Permanently deletes one failed job. The referenced memory is untouched.

§Errors

Returns ClientError::Jobs wrapping crate::jobs::JobsError::NotFound when no failed job matches id, or wrapping a database failure.

Source

pub async fn pending_jobs_count(&self) -> Result<u64, ClientError>

Returns the number of jobs currently in pending state.

Cheap observation for operators monitoring queue depth.

§Errors

Returns ClientError::Jobs wrapping any database failure.

Source

pub async fn unsupersede(&self, pid: &str) -> Result<(), ClientError>

Clears the supersession marker on pid, restoring it to active state.

Admin-only counterpart to the internal supersede path. Use when an operator decides a future contradiction-detection pass wrongly marked a row as outdated. Idempotent at the SQL level for rows that were already active, but still errors if no row matches pid.

There is no symmetric public Client::supersede: supersession is a decision made by the (forthcoming) detection engine against verified contradicting facts, not by operator hand. Hand-rolled supersession would defeat the audit trail the column is meant to preserve.

§Errors

Returns ClientError::Store wrapping crate::store::StoreError::NotFound when no memory matches pid, or wrapping a database failure.

Source

pub async fn supersession_history( &self, pid: &str, ) -> Result<Vec<SupersessionEvent>, ClientError>

Returns the full supersede/unsupersede event trail for pid.

Each SupersessionEvent is one decision against the memory, chronological (oldest first). An event with winner_pid = None is an unsupersede. A pid with no events — never superseded, or simply not present in the store — returns an empty vec, not an error.

Surfaces the audit trail behind a row’s current Memory.supersession marker, for the supersession-audit UI and rig-service introspection of contradiction-detection decisions over time.

§Errors

Returns ClientError::Store wrapping a database failure.

Source

pub async fn list_agents( &self, org_id: &str, user_id: &str, ) -> Result<Vec<String>, ClientError>

Lists the distinct agent ids with memories under org_id + user_id.

Caller-scoped agent discovery: returns only the agents within the given org and user, sorted ascending, so a tenant never sees another tenant’s agents. A scope with no memories yet returns an empty vec, not an error.

§Errors

Returns ClientError::Store wrapping a database failure.

Source

pub fn extraction_stats(&self) -> ExtractionStatsBuilder<'_>

Computes extraction accuracy per (provider, model) over a scope slice.

Returns an ExtractionStatsBuilder; its scope setters narrow the slice before awaiting. A read-only aggregate proving extraction quality to a consumer — accuracy = 1 − rejected/total, where rejected counts only wrong extractions the user corrected (not Stale or superseded rows). No LLM call. See ExtractionStatsBuilder for the builder methods.

Trait Implementations§

Source§

impl Clone for Client

Source§

fn clone(&self) -> Client

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Client

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WasmCompatSend for T
where T: Send,

Source§

impl<T> WasmCompatSync for T
where T: Sync,

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more