memory

package
v0.7.29 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package memory defines memini's core domain types.

Index

Constants

View Source
const (
	PendingEmbedKey   = "pending_embed"
	PendingEmbedValue = "true"
)

PendingEmbedKey/PendingEmbedValue are the metadata flag a degraded write carries when it was stored vectorless because the embedder was unreachable; the backfill loop re-embeds flagged rows and deletes the key on success.

View Source
const (
	// ConfidenceSeedFresh is the starting confidence of a durable fact written
	// or promoted from observed activity (some basis, not yet corroborated).
	ConfidenceSeedFresh = 0.4
	// ConfidenceSeedImported is the starting confidence of an uncorroborated
	// bulk import: lower, so imports must earn trust before outranking facts the
	// agent actually established.
	ConfidenceSeedImported = 0.25
	// ConfidenceDemoteFloor is the corroboration below which an old, never-
	// recalled durable memory is treated as uncorroborated debris (demotion
	// eligibility, and the diagnostic's low-confidence count).
	ConfidenceDemoteFloor = 0.35
)

Confidence lifecycle constants.

View Source
const EmbedStateStuck = "failed"

EmbedStateStuck is the EmbedState of a memory whose repair exhausted its attempt ceiling: it will not be retried until the sweeper re-arms it, so it is the one repair state an operator has to be told about. Mirrors store.RepairFailed.

Variables

View Source
var AssessedSalienceWeight = 0.0

AssessedSalienceWeight is the share of the importance term Salience takes from the LLM's assessment rather than the stored importance, read at ranking time. Above 0 the term becomes (1-w)*Importance + w*AssessedImportance for a row that carries an assessment; a row without one is unchanged at any w, and at 0 it is an exact no-op (stored importance only). The memini server sets it from MEMINI_ASSESSED_SALIENCE_WEIGHT (default 0) in cmd/memini; this package-level default stays 0 so direct library callers and unit tests keep the unmodulated baseline unless they opt in.

View Source
var StabilityK = 0.0

StabilityK is the spaced-repetition strength (Ebbinghaus stability), read at ranking time by Quality. Above 0 it stretches a short-term memory's effective half-life with reinforcement — S = retentionHalfLife*(1 + StabilityK*ln(1+ AccessCount)) — so a frequently-recalled memory forgets more slowly (the curve flattens, not just shifts up); at 0 it is an exact no-op (fixed half-life). The memini server sets it from MEMINI_STABILITY_K (default 1) in cmd/memini; this package-level default stays 0 so direct library callers and unit tests keep the unmodulated baseline unless they opt in. See bench/reinforcement_test.go.

Functions

func Fingerprint added in v0.2.9

func Fingerprint(content string) string

Fingerprint is a content key for exact-restatement dedup: the SHA-256 of the normalized content, so writes differing only in case or whitespace collide.

func GrowConfidence added in v0.0.11

func GrowConfidence(c float64) float64

GrowConfidence raises a confidence toward 1 logistically on corroboration: each re-observation closes 10% of the remaining gap, so confidence asymptotes to 1 and never overshoots.

func NormalizeContent

func NormalizeContent(s string) string

NormalizeContent collapses whitespace and case so trivially-duplicated memories compare equal. Used by recall dedup and the fsck duplicate audit.

func SeedImportance added in v0.7.18

func SeedImportance(t Tier) float64

SeedImportance is the tier-based importance floor for a fresh write that carried none: durable curated tiers outrank episodic turns, which outrank raw working-intake notes.

Types

type Chunk added in v0.7.3

type Chunk struct {
	Idx       int
	Text      string
	Embedding []float32
}

Chunk is one embedded segment of a memory's content. Idx is its position in the content, from 0, so a row is stable across re-splits.

Text is the segment the Embedding was built from. It is stored rather than recomputed because it is what the reranker must judge: rerank cuts a candidate down to its own budget (300 bytes for the LLM backend, 2048 runes for the cross-encoder), so handing it the whole memory means judging a prefix that need not contain the passage that retrieved it, and dropping the memory chunked recall just found. Recomputing it from content would be possible while the split config is unchanged, and wrong the moment it is not.

type Level added in v0.5.10

type Level string

Level classifies a memory by how it was derived: user-stated versus LLM-inferred. Empty string is legacy/unknown (every pre-level row) and passes filters unconstrained.

const (
	// LevelExplicit is a user-stated or directly-extracted fact, stamped by
	// the heuristic extract-on-write path and by direct RememberInput callers.
	LevelExplicit Level = "explicit"
	// LevelDeduced is an LLM-inferred fact (distilled from episodic material).
	// Traceable to its sources via metadata.source_ids.
	LevelDeduced Level = "deduced"
)

func (Level) Valid added in v0.5.10

func (l Level) Valid() bool

Valid reports whether l is a known derivation level. Empty (legacy) and any unknown value return false.

type Memory

type Memory struct {
	ID        string `json:"id"`
	Namespace string `json:"namespace"`
	Tier      Tier   `json:"tier"`
	Level     Level  `json:"level,omitempty"`
	Content   string `json:"content"`
	Summary   string `json:"summary,omitempty"`

	// Metadata is arbitrary structured data, persisted as JSON.
	Metadata map[string]any `json:"metadata,omitempty"`
	// Tags are free-form labels used for keyword retrieval and filtering.
	Tags []string `json:"tags,omitempty"`

	// Importance biases decay and ranking; higher survives longer. Range [0,1].
	Importance float64 `json:"importance"`

	CreatedAt      time.Time  `json:"created_at"`
	UpdatedAt      time.Time  `json:"updated_at"`
	LastAccessedAt time.Time  `json:"last_accessed_at"`
	AccessCount    int        `json:"access_count"`
	ExpiresAt      *time.Time `json:"expires_at,omitempty"`

	// SupersededBy points at the memory that replaced this one during
	// contradiction resolution; non-nil means this record is tombstoned.
	SupersededBy *string `json:"superseded_by,omitempty"`

	// ValidFrom / ValidTo bound the wall-clock interval a fact was true. Both nil
	// means "always" (the common case). ValidTo is stamped when a fact is
	// superseded, so a time-filtered recall (Filter.AsOf) can answer "what was
	// true in March" by surfacing facts valid then even if later replaced.
	ValidFrom *time.Time `json:"valid_from,omitempty"`
	ValidTo   *time.Time `json:"valid_to,omitempty"`

	// Confidence is how corroborated a durable (semantic/procedural) fact is,
	// in [0,1]. It starts low for a fresh or imported fact, grows logistically
	// each time the fact is re-observed, and decays without reinforcement, so a
	// corroborated fact outranks one-off noise. nil means "not tracked" (every
	// short-term memory, and durable memories written before the field existed),
	// treated as fully trusted so existing data is never retroactively penalized.
	Confidence *float64 `json:"confidence,omitempty"`

	// AssessedImportance is how intrinsically important the LLM judged this
	// content to be, in [0.1,0.9]. nil means never assessed (every memory
	// written before the field existed, and every write the assessor did not
	// reach). Invariant: it is cleared whenever a caller supplies an explicit
	// Importance, so a non-nil value always refines a tier-seeded guess and
	// never overrides what a user asked for.
	AssessedImportance *float64 `json:"assessed_importance,omitempty"`

	// LinkedMemoryIDs references related memories (same entity/topic but distinct
	// facts). Populated by the LLM consolidator when a new memory is related but
	// neither a duplicate nor a contradiction. At recall, IncludeLinked expands
	// results 1-hop via these links. Links are advisory — stale links (target
	// superseded) are resolved at recall time.
	LinkedMemoryIDs []string `json:"linked_memory_ids,omitempty"`

	// Embedding is the dense vector for similarity search. It is required when
	// writing to the store and is omitted from API responses.
	Embedding []float32 `json:"-"`

	// EmbedState is the deferred-repair state a write commits alongside the
	// memory: "" for a healthy write, "pending" when it stored without a vector
	// because the embedder was unreachable. The values are store.RepairState's;
	// it is a plain string here only because internal/memory cannot import
	// internal/store.
	//
	// Writing it on the Memory rather than through a separate call is the whole
	// point: the state and the memory it describes commit in one transaction,
	// so there is no window in which a degraded write exists without the record
	// that it needs repairing. On read paths it reflects the stored column, so
	// a caller can tell a memory that will be repaired shortly from one whose
	// repair has given up (see EmbedStuck).
	EmbedState string `json:"embed_state,omitempty"`

	// Chunks are per-segment vectors covering content that runs past the
	// per-item embed budget, so recall can match text the Embedding above does
	// not reach. Optional: a store need not implement store.ChunkStore, and
	// short content has none by design (see internal/chunk).
	//
	// On Upsert, nil means the store decides: existing chunk rows are kept
	// when the content is unchanged (same fingerprint) and cleared when it
	// changed. That default fails safe in both directions — stale chunks make
	// recall return a memory whose text no longer contains the passage that
	// matched it, while missing ones are re-created by the backfill loop, so
	// a caller that rewrites content without recomputing loses nothing
	// durable, and a caller that merely stamps metadata cannot wipe an index
	// it never touched. A non-nil slice replaces the rows exactly as given;
	// an empty non-nil slice is an explicit clear, for callers whose chunks
	// went stale without a content change (reembed: the model changed under
	// them).
	Chunks []Chunk `json:"-"`
}

Memory is a single stored memory, scoped to a namespace.

func (*Memory) DurableScore added in v0.0.13

func (m *Memory) DurableScore(now time.Time) float64

DurableScore ranks a memory as durable knowledge (e.g. a session briefing): salience × corroboration × reinforcement, without Quality's recency decay, so a core fact unrecalled for weeks is not buried under fresher trivia.

func (*Memory) EffectiveConfidence added in v0.0.11

func (m *Memory) EffectiveConfidence(now time.Time) float64

EffectiveConfidence is a durable memory's corroboration at now: the stored confidence lazily decayed for the weeks elapsed since it was last corroborated (UpdatedAt) or recalled (LastAccessedAt). Decay is applied at read time so the maintenance sweep needs no extra write. Returns 1 (neutral) for short-term memories and for any memory that never had confidence recorded — so existing data written before the field is treated as fully trusted, not penalized.

func (*Memory) EffectiveImportance added in v0.7.18

func (m *Memory) EffectiveImportance() float64

EffectiveImportance is the importance signal ranking should read: the assessed value when present, else the stored (user-supplied or tier-seeded) importance. Write paths maintain the invariant that assessed is absent on any row whose importance was explicitly set, so this never shadows a user's choice.

func (*Memory) EmbedStuck added in v0.7.16

func (m *Memory) EmbedStuck() bool

EmbedStuck reports whether this memory's repair has given up. It is the distinction the UI and doctor need: a pending repair fixes itself within seconds, a stuck one never will without intervention, and rendering them identically is what lets silent degradation hide.

func (*Memory) Expired

func (m *Memory) Expired(now time.Time) bool

Expired reports whether the memory has passed its TTL as of now.

func (*Memory) PendingEmbed added in v0.7.3

func (m *Memory) PendingEmbed() bool

PendingEmbed reports whether this memory is still awaiting its embedding (stored vectorless by a degraded write; keyword-retrieval only until the repair completes).

The metadata flag is checked alongside the column so a database written by a release that predates the repair columns still reports honestly until the sweeper adopts it.

func (*Memory) Quality added in v0.0.11

func (m *Memory) Quality(now time.Time) float64

Quality scores a memory for both recall ranking and lifecycle decisions (higher = more worth keeping and surfacing). It multiplies the base salience by corroboration (confidence), reinforcement (access frequency), and — for short-term tiers — recency (exponential decay since last access). Durable tiers skip the recency factor: they already age through confidence decay, and a 7-day half-life would zero out tier salience for any fact not recalled recently, burying core knowledge under fresh session trivia.

func (*Memory) Recency

func (m *Memory) Recency(now time.Time) float64

Recency returns an exponentially-decaying [0,1] factor for how recently the memory was accessed, halving every retentionHalfLife. Used by recall ranking.

func (*Memory) RetentionScore

func (m *Memory) RetentionScore(now time.Time) float64

RetentionScore is the legacy short-term-eviction score, retained as an alias of Quality so existing callers keep working; new code should call Quality.

func (*Memory) Salience added in v0.0.11

func (m *Memory) Salience() float64

Salience is the base, time-independent quality of a memory in [0,1]: the tier's weight modulated by importance. It does not depend on access or age. When AssessedSalienceWeight is above 0, the importance term is blended with the LLM's assessment for rows that carry one; at the default 0 it is stored importance alone.

type Term

type Term string

Term is the coarse memory horizon: short-term memories are transient and TTL'd; long-term memories are durable and curated.

const (
	ShortTerm Term = "short" // working, episodic — transient, decays
	LongTerm  Term = "long"  // semantic, procedural — durable, curated
)

type Tier

type Tier string

Tier classifies a memory by how consolidated it is: working → episodic → semantic, with procedural held separately for how-to knowledge.

const (
	// TierWorking holds raw, short-lived observations (typically session-scoped).
	TierWorking Tier = "working"
	// TierEpisodic holds summaries of what happened in a session.
	TierEpisodic Tier = "episodic"
	// TierSemantic holds durable extracted facts ("what I know").
	TierSemantic Tier = "semantic"
	// TierProcedural holds workflows and how-to knowledge.
	TierProcedural Tier = "procedural"
)

func (Tier) DefaultTTL

func (t Tier) DefaultTTL() time.Duration

DefaultTTL is the TTL for a tier; zero means never expires.

func (Tier) Term

func (t Tier) Term() Term

Term maps a tier to its memory horizon. Working/episodic are short-term; semantic/procedural are long-term.

func (Tier) Valid

func (t Tier) Valid() bool

Valid reports whether t is a known tier.

Jump to

Keyboard shortcuts

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