service

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: AGPL-3.0 Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const (
	OriginPrimary  = "primary"
	OriginAncestor = "ancestor"
	OriginHome     = "home"
	OriginLink     = "link"
	OriginCall     = "call"
)

Origin values recorded on a read-set leg — see ReadSetEntry.Origin. Each is set once, at the moment the leg is appended during resolution (never re-derived from the resolved set afterwards): "primary" for the request namespace and every member of its subtree expansion (scope=subtree is treated as part of the primary leg, not a distinct origin of its own), "ancestor" for a path-prefix cascade leg, "home" for the caller's personal namespace (X-Memini-Home), "link" for a stored namespace link, and "call" for an explicit per-call namespace (RecallInput.Namespaces / BriefingOpts.Namespaces) other than the primary namespace itself — the primary namespace is always "primary", even when it appears in an explicit list.

View Source
const DefaultPerSection = 5

DefaultPerSection is the briefing cap applied to any section whose dedicated opt is nil. It mirrors the historical "per_section=N" default so callers that don't pass per-section options see the same behavior.

Variables

View Source
var ErrInvalidInput = errors.New("invalid input")

ErrInvalidInput marks errors caused by the caller's request (missing fields, unknown tiers) as opposed to backend failures. API layers map it to 400; anything else is a server-side error.

View Source
var ErrUnsupported = errors.New("unsupported by this storage backend")

ErrUnsupported marks an operation the configured storage driver cannot serve because it lacks the optional capability the operation needs (e.g. reading the activity log from a driver with no event log). API layers map it to 501.

Functions

func OriginMap added in v0.6.6

func OriginMap(entries []ReadSetEntry) map[string]string

OriginMap builds the namespace -> origin lookup ReadSetFrom needs, from a resolved read-set's ReadSetEntry slice (Recall/Briefing/Answer's ReadSet out-param). entries is typically empty (the out-param's zero value) when the caller never asked for read-set info, in which case every ReadSetFrom lookup falls through to its default case. Shared by the MCP and REST API layers so both surfaces render "from" provenance identically — see ReadSetFrom.

func ReadSetFrom added in v0.6.6

func ReadSetFrom(origins map[string]string, ns string) string

ReadSetFrom renders a resolved read-set origin (the Origin* constants above) into API "from" provenance: the origin recorded when ns's leg was appended during read-set resolution (see ReadSetEntry), not re-derived here. origins maps namespace -> origin, built once per call from a resolved read-set via OriginMap. A namespace absent from origins (read-set info wasn't resolved, or the caller didn't ask for it) renders empty rather than guessing.

Result: "" for the primary namespace or an unresolved lookup (no annotation needed — the common case); the namespace itself for an ancestor/home leg; "link:<ns>" for a stored link; "call:<ns>" for an explicit per-call namespace.

func RecallPoolSize added in v0.0.6

func RecallPoolSize(k int) int

RecallPoolSize is the per-leg candidate pool Recall over-fetches for a final result count of k, with the default pool sizing. Exported so external pipelines (bench) that re-create recall stage-by-stage match production instead of hardcoding the constants.

func WithActor added in v0.7.0

func WithActor(ctx context.Context, name, kind string) context.Context

WithActor stamps request-scoped attribution onto ctx so every event the request logs records who performed it. The REST and MCP surfaces call it once, right after authenticating: a named key → (name, "key"); the admin env key → ("", "env"); an unauthenticated dev-mode request → ("", "none"). Attribution is automatic and unconditional — never a setting — so callers always stamp; a context with no actor (background maintenance, tests) simply logs the legacy "" kind.

Types

type ActivityEvent added in v0.6.8

type ActivityEvent struct {
	OpID      string
	Kind      store.EventKind
	Time      time.Time
	Namespace string
	Query     string
	Detail    map[string]any
	// Actor/ActorKind are who performed the operation — see store.Event. Empty
	// on a legacy row that predates attribution.
	Actor     string
	ActorKind string
	Memories  []ActivityMemory
}

ActivityEvent is one logical operation: what happened, when, against which namespace, who performed it, and — for a recall — the query and the memories it served.

type ActivityMemory added in v0.6.8

type ActivityMemory struct {
	ID        string
	Namespace string
	Summary   string
	Tier      memory.Tier
	Rank      int
	Score     *float64
	Section   string // briefing only
}

ActivityMemory is one memory as it appeared in an activity event: the snapshot taken at serve time, plus why it was there (rank, score, section).

type AnswerInput added in v0.0.4

type AnswerInput struct {
	Namespace string
	// Home is the caller's personal namespace, merged read-only into every
	// recall this answer performs (prefetch, gate, and any tool-loop
	// searches) — same semantics as RecallInput.Home.
	Home  string
	Query string
	// Limit caps how many recalled memories are given to the reader (default 10).
	Limit int
	Tiers []memory.Tier
	// Levels restricts grounding to memories whose derivation level matches one of
	// the listed values; empty means no level constraint.
	Levels   []memory.Level
	Tags     []string
	Metadata map[string]string
	// Reasoning selects the answer strategy: empty/minimal is single-shot;
	// low/medium/high run the bounded tool loop (see ReasoningLevel). Falls
	// back to single-shot when the configured LLM client can't do tool calls.
	Reasoning ReasoningLevel
	// Scope selects the grounding read-set shape, same vocabulary and
	// semantics as RecallInput.Scope: "" or "full" (default: Namespace +
	// ancestors + home + links), "project" (Namespace only, no cascade), or
	// "everywhere" (full + subtree). Threads into every recall this answer
	// performs — single-shot, expand's per-rewrite recalls, and the agentic
	// loop's prefetch and search_memory/recall_as_of tool recalls — the same
	// way Home does. An unrecognized value is an invalid-input error,
	// rejected up front before any LLM call.
	Scope string
	// ReadSet (output-only) is set to the STRUCTURAL read-set this answer can
	// draw from — same out-param pattern as RecallInput.ReadSet, but resolved
	// once up front (see Answer) with the tier-independent default cascade
	// (ResolveReadSetInfo semantics), NOT filtered by Tiers and NOT narrowed
	// by Scope. A namespace's origin (primary/ancestor/home/link) is a
	// structural property: tiers decide what gets SEARCHED, not what a
	// namespace IS, and the agentic tool loop overrides tiers per
	// search_memory call (tier="durable"), so its inner recalls can legally
	// reach cascade legs a Tiers-filtered resolution would have skipped.
	// Scope-independence keeps the labels correct too: Scope "project"
	// shrinks the inner recalls' reach (a structural superset is harmless for
	// labeling), and Scope "everywhere" only adds subtree members — which
	// carry origin "primary" and render with no "from" annotation, exactly
	// what an absent-from-the-map namespace renders anyway (see ReadSetFrom).
	// The caller passes the address of a local slice; nil disables reporting.
	ReadSet *[]ReadSetEntry
}

AnswerInput is a retrieve-then-generate request.

type AnswerResult added in v0.0.4

type AnswerResult struct {
	Answer  string
	Sources []store.Scored
}

AnswerResult is the generated answer and the memories it was grounded on.

type Briefing added in v0.0.11

type Briefing struct {
	Namespace string `json:"namespace"`
	// ScopeHeader is a one-line, human-readable summary of the read-set this
	// briefing drew from: the primary namespace, then each cascade leg that
	// actually contributed durable memories (nearest ancestor first, home
	// last), then a "+K link(s)" suffix counting contributing links. See
	// scopeHeader for the exact format and edge-case decisions.
	ScopeHeader string           `json:"scope_header,omitempty"`
	Facts       []*memory.Memory `json:"facts,omitempty"`      // semantic, highest-retention first
	Procedures  []*memory.Memory `json:"procedures,omitempty"` // procedural, highest-retention first
	Recent      []*memory.Memory `json:"recent,omitempty"`     // episodic, newest first
	Pinned      []*memory.Memory `json:"pinned,omitempty"`     // tagged pinned, any tier
	// Children summarizes the direct child namespaces (one segment deeper)
	// under the primary namespace, each aggregating its whole subtree —
	// most-recent write first, capped at childRollupMaxChildren. Empty at a
	// leaf namespace.
	Children []ChildSummary `json:"children,omitempty"`
	// ChildrenTruncated is the number of direct children omitted by the
	// childRollupMaxChildren cap (0 when everything fit). The REST wire shape
	// (T6) has no dedicated field for it, so renderers surface it themselves
	// (MCP appends an "… and N more" note; REST returns just the capped array).
	ChildrenTruncated int `json:"children_truncated,omitempty"`
}

Briefing is a layered session-start summary of a namespace: the most durable facts and procedures, the most recent episodic activity, and pinned memories.

type BriefingOpts added in v0.4.4

type BriefingOpts struct {
	Pinned     *int
	Facts      *int
	Procedures *int
	Recent     *int
	// Namespaces, when non-empty, REPLACES the default read set (namespace,
	// subtree, ancestors, home, and links) with exactly these namespaces —
	// same replace-not-extend semantics as RecallInput.Namespaces. Each is
	// read with all tiers.
	Namespaces []string
	// Subtree expands the briefing to namespace and every namespace nested
	// under it, same semantics as RecallInput.Subtree. Ignored when Namespaces
	// is set.
	Subtree bool
	// Home is the caller's personal namespace, merged read-only into the
	// default read set — durable tiers only. See RecallInput.Home.
	Home string
	// Scope selects the read-set shape, same semantics as RecallInput.Scope:
	// "" or "full" (default), "project" (bare), or "everywhere" (+ subtree).
	// Ignored when Namespaces is set. An unrecognized value is an
	// invalid-input error.
	Scope string
	// ReadSet (output-only) is set to the resolved read-set this briefing
	// drew from, with per-leg origin — same out-param pattern as
	// RecallInput.ReadSet. The caller passes the address of a local slice;
	// nil disables reporting.
	ReadSet *[]ReadSetEntry
}

BriefingOpts sets per-section caps for a Briefing. A nil field falls back to DefaultPerSection (5); a pointer to 0 explicitly disables the section so callers can opt sections out without rebalancing the others. Section caps are independent: pinned memories count against Pinned and never against Facts/Procedures/Recent, so an operator can keep a small durable "top-of-mind" set always-injected while still capping the per-section recall.

type ChildSummary added in v0.6.6

type ChildSummary struct {
	NS     string           `json:"namespace"`
	Total  int              `json:"total"`
	Pinned []*memory.Memory `json:"pinned,omitempty"`
	Recent []*memory.Memory `json:"recent,omitempty"`
}

ChildSummary is one direct-child rollup entry in a Briefing: the child namespace, its all-tier live memory count, and small pinned/recent-durable highlight sets (each capped at childRollupPerSection). All figures aggregate the child's entire subtree, so a leaf-heavy tree (memories only in grandchildren) still surfaces at the interior node.

type ConsolidateMode

type ConsolidateMode string

ConsolidateMode selects how the opt-in LLM consolidation pipeline runs.

const (
	// ConsolidateAsync stores writes immediately and consolidates in the
	// background — writes never block on the LLM. The default.
	ConsolidateAsync ConsolidateMode = "async"
	// ConsolidateSync consolidates before returning, so a write reflects its
	// dedup/supersede outcome immediately (read-your-consolidated-writes).
	ConsolidateSync ConsolidateMode = "sync"
	// ConsolidateOff disables consolidation even when a consolidator is set.
	ConsolidateOff ConsolidateMode = "off"
)

type DedupInput added in v0.0.8

type DedupInput struct {
	// Similarity gates cluster membership. 0 falls back to the package
	// default (0.85). Negative disables the pass and Dedup returns an empty
	// report without erroring.
	Similarity float64
	// MinClusterSize is the smallest cluster acted on. 0 falls back to 2.
	MinClusterSize int
	// Tiers restricts the pass to these tiers; nil/empty means all tiers.
	Tiers []memory.Tier
	// Namespaces restricts the pass to these namespaces; nil/empty means every
	// namespace. API callers scope this to the request's namespace; only an
	// explicit all-namespaces request leaves it empty.
	Namespaces []string
	// NeighboursPerAnchor bounds the per-anchor vector-search fan-out.
	// 0 falls back to 20.
	NeighboursPerAnchor int
	// DryRun reports what would be done without tombstoning anything.
	DryRun bool
}

DedupInput configures a dedup pass invoked through the service. The zero value is valid and means "use the production defaults": 0.85 similarity, cluster size >= 2, all tiers, 20 neighbours per anchor, dry-run = false.

type EventsInput added in v0.6.8

type EventsInput struct {
	// Namespace restricts the feed to one namespace; "" means every namespace.
	Namespace string
	// Namespaces narrows an all-namespaces feed to these namespaces (OR);
	// ignored when Namespace is set.
	Namespaces []string
	// Kinds restricts to these event kinds; empty means all.
	Kinds []store.EventKind
	// Actor restricts to events performed by the named API key (exact match);
	// empty means no constraint.
	Actor string
	// Tiers restricts to operations that touched a memory of one of these tiers.
	Tiers []memory.Tier
	// Text restricts to operations whose query or a served memory's summary
	// contains it, case-insensitively.
	Text string
	// Since restricts to events at or after the instant.
	Since time.Time
	// Before/BeforeID is the keyset cursor from a previous page.
	Before   time.Time
	BeforeID int64
	// Limit caps the returned operations (not rows); <= 0 uses the default.
	Limit int
}

EventsInput selects a page of the activity feed.

Tiers and Text select whole operations rather than individual memories — see store.EventFilter — so a filtered recall still reports everything it served.

type EventsPage added in v0.6.8

type EventsPage struct {
	Events       []ActivityEvent
	NextBefore   time.Time
	NextBeforeID int64
	HasMore      bool
}

EventsPage is one page of the feed, with the cursor for the next.

type ListInput

type ListInput struct {
	Namespace string
	Tiers     []memory.Tier
	// Levels restricts the listing to memories whose derivation level matches one
	// of the listed values; empty means no level constraint.
	Levels []memory.Level
	// Tags narrows the listing to memories carrying every listed tag (AND).
	Tags []string
	// Metadata narrows the listing to memories whose top-level metadata contains
	// each listed key=value string pair (AND).
	Metadata map[string]string
	// MemoryTypes narrows to memories whose metadata.memory_type is any of the
	// listed values (OR) — the multi-select the browser's type filter needs,
	// which Metadata's AND-one-value-per-key semantics cannot express.
	MemoryTypes []string
	// CreatedAfter/AccessedAfter narrow to memories created / last accessed at or
	// after the instant. Zero means no constraint.
	CreatedAfter      time.Time
	AccessedAfter     time.Time
	IncludeExpired    bool
	IncludeSuperseded bool
	// Sort orders the listing; the zero value is newest-created first.
	Sort store.Sort
	// Limit caps the result count; <= 0 returns all matches.
	Limit int
	// AllNamespaces lists across every namespace instead of in.Namespace, with
	// Limit applied as a single global cap under Sort. Backs the admin UI's
	// "All projects" view.
	AllNamespaces bool
	// Namespaces, with AllNamespaces, restricts the aggregate to these namespaces
	// (exact match); empty means every namespace. Ignored without AllNamespaces.
	Namespaces []string
}

ListInput selects a slice of a namespace's memories for browsing. The zero value (besides Namespace) lists all live memories, newest store order.

type MergeHint added in v0.4.19

type MergeHint struct {
	// SimilarID is the id of the near-duplicate memory. Empty when unknown.
	SimilarID string
	// SimilarContent is a preview of the near-duplicate memory's content.
	SimilarContent string
	// Score is the fused similarity (0..1) between the new write and the
	// near-duplicate.
	Score float64
	// Tier is the tier of the near-duplicate.
	Tier memory.Tier
}

MergeHint surfaces a near-duplicate the caller may want to merge into.

type Metrics

type Metrics interface {
	// ConsolidateResult records one consolidation outcome: one of
	// "gated", "new", "update", "supersede", "noop", "error", "dropped".
	ConsolidateResult(result string)
	// ConsolidateQueueDepth reports the current async queue depth.
	ConsolidateQueueDepth(depth int)
	// RememberResult records the outcome of a Remember call: result is
	// "ok"|"error" and tier is the memory's tier.
	RememberResult(result, tier string)
	// RecallResult records the outcome of a Recall call. result is
	// "ok"|"error"; tierFilter is one of "all"|"working"|"episodic"|
	// "semantic"|"procedural"|"mixed"; hitsBucket is a pre-bucketed
	// count of returned memories: "0"|"1"|"2-5"|"6-20"|"21+".
	RecallResult(result, tierFilter, hitsBucket string)
	// ForgetResult records the outcome of a Forget call: "ok"|"not_found"|"error".
	ForgetResult(result string)
	// SupersedeResult records the outcome of a Supersede call:
	// "ok"|"not_found"|"error". Supersede tombstones a memory (sets
	// superseded_by) rather than deleting it; the maintenance sweeper
	// hard-deletes tombstoned rows after TombstoneTTL.
	SupersedeResult(result string)
	// PromoteResult records one Promote batch: result is "ok"|"error";
	// facts is the number of semantic facts written.
	PromoteResult(result string, facts int)
	// FsckResult records one fsck pass: "ok"|"error". Counters for the
	// work done (purged, evicted, duplicate groups) are exposed separately
	// via the store's maintenance metrics.
	FsckResult(result string)
	// OpDuration observes end-to-end latency for a public operation
	// (e.g. "recall", "answer").
	OpDuration(op string, d time.Duration)
	// AnswerResult records one Answer call: "ok" or "error".
	AnswerResult(result string)
	// RerankResult records one recall rerank attempt: backend is the reranker's
	// label ("llm"|"cross_encoder"); result is "ok" or "fallback".
	RerankResult(backend, result string)
	// RecallDegraded records one recall that fell back to keyword-only search
	// because the query embed failed or timed out. reason is "embed_timeout" or
	// "embed_error".
	RecallDegraded(reason string)
	// RememberDegraded records one write that stored without a vector (embedding
	// omitted, keyword-searchable only, marked pending_embed) because the content
	// embed failed or timed out. reason is "embed_timeout" or "embed_error".
	RememberDegraded(reason string)
	// WriteSanitized records one ingestion content-hygiene action: "cleaned"
	// (unambiguous corruption stripped from content) or "quarantined"
	// (script-salad downranked when corruption quarantine is enabled).
	WriteSanitized(action string)
	// ReinforceResult records one best-effort recall reinforcement write:
	// "ok" or "error".
	ReinforceResult(result string)
	// DedupTombstoned records the total memories tombstoned by one one-shot
	// Service.Dedup call. Called once per call.
	DedupTombstoned(n int)
	// CorroborateResult records one corroboration-routing attempt on a fresh
	// short-term write: "corroborated" (durable fact reinforced), "cooldown"
	// (match found but inside the per-fact window), "miss" (no durable
	// neighbour at or above the threshold), or "error".
	CorroborateResult(result string)
	// ContradictResult records one contradiction-routing attempt on a fresh
	// durable write: "contradicted" (stale fact invalidated), "no_signal" (a
	// near neighbour, but the detector saw no value/polarity change), "cooldown"
	// (match inside the per-fact window), "miss" (no durable neighbour at or
	// above the threshold, or an untracked-confidence row), or "error".
	ContradictResult(result string)
	// TierClassified records an omitted-tier write the marker classifier
	// routed to a durable tier; tier is "semantic" or "procedural".
	TierClassified(tier string)
	// EmbedBackfillPending reports the number of memories still marked
	// pending_embed after one backfill tick (0 once the queue is drained).
	EmbedBackfillPending(n int)
}

Metrics receives service-level events for observability. Methods must be safe for concurrent use; a nil Metrics is replaced by a no-op.

type Option

type Option func(*Service)

Option customizes a Service.

func WithAnswerer added in v0.0.4

func WithAnswerer(c llm.Completer) Option

WithAnswerer enables Answer: recall memories, then generate a grounded answer from them with this chat client.

func WithCascade added in v0.6.6

func WithCascade(on bool) Option

WithCascade toggles the ancestor/home/link read cascade (default on). When off, the default read set is the request namespace (and its subtree, when asked) only — pre-cascade isolation — and a Scope of "full"/"everywhere" no longer adds the ancestor, home, or link legs. An explicit per-call Namespaces list is unaffected (it already replaces the cascade outright). See MEMINI_CASCADE.

func WithClock

func WithClock(now func() time.Time) Option

WithClock overrides the time source (tests).

func WithConsolidateMinScore

func WithConsolidateMinScore(minScore float64) Option

WithConsolidateMinScore sets the similarity gate: the LLM is only consulted when the nearest candidate scores at least minScore. 0 disables the gate.

func WithConsolidateMode

func WithConsolidateMode(m ConsolidateMode) Option

WithConsolidateMode selects async (default), sync, or off.

func WithConsolidator

func WithConsolidator(c llm.Consolidator) Option

WithConsolidator enables the opt-in LLM consolidation pipeline.

func WithContradictionDownrank added in v0.5.6

func WithContradictionDownrank(minScore float64) Option

WithContradictionDownrank enables contradiction routing: a fresh durable write whose nearest durable neighbour scores at or above minScore, and which the lexical detector (internal/contradict) confirms is a value/polarity change rather than a restatement, invalidates that stale neighbour — stamping its valid_to (so it leaves live recall but stays reachable via AsOf) and shrinking its confidence, off the request path and rate-limited by contradictCooldown. The new write is stored unchanged. minScore <= 0 disables.

func WithCorroboration added in v0.5.4

func WithCorroboration(minScore float64) Option

WithCorroboration enables corroboration routing: a fresh short-term write whose nearest durable neighbour scores at or above minScore reinforces that fact and grows its confidence (rate-limited by corroborateCooldown) instead of only piling up as chatter. The write is still stored. minScore <= 0 disables.

func WithCorruptionQuarantine added in v0.4.11

func WithCorruptionQuarantine(on bool) Option

WithCorruptionQuarantine toggles downranking of writes whose content looks like script-salad — garbled multilingual output from an upstream model or harness glitch (off by default). When on, a flagged write is still stored but has its importance zeroed and metadata.quarantined set, so it sinks in recall instead of surfacing verbatim. It is a heuristic and can misjudge rare legitimate mixed-script text, so it only downranks (never rejects); leave it off unless garbled digests are a problem for a deployment.

func WithDistillBatch added in v0.5.9

func WithDistillBatch(maxTokens int, maxAge time.Duration) Option

WithDistillBatch batches distill-on-write per (namespace, session_id): captures accumulate until their estimated tokens reach maxTokens or the oldest has waited maxAge, then distill as one LLM call with cross-turn context. maxTokens <= 0 disables batching (per-capture distill); captures without a session_id always use the per-capture path. Crash-safe by construction: sources are already durably stored and are stamped promoted_at only at flush, so a lost buffer stays eligible for the batch promoter.

func WithDistillDropNoFact added in v0.4.14

func WithDistillDropNoFact(on bool) Option

WithDistillDropNoFact, with WithDistillOnWrite, deletes an episodic capture when distillation extracts no durable fact. Off by default; not wired to a server flag (the server always keeps episodic captures).

func WithDistillOnWrite added in v0.4.14

func WithDistillOnWrite(on bool) Option

WithDistillOnWrite distils each fresh episodic capture into durable facts at write time. No-op without a distiller, so the server always enables it and lets LLM presence decide whether it runs.

func WithDistiller

func WithDistiller(d llm.Distiller) Option

WithDistiller enables episodic→semantic promotion via RunPromoter.

func WithEpisodicMinChars added in v0.4.14

func WithEpisodicMinChars(n int) Option

WithEpisodicMinChars drops episodic writes whose substantive content is below n characters. 0 (the default) disables it. See MEMINI_EPISODIC_MIN_CHARS.

func WithEventLog added in v0.6.8

func WithEventLog(on bool) Option

WithEventLog records reads and writes to the activity log (see events.go). A no-op against a driver that does not implement store.EventLogStore.

func WithExtractOnWrite added in v0.4.20

func WithExtractOnWrite(on bool) Option

WithExtractOnWrite runs each fresh episodic capture through the no-LLM heuristic extractor. Only fires when no distiller is configured, so the server always enables it and lets LLM absence decide whether it runs.

func WithFingerprintDedup added in v0.2.9

func WithFingerprintDedup(on bool) Option

WithFingerprintDedup toggles exact-restatement dedup: when on (the default), a fresh write whose normalized content exactly matches a live same-tier memory reinforces that memory instead of storing a duplicate, without embedding it. It is independent of WithWriteDedup (the fuzzy vector gate) and the LLM consolidation pipeline.

func WithIDGenerator

func WithIDGenerator(gen func() string) Option

WithIDGenerator overrides ID generation (tests).

func WithMetrics

func WithMetrics(m Metrics) Option

WithMetrics installs an observability sink for consolidation events.

func WithPromoteMinAccess

func WithPromoteMinAccess(n int) Option

WithPromoteMinAccess sets the minimum access_count for an episodic memory to be eligible for promotion.

func WithQueryPrefix

func WithQueryPrefix(p string) Option

WithQueryPrefix prepends an instruction to recall queries before embedding (e.g. the retrieval instruct expected by Qwen3-Embedding or bge models). Documents keep bare embeddings; the keyword leg keeps the raw query.

func WithRecallEmbedTimeout added in v0.4.0

func WithRecallEmbedTimeout(d time.Duration) Option

WithRecallEmbedTimeout bounds the query embed on the recall path. Past the deadline, or on any embed error, recall degrades to keyword-only search rather than failing or stalling on a slow embeddings backend. d <= 0 keeps the query embed unbounded and an embed error fatal (the default).

func WithRecallMinScore added in v0.4.3

func WithRecallMinScore(minScore float64) Option

WithRecallMinScore sets an absolute relevance floor on the fused score: candidates below the threshold are dropped before composite re-ranking and before the reranker. 0 (the default) disables filtering. For score fusion (alpha >= 0) the fused score is in [0,1]; for RRF it is a small rank-based value (~0.016 for the top position). Baked to 0.1 by the server; the benchmark harness overrides it via this Option.

func WithRecallMinSemanticScore added in v0.4.13

func WithRecallMinSemanticScore(minSemanticScore float64) Option

WithRecallMinSemanticScore sets an absolute relevance floor on the raw vector (semantic) score: a candidate below the floor is excluded entirely, so the keyword leg cannot reintroduce an off-topic memory on a shared token. 0 (the default) disables it. The usable value is embedder-dependent; baked to 0 (off) by the server, overridden by the benchmark harness via this Option.

func WithRecallPool

func WithRecallPool(factor, floor int) Option

WithRecallPool overrides the per-leg candidate pool sizing (max(k*factor, floor)) for hybrid recall. Non-positive values keep the defaults. Used by the benchmark harness to sweep pool depth.

func WithRecallRewriteTimeout added in v0.5.12

func WithRecallRewriteTimeout(d time.Duration) Option

WithRecallRewriteTimeout bounds the LLM query-expansion call on query_rewrite recalls. Past the deadline, expansion yields just the original query and recall falls through to normal single-query recall, rather than blocking on the LLM client's much longer HTTP timeout. d <= 0 keeps the rewrite call unbounded (the default).

func WithRecallSemanticReserve added in v0.4.14

func WithRecallSemanticReserve(n int) Option

WithRecallSemanticReserve reserves up to n of the recall slots for durable tiers (semantic/procedural); a durable takes a slot only when it is relevance-competitive with the entry it displaces (reservePromoteRatio). 0 (the default) disables it. Baked to 2 by the server; the benchmark harness overrides it via this Option.

func WithReinforceSkipMarkers added in v0.4.12

func WithReinforceSkipMarkers(on bool) Option

WithReinforceSkipMarkers drops session-end / stop marker memories from recall reinforcement. The pre-tool-use hook searches once per edited file, so markers would otherwise inflate their access_count and TTL out of proportion. They stay searchable; only the reinforce write is skipped.

func WithRerankPool added in v0.6.8

func WithRerankPool(n int) Option

WithRerankPool sets how many composite-ranked candidates are handed to the reranker before the result is truncated to the caller's limit. n <= 0 (the default) reranks exactly the limit, which reorders the result set but can never surface a memory the vector and keyword legs ranked below it.

A cross-encoder scores query and document together, so it is a far better judge of relevance than the fused retrieval score — but only over candidates it is shown. Recall already retrieves RecallPoolSize candidates per leg, so a deeper pool costs reranker time, not another search. That cost is linear: one model forward pass per candidate.

func WithRerankTimeout added in v0.2.11

func WithRerankTimeout(d time.Duration) Option

WithRerankTimeout bounds a single reranker call; at the deadline recall degrades to composite order. d <= 0 keeps the default.

func WithReranker added in v0.0.4

func WithReranker(r rerank.Reranker, name string) Option

WithReranker enables reranking of recall candidates: after composite ranking, the top k candidates are reordered by the reranker (an LLM or cross-encoder model), then truncated to the limit. name labels the backend in metrics. It adds one reranker call per Recall, so it is opt-in; a failed rerank falls back to the composite order.

func WithReserveGatePercentile added in v0.5.5

func WithReserveGatePercentile(pct float64) Option

WithReserveGatePercentile (pct > 0) switches the reserve's relevance gate to the adaptive form: a durable takes a reserved slot only when its composite score reaches the pct-th percentile of the window's own scores, so the bar derives from the pool's score distribution instead of a fixed ratio. Tuning/bench knob (bench/reserve_sweep_test.go); 0 keeps the ratio gate.

func WithReservePromoteRatio added in v0.5.5

func WithReservePromoteRatio(ratio float64) Option

WithReservePromoteRatio overrides the evictee-relative leg of the reserve's relevance gate: a durable takes a reserved slot only when its composite score is at least ratio× the entry it evicts. Tuning/bench knob (bench/reserve_sweep_test.go); the production default is defaultReservePromoteRatio.

func WithReserveTopAnchor added in v0.5.5

func WithReserveTopAnchor(anchor float64) Option

WithReserveTopAnchor overrides the absolute leg of the reserve's relevance gate: a durable takes a reserved slot only when its composite score is at least anchor× the window's top hit. Tuning/bench knob (bench/reserve_sweep_test.go); the production default is defaultReserveTopAnchor, and 0 disables the leg.

func WithScoreFusion

func WithScoreFusion(alpha float64) Option

WithScoreFusion sets the hybrid fusion weight: the vector leg by alpha and the keyword leg by 1-alpha (score fusion). alpha < 0 selects rank fusion (RRF). The package default is score fusion at DefaultFusionAlpha.

func WithSecretRedaction added in v0.3.8

func WithSecretRedaction(on bool) Option

WithSecretRedaction toggles server-side scrubbing of live credentials from a memory's Content/Summary/Metadata at ingestion (on by default). It bounds a database compromise to information disclosure — leaked memory holds no usable tokens, keys, or passwords. Disable only if redaction mangles legitimate content; storing raw secrets re-opens the lateral-movement risk.

func WithShortTermCap

func WithShortTermCap(cap int) Option

WithShortTermCap bounds short-term memories per namespace, enforced by fsck.

func WithSplitDedupLLMMerge added in v0.5.12

func WithSplitDedupLLMMerge(b bool) Option

WithSplitDedupLLMMerge enables the opt-in LLM merge path in the split-dedup pipeline: when ≥2 candidates score above writeDedupScore and are within 0.05 of each other, the LLM consolidator is consulted for a merge/supersede verdict before the deterministic action fires. Default off — requires a consolidator (WithConsolidator) to have any effect.

func WithSyncEventLog added in v0.6.8

func WithSyncEventLog() Option

WithSyncEventLog makes activity-log writes run synchronously (tests).

func WithSyncReinforce

func WithSyncReinforce() Option

WithSyncReinforce makes recall reinforcement run synchronously (tests).

func WithTemporalTargeting added in v0.0.4

func WithTemporalTargeting(boost float64, ex search.AnchorExtractor) Option

WithTemporalTargeting enables temporal targeting in the re-ranker: when a query names a relative time, candidates dated near the referenced point are boosted by up to `boost` on the composite score. ex resolves the reference (use search.RegexAnchorExtractor{} for the no-LLM default). boost <= 0 or a nil extractor disables it.

func WithTurnEchoWindow added in v0.5.11

func WithTurnEchoWindow(d time.Duration) Option

WithTurnEchoWindow sets the server-wide default temporal exclusion window for freshly-captured episodic turns. It fires by default on every recall; a caller opts out via IncludeFreshTurns. Zero disables it server-wide.

func WithWriteDedup

func WithWriteDedup(score float64, action WriteDedupAction) Option

WithWriteDedup configures write-time dedup: when a fresh write's nearest same-tier memory scores at or above score, the given action fires (see WriteDedupAction). A score of 0 or action "off" disables it. This replaces the former three-gate band system — there is no ordering to misconfigure.

func WithWriteEmbedTimeout added in v0.5.12

func WithWriteEmbedTimeout(d time.Duration) Option

WithWriteEmbedTimeout bounds the content embed on the remember path. Past the deadline, or on any embed error, the write degrades to a vectorless (keyword-searchable only) row marked pending_embed rather than failing or stalling on a slow embeddings backend. d <= 0 keeps the content embed unbounded and an embed error fatal (the default).

type ReadSetEntry added in v0.6.6

type ReadSetEntry struct {
	NS     string
	Origin string
	Tiers  []memory.Tier
}

ReadSetEntry is the public shape of one resolved read-set leg: the namespace, why it's in the read-set (Origin, one of the Origin constants above), and any tier restriction applied to it (nil = the request's own tier filter). Recall and Briefing expose the resolved read-set through this type via their ReadSet out-param (mirroring the Degraded out-param pattern), and ResolveReadSetInfo returns it directly for the read-set introspection endpoint (T6).

type ReasoningLevel added in v0.5.9

type ReasoningLevel string

ReasoningLevel selects the answer strategy: empty/minimal is the single-shot recall+complete path; expand is one query-rewrite completion plus a unioned multi-query recall and one synthesis (no tool loop); low/medium/high run a bounded tool loop where the model may search memory again before answering — the latency/cost dial for multi-hop and temporal questions.

const (
	ReasoningMinimal ReasoningLevel = "minimal"
	ReasoningExpand  ReasoningLevel = "expand"
	ReasoningLow     ReasoningLevel = "low"
	ReasoningMedium  ReasoningLevel = "medium"
	ReasoningHigh    ReasoningLevel = "high"
)

type RecallInput

type RecallInput struct {
	Namespace string
	Query     string
	// Source is the "why" behind this recall — which integration or code path
	// asked for it (documented vocabulary: "pretool", "session_start", "mcp",
	// "ui", "api", "answer", "doctor"). It is recorded verbatim in the recall
	// event's detail (including the zero-hit sentinel), never validated, so an
	// unknown value from a fail-soft client is logged rather than rejected.
	// "" means the caller supplied none (the event omits it).
	Source string
	Tiers  []memory.Tier
	// Levels restricts recall to memories whose derivation level matches one of the
	// listed values; empty means no level constraint.
	Levels []memory.Level
	// Tags narrows recall to memories carrying every listed tag (AND).
	Tags []string
	// Metadata narrows recall to memories whose top-level metadata contains each
	// listed key=value string pair (AND).
	Metadata map[string]string
	// ExcludeMetadata drops memories whose top-level metadata contains every
	// listed key=value pair (AND), applied after Metadata. Lets a caller exclude
	// its own session's just-captured turns from auto-recall.
	ExcludeMetadata map[string]string
	// ExcludeIDs drops memories with the listed ids, before ranking and Limit,
	// so an excluded hit never consumes a result slot. Lets a long-lived client
	// keep memories it has already injected out of recall and still receive the
	// next-best fresh hits. Capped at maxRecallExcludeIDs entries.
	ExcludeIDs []string
	// IncludeFreshTurns, when true, disables the server-side temporal echo
	// guard for this call: just-captured episodic turns (metadata.format="turn"
	// younger than the server's turnEchoWindow) are NOT dropped. Default
	// (false) means the guard fires — a just-captured turn is still live
	// context and must not be recalled back as long-term memory. Opt out only
	// when a caller genuinely needs fresh turns (e.g. a "what did I just say"
	// debug query).
	IncludeFreshTurns bool
	// QueryRewrite, when true and an LLM answerer is configured, rewrites the
	// query into 2-3 diverse variants before recall and fuses the results via
	// RRF. Cheapest read-path LLM lever; opt-in per call. No-op when no answerer
	// is configured (falls through to single-query recall).
	QueryRewrite bool
	Limit        int
	// IncludeExpired / IncludeSuperseded relax the default live-only filter.
	IncludeExpired    bool
	IncludeSuperseded bool
	// AsOf, when non-zero, runs time-travel recall: it returns the facts whose
	// validity window contained AsOf (including ones since superseded), instead
	// of only currently-live memories.
	AsOf time.Time
	// Subtree expands recall to Namespace and every namespace nested under it
	// ("project" also reads "project/agent-a", "project/agent-b", ...), for the
	// multi-agent "read shared + private" pattern. Default (false) is exact scope,
	// so cross-agent recall never happens unless asked for.
	Subtree bool
	// Namespaces, when non-empty, REPLACES the default read set (Namespace +
	// ancestors + home + links, optionally + subtree) with exactly these
	// namespaces — no cascade merge, no subtree of Namespace unless an entry
	// spells it with "/*". Wins over Scope regardless of its value. An entry
	// ending in "/*" also includes every namespace nested under it. Max 16
	// entries; each is searched with the request's own tier filter (Tiers).
	Namespaces []string
	// Home is the caller's personal namespace (from the X-Memini-Home
	// header), merged read-only into the default read set — durable tiers
	// only, like an ancestor. Empty means no home leg.
	Home string
	// Scope selects the read-set shape: "" or "full" (default: Namespace +
	// ancestors + home + links), "project" (Namespace only, no cascade), or
	// "everywhere" (full + subtree). An unrecognized value is an invalid-input
	// error. Namespaces, when set, replaces the cascade outright regardless of
	// Scope (explicit beats scope). Subtree (legacy) still works standalone;
	// Scope "everywhere" is equivalent to Subtree: true.
	Scope string
	// MinScore, when > 0, overrides the server's default recallMinScore for
	// this call. Lets a caller request a stricter relevance floor per
	// integration (e.g. the pre-tool-use hook only injects highly-relevant
	// hits). 0 (the zero value) falls back to the server-wide gate. Only
	// meaningful with score fusion; RRF scores are not comparable to [0,1].
	MinScore float64
	// MinSemanticScore, when > 0, overrides the server's default
	// recallMinSemanticScore (the absolute vector-relevance gate) for this call.
	// 0 falls back to the server-wide gate.
	MinSemanticScore float64
	// SemanticReserve, when > 0, overrides the server's default
	// recallSemanticReserve (durable-tier slot reservation) for this call. 0
	// falls back to the server-wide value.
	SemanticReserve int
	// Degraded (output-only) is set to the degradation reason
	// ("embed_error"/"embed_timeout") when this recall fell back to
	// keyword-only search because the query embed failed or timed out. The
	// caller passes the address of a local string; it is left untouched
	// (empty) on a healthy recall. nil disables reporting. Same out-param
	// pattern as MergeHint/AutoSuperseded on RememberInput.
	Degraded *string
	// ReadSet (output-only) is set to the resolved read-set — every namespace
	// this recall searched, with the origin recorded when that leg was
	// appended during resolution (primary/ancestor/home/link/call) and any
	// per-namespace tier restriction. The caller passes the address of a
	// local slice; it is left untouched (nil) when ReadSet is nil. Same
	// out-param pattern as Degraded — lets MCP/REST render read-set
	// provenance (e.g. "from: acme") without a second resolveReadSet call.
	ReadSet *[]ReadSetEntry
	// IncludeLinked, when true, expands recall to include memories linked to
	// any result via LinkedMemoryIDs (1-hop expansion). Linked memories that
	// are superseded are skipped. Default (false) is no expansion.
	IncludeLinked bool
}

RecallInput describes a hybrid recall query.

type RememberInput

type RememberInput struct {
	Namespace string
	// Home is the caller's personal namespace (X-Memini-Home / MEMINI_HOME).
	// Consumed by resolveVisibility when Visibility is "personal".
	Home string
	// Visibility steers which namespace the write actually lands in: ""/
	// "project" (default) is Namespace itself; "personal" is Home; anything
	// else must name an ancestor of Namespace (by exact path or unambiguous
	// last segment). See resolveVisibility for the full resolution and the
	// tier clamp (episodic/working writes always stay in Namespace).
	Visibility string
	Content    string
	Tier       memory.Tier
	Summary    string
	Tags       []string
	Metadata   map[string]any
	Importance float64
	// TTL overrides the tier default. A negative TTL means "never expire".
	TTL *time.Duration
	// ID upserts an existing memory when set; otherwise a new ID is generated.
	ID string
	// Confidence overrides the seed corroboration for a durable fact (e.g. a
	// trusted import). nil uses the default seed. Ignored for short-term tiers.
	Confidence *float64
	// ValidFrom / ValidTo set the interval the fact was true, for recording
	// historical facts that time-travel (AsOf) recall can surface. ValidFrom
	// defaults to now (or the existing row on update); ValidTo defaults to open.
	ValidFrom *time.Time
	ValidTo   *time.Time
	// Level labels the derivation provenance at write time: explicit (user-stated
	// or heuristic) vs deduced (LLM-distilled). Empty string means legacy/unknown
	// and falls through to "no constraint" in filter operations.
	Level memory.Level
	// MergeHint (output-only) is set to a non-nil MergeHint when the write's
	// nearest same-tier candidate landed in the merge-hint band. The caller
	// passes the address of a local `*MergeHint`; after the call it holds the
	// hint (or remains nil). nil disables hint reporting.
	MergeHint *MergeHint
	// AutoSuperseded (output-only) is set to true when the write triggered a
	// background supersede. The caller passes the address of a local bool.
	// nil disables reporting.
	AutoSuperseded *bool
	// Reinforced (output-only) is set to true when the write did NOT create a new
	// memory: the fact was already known, so the existing memory was strengthened
	// (reinforced/corroborated) and returned instead.
	//
	// This matters because Remember returns a non-nil Memory on those paths, so a
	// caller cannot otherwise tell "I stored a new fact" from "that was already
	// known". Two paths reach it: the exact-restatement fingerprint fast path, and
	// the write-dedup coalesce action when the incoming phrasing is not richer
	// than the stored one. An agent told `stored: true` in either case believes it
	// created something it did not, and the id it gets back belongs to a memory it
	// did not write — which is exactly the memory it would then go and clobber.
	//
	// The caller passes the address of a local bool; nil disables reporting.
	Reinforced *bool
	// Author names the NAMED API key that authenticated this write (set by
	// the REST/MCP handlers from the request principal — see
	// internal/api/rest's principalFromContext / internal/apiauth.Principal).
	// "" for the admin key or an unauthenticated/auth-disabled request, which
	// stamp no author at all. stampAuthor writes this into
	// metadata["author"], but only when the caller hasn't already set one —
	// see its doc.
	Author string
}

RememberInput describes a memory to store. Only Namespace and Content are required; an omitted Tier is classified from the content (episodic when unclear) and TTL follows the tier default.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service wires storage and embeddings together. It is safe for concurrent use.

func New

func New(st store.Store, e embed.Embedder, opts ...Option) *Service

New builds a Service from a store and embedder.

func (*Service) Answer added in v0.0.4

func (s *Service) Answer(ctx context.Context, in AnswerInput) (AnswerResult, error)

Answer recalls memories for the query and asks the configured LLM to answer from them, grounding the response and returning the supporting memories. It requires an answerer (see WithAnswerer); recall reuses the full hybrid + rerank path, so a configured reranker applies here too.

func (*Service) BackfillEmbeddings added in v0.5.12

func (s *Service) BackfillEmbeddings(ctx context.Context) (int, error)

BackfillEmbeddings re-embeds memories left vectorless by a degraded write (metadata pending_embed="true") directly against the store, bypassing Remember: this is a repair of an existing row's vector, not a fresh write, so re-running scrubbing, tier classification, or the episodic value gate could mutate or drop a row that already passed those gates once. Deferred similarity jobs (dedup/corroborate/contradict) are deliberately NOT re-run here -- re-entering them outside Remember's write ordering risks touching rows this pass has no business touching. A backfilled fact simply rejoins those jobs the next time it (or something similar) is naturally restated; an acceptable v1 tradeoff over the complexity of re-triggering them safely.

Processes at most backfillBatch rows per tick across all namespaces. If the very first row in a tick fails to embed, the embedder is almost certainly still down: the whole tick aborts right there with a single Warn instead of probing every remaining pending row against a dead backend. A later row failing on its own (e.g. bad content) is logged and skipped so one bad row can't wedge the rest of the tick's progress. Returns the number of rows successfully backfilled.

func (*Service) Briefing added in v0.0.11

func (s *Service) Briefing(ctx context.Context, namespace string, opts BriefingOpts) (Briefing, error)

Briefing builds a session-start briefing for a namespace: up to Facts semantic facts (ranked by DurableScore), Procedures procedural how-tos, Recent episodic entries (newest first), and Pinned pinned memories (any tier). Each opt is a pointer so nil falls back to DefaultPerSection and a pointer to 0 disables the section. It is a cheap, query-less read for hooks to inject context when a session opens.

func (*Service) Dedup added in v0.0.8

Dedup runs a vector-cluster dedup pass: each cluster's representative (the member with the highest RetentionScore) is kept; the rest are tombstoned (SupersededBy → representative) so they're hidden from default search results. The action is reversible. With in.Namespaces empty the pass spans every namespace; callers usually scope it to one.

It's mainly a post-import cleanup tool, since exports tend to be full of restatements. The default similarity (0.85) is a paraphrase-level threshold; raise it for stricter, lower it for looser merging.

func (*Service) DeleteNamespace added in v0.0.8

func (s *Service) DeleteNamespace(ctx context.Context, namespace string) (int64, error)

DeleteNamespace removes every memory in a namespace. Returns the number of memories deleted.

func (*Service) Events added in v0.6.8

func (s *Service) Events(ctx context.Context, in EventsInput) (EventsPage, error)

Events reads the activity feed, regrouping the store's flat (event, memory) rows back into whole operations. Returns ErrUnsupported when the driver has no activity log.

The regrouping leans on a property the store guarantees: one operation's rows are written as a single batch, so they share a created_at and sit contiguously in the (created_at DESC, id DESC) ordering. That lets a flat row page be grouped by walking consecutive rows — no join table, no second query.

func (*Service) FlushConsolidation added in v0.5.9

func (s *Service) FlushConsolidation(ctx context.Context) error

FlushConsolidation blocks until every consolidation job queued before the call has been processed. It is a no-op without an async consolidator, and needs StartConsolidator running (otherwise it waits until ctx is done). Intended for benches and tests that must let consolidation settle before measuring.

func (*Service) Forget

func (s *Service) Forget(ctx context.Context, namespace, id string) error

Forget deletes a memory by ID.

func (*Service) ForgetByTag added in v0.0.11

func (s *Service) ForgetByTag(ctx context.Context, namespace, tag string) (int64, error)

ForgetByTag deletes every memory in a namespace carrying tag, including superseded and expired ones, and returns the count deleted. With the import provenance tag (import:<source>:<date>), this undoes a bulk import in one call.

func (*Service) Fsck

func (s *Service) Fsck(ctx context.Context) (maintenance.Report, error)

Fsck runs a consistency sweep: purge expired, enforce the short-term cap, and audit live memories for duplicate clusters.

func (*Service) Get

func (s *Service) Get(ctx context.Context, namespace, id string) (*memory.Memory, error)

Get returns a single memory by ID.

A get is logged to the activity feed but deliberately does not reinforce: reinforcement is a relevance signal (it slides TTLs and gates promotion), and addressing a memory by ID — which is what the UI drawer does — says nothing about whether it answered a question.

func (*Service) HasAnswerer added in v0.5.12

func (s *Service) HasAnswerer() bool

HasAnswerer reports whether an LLM completer is configured for answering. Callers (e.g. the MCP server) use this to decide whether to expose answer-dependent surfaces at all, rather than exposing them and erroring on every call in a headless deployment.

func (*Service) History added in v0.4.19

func (s *Service) History(ctx context.Context, namespace, id string) ([]*memory.Memory, error)

History returns the full supersession lineage of a memory: the memory itself, every memory it superseded (walking PredecessorIDs backwards) and every one that superseded it (following SupersededBy forwards), including tombstoned rows, ordered oldest-first by CreatedAt. Returns ErrNotFound when id is absent. Walks breadth-first so a merge (several memories superseded by one) is followed in every direction without revisiting a node.

func (*Service) List

func (s *Service) List(ctx context.Context, in ListInput) ([]*memory.Memory, error)

List returns memories in a namespace matching the filter, without embeddings. It backs the UI memory browser and the client-derived relationship graph.

func (*Service) LogConfigEvent added in v0.7.0

func (s *Service) LogConfigEvent(ctx context.Context, kind store.EventKind, namespace string, detail map[string]any)

LogConfigEvent records a config-surface write — a pin (EventPin), an unpin (EventUnpin), or a behavioral-settings change (EventSettings) — to the activity log. Unlike the memory events above these carry no memory snapshot: the payload that matters lives in detail (a pin's keys/author/note, which settings layer changed), so the event is a single memory-less row. namespace is what the event is recorded against (the pinned namespace for a pin/unpin, "" for a global-defaults change). Exposed so the REST handlers can log through the service that owns logEvents rather than reaching into the store themselves. Best-effort like every other log write: a failure is logged, never returned, and it is a no-op against a backend with no activity log.

func (*Service) Namespaces

func (s *Service) Namespaces(ctx context.Context) ([]string, error)

Namespaces returns the distinct namespaces holding memories, for the UI tenant switcher.

func (*Service) Promote

func (s *Service) Promote(ctx context.Context) (int, error)

Promote distills frequently-accessed, not-yet-promoted short-term memories (working and episodic) in each namespace into durable semantic facts (written via Remember so they get the similarity gate and consolidation dedup), then stamps the sources so they aren't reprocessed. Working memories that have proven valuable (AccessCount >= promoteMinAccess) are first retiered to episodic, then the combined pool is distilled. Without a distiller it falls back to the marker extractor, so usage-earned promotion also works on LLM-less deployments. Returns the number of facts written.

func (*Service) PruneEvents added in v0.6.8

func (s *Service) PruneEvents(ctx context.Context, olderThan time.Time, keepMax int) (int64, error)

PruneEvents trims the activity log to the configured retention window and row cap. Called by the maintenance sweeper; a no-op against a driver with no activity log, or when both bounds are unset (keep forever).

func (*Service) Recall

func (s *Service) Recall(ctx context.Context, in RecallInput) ([]store.Scored, error)

Recall runs hybrid (vector + keyword) retrieval fused with RRF.

func (*Service) Remember

func (s *Service) Remember(ctx context.Context, in RememberInput) (*memory.Memory, error)

Remember embeds and stores a memory, returning the persisted record.

func (*Service) ResolveReadSetInfo added in v0.6.6

func (s *Service) ResolveReadSetInfo(ctx context.Context, ns, home string) ([]ReadSetEntry, error)

ResolveReadSetInfo resolves the default read-set for ns (request namespace) and home (caller's personal namespace) — the same cascade Recall and Briefing use with no scope/explicit-namespace override, and with no tier filter (the full durable cascade). This is the STRUCTURAL read-set: which namespaces are reachable and why (origin), independent of any one request's tier filter — tiers decide what gets searched, not what a namespace is. Used by the read-set introspection endpoint (T6) and by Answer's ReadSet out-param (see AnswerInput.ReadSet for why answer provenance must be tier-independent).

func (*Service) RunEmbedBackfill added in v0.5.12

func (s *Service) RunEmbedBackfill(ctx context.Context, interval time.Duration)

RunEmbedBackfill periodically re-embeds memories that were stored vectorless (metadata pending_embed="true", stamped by embedForRemember when the write-time embed budget was exceeded or the embedder errored) until ctx is cancelled. It is a no-op without a positive interval. Call once, typically in its own goroutine.

func (*Service) RunPromoter

func (s *Service) RunPromoter(ctx context.Context, interval time.Duration)

RunPromoter periodically distills frequently-accessed short-term memories into durable semantic facts until ctx is cancelled. It is a no-op without a positive interval. Call once, typically in its own goroutine.

func (*Service) StartConsolidator

func (s *Service) StartConsolidator(ctx context.Context)

StartConsolidator runs the background consolidation worker until ctx is cancelled, then drains queued jobs within a bounded timeout. It is a no-op unless the service was built with a consolidator in async mode. Call once, typically in its own goroutine.

func (*Service) StartDistillBatcher added in v0.5.9

func (s *Service) StartDistillBatcher(ctx context.Context)

StartDistillBatcher runs the age-flush loop for batched distill-on-write until ctx is cancelled, then flushes every remaining buffer. A no-op unless the service was built with WithDistillBatch. Call once, typically in its own goroutine (mirrors StartConsolidator).

func (*Service) Stats

func (s *Service) Stats(ctx context.Context, namespace string) (Stats, error)

Stats computes a per-namespace overview by scanning all of its memories (including expired and superseded, so those can be counted separately).

func (*Service) StatsAll added in v0.0.10

func (s *Service) StatsAll(ctx context.Context) (Stats, error)

StatsAll merges per-namespace overviews into a single store-wide one (namespace reported as ""), backing the admin UI's "All projects" dashboard.

func (*Service) Store added in v0.6.0

func (s *Service) Store() store.Store

Store returns the underlying store so the REST layer can call maintenance-level operations (reassign, split, move) without a separate service facade. Exported sparingly; callers should not mutate internal state.

func (*Service) Supersede added in v0.4.12

func (s *Service) Supersede(ctx context.Context, namespace, id, supersededBy string) error

Supersede tombstones (namespace, id), recording that it was replaced by supersededBy. The row is hidden from default recall but kept for the audit/time-travel chain; the sweeper hard-deletes it after TombstoneTTL. NotFound surfaces to the caller so a missing target is not silently swallowed. Idempotent: re-superseding overwrites superseded_by.

func (*Service) WaitBackground added in v0.0.6

func (s *Service) WaitBackground()

WaitBackground blocks until detached background goroutines (async recall reinforcement) finish. Call during shutdown, after the workers have been stopped and before closing the store.

type Stats

type Stats struct {
	Namespace    string              `json:"namespace"`
	Total        int                 `json:"total"`                    // live memories (excludes expired/superseded)
	ByTier       map[memory.Tier]int `json:"by_tier"`                  // live count per tier
	ByMemoryType map[string]int      `json:"by_memory_type,omitempty"` // live count per metadata.memory_type (typed extractions)
	Expired      int                 `json:"expired"`                  // past-TTL, not yet swept
	Superseded   int                 `json:"superseded"`               // contradiction-tombstoned
	// uncorroborated durable debris (confidence below the demote floor); unbounded
	// by short-term caps, so a growing value signals reclaimable bloat
	LowConfidenceDurable int        `json:"low_confidence_durable"`
	TotalAccesses        int        `json:"total_accesses"`
	AvgImportance        float64    `json:"avg_importance"`
	LastWriteAt          *time.Time `json:"last_write_at,omitempty"`
}

Stats summarizes a namespace for the UI dashboard. Counts are computed from a full listing, so callers should treat it as a curated-namespace overview, not a hot-path metric (Prometheus /metrics remains the source for operational counters).

type WriteDedupAction added in v0.5.0

type WriteDedupAction string

WriteDedupAction selects what write-time dedup does when a fresh write scores at or above the dedup threshold against its nearest same-tier memory.

const (
	// WriteDedupOff disables write-time fuzzy dedup. The exact-restatement
	// fingerprint pass (WithFingerprintDedup) is independent and still runs.
	WriteDedupOff WriteDedupAction = "off"
	// WriteDedupHint stores the write and returns a MergeHint for the caller to
	// merge. Non-destructive; scoped to durable tiers (semantic/procedural).
	WriteDedupHint WriteDedupAction = "hint"
	// WriteDedupCoalesce reinforces the existing memory and drops the write
	// (headless corpus hygiene). Applies to all tiers.
	WriteDedupCoalesce WriteDedupAction = "coalesce"
	// WriteDedupSupersede stores the write and tombstones the old memory.
	WriteDedupSupersede WriteDedupAction = "supersede"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL