Skip to main content

MemoryService

Struct MemoryService 

Source
pub struct MemoryService<E: Embedder, S: MemoryStore = NativeStore> { /* private fields */ }
Expand description

Local-first agent memory backed by a single VelesDB instance.

Generic over the Embedder so production can use an on-device model while tests use a deterministic, network-free one, and over the MemoryStore backend S so the same orchestration runs over the native, file-backed engine (the default — nothing changes for existing callers) or any other backend that implements the trait (e.g. an in-memory one for WASM).

Two definitions, persistence-gated: the default type parameter itself references NativeStore, which doesn’t exist as a type at all without the feature, so a persistence-free build (e.g. velesdb-wasm) drops the default and every caller names its own MemoryStore backend explicitly.

Implementations§

Source§

impl<E: Embedder, S: MemoryStore> MemoryService<E, S>

Source

pub fn recall_fused( &self, query: &str, k: usize, filter: Option<&Metadata>, opts: FusionOptions, ) -> Result<Vec<Recollection>, MemoryError>

Fused recall: like Self::recall, but also walks the graph from the query’s top vector hit and folds any fact it reaches (hop ≥ 1) into the ranking, scored by opts.graph_boost · graph_weight on top of its normalised vector similarity. A fact the graph reaches never displaces a strong vector hit unless the boosted score genuinely outranks it; a fact the vector pool ranked low (or missed) can still surface if the graph connects it. This is the tri-engine ranking measured on HotpotQA/TimeQA/LoCoMo (examples/multihop, examples/timeqa, examples/locomo) — Self::recall stays pure-vector and unchanged, so existing callers see no behavior shift.

The graph reach requires a wired graph to find anything: it walks edges from Self::relate or the entity hubs Self::remember_extracted auto-wires. Entity hubs themselves are never returned, exactly like Self::recall.

§Errors

Returns MemoryError if embedding, vector search, or graph traversal fails.

Source

pub fn recall_fused_dated( &self, query: &str, k: usize, filter: Option<&Metadata>, opts: FusionOptions, date_field: &str, ) -> Result<(Vec<Recollection>, DatedContext), MemoryError>

Self::recall_fused paired with the dated-context rendering of its results: returns the recalled facts and the DatedContext built from their date_field metadata (see format_dated_context). Every binding that exposes a “dated recall” (the MCP recall_fused tool’s date_field, Node/WASM recallFusedDated) calls this, so the “recall then format” pairing lives in exactly one place and can’t drift between surfaces.

date_field can name any caller metadata key, but passing crate::storage::AUTO_DATE_FIELD needs zero setup: remember auto-stamps that key on every fact already, so a caller gets a correct dated_context without ever having managed a date field itself.

§Errors

Returns MemoryError if the underlying Self::recall_fused fails.

Source

pub fn recall_fused_reranked<R: Reranker>( &self, query: &str, k: usize, filter: Option<&Metadata>, opts: FusionOptions, reranker: &R, ) -> Result<Vec<Recollection>, MemoryError>

Like Self::recall_fused, but hands the FULL fused-ranked candidate pool (before the final k cutoff) to reranker for a second-stage re-score, then truncates to k. Closes the ranking-miss gap the LoCoMo ceiling diagnostic found: a relevant fact can be IN the pool (recall@64 ≈ 89% on multi-hop) yet outranked out of a tight k (recall@8 ≈ 50%) — a reranker recovers it without widening k itself.

No built-in reranker ships: bring your own (cross-encoder, LLM judge, …) via Reranker. Never call this as a default — a reranker can also hurt out-of-distribution conversational queries (measured on LoCoMo), so it is opt-in, one call at a time.

§Errors

Returns MemoryError if embedding, vector search, graph traversal, or reranker itself fails.

Source§

impl<E: Embedder, S: MemoryStore> MemoryService<E, S>

Source

pub fn feedback(&self, id: u64, success: bool) -> Result<f32, MemoryError>

Record an outcome for a recalled fact and return its new confidence.

success = true reinforces the fact (it was useful), false weakens it (it was noise). The update is applied by a ReinforcementStrategy (FixedRate by default) over the fact’s current confidence and its success/failure history, then persisted durably. Over repeated feedback the fact drifts up or down the Self::recall ranking — the agent’s memory learns which facts are worth surfacing.

§Concurrency

The update is a read-modify-write that is not atomic across the get_metadata/update_metadata pair. Two feedback calls racing on the same id are last-writer-wins: one increment can be lost. This is acceptable for a soft, approximate ranking signal — feedback still moves confidence in the right direction — but callers needing exact tallies must serialize their own calls per id.

§Errors

Returns MemoryError::UnknownMemory if id is not a live fact, or a storage error if the read-back or persist fails.

Source§

impl<E: Embedder, S: MemoryStore> MemoryService<E, S>

Source

pub fn compile_context( &self, compiler: &ContextCompiler, request: &CompileRequest, ) -> Result<CompiledContext, MemoryError>

ContextCompiler::compile with this service’s memory folded in: when the request carries a MemoryScope, relevant memories are pulled through the fused vector+graph recall and compiled alongside the caller’s fragments, each with its memory_id and a normalised fused-ranking relevance recorded in provenance. Afterwards (policy permitting) the distinct originals are stored so every ctx://source/<hash> handle round-trips, and a metadata-only compilation event is recorded for Self::context_savings.

§Errors

Returns MemoryError if compilation itself fails (budget, caps), or if recall, embedding, or storage fails.

Source

pub fn compile_context_reranked<R: Reranker>( &self, compiler: &ContextCompiler, request: &CompileRequest, reranker: &R, ) -> Result<CompiledContext, MemoryError>

Self::compile_context with a caller-supplied crate::Reranker driving memory selection: the reranker receives the FULL fused candidate pool (vector + graph, before the k cutoff) and its ordering decides which k memories are compiled in — the seam for a semantic cross-encoder or LLM judge a Rust embedder brings along. Not exposed on the wire (a reranker is code, not JSON), and never a default: the shipped crate::context::DeterministicReranker is lexical, and a lexical second stage demotes exactly the zero-vocabulary-overlap evidence the graph walk rescues (measured in the BDD suite) — bring a semantic one.

§Errors

Returns MemoryError if compilation, recall, the reranker itself, or storage fails.

Source

pub fn retrieve_context_source( &self, handle: &str, ) -> Result<ContextSource, MemoryError>

The original content — and media, when the fragment carried one — behind a ctx://source/<hash> handle.

§Errors

Returns MemoryError::UnknownHandle when the handle is malformed or nothing is stored under it (never stored, expired, or forgotten).

Source

pub fn explain_compilation( &self, request: &CompileRequest, fragment_id: u64, fragment_index: Option<usize>, ) -> Result<ContextDecision, MemoryError>

Explain why one fragment of request was preserved, abstracted, externalized, dropped, or cached — the selection primitive the MCP explain_compilation tool delegates to, extracted here so every adapter (MCP, Node, Python) shares one implementation instead of reimplementing it. Compilation is deterministic, so request is simply re-compiled — with event/source recording forced off, since an explanation must not have side effects — and the matching decision is returned.

fragment_index (0-based position in request.fragments), when given, TAKES PRIORITY over fragment_id for locating the decision: compile_context records exactly one decision per input fragment, in order, so decisions[fragment_index] is unambiguous even when several fragments are byte-identical and therefore share the same content-addressed fragment_id — a plain fragment_id lookup always resolves to the FIRST such decision (the deduplication survivor’s), never a dropped twin’s.

Caveat inherited from re-compiling rather than replaying stored state: with a memory_scope the re-compile recalls from CURRENT memory, so the decision reflects memory as it is now, not as it was at the original compile_context call; a caller that already resolved a path fragment to content is unaffected (this method does no I/O of its own).

§Errors

Returns MemoryError::FragmentIndexOutOfBounds when fragment_index is beyond request.fragments, MemoryError::FragmentNotFound when no decision matches the selector, or any error Self::compile_context itself can return (budget, caps, recall, embedding, storage).

Source

pub fn context_savings( &self, project: Option<&str>, ) -> Result<ContextSavings, MemoryError>

Aggregate the recorded compilation events, optionally per project. Sweeps at most crate::limits::MAX_RECALL_LIMIT events (newest need not be first — the sweep is similarity-ordered over a constant anchor, i.e. effectively the whole family until the cap); ContextSavings::truncated reports when the cap was hit.

§Errors

Returns MemoryError if the underlying filtered recall fails.

Source

pub fn save_working_context( &self, project: &str, session: &str, working: &WorkingContext, ) -> Result<u64, MemoryError>

Persist working under project + session (idempotent upsert: saving again replaces the previous state). Returns the system fact id.

Serialized size is capped at crate::limits::MAX_FACT_BYTES (1 MiB) — the same ceiling every other stored fact honors — checked BEFORE anything is written, so an oversized working context is never partially stored.

An entirely empty working (WorkingContext::is_empty) is refused. Because the write is an upsert, saving one would replace — destroy — the state a previous save stored under the same project and session, and the one tool whose job is surviving a context loss must not be able to cause one on a call that carries nothing (issue #1654).

§Errors

Returns MemoryError::EmptyWorkingContext if working records nothing, MemoryError::WorkingContextCodec if serialization fails, MemoryError::ContextOverLimit if the serialized working exceeds crate::limits::MAX_FACT_BYTES, or a storage/embedding error.

Source

pub fn load_working_context( &self, project: &str, session: &str, ) -> Result<Option<WorkingContext>, MemoryError>

The working context previously saved under project + session, None when there is none.

Symmetric to Self::context_source_metadata’s squatter guard: the slot is only ever served back when its metadata carries the reserved CTX_WORKING_FIELD marker (set exclusively by Self::save_working_context). A slot occupied by an unmarked caller fact — one that happened to land on this salted id, or a forged probe — is indistinguishable from “nothing saved” on purpose: None, never the forged content, and never an error (the caller cannot tell a squatted slot from a genuinely empty one, which is the point — it must never learn that something occupies this id).

A pure read: it never writes, never prunes, never heals. Index convergence happens on the WRITE path (Self::update_working_index) — a lookup that rewrites shared state turns every transient miss into permanent data loss and cannot safely be retried.

§Errors

Returns MemoryError::WorkingContextCodec if the stored payload does not parse, or if the slot is marked but its body is gone (a torn fact is corruption — reporting it as “nothing saved” would tell the caller the one thing that is certainly false), or a storage error.

Source

pub fn resume_working_context( &self, project: &str, session: &str, ) -> Result<LoadedWorkingContext, MemoryError>

The full resumption envelope for project + session: what Self::load_working_context found, plus the OTHER sessions saved under the same project so a typo in session is recoverable.

This is the ONE place the three policy rules live:

  1. other_sessions is listed on a HIT too, not just on a miss — a typo that lands on another REAL session returns found: true, and the caller has no other way to notice it resumed the wrong work. Costs one extra O(1) index read per successful load.
  2. The requested session is never echoed back: the field is named other_sessions, so returning the requested id would be a contradiction the caller cannot act on.
  3. An unreadable index is fatal on a MISS and survivable on a HIT — see Self::other_sessions_for.

Every surface (the load_working_context MCP tool and the Node, Python and WASM bindings) calls this rather than recomposing the envelope from Self::load_working_context + Self::list_working_contexts: four recompositions are four copies of those rules, and a copy that stops matching the others fails silently — the caller still gets a well-formed envelope, just a different one.

§Errors

Propagates Self::load_working_context’s errors (a corrupt or unparseable stored payload), and Self::list_working_contexts’s (a corrupt index, or a storage failure) on a miss only — rule 3.

Source

pub fn list_working_contexts( &self, project: &str, ) -> Result<Vec<WorkingContextSession>, MemoryError>

Every session still resumable under project’s working-context index (V2a-1 quick win), most-recently-saved first. Empty when the project never saved anything — that, and only that, is the empty case.

Cost: one O(1) index read plus ONE batched metadata lookup of the listed ids — never a store scan, but no longer a single read either. The lookup is what drops sessions whose fact was forgotten since; unlike the previous read-path prune it persists nothing, so a listing can be retried and a transient miss costs nothing durable.

§Errors

Returns a storage error if the index fact cannot be read, or MemoryError::WorkingContextCodec if it does not parse or is corrupt (marked, but with no body).

Source§

impl<E: Embedder> MemoryService<E, NativeStore>

Source

pub fn open<P: AsRef<Path>>(path: P, embedder: E) -> Result<Self, MemoryError>

Open (or create) a native, file-backed memory store at path, using embedder for text vectorization. The store never leaves this directory.

§Errors

Returns MemoryError if the store cannot be opened or the agent memory cannot be initialized for the embedder’s dimension.

Source§

impl<E: Embedder, S: MemoryStore> MemoryService<E, S>

Source

pub fn with_store(store: S, embedder: E) -> Self

Build a service directly over a store backend, bypassing Self::open’s filesystem-specific setup — the constructor a non-native backend (e.g. velesdb-wasm’s in-memory store) uses.

Source

pub fn with_autograph(self, extractor: DynExtractor) -> Self

Turn on autograph: every Self::remember additionally reads the stored fact for entities, entity→entity edges and entity attributes, and wires them — so the knowledge graph builds itself from ordinary remember calls, with no separate Self::remember_extracted.

Opt-in, and off unless this is called. It runs in one of two modes: inline by default — the enrichment costs one generation per remember, on the caller’s write path, which is a real latency and availability change: a memory write that silently depends on a local model being up is not a default anyone should inherit — or decoupled when Self::spawn_autograph_worker is active, where remember returns as soon as the fact is durably stored and the derived edges lag by one generation (an entity/why read issued immediately after may not see them yet; the fact itself is always immediately readable).

The caller’s fact is stored verbatim and first. Autograph only adds structure around it; it never rewrites or replaces what the caller asked to remember.

Source

pub fn remember( &self, fact: &str, links: &[Link], metadata: Option<&Metadata>, ) -> Result<u64, MemoryError>

Remember a fact, optionally tagging it with structured metadata (ColumnStore facet) and linking it to existing memories (graph facet). Returns the stable id of the fact (idempotent on identical content).

The stored metadata is auto-stamped with today’s date under crate::storage::AUTO_DATE_FIELD unless metadata already carries that key — see Self::remember_with_ttl (this method’s only caller) for the full contract.

Every link is validated — target existence AND relation label — before the fact is stored, so bad link input never leaves the fact half-written. If an edge write itself fails afterwards (e.g. a target expiring concurrently), a freshly-created fact is rolled back; a re-remembered fact keeps its updated payload (re-remembering updates metadata by design, and deleting it would destroy prior state). Concurrent remembers of identical content are last-writer-wins, not transactional.

§Errors

Returns MemoryError::EmptyFact for empty/whitespace facts, MemoryError::FactTooLarge if the fact exceeds crate::limits::MAX_EMBEDDABLE_TEXT_BYTES, MemoryError::SelfRelation if a link points the fact at itself, MemoryError::ReservedKey if metadata names a reserved key (content or any _veles_-prefixed system key, crate::storage::AUTO_DATE_FIELD excepted), MemoryError::MetadataTooLarge if metadata exceeds crate::limits::MAX_METADATA_BYTES, MemoryError::UnknownMemory if a link points at a missing memory, MemoryError::InvalidRelation for a bad relation label, MemoryError::RollbackFailed if an edge write failed and the compensating delete also failed (the fact remains stored), or a storage error if persistence fails.

Source

pub fn remember_with_ttl( &self, fact: &str, links: &[Link], metadata: Option<&Metadata>, ttl_seconds: Option<u64>, ) -> Result<u64, MemoryError>

Like Self::remember, but the fact expires after ttl_seconds.

The expiry is a durable TTL — persisted with the fact (reserved _veles_expires_at payload field), so it survives a process restart, and expired facts stop being recalled. None stores the fact permanently, exactly like Self::remember; an explicit Some(0) is refused (MemoryError::ZeroTtl) rather than silently normalised to “permanent”, which is the opposite of what a caller writing 0 means. Metadata and a TTL combine: the metadata is written and the expiry preserved.

The stored metadata is auto-stamped with today’s date under crate::storage::AUTO_DATE_FIELD (_veles_date, a YYYYMMDD integer read from the system clock at write time — see [crate::clock::today_ymd]) whenever metadata doesn’t already carry that key; an explicit value in metadata (e.g. to date a fact retroactively) is never overwritten. No clock is available on wasm32-unknown-unknown, so that target stamps nothing and metadata passes through unchanged. This is the ONE place in the crate that reads wall-clock time on the write path — the context compiler (compile_context and friends) stays clock-free and deterministic, unaffected by this stamp (it never re-derives a date from now(), only ever reads whatever a fact already carries).

Because Self::remember_extracted stores each extracted fact via Self::remember (which delegates here), it gets the same auto-stamp for free — entity hubs it also creates go through Self::store_fact directly and are never stamped, since they are internal graph scaffolding, not caller facts.

§Errors

Same as Self::remember.

Source

pub fn autograph_dropped(&self) -> u64

How many autograph enrichments a FULL queue refused since this service was built (#1846). The facts themselves were stored; only their graph wiring was skipped, and re-remembering a fact rebuilds it.

Source

pub fn autograph_queue_open(&self) -> bool

Whether the background autograph queue is OPEN — a worker is spawned and remember enqueues instead of running the enrichment inline. Turns false the moment a worker handle’s drop closes the queue.

Source

pub fn has_autograph(&self) -> bool

Whether an autograph extractor is configured at all.

Source

pub fn fact_count(&self) -> usize

The total number of live tracked facts, internal entity hubs included — the store’s MemoryStore::count, relayed for memory_status.

Source

pub fn edge_count(&self) -> Option<usize>

The total number of graph edges, when the backend can say — MemoryStore::edge_count, relayed for memory_status. None means “cannot say”, never “zero”: the two answers tell a caller different things about why().

Source

pub fn list( &self, cursor: Option<u64>, limit: usize, filter: Option<&Metadata>, include_internal: bool, ) -> Result<(Vec<ListedMemory>, Option<u64>), MemoryError>

One page of the store’s facts, for auditing — “what does my agent know?” — which recall structurally cannot answer: it ranks by resemblance to a query, and what resembles nothing you thought to ask stays invisible.

The store hands back raw pages (MemoryStore::list); the visibility policy is applied here, once, for every backend: internal entity hubs are skipped unless include_internal (they are the graph’s scaffolding, not the user’s facts), reserved _veles_* keys are stripped exactly as recall strips them (the auto-stamped date survives — an audit legitimately asks WHEN), and filter keeps only facts whose metadata equals every given key. A filtered page may come back sparse — the cursor still advances over what was skipped, so the WALK stays exhaustive.

§Errors

Returns MemoryError if the backend cannot enumerate or the walk fails.

Source§

impl<E, S> MemoryService<E, S>
where E: Embedder + Send + Sync + 'static, S: MemoryStore + Send + Sync + 'static,

Source

pub fn spawn_autograph_worker( self: &Arc<Self>, capacity: usize, ) -> Result<AutographWorkerHandle, MemoryError>

Move autograph off the response path: spawn ONE background worker consuming a bounded queue, so remember returns as soon as the fact is durably stored and the graph is wired behind (#1846).

Measured motivation: with the production extractor, an inline autograph held every remember for 46-52 s while the embedding cost 0.12 s — and the MCP client timed out mid-generation, making a stored fact indistinguishable from a lost one (#1839).

The read-after-write contract changes, deliberately and visibly: an entity() issued right after remember may not see the new edges yet. The fact itself is always readable immediately — only the DERIVED structure lags by one generation.

One worker on purpose: the store is single-writer, and a second in-flight generation would only add contention, not throughput. capacity bounds the queue (crate::limits::MAX_AUTOGRAPH_QUEUE is the daemon’s choice); a full queue DROPS new enrichments, counted by Self::autograph_dropped and logged — never silent, never blocking the write path.

§Errors

Returns MemoryError::Extract when a worker is already spawned for this service — two workers would race the single-writer store for no gain — or when the OS refuses the thread.

Source§

impl<E: Embedder, S: MemoryStore> MemoryService<E, S>

Source

pub fn remember_extracted<X: Extractor>( &self, text: &str, extractor: &X, metadata: Option<&Metadata>, ) -> Result<RememberedExtraction, MemoryError>

Remember a passage of raw text by running it through an Extractor and storing every fact it yields, auto-wiring the fact↔entity graph.

This is the commodity on top of Self::remember’s bring-your-own-links core: each extracted fact is stored (tagged with metadata), each salient topic becomes a deduplicated hub memory, and every fact is linked to its topics with a bidirectional about/mentions edge. Two facts sharing a topic therefore become reachable from one another, so Self::why has a real graph to traverse with no manual relate().

Entity hubs are content-addressed, so the same topic seen across many calls collapses onto one hub. Returns the ids of the stored facts (entity hubs excluded), in extraction order, plus how many facts were skipped for exceeding the embeddable cap — one unusable fact must not cost the others, the policy every other stage of this pipeline already follows (a malformed triple is skipped, a blank entity is skipped).

§Errors

Returns MemoryError::EmptyFact for empty/whitespace text, MemoryError::Extract if extraction fails, MemoryError::ReservedKey if metadata names a reserved key, MemoryError::MetadataTooLarge if metadata exceeds crate::limits::MAX_METADATA_BYTES, or a storage error if persistence fails. A fact past crate::limits::MAX_EMBEDDABLE_TEXT_BYTES is NOT an error: it is counted in RememberedExtraction::skipped_over_cap and the call carries on.

Source

pub fn entity_profile( &self, name: &str, ) -> Result<Option<EntityProfile>, MemoryError>

Look up everything known about a named entity: the attributes merged onto its hub, and the typed edges leaving it.

This is the read side of the auto-built graph, and it exists because entity hubs are deliberately invisible to Self::recall and Self::recall_where — a hub ranking for its own topic would evict a real fact from the caller’s results. Without this accessor an attribute merged onto a hub would be stored correctly and yet be unreachable through every public read path: the worst kind of feature, one that looks done and silently returns nothing.

name is canonicalized exactly like an extracted entity (trimmed, lowercased), so the caller may pass "Theo Durand" and reach the node built from "theo durand". Returns None when no hub exists for the name — nothing has ever mentioned that entity.

§Errors

Returns MemoryError if the store lookup fails.

Source

pub fn recall( &self, query: &str, k: usize, filter: Option<&Metadata>, ) -> Result<Vec<Recollection>, MemoryError>

Recall up to k memories semantically similar to query (vector facet), optionally narrowed to an exact-match metadata filter (ColumnStore facet) — e.g. { "project": "veles", "status": "resolved" }.

A highly selective filter may return fewer than k hits even when more matches exist — raise k for fuller coverage with a narrow filter.

Entity hubs created by Self::remember_extracted are never returned: they are internal graph scaffolding, not facts the caller stored.

Each hit carries its caller metadata (Recollection::metadata, None when the fact carries none) — store a date field (e.g. occurred_at) and it round-trips here, so a caller can sort the result into a chronological, date-stamped context without recall_where’s explicit filters. One extra, single batched lookup covers every returned hit.

§Errors

Returns MemoryError if the semantic query or the metadata lookup fails.

Source

pub fn recall_where( &self, query: &str, k: usize, filters: &[ColumnFilter], ) -> Result<Vec<Recollection>, MemoryError>

Fused recall: semantic NEAR search combined with structured ColumnStore predicates over metadata columns — ranges and comparisons, not just the equality of Self::recall. One query spanning the vector and column facets (e.g. “most similar facts with timestamp in this window”), which a vector-only or equality-only recall cannot express.

Filter values are bound as query parameters (never interpolated), so they cannot inject; filter field names are validated to be plain identifiers. Results come back in similarity order.

Caller memories only. The store also holds internal scaffolding — the entity hubs of Self::remember_extracted and the context compiler’s four artefact classes (stored sources, compilation events, working contexts, and the per-project working-context index). They sit in the same collection as caller facts and are excluded from every result here, whatever the predicate.

That exclusion is applied by the backend against crate::storage::INTERNAL_MARKER_FIELDS; it is NOT a consequence of those facts being unfilterable. A caller cannot write a filter naming a reserved key, but field ne value MATCHES a fact that has no such field at all — and scaffolding has none of the caller’s columns, so before #1737 every ne predicate returned all of it.

§Errors

Returns MemoryError::InvalidFilter if a filter field is not a plain identifier, MemoryError::Embed if the query cannot be embedded, or a storage error if the query fails. An empty query or k == 0 yields [].

Source

pub fn relate( &self, from: u64, to: u64, relation: &str, ) -> Result<u64, MemoryError>

Create a typed edge from -> to. Returns the edge id.

Both endpoints are validated to exist first, so the tool reports an unknown id as client input (UnknownMemory) rather than a generic storage fault — and the graph never gains an edge dangling off a memory that was never stored.

A self-loop (from == to) is refused: it states nothing, and why traverses it like any other edge, so it only adds noise to the evidence trail. The same rule covers Self::remember’s links.

§Errors

Returns MemoryError::InvalidRelation for a bad label, MemoryError::SelfRelation if both endpoints are the same memory, MemoryError::UnknownMemory if either endpoint is missing, or a storage error if the edge cannot be created.

Source

pub fn unrelate( &self, from: u64, to: u64, relation: &str, ) -> Result<UnrelateOutcome, MemoryError>

Remove the edge(s) from -relation-> to: Self::relate’s exact undo (issue #1661), so a mistaken edge no longer costs the facts at its endpoints. Neither the facts nor any entity hub are touched — collecting an orphaned hub stays Self::forget’s job.

Idempotent: an absent edge is found: false, not an error, so a cleanup is replayable. It refuses exactly what relate refuses (empty label, self-loop), and deliberately does NOT require the endpoints to exist — the edge of a forgotten fact is already gone, and reporting that as an error would break replay.

Scope: the store does not distinguish an explicit edge from one the autograph derived from a passage, so unrelate removes both alike. To correct an autograph edge, prefer forget + remember of the source fact — otherwise a later remember of the same passage can rebuild the edge removed here.

§Errors

Returns MemoryError::InvalidRelation for a bad label, MemoryError::SelfRelation if both endpoints are the same memory, or a storage error if lookup or removal fails.

Source

pub fn forget(&self, fact_id: u64) -> Result<bool, MemoryError>

Forget (delete) the memory with fact_id. Returns whether a memory actually existed under that id — the underlying store’s delete is a silent no-op on an unknown id (matching most backends’ idempotent delete semantics), which is indistinguishable from a real deletion unless existence is checked first. Every surface that exposes forget (MCP, Node, WASM, Python) forwards this so a caller can tell “I removed something” from “that id was a typo”.

The delete always runs, even when get reports the id absent: get filters TTL-expired facts, and an expired-but-unpurged row must still be reclaimed (the caller is told false — the memory was already gone from its perspective). Existence check and delete are two store calls, not one atomic operation: two concurrent forgets of one id may both report true.

§Errors

Returns MemoryError if the existence check or the deletion fails.

Source

pub fn why( &self, decision: &str, max_hops: usize, filter: Option<&Metadata>, ) -> Result<Explanation, MemoryError>

Explain a decision: find the best-matching memory (optionally scoped to a metadata filter, e.g. the current project), then walk its typed links up to max_hops away — fusing the vector, ColumnStore, and graph facets.

Returns an empty Explanation when nothing matches the decision.

§Errors

Returns MemoryError if recall or graph traversal fails.

Auto Trait Implementations§

§

impl<E, S = NativeStore> !Freeze for MemoryService<E, S>

§

impl<E, S = NativeStore> !RefUnwindSafe for MemoryService<E, S>

§

impl<E, S = NativeStore> !UnwindSafe for MemoryService<E, S>

§

impl<E, S> Send for MemoryService<E, S>
where S: Send, E: Send,

§

impl<E, S> Sync for MemoryService<E, S>
where S: Sync, E: Sync,

§

impl<E, S> Unpin for MemoryService<E, S>
where S: Unpin, E: Unpin,

§

impl<E, S> UnsafeUnpin for MemoryService<E, S>
where S: UnsafeUnpin, E: UnsafeUnpin,

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> Paint for T
where T: ?Sized,

Source§

fn fg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the foreground set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like red() and green(), which have the same functionality but are pithier.

§Example

Set foreground color to white using fg():

use yansi::{Paint, Color};

painted.fg(Color::White);

Set foreground color to white using white().

use yansi::Paint;

painted.white();
Source§

fn primary(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Primary].

§Example
println!("{}", value.primary());
Source§

fn fixed(&self, color: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Fixed].

§Example
println!("{}", value.fixed(color));
Source§

fn rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Rgb].

§Example
println!("{}", value.rgb(r, g, b));
Source§

fn black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Black].

§Example
println!("{}", value.black());
Source§

fn red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Red].

§Example
println!("{}", value.red());
Source§

fn green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Green].

§Example
println!("{}", value.green());
Source§

fn yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Yellow].

§Example
println!("{}", value.yellow());
Source§

fn blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Blue].

§Example
println!("{}", value.blue());
Source§

fn magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Magenta].

§Example
println!("{}", value.magenta());
Source§

fn cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Cyan].

§Example
println!("{}", value.cyan());
Source§

fn white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: White].

§Example
println!("{}", value.white());
Source§

fn bright_black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlack].

§Example
println!("{}", value.bright_black());
Source§

fn bright_red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightRed].

§Example
println!("{}", value.bright_red());
Source§

fn bright_green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightGreen].

§Example
println!("{}", value.bright_green());
Source§

fn bright_yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightYellow].

§Example
println!("{}", value.bright_yellow());
Source§

fn bright_blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlue].

§Example
println!("{}", value.bright_blue());
Source§

fn bright_magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.bright_magenta());
Source§

fn bright_cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightCyan].

§Example
println!("{}", value.bright_cyan());
Source§

fn bright_white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightWhite].

§Example
println!("{}", value.bright_white());
Source§

fn bg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the background set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like on_red() and on_green(), which have the same functionality but are pithier.

§Example

Set background color to red using fg():

use yansi::{Paint, Color};

painted.bg(Color::Red);

Set background color to red using on_red().

use yansi::Paint;

painted.on_red();
Source§

fn on_primary(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Primary].

§Example
println!("{}", value.on_primary());
Source§

fn on_fixed(&self, color: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Fixed].

§Example
println!("{}", value.on_fixed(color));
Source§

fn on_rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Rgb].

§Example
println!("{}", value.on_rgb(r, g, b));
Source§

fn on_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Black].

§Example
println!("{}", value.on_black());
Source§

fn on_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Red].

§Example
println!("{}", value.on_red());
Source§

fn on_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Green].

§Example
println!("{}", value.on_green());
Source§

fn on_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Yellow].

§Example
println!("{}", value.on_yellow());
Source§

fn on_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Blue].

§Example
println!("{}", value.on_blue());
Source§

fn on_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Magenta].

§Example
println!("{}", value.on_magenta());
Source§

fn on_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Cyan].

§Example
println!("{}", value.on_cyan());
Source§

fn on_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: White].

§Example
println!("{}", value.on_white());
Source§

fn on_bright_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlack].

§Example
println!("{}", value.on_bright_black());
Source§

fn on_bright_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightRed].

§Example
println!("{}", value.on_bright_red());
Source§

fn on_bright_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightGreen].

§Example
println!("{}", value.on_bright_green());
Source§

fn on_bright_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightYellow].

§Example
println!("{}", value.on_bright_yellow());
Source§

fn on_bright_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlue].

§Example
println!("{}", value.on_bright_blue());
Source§

fn on_bright_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.on_bright_magenta());
Source§

fn on_bright_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightCyan].

§Example
println!("{}", value.on_bright_cyan());
Source§

fn on_bright_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightWhite].

§Example
println!("{}", value.on_bright_white());
Source§

fn attr(&self, value: Attribute) -> Painted<&T>

Enables the styling Attribute value.

This method should be used rarely. Instead, prefer to use attribute-specific builder methods like bold() and underline(), which have the same functionality but are pithier.

§Example

Make text bold using attr():

use yansi::{Paint, Attribute};

painted.attr(Attribute::Bold);

Make text bold using using bold().

use yansi::Paint;

painted.bold();
Source§

fn bold(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Bold].

§Example
println!("{}", value.bold());
Source§

fn dim(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Dim].

§Example
println!("{}", value.dim());
Source§

fn italic(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Italic].

§Example
println!("{}", value.italic());
Source§

fn underline(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Underline].

§Example
println!("{}", value.underline());

Returns self with the attr() set to [Attribute :: Blink].

§Example
println!("{}", value.blink());

Returns self with the attr() set to [Attribute :: RapidBlink].

§Example
println!("{}", value.rapid_blink());
Source§

fn invert(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Invert].

§Example
println!("{}", value.invert());
Source§

fn conceal(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Conceal].

§Example
println!("{}", value.conceal());
Source§

fn strike(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Strike].

§Example
println!("{}", value.strike());
Source§

fn quirk(&self, value: Quirk) -> Painted<&T>

Enables the yansi Quirk value.

This method should be used rarely. Instead, prefer to use quirk-specific builder methods like mask() and wrap(), which have the same functionality but are pithier.

§Example

Enable wrapping using .quirk():

use yansi::{Paint, Quirk};

painted.quirk(Quirk::Wrap);

Enable wrapping using wrap().

use yansi::Paint;

painted.wrap();
Source§

fn mask(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Mask].

§Example
println!("{}", value.mask());
Source§

fn wrap(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Wrap].

§Example
println!("{}", value.wrap());
Source§

fn linger(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Linger].

§Example
println!("{}", value.linger());
Source§

fn clear(&self) -> Painted<&T>

👎Deprecated since 1.0.1:

renamed to resetting() due to conflicts with Vec::clear(). The clear() method will be removed in a future release.

Returns self with the quirk() set to [Quirk :: Clear].

§Example
println!("{}", value.clear());
Source§

fn resetting(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Resetting].

§Example
println!("{}", value.resetting());
Source§

fn bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Bright].

§Example
println!("{}", value.bright());
Source§

fn on_bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: OnBright].

§Example
println!("{}", value.on_bright());
Source§

fn whenever(&self, value: Condition) -> Painted<&T>

Conditionally enable styling based on whether the Condition value applies. Replaces any previous condition.

See the crate level docs for more details.

§Example

Enable styling painted only when both stdout and stderr are TTYs:

use yansi::{Paint, Condition};

painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);
Source§

fn new(self) -> Painted<Self>
where Self: Sized,

Create a new Painted with a default Style. Read more
Source§

fn paint<S>(&self, style: S) -> Painted<&Self>
where S: Into<Style>,

Apply a style wholesale to self. Any previous style is replaced. Read more
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> Same for T

Source§

type Output = T

Should always be Self
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<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