memory

package
v1.40.2 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultSummaryBudget    = 8000 // Token budget before triggering summarization
	DefaultSummaryThreshold = 0.85 // Percentage of budget that triggers summarization
	DefaultSummaryTarget    = 0.7  // Target percentage of budget after summarization
	// DefaultMinMessagesBeforeSummary   = 20   // (Deprecated) Minimum messages before allowing summarization
	DefaultMinMessagesToKeep          = 10  // Minimum recent messages to keep
	DefaultRecentMessageBudgetPercent = 0.8 // Percentage of target budget for recent messages
)

Default summary buffer settings (from legacy pkg/memory/summary_buffer.go)

View Source
const (
	DefaultTokenBudget    = 8000 // Token budget for history
	DefaultPreserveRecent = 5    // Minimum messages to always keep
)

Default token window settings

View Source
const DefaultBufferWindowSize = 20

DefaultBufferWindowSize is the default number of events to keep.

Variables

This section is empty.

Functions

func EnsureUserFirst added in v1.21.0

func EnsureUserFirst(events []*agent.Event, startIdx int) int

EnsureUserFirst adjusts startIdx to ensure history starts with a User message. This satisfies strict turn-based API constraints (e.g. Gemini) that require conversations to begin with a User message.

It scans backward from startIdx until it finds a User message. If no User message is found before index 0, returns 0.

func NilMemory

func NilMemory() agent.Memory

NilMemory returns a no-op memory implementation.

Use this when memory is not configured to avoid nil checks. All operations succeed but do nothing.

Types

type Adapter

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

Adapter wraps an IndexService to implement agent.Memory.

The adapter is scoped to a specific user/app context, allowing the Search method to automatically scope queries without requiring callers to provide user/app info on every call.

Architecture note:

  • IndexService is a SEARCH INDEX (not storage)
  • Session data is stored in session.Service (the source of truth)
  • The adapter exposes search capabilities to agents

Usage:

indexSvc := memory.NewKeywordIndexService()
adapter := memory.NewAdapter(indexSvc, "my-app", "user-123")

// Now use adapter as agent.Memory
results, err := adapter.Search(ctx, "what is the user's favorite color?")

func NewAdapter

func NewAdapter(svc SearchableService, appName, userID string) *Adapter

NewAdapter creates an agent.Memory adapter for the given SearchableService.

The appName and userID scope all operations to a specific user context. This is typically created per-invocation using session metadata.

func (*Adapter) AddSession

func (a *Adapter) AddSession(ctx context.Context, session agent.Session) error

AddSession is a no-op for the adapter.

Session indexing is handled by the runner calling IndexService.Index() after each turn. The adapter only exposes search capabilities to agents.

func (*Adapter) Search

func (a *Adapter) Search(ctx context.Context, query string) (*agent.MemorySearchResponse, error)

Search returns memory entries relevant to the given query.

The search is automatically scoped to the adapter's appName and userID. Results are converted from memory.SearchResult to agent.MemoryResult.

type BufferWindowConfig

type BufferWindowConfig struct {
	// WindowSize is the maximum number of events to keep.
	// Default: 20
	WindowSize int
}

BufferWindowConfig holds configuration for the buffer window strategy.

type BufferWindowStrategy

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

BufferWindowStrategy implements a simple sliding window that keeps the last N events. This is the simplest and fastest strategy.

BufferWindowMemory keeps the last N conversation turns as context.

func NewBufferWindowStrategy

func NewBufferWindowStrategy(cfg BufferWindowConfig) *BufferWindowStrategy

NewBufferWindowStrategy creates a new buffer window strategy.

func (*BufferWindowStrategy) CheckAndSummarize

func (s *BufferWindowStrategy) CheckAndSummarize(ctx context.Context, events []*agent.Event, onProgress func(status, message string)) ([]*agent.Event, error)

CheckAndSummarize always returns nil (buffer window doesn't summarize).

func (*BufferWindowStrategy) FilterEvents

func (s *BufferWindowStrategy) FilterEvents(events []*agent.Event) []*agent.Event

FilterEvents applies a sliding window that GUARANTEES the history starts with a valid User message (Author == "user"), satisfying strict turn-based API constraints (e.g. Gemini).

It uses a "Backward Scan / Soft Window" approach:

  1. Calculates the ideal window cut point (len - N).
  2. If the cut point lands in the middle of a turn (e.g. Model response), it scans BACKWARD until it finds the initiating User message.
  3. This means the returned history may be slightly larger than windowSize to preserve integrity.

func (*BufferWindowStrategy) Name

func (s *BufferWindowStrategy) Name() string

Name returns the strategy name.

func (*BufferWindowStrategy) WindowSize

func (s *BufferWindowStrategy) WindowSize() int

WindowSize returns the configured window size.

type Entry

type Entry struct {
	SessionID string
	EventID   string
	AppName   string
	UserID    string
	Author    string
	Content   string
	Timestamp time.Time
	Words     map[string]struct{} // Pre-computed word index for keyword search
	Metadata  map[string]any
}

Entry represents a memory entry stored in the index.

type IndexService

type IndexService interface {
	// Index adds session events to the semantic search index.
	//
	// This is called after each turn completes. The session data is already
	// persisted in session.Service (SQL) - this method just builds the
	// search index for fast retrieval.
	//
	// Implementation note: This should be idempotent - calling Index
	// multiple times with the same session should produce the same result.
	Index(ctx context.Context, sess agent.Session) error

	// Search performs semantic similarity search over indexed sessions.
	//
	// The search is scoped to (app_name, user_id) to ensure isolation.
	// Returns results ordered by relevance score (highest first).
	//
	// For vector-based implementations, this uses cosine similarity.
	// For keyword-based implementations, this uses word matching.
	Search(ctx context.Context, req *SearchRequest) (*SearchResponse, error)

	// Rebuild repopulates the entire index from session.Service.
	//
	// This is called:
	//   - On startup when index persistence is disabled
	//   - When the index file is corrupted
	//   - When migrating to a new index format
	//
	// The rebuild process:
	//   1. Clear existing index entries for (app_name, user_id)
	//   2. Load all sessions from session.Service
	//   3. Index each session
	//
	// This can be expensive for large datasets but ensures consistency.
	Rebuild(ctx context.Context, sessions session.Service, appName, userID string) error

	// Clear removes all index entries for a specific session.
	//
	// Called when a session is deleted from session.Service.
	Clear(ctx context.Context, appName, userID, sessionID string) error

	// Name returns the index implementation name (e.g., "chromem", "keyword").
	Name() string
}

IndexService provides semantic search over session data.

This follows the legacy Hector pattern where:

  • session.Service is the SOURCE OF TRUTH (stores all data in SQL)
  • IndexService is a SEARCH INDEX (can be rebuilt from session.Service)

The index is populated after each turn and can be rebuilt on startup if the index is corrupted or needs to be migrated.

This architecture ensures:

  • No data loss (SQL is the source of truth)
  • Fast semantic search (vector index)
  • Rebuild capability (index from session.Service)

Derived from legacy pkg/memory/longterm_strategy.go:

type LongTermMemoryStrategy interface {
    Store(agentID, sessionID string, messages []*pb.Message) error
    Recall(agentID, sessionID, query string, limit int) ([]*pb.Message, error)
    Clear(agentID, sessionID string) error
    Name() string
}

func NewIndexServiceFromConfig

func NewIndexServiceFromConfig(cfg interface{}, embedders map[string]embedder.Embedder) (IndexService, error)

NewIndexServiceFromConfig creates an IndexService based on configuration.

Architecture (derived from legacy Hector):

┌─────────────────────────────────────────────────────────────┐
│   LAYER 3: IndexService (search index)                      │
│   - keyword: Simple word matching (default)                 │
│   - vector: Semantic search using embeddings                │
│   - CAN BE REBUILT from session.Service                     │
├─────────────────────────────────────────────────────────────┤
│   LAYER 2: session.Service (source of truth)                │
│   - SQL storage for all events                              │
│   - THIS IS THE SOURCE OF TRUTH                             │
├─────────────────────────────────────────────────────────────┤
│   LAYER 1: WorkingMemoryStrategy (context window)           │
│   - Ephemeral runtime cache                                 │
│   - Filters events for LLM context                          │
└─────────────────────────────────────────────────────────────┘

Example config:

vector_stores:
  myvector:
    type: chromem
    persist_path: .hector/vectors

embedders:
  default:
    provider: openai
    model: text-embedding-3-small
    api_key: ${OPENAI_API_KEY}

storage:
  memory:
    backend: vector
    embedder: default
    vector_store: myvector

NewIndexServiceFromConfig was the old interface that used config.Config. It has been removed during config refactoring.

type KeywordIndexService

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

KeywordIndexService provides keyword-based search over session data.

This is the default index implementation when no vector database is configured. It uses simple word matching for search, which is fast but not semantic.

Use chromem for semantic search capabilities.

func NewKeywordIndexService

func NewKeywordIndexService() *KeywordIndexService

NewKeywordIndexService creates a new keyword-based index service.

func (*KeywordIndexService) Clear

func (s *KeywordIndexService) Clear(ctx context.Context, appName, userID, sessID string) error

Clear removes index entries for a specific session.

func (*KeywordIndexService) Index

func (s *KeywordIndexService) Index(ctx context.Context, sess agent.Session) error

Index adds session events to the keyword index.

func (*KeywordIndexService) Name

func (s *KeywordIndexService) Name() string

Name returns the index implementation name.

func (*KeywordIndexService) Rebuild

func (s *KeywordIndexService) Rebuild(ctx context.Context, sessions session.Service, appName, userID string) error

Rebuild repopulates the index from session.Service.

func (*KeywordIndexService) Search

Search performs keyword-based search.

type LLMSummarizer

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

LLMSummarizer implements the Summarizer interface using an LLM.

func NewLLMSummarizer

func NewLLMSummarizer(cfg LLMSummarizerConfig) (*LLMSummarizer, error)

NewLLMSummarizer creates a new LLM-based summarizer.

func (*LLMSummarizer) SummarizeConversation

func (s *LLMSummarizer) SummarizeConversation(ctx context.Context, events []*agent.Event) (string, error)

SummarizeConversation summarizes the given events into a concise summary.

type LLMSummarizerConfig

type LLMSummarizerConfig struct {
	// LLM is the language model to use for summarization.
	LLM model.LLM

	// Prompt is a custom summarization prompt template.
	// Use %s twice: 1st for Existing Summary, 2nd for New Dialogue.
	// If empty, uses the default prompt.
	Prompt string
}

LLMSummarizerConfig configures the LLM summarizer.

type NilIndexService

type NilIndexService struct{}

NilIndexService is a no-op implementation for when indexing is disabled.

func (NilIndexService) Clear

func (NilIndexService) Clear(ctx context.Context, appName, userID, sessionID string) error

func (NilIndexService) Index

func (NilIndexService) Index(ctx context.Context, sess agent.Session) error

func (NilIndexService) Name

func (NilIndexService) Name() string

func (NilIndexService) Rebuild

func (NilIndexService) Rebuild(ctx context.Context, sessions session.Service, appName, userID string) error

func (NilIndexService) Search

type NilWorkingMemory

type NilWorkingMemory struct{}

NilWorkingMemory is a no-op strategy that returns all events unchanged. Used when no working memory strategy is configured.

func (NilWorkingMemory) CheckAndSummarize

func (NilWorkingMemory) CheckAndSummarize(ctx context.Context, events []*agent.Event, onProgress func(status, message string)) ([]*agent.Event, error)

CheckAndSummarize always returns nil (no summarization).

func (NilWorkingMemory) FilterEvents

func (NilWorkingMemory) FilterEvents(events []*agent.Event) []*agent.Event

FilterEvents returns all events unchanged.

func (NilWorkingMemory) Name

func (NilWorkingMemory) Name() string

Name returns the strategy name.

type SearchRequest

type SearchRequest struct {
	// Query is the search query (natural language or keywords).
	Query string

	// UserID scopes the search to a specific user's memories.
	UserID string

	// AppName scopes the search to a specific application.
	AppName string
}

SearchRequest represents a request for memory search.

type SearchResponse

type SearchResponse struct {
	// Results contains the matching memory entries.
	Results []SearchResult
}

SearchResponse represents the response from a memory search.

type SearchResult

type SearchResult struct {
	// SessionID identifies which session this memory came from.
	SessionID string

	// EventID identifies the specific event within the session.
	EventID string

	// Content is the text content of the memory.
	Content string

	// Author identifies who created this content (agent name or "user").
	Author string

	// Timestamp indicates when this memory was created.
	Timestamp time.Time

	// Score represents the relevance score (higher is better).
	// For keyword search: number of matching words.
	// For semantic search: cosine similarity.
	Score float64

	// Metadata contains additional context about the memory.
	Metadata map[string]any
}

SearchResult represents a single memory search result.

type SearchableService

type SearchableService interface {
	Search(ctx context.Context, req *SearchRequest) (*SearchResponse, error)
}

SearchableService is implemented by services that support Search. IndexService implements this interface.

type Summarizer

type Summarizer interface {
	// SummarizeConversation summarizes the given messages into a concise summary.
	SummarizeConversation(ctx context.Context, events []*agent.Event) (string, error)
}

Summarizer is the interface for conversation summarization. Implementations should use an LLM to summarize conversation history.

type SummaryBufferConfig

type SummaryBufferConfig struct {
	// Budget is the maximum number of tokens before summarization triggers.
	// Default: 8000
	Budget int

	// Threshold is the percentage of budget that triggers summarization.
	// When current tokens > budget * threshold, summarization occurs.
	// Default: 0.85 (85%)
	Threshold float64

	// Target is the percentage of budget to reduce to after summarization.
	// Recent messages are kept within budget * target.
	// Default: 0.7 (70%)
	Target float64

	// Model is the LLM model name for accurate token counting.
	// Required for accurate counting.
	Model string

	// Summarizer performs conversation summarization.
	// If nil, summarization is disabled (behaves like token_window).
	Summarizer Summarizer

	// MinMessages is a minimum message count for debug logging purposes.
	// Note: Token budget is the primary trigger for summarization, not message count.
	// This field is used in debug logs to help diagnose summarization behavior.
	// Default: 10
	MinMessages int
}

SummaryBufferConfig holds configuration for the summary buffer strategy.

type SummaryBufferStrategy

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

SummaryBufferStrategy implements token-based context window management with automatic summarization when the budget is exceeded.

When the token count exceeds (budget * threshold), old messages are summarized and replaced with a summary message. Recent messages are preserved to maintain conversational continuity.

SummaryBufferMemory keeps a rolling LLM-generated summary of conversation history.

func NewSummaryBufferStrategy

func NewSummaryBufferStrategy(cfg SummaryBufferConfig) (*SummaryBufferStrategy, error)

NewSummaryBufferStrategy creates a new summary buffer strategy.

func (*SummaryBufferStrategy) Budget

func (s *SummaryBufferStrategy) Budget() int

Budget returns the configured token budget.

func (*SummaryBufferStrategy) CheckAndSummarize

func (s *SummaryBufferStrategy) CheckAndSummarize(ctx context.Context, events []*agent.Event, onProgress func(status, message string)) ([]*agent.Event, error)

CheckAndSummarize checks if summarization should occur and performs it. Returns the new working memory state [Summary + Recent] to REPLACE the session, or nil if no summarization is needed.

func (*SummaryBufferStrategy) FilterEvents

func (s *SummaryBufferStrategy) FilterEvents(events []*agent.Event) []*agent.Event

FilterEvents returns events that fit within the target token budget. It looks for existing summaries (checkpoint) and loads from there, or applies token-based filtering.

func (*SummaryBufferStrategy) Name

func (s *SummaryBufferStrategy) Name() string

Name returns the strategy name.

func (*SummaryBufferStrategy) Target

func (s *SummaryBufferStrategy) Target() float64

Target returns the configured target.

func (*SummaryBufferStrategy) Threshold

func (s *SummaryBufferStrategy) Threshold() float64

Threshold returns the configured threshold.

type TokenWindowConfig

type TokenWindowConfig struct {
	// Budget is the maximum number of tokens for conversation history.
	// Default: 8000
	Budget int

	// PreserveRecent is the minimum number of recent messages to always keep,
	// even if they exceed the budget slightly.
	// Default: 5
	PreserveRecent int

	// Model is the LLM model name for accurate token counting.
	// Required for accurate counting; falls back to estimation if not provided.
	Model string
}

TokenWindowConfig holds configuration for the token window strategy.

type TokenWindowStrategy

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

TokenWindowStrategy implements token-based context window management. It keeps events that fit within the token budget, working backwards from the most recent events (which are typically most relevant).

This is a simplified version of SummaryBufferStrategy that focuses only on truncation without summarization.

func NewTokenWindowStrategy

func NewTokenWindowStrategy(cfg TokenWindowConfig) (*TokenWindowStrategy, error)

NewTokenWindowStrategy creates a new token window strategy.

func (*TokenWindowStrategy) Budget

func (s *TokenWindowStrategy) Budget() int

Budget returns the configured token budget.

func (*TokenWindowStrategy) CheckAndSummarize

func (s *TokenWindowStrategy) CheckAndSummarize(ctx context.Context, events []*agent.Event, onProgress func(status, message string)) ([]*agent.Event, error)

CheckAndSummarize always returns nil (token window doesn't summarize). For summarization support, use SummaryBufferStrategy.

func (*TokenWindowStrategy) FilterEvents

func (s *TokenWindowStrategy) FilterEvents(events []*agent.Event) []*agent.Event

FilterEvents returns events that fit within the token budget. It preserves at least preserveRecent messages and works backwards from the most recent events.

func (*TokenWindowStrategy) Name

func (s *TokenWindowStrategy) Name() string

Name returns the strategy name.

type VectorIndexConfig

type VectorIndexConfig struct {
	// Provider for vector storage and search (required).
	Provider vector.Provider

	// Embedder for generating vector embeddings (required).
	Embedder embedder.Embedder

	// CollectionName for storing memory entries (optional).
	// Default: "hector_memory"
	CollectionName string
}

VectorIndexConfig configures the vector index service.

type VectorIndexService

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

VectorIndexService provides semantic vector search using the vector.Provider abstraction.

This implementation uses the unified vector.Provider interface, allowing different backends (chromem-go, Qdrant, etc.) to be used interchangeably.

Architecture:

session.Service (SQL) → SOURCE OF TRUTH
     ↓
VectorIndexService → SEARCH INDEX
     │
     ├── vector.Provider (chromem, qdrant, etc.)
     └── embedder.Embedder (OpenAI, Ollama)

func NewVectorIndexService

func NewVectorIndexService(cfg VectorIndexConfig) (*VectorIndexService, error)

NewVectorIndexService creates a new vector-based index service.

func (*VectorIndexService) Clear

func (s *VectorIndexService) Clear(ctx context.Context, appName, userID, sessionID string) error

Clear removes index entries for a specific session.

func (*VectorIndexService) Close

func (s *VectorIndexService) Close() error

Close releases resources.

func (*VectorIndexService) Index

func (s *VectorIndexService) Index(ctx context.Context, sess agent.Session) error

Index adds session events to the vector index.

func (*VectorIndexService) Name

func (s *VectorIndexService) Name() string

Name returns the index implementation name.

func (*VectorIndexService) Rebuild

func (s *VectorIndexService) Rebuild(ctx context.Context, sessions session.Service, appName, userID string) error

Rebuild repopulates the index from session.Service.

func (*VectorIndexService) Search

Search performs semantic similarity search.

type WorkingMemoryProvider

type WorkingMemoryProvider interface {
	// WorkingMemory returns the agent's working memory strategy.
	// Returns nil if no strategy is configured.
	WorkingMemory() WorkingMemoryStrategy
}

WorkingMemoryProvider is implemented by agents that have a working memory strategy. This allows the runner to access the strategy for post-turn summarization.

type WorkingMemoryStrategy

type WorkingMemoryStrategy interface {
	// Name returns the strategy identifier.
	Name() string

	// FilterEvents applies the strategy to filter/truncate events for context window.
	// This is called before building messages for the LLM.
	// Returns the filtered events that should be included in the context.
	FilterEvents(events []*agent.Event) []*agent.Event

	// CheckAndSummarize checks if summarization should occur and performs it if needed.
	// This is called after a turn completes (when events are persisted).
	//
	// The onProgress callback allows the strategy to report progress (e.g. "Summarizing...")
	// before the potentially long-running operation completes.
	//
	// Returns:
	//   - nil, nil: No summarization needed, session unchanged
	//   - []*agent.Event, nil: New working memory state to REPLACE the session events
	//
	// The returned events represent the compacted working memory:
	//   [SummaryEvent] + [RecentEvents]
	//
	// This is optional - strategies like buffer_window return nil.
	CheckAndSummarize(ctx context.Context, events []*agent.Event, onProgress func(status, message string)) ([]*agent.Event, error)
}

WorkingMemoryStrategy defines the interface for context window management. Different strategies can implement different approaches:

  • buffer_window: Keep last N messages (simple, fast)
  • token_window: Keep messages within token budget (accurate)
  • summary_buffer: Summarize old messages when exceeding budget (compact)

Working memory tracks the current conversation context.

NOTE: Future optimization opportunity - session loading could be checkpoint-aware to avoid loading all events for strategies like summary_buffer. The session.GetRequest already supports NumRecentEvents for this purpose. See pkg/memory/summary_buffer.go LoadState for the legacy approach.

func NewWorkingMemoryStrategyFromConfig added in v1.14.0

func NewWorkingMemoryStrategyFromConfig(cfg *config.ContextConfig, defaultModel string, llms map[string]model.LLM, defaultLLM model.LLM) (WorkingMemoryStrategy, error)

NewWorkingMemoryStrategyFromConfig creates a working memory strategy from configuration.

Jump to

Keyboard shortcuts

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