memory

package
v0.22.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package memory defines the pluggable memory provider contract.

Every memory plugin implements Provider as its core interface. Optional capabilities are discovered via type assertion at runtime, following the same pattern as github.com/vaayne/anna/pkg/hooks.

Index

Constants

View Source
const (
	SummarizePolicyNormal     = "normal"
	SummarizePolicyAggressive = "aggressive"
)

SummarizePolicy constants control how aggressively content is compressed.

Variables

This section is empty.

Functions

func AgentIDFromContext

func AgentIDFromContext(ctx context.Context) string

AgentIDFromContext extracts the agent ID from context.

func BuildPrompt

func BuildPrompt(text string, opts SummarizeOptions) string

BuildPrompt constructs the appropriate summarization prompt.

func BuildTool

func BuildTool(provider Provider, opts ...ToolOption) tools.Tool

BuildTool inspects provider capabilities and returns a memory tool whose available actions match exactly what the provider supports. If the provider supports no optional capabilities, the tool still exists but only offers a "status" action.

func EstimateTokens

func EstimateTokens(text string) int

EstimateTokens returns a rough token count (~4 chars per token).

func MessageRole

func MessageRole(msg ai.Message) string

MessageRole returns the role string for an ai.Message.

func MessageText

func MessageText(msg ai.Message) string

MessageText extracts the plain text content from an ai.Message. Returns the text for user/assistant messages, or the tool result for tool messages.

func MessageTimestamp

func MessageTimestamp(msg ai.Message) time.Time

MessageTimestamp returns the timestamp of an ai.Message.

func SessionIDFromContext

func SessionIDFromContext(ctx context.Context) string

SessionIDFromContext extracts the session ID from context.

func UserIDFromContext

func UserIDFromContext(ctx context.Context) int64

UserIDFromContext extracts the user ID from context.

func WithAgentID

func WithAgentID(ctx context.Context, agentID string) context.Context

WithAgentID attaches an agent ID to the context.

func WithChangeSource added in v0.18.0

func WithChangeSource(ctx context.Context, source ChangeSource) context.Context

WithChangeSource stores a ChangeSource in the context. This lets callers (e.g. Reflect) tag writes without changing function signatures.

func WithSessionID

func WithSessionID(ctx context.Context, sessionID string) context.Context

WithSessionID attaches a session ID to the context.

func WithUserID

func WithUserID(ctx context.Context, userID int64) context.Context

WithUserID attaches a user ID to the context.

Types

type ChangeEntry added in v0.18.0

type ChangeEntry struct {
	ID                  int64
	UserID              int64
	AgentID             string
	SessionID           string
	Scope               string // "profile", "soul", "constraint", "skill", "compaction"
	Action              string // "create", "update", "delete", "compact"
	Source              ChangeSource
	MemoryVersionBefore *int64
	MemoryVersionAfter  *int64
	BeforeText          string
	AfterText           string
	CreatedAt           string
}

ChangeEntry represents one changelog record.

type ChangeSource added in v0.18.0

type ChangeSource string

ChangeSource identifies who triggered a memory write.

const (
	SourceUser    ChangeSource = "user"
	SourceAgent   ChangeSource = "agent"
	SourceReflect ChangeSource = "reflect"
	SourceSystem  ChangeSource = "system"
)

func ChangeSourceFromContext added in v0.18.0

func ChangeSourceFromContext(ctx context.Context) ChangeSource

ChangeSourceFromContext retrieves the ChangeSource from context. Returns SourceAgent if not set.

type ChangelogReader added in v0.18.0

type ChangelogReader interface {
	// ReadChangelog returns the last N changelog entries for a given scope.
	ReadChangelog(ctx context.Context, userID int64, agentID string, scope string, limit int) ([]ChangeEntry, error)
}

ChangelogReader is implemented by providers that expose memory write history.

type ChangelogWriter added in v0.18.0

type ChangelogWriter interface {
	// WriteChangelog records a memory change.
	WriteChangelog(ctx context.Context, entry ChangeEntry) error
}

ChangelogWriter is implemented by providers that record memory write history.

type CompactionMode

type CompactionMode int

CompactionMode controls compaction behavior.

const (
	// CompactionIncremental runs one leaf pass + one condensed pass.
	CompactionIncremental CompactionMode = iota
	// CompactionFull repeats until no more compaction is possible.
	CompactionFull
)

func (CompactionMode) String

func (m CompactionMode) String() string

String returns the human-readable name of the compaction mode.

type CompactionResult

type CompactionResult struct {
	LeafSummariesCreated      int
	CondensedSummariesCreated int
	MessagesCompacted         int
	TokensBefore              int
	TokensAfter               int
	Duration                  time.Duration
}

CompactionResult reports what a compaction cycle accomplished.

type Compactor

type Compactor interface {
	// NeedsCompaction returns true if the session's context has grown large enough
	// to warrant compaction. threshold is a fraction of the session's token budget
	// (e.g. 0.75 means "compact when context is 75% full").
	NeedsCompaction(ctx context.Context, session Session, threshold float64) bool

	// Compact runs the compaction algorithm on the session.
	// Incremental mode runs a single summarisation pass (fast, called automatically).
	// Full mode runs repeated passes until no further reduction is possible
	// (slow, called on demand e.g. via /compact slash command).
	Compact(ctx context.Context, session Session, mode CompactionMode) (*CompactionResult, error)
}

Compactor is implemented by providers that support background compaction. The Pool calls NeedsCompaction before each chat turn and Compact when needed.

type ConstraintEntry added in v0.18.0

type ConstraintEntry struct {
	ID        string `json:"id"`
	Text      string `json:"text"`
	CreatedAt string `json:"created_at"`
}

ConstraintEntry represents a single user constraint.

type ConstraintStore added in v0.18.0

type ConstraintStore interface {
	// GetConstraints returns all active constraints for the (userID, agentID) pair.
	GetConstraints(ctx context.Context, userID int64, agentID string) ([]ConstraintEntry, error)

	// AddConstraint adds a new constraint with a generated UUID-like ID.
	// Returns the updated list of constraints.
	AddConstraint(ctx context.Context, userID int64, agentID string, text string) ([]ConstraintEntry, error)

	// RemoveConstraint removes the constraint with the given ID.
	// Returns the updated list of constraints.
	RemoveConstraint(ctx context.Context, userID int64, agentID string, id string) ([]ConstraintEntry, error)
}

ConstraintStore is implemented by providers that support storing user-defined hard constraints (rules the agent must always follow). Constraints are separate from Profile to give them special protection: Reflect cannot modify them; only user-initiated writes are allowed by convention.

type DescribeResult

type DescribeResult struct {
	SummaryID       string
	Kind            string     // "leaf" or "condensed"
	Depth           int        // 0 = leaf, 1+ = condensed
	Content         string     // the summary text
	EarliestAt      *time.Time // timestamp of oldest source message
	LatestAt        *time.Time // timestamp of newest source message
	DescendantCount int        // total original messages this summary covers
	ParentIDs       []string   // summaries that contain this one (condensed parents)
	ChildIDs        []string   // summaries or messages this one was built from
}

DescribeResult represents summary metadata and lineage.

type ExpandChild

type ExpandChild struct {
	SummaryID string
	Kind      string
	Depth     int
	Content   string
}

ExpandChild is a child summary in an expand result.

type ExpandMessage

type ExpandMessage struct {
	MessageID int64
	Role      string
	Content   string
	CreatedAt time.Time
}

ExpandMessage is a source message in an expand result.

type ExpandResult

type ExpandResult struct {
	SummaryID string
	// For leaf summaries: the original source messages.
	Messages []ExpandMessage
	// For condensed summaries: the child summaries.
	Children []ExpandChild
}

ExpandResult represents drill-down results from exploring a summary.

type Explorer

type Explorer interface {
	// Describe returns metadata and lineage for a summary ID.
	Describe(ctx context.Context, summaryID string) (*DescribeResult, error)

	// Expand drills into a summary:
	//   - For leaf summaries: returns the original source messages
	//   - For condensed summaries: returns the child summaries
	// tokenCap limits how many tokens of content are returned.
	Expand(ctx context.Context, summaryID string, tokenCap int) (*ExpandResult, error)
}

Explorer is implemented by providers that store summaries in a navigable hierarchy. It lets the agent inspect and drill into compressed history.

Note: these methods take summaryID only, not Session. Summary IDs are globally unique (e.g. "sum_a1b2c3d4") and already scoped to a conversation internally.

type LLMSummarizer

type LLMSummarizer struct {
	// Generate calls the LLM with the given prompt and returns the response text.
	Generate func(ctx context.Context, prompt string) (string, error)
}

LLMSummarizer generates summaries using an LLM via a callback function.

func (*LLMSummarizer) Summarize

func (s *LLMSummarizer) Summarize(ctx context.Context, text string, opts SummarizeOptions) (string, error)

Summarize generates a summary with escalation: normal -> aggressive -> deterministic fallback.

type ListOptions

type ListOptions struct {
	AgentID         string // filter by agent (empty = all)
	UserID          int64  // filter by user (0 = all)
	IncludeArchived bool
	Limit           int // 0 = no limit
	Offset          int // skip first N matching results
}

ListOptions controls session listing filters.

type ProfileStore

type ProfileStore interface {
	// GetProfile returns the current user profile for the (userID, agentID) pair.
	// Returns ("", nil) if no profile exists yet (not an error).
	GetProfile(ctx context.Context, userID int64, agentID string) (string, error)

	// SetProfile overwrites the user profile for the (userID, agentID) pair.
	// Callers are responsible for merging new content with existing content
	// before calling SetProfile — this method always replaces, never appends.
	SetProfile(ctx context.Context, userID int64, agentID string, content string) error

	// GetAgentSoul returns the agent soul for the (userID, agentID) pair.
	// The soul defines the agent's identity, personality, and behavior as
	// customised by this specific user. Returns ("", nil) if not set.
	GetAgentSoul(ctx context.Context, userID int64, agentID string) (string, error)

	// SetAgentSoul overwrites the agent soul for the (userID, agentID) pair.
	// Callers are responsible for merging; this method always replaces.
	SetAgentSoul(ctx context.Context, userID int64, agentID string, content string) error
}

ProfileStore is implemented by providers that support per-user-per-agent persistent memory: agent soul (identity/personality customization) and user profile (facts/context about the user).

Both are scoped to (userID, agentID) — each user can customise the agent's soul independently, and the agent maintains separate profile notes per user. Content is free-form text managed entirely by the agent. The system injects both into the system prompt at session start.

type Provider

type Provider interface {
	// Name returns the plugin identifier (e.g. "lcm", "simple").
	// Used in logs and admin UI.
	Name() string

	// Bootstrap ensures the session is initialized and ready for use.
	// Called once at the start of every Pool.Chat call before any Append or Assemble.
	// Implementations use this to create conversation records, initialize caches,
	// or establish remote connections for the session.
	Bootstrap(ctx context.Context, session Session) error

	// Append persists one or more messages to the session's event log.
	// Messages must be appended in the order they arrive.
	// Callers pass all messages from a single turn together so implementations
	// can store them in a single atomic transaction if they choose.
	//
	// Concurrency: implementations MUST be safe for concurrent Append calls
	// on different sessions. Concurrent Append calls on the SAME session
	// MUST be serialised by the implementation (see Concurrency Contract).
	Append(ctx context.Context, session Session, msgs ...ai.Message) error

	// Assemble builds the context window to send to the LLM.
	// budget: maximum number of tokens the returned messages may consume.
	// freshTail: minimum number of recent messages to always include verbatim,
	//   regardless of budget pressure. Implementations MUST honour this.
	// Returns messages in chronological order (oldest first).
	// Older content that does not fit in the budget is either summarised
	// (if the plugin supports Compactor) or omitted.
	Assemble(ctx context.Context, session Session, budget, freshTail int) ([]ai.Message, error)

	// Stats returns basic statistics about a session's memory state.
	// Used by the memory tool's "status" action and by admin endpoints.
	// Returns zero-value stats (not an error) if the session does not exist.
	Stats(ctx context.Context, session Session) (SessionStats, error)

	// Close releases any resources held by the provider (DB connections, caches, etc.).
	// Called when the Pool shuts down. Must be safe to call multiple times.
	Close() error
}

Provider is the memory plugin contract. Every memory plugin must implement this interface. Optional capabilities are discovered via type assertion — see Capability Interfaces.

func Unwrap

func Unwrap(p Provider) Provider

Unwrap returns the innermost Provider by recursively unwrapping traced layers. If p does not implement the unwrapper interface, p itself is returned.

func WithTracing

func WithTracing(provider Provider, hooksFn func() *hooks.HookSet) Provider

WithTracing wraps a Provider to emit PostMemoryCall hooks after each operation. hooksFn is called on each operation to get the current HookSet; it may return nil. The returned Provider implements all optional capability interfaces (Compactor, Searcher, Explorer, ProfileStore, SessionManager, Reviewer). Methods for capabilities not supported by the inner provider return sensible zero values or errors. Use Unwrap to check the inner provider's actual capabilities.

The Detail field is always populated with content previews. The trace hook decides whether to emit it based on log level (see [LevelTrace]).

type Reviewer

type Reviewer interface {
	// BuildReviewContext returns a text representation of the conversation
	// suitable for passing to a reviewer agent's prompt.
	//
	// since: if non-zero, only include content created after this time.
	//   The provider should include prior context (e.g. summaries from before
	//   this timestamp) to give the reviewer enough background.
	//   If zero, include all content.
	//
	// Returns ("", nil) if there is no content to review.
	BuildReviewContext(ctx context.Context, session Session, since time.Time) (string, error)
}

Reviewer is implemented by providers that can format conversation content for review by a background agent (the reflect system).

This is deliberately a single method. Watermark tracking (which sessions have been reviewed, when) is not the memory plugin's concern — it belongs to the consumer (`plugins/reflect`). The memory plugin's only job is: "given a session and an optional time boundary, produce reviewable text."

type SearchQuery

type SearchQuery struct {
	Text  string      // search term (keyword or natural language depending on plugin)
	Scope SearchScope // which layer of storage to search
	Limit int         // max results (default 20)
}

SearchQuery describes a search request.

type SearchResult

type SearchResult struct {
	SourceType string    // "message" or "summary"
	SourceID   string    // message ID or summary ID
	Content    string    // snippet of the matching content (truncated at ~500 chars)
	Score      float64   // relevance score: 0 for keyword match, 0.0-1.0 for semantic
	Timestamp  time.Time // when the source was created
}

SearchResult represents a single search hit.

type SearchScope

type SearchScope int

SearchScope controls which storage layer to search.

const (
	SearchScopeBoth      SearchScope = iota // default: search everything
	SearchScopeMessages                     // raw messages only
	SearchScopeSummaries                    // summaries only
)

type Searcher

type Searcher interface {
	Search(ctx context.Context, session Session, query SearchQuery) ([]SearchResult, error)
}

Searcher is implemented by providers that support history search.

type Session

type Session struct {
	ID      string // unique session key (e.g. "default:cli:42:main")
	AgentID string // agent this session belongs to (e.g. "default")
	UserID  int64  // internal user ID (0 for anonymous/legacy)
	Channel string // originating channel (e.g. "cli", "telegram")
}

Session identifies the context of a single conversation. It is created by Pool.Chat and passed to all Provider methods.

type SessionInfo

type SessionInfo struct {
	ID         string
	AgentID    string
	UserID     int64
	Channel    string
	Title      string // auto-generated from first message
	CreatedAt  time.Time
	LastActive time.Time
	Archived   bool
}

SessionInfo holds metadata about a session.

type SessionManager

type SessionManager interface {
	// SaveInfo persists or updates session metadata.
	SaveInfo(ctx context.Context, info SessionInfo) error

	// LoadInfo retrieves metadata for a single session.
	LoadInfo(ctx context.Context, sessionID string) (SessionInfo, error)

	// ListInfo lists sessions matching the options.
	ListInfo(ctx context.Context, opts ListOptions) ([]SessionInfo, error)

	// LoadHistory returns the complete raw message history for a session
	// in chronological order. Used by the admin panel viewer and export.
	LoadHistory(ctx context.Context, sessionID string) ([]ai.Message, error)
}

SessionManager is implemented by providers that support session lifecycle management.

type SessionSnapshot added in v0.18.0

type SessionSnapshot struct {
	SessionID string
	UserID    int64
	AgentID   string
	Version   int64
	// UpdatedAt is the wall-clock time when this snapshot was last advanced.
	// Used to filter knowledge entries so that knowledge activated after the
	// snapshot was taken does not appear in the frozen session.
	UpdatedAt time.Time
}

SessionSnapshot holds the frozen version for a session.

type SessionSnapshotStore added in v0.18.0

type SessionSnapshotStore interface {
	// GetOrCreateSessionSnapshot returns the snapshot for the session, creating it
	// if it doesn't exist. When creating, freezes the current ctx_agent_memory.version.
	GetOrCreateSessionSnapshot(ctx context.Context, sessionID string, userID int64, agentID string) (SessionSnapshot, error)

	// AdvanceSessionSnapshot updates the snapshot version to the current
	// ctx_agent_memory.version for (userID, agentID).
	// Called after front-end memory writes so the current session sees the update.
	AdvanceSessionSnapshot(ctx context.Context, sessionID string, userID int64, agentID string) error
}

SessionSnapshotStore is implemented by providers that support session snapshots.

type SessionStats

type SessionStats struct {
	MessageCount int       // total messages stored for this session
	TokenCount   int       // estimated total tokens across all stored messages
	SummaryCount int       // number of summaries (0 for providers without Compactor)
	OldestAt     time.Time // timestamp of the earliest message (zero if empty)
	NewestAt     time.Time // timestamp of the most recent message (zero if empty)
}

SessionStats contains basic statistics about a session's memory state. Every provider can produce this since it only requires knowledge of what was stored via Append.

type StaticSummarizer

type StaticSummarizer struct {
	Response string
	Err      error
}

StaticSummarizer always returns a fixed response (for testing).

func (*StaticSummarizer) Summarize

Summarize returns the static response.

type SummarizeOptions

type SummarizeOptions struct {
	IsCondensed  bool
	Depth        int
	Aggressive   bool
	Previous     string // previous summary for continuity
	TargetTokens int
}

SummarizeOptions controls summarization behavior.

type Summarizer

type Summarizer interface {
	Summarize(ctx context.Context, text string, opts SummarizeOptions) (string, error)
}

Summarizer generates summaries from content.

type ToolOption

type ToolOption func(*toolConfig)

ToolOption configures the generated memory tool.

func WithActionsOnly

func WithActionsOnly(actions ...string) ToolOption

WithActionsOnly restricts the tool to the named actions only. Actions not in the list are omitted even if the provider supports them.

func WithReadOnlyProfile

func WithReadOnlyProfile() ToolOption

WithReadOnlyProfile disables the profile_update action. Used when building the tool for the self-improve reviewer agent, which should read but not arbitrarily overwrite profile notes.

func WithReadOnlySoul

func WithReadOnlySoul() ToolOption

WithReadOnlySoul disables the soul_update action.

type VersionedConstraintStore added in v0.18.0

type VersionedConstraintStore interface {
	GetConstraintsAt(ctx context.Context, userID int64, agentID string, version int64) ([]ConstraintEntry, error)
}

VersionedConstraintStore is implemented by providers that can read constraints at a specific memory version.

type VersionedProfileStore added in v0.18.0

type VersionedProfileStore interface {
	GetProfileAt(ctx context.Context, userID int64, agentID string, version int64) (string, error)
	GetAgentSoulAt(ctx context.Context, userID int64, agentID string, version int64) (string, error)
}

VersionedProfileStore is implemented by providers that can read profile/soul at a specific memory version (from the changelog). Version 0 or negative means "current" (same as ProfileStore).

Directories

Path Synopsis
Package memorytest provides test doubles and conformance testing for memory.Provider implementations.
Package memorytest provides test doubles and conformance testing for memory.Provider implementations.

Jump to

Keyboard shortcuts

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