Documentation
¶
Index ¶
- Constants
- Variables
- func DegradedWire(reasons string, dropped []string) (value, note string)
- func OriginMap(entries []ReadSetEntry) map[string]string
- func ReadSetFrom(origins map[string]string, ns string) string
- func RecallPoolSize(k int) int
- func WithActor(ctx context.Context, name, kind string) context.Context
- type ActivityEvent
- type ActivityMemory
- type AmbiguousIDError
- type AnswerInput
- type AnswerResult
- type Briefing
- type BriefingOpts
- type ChildSummary
- type ConsolidateMode
- type DedupInput
- type EventsInput
- type EventsPage
- type InjectedReport
- type InjectedSuppressed
- type ListInput
- type MergeHint
- type Metrics
- type Option
- func WithAnswerer(c llm.Completer) Option
- func WithBackgroundEmbedTimeout(d time.Duration) Option
- func WithCascade(on bool) Option
- func WithChunkEmbed(cfg chunk.Config) Option
- func WithChunkScoreWeight(w float64) Option
- func WithClassifyMaxChars(n int) Option
- func WithClock(now func() time.Time) Option
- func WithConsolidateMinScore(minScore float64) Option
- func WithConsolidateMode(m ConsolidateMode) Option
- func WithConsolidator(c llm.Consolidator) Option
- func WithContradictionDownrank(minScore float64) Option
- func WithCorroboration(minScore float64) Option
- func WithCorruptionQuarantine(on bool) Option
- func WithDistillBatch(maxTokens int, maxAge time.Duration) Option
- func WithDistillDropNoFact(on bool) Option
- func WithDistillOnWrite(on bool) Option
- func WithDistillTimeout(d time.Duration) Option
- func WithDistiller(d llm.Distiller) Option
- func WithEpisodicMinChars(n int) Option
- func WithEventLog(on bool) Option
- func WithExtractOnWrite(on bool) Option
- func WithFingerprintDedup(on bool) Option
- func WithIDGenerator(gen func() string) Option
- func WithImportancePoolMin(f float64) Option
- func WithImportancePoolReserve(n int) Option
- func WithMetrics(m Metrics) Option
- func WithPromoteMinAccess(n int) Option
- func WithPromoteWholeMaxChars(n int) Option
- func WithQueryPrefix(p string) Option
- func WithRecallEmbedTimeout(d time.Duration) Option
- func WithRecallMinScore(minScore float64) Option
- func WithRecallMinSemanticScore(minSemanticScore float64) Option
- func WithRecallPool(factor, floor int) Option
- func WithRecallRewriteTimeout(d time.Duration) Option
- func WithRecallSemanticReserve(n int) Option
- func WithReinforceSkipMarkers(on bool) Option
- func WithRerankEmptyVerdict() Option
- func WithRerankPool(n int) Option
- func WithRerankTimeout(d time.Duration) Option
- func WithReranker(r rerank.Reranker, name string) Option
- func WithReserveGatePercentile(pct float64) Option
- func WithReservePromoteRatio(ratio float64) Option
- func WithReserveTopAnchor(anchor float64) Option
- func WithScoreFusion(alpha float64) Option
- func WithSecretRedaction(on bool) Option
- func WithShortTermCap(cap int) Option
- func WithSplitDedupLLMMerge(b bool) Option
- func WithSyncEventLog() Option
- func WithSyncReinforce() Option
- func WithTemporalTargeting(boost float64, ex search.AnchorExtractor) Option
- func WithTurnEchoWindow(d time.Duration) Option
- func WithWriteDedup(score float64, action WriteDedupAction) Option
- func WithWriteEmbedTimeout(d time.Duration) Option
- type ReadSetEntry
- type ReasoningLevel
- type RecallInput
- type RememberInput
- type Service
- func (s *Service) Answer(ctx context.Context, in AnswerInput) (AnswerResult, error)
- func (s *Service) BackfillChunks(ctx context.Context) (int, error)
- func (s *Service) BackfillEmbeddings(ctx context.Context) (int, error)
- func (s *Service) Briefing(ctx context.Context, namespace string, opts BriefingOpts) (Briefing, error)
- func (s *Service) ChunkBacklog(ctx context.Context) (int, error)
- func (s *Service) Dedup(ctx context.Context, in DedupInput) (maintenance.DedupReport, error)
- func (s *Service) DeleteNamespace(ctx context.Context, namespace string) (int64, error)
- func (s *Service) DrainRepairs(ctx context.Context) (int, error)
- func (s *Service) Events(ctx context.Context, in EventsInput) (EventsPage, error)
- func (s *Service) FlushConsolidation(ctx context.Context) error
- func (s *Service) Forget(ctx context.Context, namespace, id string) error
- func (s *Service) ForgetByTag(ctx context.Context, namespace, tag string) (int64, error)
- func (s *Service) Fsck(ctx context.Context) (maintenance.Report, error)
- func (s *Service) Get(ctx context.Context, namespace, id string) (*memory.Memory, error)
- func (s *Service) HasAnswerer() bool
- func (s *Service) History(ctx context.Context, namespace, id string) ([]*memory.Memory, error)
- func (s *Service) List(ctx context.Context, in ListInput) ([]*memory.Memory, error)
- func (s *Service) LogConfigEvent(ctx context.Context, kind store.EventKind, namespace string, ...)
- func (s *Service) Namespaces(ctx context.Context) ([]string, error)
- func (s *Service) Promote(ctx context.Context) (int, error)
- func (s *Service) PruneEvents(ctx context.Context, olderThan time.Time, keepMax int) (int64, error)
- func (s *Service) Recall(ctx context.Context, in RecallInput) ([]store.Scored, error)
- func (s *Service) RecordInjected(ctx context.Context, namespace string, r InjectedReport)
- func (s *Service) Remember(ctx context.Context, in RememberInput) (*memory.Memory, error)
- func (s *Service) RepairBacklog(ctx context.Context) ([]store.RepairStat, error)
- func (s *Service) ResolveReadSetInfo(ctx context.Context, ns, home string) ([]ReadSetEntry, error)
- func (s *Service) RunChunkBackfill(ctx context.Context, interval time.Duration)
- func (s *Service) RunEmbedBackfill(ctx context.Context, interval time.Duration)
- func (s *Service) RunPromoter(ctx context.Context, interval time.Duration)
- func (s *Service) RunRepairSweeper(ctx context.Context, interval time.Duration)
- func (s *Service) RunRepairWorker(ctx context.Context, interval time.Duration)
- func (s *Service) StartConsolidator(ctx context.Context)
- func (s *Service) StartDistillBatcher(ctx context.Context)
- func (s *Service) Stats(ctx context.Context, namespace string) (Stats, error)
- func (s *Service) StatsAll(ctx context.Context) (Stats, error)
- func (s *Service) Store() store.Store
- func (s *Service) Supersede(ctx context.Context, namespace, id, supersededBy string) error
- func (s *Service) SweepRepairs(ctx context.Context) error
- func (s *Service) Update(ctx context.Context, in UpdateInput) (*memory.Memory, error)
- func (s *Service) WaitBackground()
- type Stats
- type UpdateInput
- type WriteDedupAction
Constants ¶
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.
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.
const DefaultPromoteWholeMaxChars = 240
DefaultPromoteWholeMaxChars bounds whole-content heuristic promotion, in runes: a source this short reads as a single statement, so it can become a fact verbatim. Runes, not bytes — a byte bound would deny promotion to non-ASCII prose at a third of the nominal length, however often it was recalled. Overridden by WithPromoteWholeMaxChars.
Exported so the server's MEMINI_PROMOTE_WHOLE_MAX_CHARS default can be pinned against it: the env default is a struct tag and cannot reference this constant, so only a test keeps the two from drifting apart.
Variables ¶
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.
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 DegradedWire ¶ added in v0.7.16
DegradedWire renders a recall's degradation for the caller: a short machine value and a plain-language note.
Both transports built this string themselves and had drifted into identical copies, which is how the dropped-namespace case would have ended up reported on one surface and not the other. reasons is the embed-degradation reason ("embed_error"/"embed_timeout", empty when the query embed succeeded); dropped names read-set namespaces whose search failed. Returns empty strings on a healthy recall.
Telling the caller matters more here than it looks: an agent handed fewer results with no explanation concludes the memory does not exist, which is a worse outcome than being told the search was partial.
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
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
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
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
// Injected is the served→injected join's verdict for a recall event's
// memory: true when a nearby inject report named it as actually reaching
// model context, false when a report existed but omitted it (the client
// suppressed it). nil when no report covered the serve — absent means
// unknown, so old data and non-reporting integrations render unchanged,
// while false means reported-suppressed. See annotateInjected.
Injected *bool
// Filtered marks a hit the recall dropped from its response but still logged:
// "rank_floor" when the composite floor (min_rank_score) cut it. Empty for a
// served hit. Lets the feed dim what was filtered instead of hiding it.
Filtered string
}
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 AmbiguousIDError ¶ added in v0.7.7
AmbiguousIDError is returned by Get when an id prefix matches more than one memory in the namespace. It lists the colliding full ids so the caller can retry with a longer prefix (or the full id), and matches store.ErrConflict under errors.Is so the REST layer maps it to 409.
func (*AmbiguousIDError) Error ¶ added in v0.7.7
func (e *AmbiguousIDError) Error() string
func (*AmbiguousIDError) Is ¶ added in v0.7.7
func (e *AmbiguousIDError) Is(target error) bool
Is makes the conflict classifiable without a dedicated sentinel: errors.Is(err, store.ErrConflict) holds, which statusFor in the REST layer already maps to http.StatusConflict.
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
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"`
// Degraded names the read-set namespaces whose load failed and were skipped,
// so a caller can tell an empty section from an unreachable one. Empty on a
// healthy briefing. The primary namespace never appears here: losing it is
// fatal rather than degrading, because a briefing without the project's own
// context is not a briefing.
Degraded []string `json:"degraded,omitempty"`
// Omitted is the total number of items BriefingOpts.MaxTokens dropped
// across the four sections (0 without a budget or when everything fit).
// The briefing has no per-call activity detail for it — this field IS the
// budget's visibility on this surface.
Omitted int `json:"omitted,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
// MaxTokens, when > 0, is a server-enforced token budget across the whole
// briefing, filled in section order pinned → facts → procedures → recent
// (fill order IS priority order: pinned fills first, recent starves
// first), dropping whole tail items — never splitting one — and reporting
// the drop count in Briefing.Omitted. The first item overall always
// ships. 0 is unbounded. See applyBriefingBudget.
MaxTokens int
// EstimateConcise makes MaxTokens estimate over each item's concise
// projection (render.BriefingMax) instead of full content — set when the
// response will ship the concise form (?format=concise), same doctrine as
// RecallInput.EstimateConcise.
EstimateConcise bool
}
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 InjectedReport ¶ added in v0.7.7
type InjectedReport struct {
// SessionID is the client session the injection happened in; "" when the
// client did not say.
SessionID string
// Surface is the hook surface that reported: "briefing", "prompt" or
// "pretool". The transport layer validates it; it is recorded verbatim.
Surface string
// Source is the free-form client name (e.g. "claude-code"); "" omits it
// from the event detail, mirroring RecallInput.Source.
Source string
// InjectedIDs are the memory ids actually injected, in injection order.
// May be empty: a suppression-only report still records (as a single
// memory-less event, the zero-hit recall's precedent).
InjectedIDs []string
// TokensEst and Chars are the client's size estimates for what it
// injected; nil means unreported (the detail key is then absent, not 0).
TokensEst *int
Chars *int
// Suppressed counts what the client's local gates held back, by reason.
Suppressed InjectedSuppressed
}
InjectedReport is one client injection-telemetry beacon (POST /v1/activity/injected): after the server served memories (a recall or a briefing), the client hook reports which of them actually reached model context and what its local gates held back. Memory ids are taken on faith — an unknown id is recorded as-is, never rejected — because the report is best-effort observability, not a write to the memories themselves. An id the caller's namespace was demonstrably served does pick up that serve's snapshot so the row is renderable (hydrateInjected); one that was not stays bare.
type InjectedSuppressed ¶ added in v0.7.7
type InjectedSuppressed struct {
Seen, Cooldown, Budget, Unchanged, Score int
}
InjectedSuppressed counts client-side injection suppressions by reason. A zero count means "none reported" for that reason and is omitted from the event detail (absent, not 0), like every other optional detail key.
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 namespaces" 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)
// RecallFloored records the candidates one recall's composite floor
// (min_rank_score) dropped from the response. Called once per recall that
// floored anything, with the drop count; tierFilter matches RecallResult's
// label.
RecallFloored(tierFilter string, n int)
// 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)
// InjectedResult records one bucket of a client injection-telemetry
// report (RecordInjected): surface is the reporting hook surface
// ("briefing"|"prompt"|"pretool"); result is "injected" or a suppression
// reason ("suppressed_seen"|"suppressed_cooldown"|"suppressed_budget"|
// "suppressed_unchanged"|"suppressed_score"); n is how many memories fell
// in that bucket. Zero buckets are not reported.
InjectedResult(surface, result string, n int)
// InjectedTokens adds a report's client-side estimate of the tokens its
// injections consumed, by surface.
InjectedTokens(surface string, tokens int)
// EmbedBackfillPending reports the number of memories still marked
// pending_embed after one backfill tick (0 once the queue is drained).
EmbedBackfillPending(n int)
// ChunkBackfillPending reports the number of long memories still without
// chunk vectors after one chunk-backfill tick (0 once the queue is
// drained). Only meaningful with MEMINI_CHUNK_EMBED on.
ChunkBackfillPending(n int)
// RepairResult records one deferred-repair outcome. stage is the repair
// state the row was claimed in ("pending" or "enrich"); result is "ok",
// "moot" (the row no longer needed the work), "retry" (failed, will run
// again under backoff), "parked" (failed at the attempt ceiling), or a
// stage-specific detail ("embedded", "coalesced", "superseded").
RepairResult(stage, result string)
// RepairDuration observes one deferred repair's end-to-end execution time.
RepairDuration(stage string, d time.Duration)
// RepairDepth reports how many memories are currently in one repair state.
RepairDepth(state string, n int)
// RepairOldestAge reports the age in seconds of the oldest memory in one
// repair state — the signal that the backlog is filling faster than it
// drains.
RepairOldestAge(state string, seconds float64)
}
Metrics receives service-level events for observability. Methods must be safe for concurrent use; a nil Metrics is replaced by a no-op.
func NopMetrics ¶ added in v0.7.3
func NopMetrics() Metrics
NopMetrics is exported for tests, mirroring store.NopMetrics: it lets a test outside this package embed a working sink and override only the one method it cares about, rather than restating the whole interface.
type Option ¶
type Option func(*Service)
Option customizes a Service.
func WithAnswerer ¶ added in v0.0.4
WithAnswerer enables Answer: recall memories, then generate a grounded answer from them with this chat client.
func WithBackgroundEmbedTimeout ¶ added in v0.7.16
WithBackgroundEmbedTimeout bounds the embed inside a background repair, independently of WithWriteEmbedTimeout.
Keeping the two apart is not cosmetic. The write budget is a latency budget for a caller who is waiting; a background repair has no caller. Holding a repair to the write budget means a merely-slow embedder — a network stall, exactly the degraded scenario repairs exist to recover from — makes every repair fail forever while the write path keeps degrading. d <= 0 selects repairEmbedTimeout.
func WithCascade ¶ added in v0.6.6
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 WithChunkEmbed ¶ added in v0.7.3
WithChunkEmbed turns chunked embedding on under cfg. It is a no-op unless the store implements store.ChunkStore — a driver without the capability simply keeps the previous behaviour rather than erroring, matching how every other optional capability degrades.
func WithChunkScoreWeight ¶ added in v0.7.3
WithChunkScoreWeight scales chunk scores against document scores when the two legs are merged. See mergeVectorLegs.
func WithClassifyMaxChars ¶ added in v0.7.3
WithClassifyMaxChars bounds a write that picked no tier, in runes. 0 declines every classification, so untier'd writes take the working default.
func WithConsolidateMinScore ¶
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
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
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
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
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
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
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 WithDistillTimeout ¶ added in v0.7.11
WithDistillTimeout bounds one write-time distillation (the LLM call plus fact writes). Distillation runs in the background, so a longer deadline never blocks the write path; raise it when the chat model runs on a slow or shared backend where a distill call can queue behind long-running requests. d <= 0 keeps the default.
func WithDistiller ¶
WithDistiller enables episodic→semantic promotion via RunPromoter.
func WithEpisodicMinChars ¶ added in v0.4.14
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
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
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
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 ¶
WithIDGenerator overrides ID generation (tests).
func WithImportancePoolMin ¶ added in v0.7.18
WithImportancePoolMin sets the effective-importance threshold a candidate must meet to claim a slot reserved by WithImportancePoolReserve. Values outside [0,1] are ignored. The default is 0.75 — high enough that only memories the LLM (or the user) marked as genuinely important qualify, so the reserve does not fire on the tier-seeded baseline importance every memory carries.
func WithImportancePoolReserve ¶ added in v0.7.18
WithImportancePoolReserve reserves up to n slots of the reranker's candidate pool for high-importance memories (effective importance >= the WithImportancePoolMin threshold) that the composite score left below the pool cut. n < 0 is ignored; 0 disables the reserve. The default is 2.
This only ever changes pool MEMBERSHIP — the reranker re-scores every candidate it is shown, so pool order is irrelevant to it, and the top-k composite prefix every non-rerank path returns is untouched. It is therefore structurally inert unless a reranker is configured AND WithRerankPool is deeper than the caller's limit: without both there is no membership to change.
func WithMetrics ¶
WithMetrics installs an observability sink for consolidation events.
func WithPromoteMinAccess ¶
WithPromoteMinAccess sets the minimum access_count for an episodic memory to be eligible for promotion.
func WithPromoteWholeMaxChars ¶ added in v0.7.3
WithPromoteWholeMaxChars bounds LLM-less whole-content promotion, in runes. 0 leaves only marker extraction.
func WithQueryPrefix ¶
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
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
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
WithRecallMinSemanticScore sets an absolute relevance floor on the raw vector (semantic) score: a known vector candidate below the floor is excluded, so the keyword leg cannot reintroduce that candidate on a shared token. Vectorless and unknown vector-pool candidates remain keyword-searchable. 0 (the default) disables it. The usable value is embedder-dependent; the server wires its configured default via this Option, and the benchmark harness can override it.
func WithRecallPool ¶
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
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
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
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 WithRerankEmptyVerdict ¶ added in v0.7.4
func WithRerankEmptyVerdict() Option
WithRerankEmptyVerdict makes an EMPTY rerank result final instead of falling back to composite order. Set it when the reranker gates candidates on a relevance-score threshold (rerank.Config.MinScore): a fully-gated pool means "nothing relevant exists", and falling back would undo the gate exactly on the queries it exists for. Without a gate, empty output is a backend pathology (an LLM answering "none", a /rerank server omitting everything) and the fallback stays the right call — so this is opt-in, wired only when a gate is configured. Rerank FAILURES (error, timeout) keep the composite fallback either way: a dead reranker never rendered a verdict to honor.
func WithRerankPool ¶ added in v0.6.8
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
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
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
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
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
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 ¶
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
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 ¶
WithShortTermCap bounds short-term memories per namespace, enforced by fsck.
func WithSplitDedupLLMMerge ¶ added in v0.5.12
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
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
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
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
// MinRankScore, when > 0, floors the FINAL ranked (composite) score — the
// scale the response Score field and the activity feed show — applied after
// re-ranking, so it changes only which hits reach the response, never the
// candidate pool the reranker judged or its ordering. It is a distinct floor
// from the two above: MinScore gates the raw fused (vector+keyword) score
// pre-rank, the reranker's own MEMINI_RERANK_MIN_SCORE gates the absolute
// cross-encoder score (never on the wire), and this gates the composite that
// finalizeRecall re-attaches. Hits it drops are still logged to the activity
// feed marked as filtered; results added by include_linked expansion are
// exempt (the floor runs before expansion). Valid range is [0,1); 0 (the
// zero value) disables it.
MinRankScore 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
// DroppedNamespaces (output-only) receives the read-set namespaces whose
// search failed and were skipped, so a caller can tell "nothing matched"
// from "part of the corpus was unreachable". Empty on a healthy recall;
// the primary namespace never appears here because losing it is fatal
// rather than degrading. nil disables reporting. Same out-param pattern as
// Degraded.
DroppedNamespaces *[]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
// MaxTokens, when > 0, is a server-enforced token budget over the final
// ranked results: they fill in rank order until the estimated cost
// (render.ApproxTokens over the shipped content + render.ItemOverheadTokens
// per item) would exceed it, and the tail is dropped whole — except the
// first result, which always ships (a non-empty recall never becomes
// empty by budget). 0 is unbounded. See applyRecallBudget.
MaxTokens int
// EstimateConcise makes MaxTokens estimate over the CONCISE projection of
// each result (summary-or-boundary-cut, render.SearchMax) instead of the
// full content — set by callers whose response will ship the concise form
// (REST response_format=concise), so the budget prices what actually goes
// on the wire. It does NOT change what content ships; the response
// projection stays the transport layer's job. Meaningless without
// MaxTokens.
EstimateConcise bool
// Omitted (output-only) receives the number of results MaxTokens dropped
// (0 without a budget or when everything fit). Same out-param pattern as
// Degraded/ReadSet; nil disables reporting. The count also lands in the
// recall activity event's detail as budget_omitted.
Omitted *int
}
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
// AssessedImportance carries the LLM's own read of how important the content
// is. Set by the promote/consolidate paths only, never by external callers —
// those set Importance, which clears the assessment (see below).
AssessedImportance *float64
// ClearAssessedImportance forces that clear even when the incoming
// Importance equals the stored one. Update seeds Importance from the stored
// row on every edit, so value equality alone cannot distinguish "the caller
// chose this number" from "the caller touched nothing"; Update sets this
// when the edit really carried an importance.
ClearAssessedImportance bool
// 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
// EffectiveTier (output-only) receives the tier the write RESOLVED to —
// the explicit tier when one was given, otherwise the auto-classified or
// default tier. It is populated as soon as validation succeeds, so it is
// set even when the value gate drops the write and Remember returns
// (nil, nil): without it a caller reporting a dropped write back to an
// agent can only echo the raw input tier, which is "" when the agent
// omitted it.
//
// The caller passes the address of a local memory.Tier; nil disables
// reporting.
EffectiveTier *memory.Tier
// 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 (*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) BackfillChunks ¶ added in v0.7.3
BackfillChunks splits and embeds long memories that carry no chunk rows, writing them through the store's PutChunks rather than an Upsert: PutChunks touches only the chunk tables under an updated_at guard held in the same transaction, so it cannot disturb the document vector (which Get never carries), cannot revert a concurrent Reinforce or update (a full-snapshot Upsert writes every column back), and closes the check-then-act window a service-level re-read would leave open. Returns how many memories were chunked.
Discovery is a query (ListUnchunked), not a metadata flag, and that choice does more work than it looks. Rows that predate chunking carry no flag to find them by — a flag would mean rewriting every row before the feature could do anything. It also means every path that drops chunks self-heals for free: consolidation rewriting merged content and `memini reembed` swapping the model both leave rows this query finds again on the next tick. Nothing has to remember to stamp anything.
The queue is walked with a cursor, and failures are judged by run length rather than position: two rows failing back to back is an embedder outage (defer the tick — probing every remaining row against a dead backend helps nobody), while a single row failing is that row's problem (skip it and move on). Both matter because the queue's order is deterministic — without the cursor and the run-length rule, one row whose batch the embedder always rejects, or a run of rows the splitter declines, would sit at the head and starve everything behind it forever.
func (*Service) BackfillEmbeddings ¶ added in v0.5.12
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) ChunkBacklog ¶ added in v0.7.3
ChunkBacklog reports how many long memories still lack chunk rows, 0 when chunking is off. The bench's drain loop uses it to tell "queue empty" from "tick deferred": BackfillChunks reports both as 0, deliberately, because the server's ticker retries either way — a one-shot caller cannot.
func (*Service) Dedup ¶ added in v0.0.8
func (s *Service) Dedup(ctx context.Context, in DedupInput) (maintenance.DedupReport, error)
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
DeleteNamespace removes every memory in a namespace. Returns the number of memories deleted.
func (*Service) DrainRepairs ¶ added in v0.7.16
DrainRepairs runs repair batches until nothing is due, returning how many rows were processed. Exported for tests and for `doctor --fix`: it is the durable analogue of FlushConsolidation.
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
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) ForgetByTag ¶ added in v0.0.11
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 ¶
Fsck runs a consistency sweep: purge expired, enforce the short-term cap, and audit live memories for duplicate clusters.
func (*Service) Get ¶
Get returns a single memory by ID. The id may also be a unique prefix of at least idPrefixMinLen hex chars (exact match always wins; see getByIDPrefix): recall surfaces render short ids, so the model can address a memory without echoing the full UUID.
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
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
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 ¶
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 ¶
Namespaces returns the distinct namespaces holding memories, for the UI namespace switcher.
func (*Service) Promote ¶
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
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) RecordInjected ¶ added in v0.7.7
func (s *Service) RecordInjected(ctx context.Context, namespace string, r InjectedReport)
RecordInjected records one injection-telemetry report as a single inject activity event: the session/surface/source and size estimates ride the event detail (the way recall stores its source), the injected ids become the event's memory refs — hydrated with the snapshot of the serve that produced them, see hydrateInjected — and the suppression counts land under detail "suppressed" with zero reasons omitted. Metrics are incremented per report regardless of the event log's availability. Best-effort like every other event writer: nothing here can fail the request path — the report IS the request, so a lost row costs an audit line, never a client error — and it degrades to metrics-only against a backend with no activity log.
func (*Service) RepairBacklog ¶ added in v0.7.16
RepairBacklog returns the current repair queue by state, for /healthz, the UI and doctor. Empty when the store provides no repair queue.
func (*Service) ResolveReadSetInfo ¶ added in v0.6.6
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) RunChunkBackfill ¶ added in v0.7.3
RunChunkBackfill periodically embeds the chunks of long memories that have none, until ctx is cancelled. No-op without a positive interval or with chunking off. Call once, in its own goroutine.
Chunking runs here rather than on the write path deliberately. A long memory is several sequential embedder round-trips; doing that inside Remember would blow writeEmbedTimeout (5s by default) and degrade the write to pending_embed — for precisely the long memories this feature exists to help. The cost is that a long memory becomes fully searchable shortly after it is written rather than instantly. Its document vector works the whole time, so this is a gap in extra recall, not in recall.
func (*Service) RunEmbedBackfill ¶ added in v0.5.12
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 ¶
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) RunRepairSweeper ¶ added in v0.7.16
RunRepairSweeper is the safety net behind the repair worker: it re-arms rows parked by a long outage, and adopts rows that owe a vector but carry no repair state.
Unlike the loop it replaces, it never touches the embedder. Because repair state is committed with the write that needs it, this sweeper is an optimization rather than a correctness requirement — but it still earns its place, because rows written by a release that predates the repair columns, or by a path that bypasses Remember (import, restore), carry no state and would otherwise never be repaired.
It is a no-op without a positive interval. Falls back to the pre-repair backfill loop when the store provides no repair queue.
func (*Service) RunRepairWorker ¶ added in v0.7.16
RunRepairWorker drains the deferred-repair queue until ctx is cancelled. It is a no-op without a positive interval or a store that provides the queue. Call once, typically in its own goroutine.
func (*Service) StartConsolidator ¶
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
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 ¶
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
StatsAll merges per-namespace overviews into a single store-wide one (namespace reported as ""), backing the admin UI's "All namespaces" dashboard.
func (*Service) Store ¶ added in v0.6.0
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
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) SweepRepairs ¶ added in v0.7.16
SweepRepairs runs one sweep: re-arm parked rows, adopt unmarked ones, and refresh the backlog gauges.
func (*Service) Update ¶ added in v0.7.3
Update applies a partial edit to an existing memory and returns the stored result. Returns store.ErrNotFound when id is absent, and ErrInvalidInput when the edit is rejected.
It composes Get + Remember with the current ID (the documented upsert path) rather than writing fields directly, so an edit still runs the full write lifecycle: validation, secret redaction, and content sanitization all apply to updated content exactly as they would to a fresh write. Content is re-embedded only when it actually changes (see reusableVector), so a tags- or metadata-only edit costs no embedder call. Remember's dedup, consolidation and corroborate/contradict routing are fresh-write-only and never fire here.
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"`
// vectorless degraded writes awaiting repair; a persistently nonzero value
// means the embedder is down
PendingEmbed int `json:"pending_embed"`
// degraded writes whose repair exhausted its attempt ceiling. Unlike
// PendingEmbed these do NOT fix themselves on the next tick: they stay
// keyword-only until the sweeper's circuit breaker re-arms them or an
// operator intervenes. Reported separately precisely because rendering the
// two identically is what lets a permanently degraded memory hide.
EmbedStuck int `json:"embed_stuck"`
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 UpdateInput ¶ added in v0.7.3
type UpdateInput struct {
Namespace string
ID string
// Home is the caller's personal namespace, and Author the named API key
// behind the edit — both forwarded to RememberInput. See its docs.
Home string
Author string
Content *string
Summary *string
Tier *memory.Tier
Level *memory.Level
// Known limitation: an explicit 0 is silently ignored and the stored
// importance kept, because resolveImportance uses a `!= 0` sentinel on
// RememberInput.Importance (a float64, not a pointer). Fixing it means
// pointer-izing that field across every write path. Do not document an
// update surface as being able to set importance to 0.
Importance *float64
Confidence *float64
// Tags replaces the stored set wholesale: nil keeps it, a non-nil empty
// slice clears it. Not a *[]string — nil-vs-empty already carries both
// meanings, and it is the shape both surfaces decode into natively.
Tags []string
// Metadata merges into the stored metadata key-by-key rather than replacing
// it (RFC 7386 style): nil leaves metadata untouched, and an explicit nil
// value deletes that key. This is the one place Update deliberately differs
// from RememberInput.Metadata, which replaces wholesale — see mergeMetadata.
Metadata map[string]any
}
UpdateInput describes a partial edit to an existing memory. Every field is optional: a nil pointer (or nil slice/map) keeps whatever the stored record holds, so a caller enriching one field never has to resend the rest.
The pointers are deliberate. An earlier ""-sentinel form could not tell "omit to keep" from "set this to empty", which made a summary impossible to clear. A non-nil pointer to the zero value is an explicit write of that value.
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" )