Documentation
¶
Index ¶
- Constants
- Variables
- func BuildTranscript(messages []types.Message, maxChars int) string
- type Consolidatable
- type ConsolidationReport
- type Consolidator
- type ConsolidatorConfig
- type Entity
- type EntityInput
- type Extractor
- type ExtractorConfig
- type Graph
- type LLMCaller
- type Lister
- type ObservationInput
- type ObservationResult
- type Recaller
- type Relation
- type RelationInput
- type SQLiteStore
- func (s *SQLiteStore) AddObservations(ctx context.Context, userID string, inputs []ObservationInput) ([]ObservationResult, error)
- func (s *SQLiteStore) ListEntities(ctx context.Context, userID string) ([]Entity, error)
- func (s *SQLiteStore) ListSnapshots(ctx context.Context, userID string) ([]SnapshotInfo, error)
- func (s *SQLiteStore) OpenNodes(ctx context.Context, userID string, names []string) (*Graph, error)
- func (s *SQLiteStore) ReplaceEntities(ctx context.Context, userID string, snapshot []Entity, entities []EntityInput, ...) error
- func (s *SQLiteStore) RestoreSnapshot(ctx context.Context, userID, snapshotID string) error
- func (s *SQLiteStore) RetrieveForContext(ctx context.Context, userID, query string, maxTokens int) (string, error)
- func (s *SQLiteStore) SaveSnapshot(ctx context.Context, userID, reason string) (SnapshotInfo, error)
- func (s *SQLiteStore) SearchNodes(ctx context.Context, userID, query string) (*Graph, error)
- func (s *SQLiteStore) UpsertEntities(ctx context.Context, userID string, inputs []EntityInput) ([]Entity, error)
- type SnapshotInfo
- type Snapshotter
- type Store
Constants ¶
const DefaultRecallChars = 6_000
DefaultRecallChars is the size of the block, about 1,500 tokens.
Variables ¶
var ErrChangedDuringConsolidation = errors.New("memory changed while it was being consolidated")
ErrChangedDuringConsolidation is returned when memory was written while a consolidation was being computed. The consolidation is dropped, not retried: the next one starts from the new state.
var ErrNoSnapshotSupport = errors.New("the memory store cannot take snapshots, so memory is not consolidated")
ErrNoSnapshotSupport is returned by a consolidation whose store cannot copy memory first: it does not run without a way back.
var ErrRefusedConsolidation = errors.New("consolidation result refused")
ErrRefusedConsolidation is returned when the model's result was refused as unsafe to apply.
var ErrSnapshotNotFound = errors.New("memory snapshot not found")
ErrSnapshotNotFound is returned when a snapshot does not exist for the user.
Functions ¶
Types ¶
type Consolidatable ¶ added in v1.10.1
type Consolidatable interface {
Store
// ListEntities returns every entity of the user, ordered by name.
ListEntities(ctx context.Context, userID string) ([]Entity, error)
// ReplaceEntities rewrites the entities of snapshot (what ListEntities returned) as entities, atomically: a
// snapshot entity not in entities is removed, one in entities is created or rewritten. It returns
// ErrChangedDuringConsolidation, changing nothing, if any snapshot entity was modified or removed since the
// snapshot; entities created since are left alone. renames maps the name of an entity merged into another to the
// name it now has, so that relations follow it; a relation left without both endpoints, or pointing to itself, is
// removed.
ReplaceEntities(ctx context.Context, userID string, snapshot []Entity, entities []EntityInput, renames map[string]string) error
}
Consolidatable is a Store that can be rewritten as a whole, which consolidation needs: the plain Store only adds. SQLiteStore implements it; a host with its own Store opts in by implementing it too.
type ConsolidationReport ¶ added in v1.10.1
type ConsolidationReport struct {
EntitiesBefore, EntitiesAfter int
ObservationsBefore, ObservationsAfter int
// SnapshotID names the copy of the memory taken before the rewrite; UndoLast, or Snapshotter.RestoreSnapshot with
// this id, puts it back.
SnapshotID string
}
ConsolidationReport says what a consolidation changed.
type Consolidator ¶ added in v1.10.1
type Consolidator struct {
// contains filtered or unexported fields
}
Consolidator is the second phase of memory: extraction only ever adds, so memory fills with near-duplicates and facts that were later contradicted. The consolidator rereads a user's whole memory and rewrites it smaller: merging duplicates, keeping the newest of two statements that conflict, and dropping what no longer holds.
func NewConsolidator ¶ added in v1.10.1
func NewConsolidator(store Consolidatable, caller LLMCaller, cfg ConsolidatorConfig) *Consolidator
NewConsolidator creates a Consolidator.
func (*Consolidator) Consolidate ¶ added in v1.10.1
func (c *Consolidator) Consolidate(ctx context.Context, userID string) (*ConsolidationReport, error)
Consolidate rewrites the user's memory now, if it holds at least MinObservations. It returns a nil report when it is too small.
func (*Consolidator) MaybeConsolidate ¶ added in v1.10.1
func (c *Consolidator) MaybeConsolidate(ctx context.Context, userID string) (*ConsolidationReport, error)
MaybeConsolidate consolidates the user's memory if it is big enough and was not consolidated recently, and does nothing otherwise (a nil report). An attempt that fails still counts as recent, so a failing model is not called again at every session.
type ConsolidatorConfig ¶ added in v1.10.1
type ConsolidatorConfig struct {
// MinObservations is the number of observations a user must hold before consolidation is worth a model call.
// Default: 60.
MinObservations int
// MinInterval is the shortest time between two consolidations of the same user, kept per process. Default: 24h.
MinInterval time.Duration
// MaxInputChars caps the memory sent to the model. Entities past it are left as they are. Default: 60000.
MaxInputChars int
// MinKeepRatio is the share of the observations a result must keep to be applied: a result that drops more is
// taken for a model error and refused. Default: 0.3.
MinKeepRatio float64
// Model is the model that consolidates. Zero uses the caller's default.
Model types.ModelIdentifier
// Timeout is the deadline of the model call. Default: 120s.
Timeout time.Duration
}
ConsolidatorConfig controls when and how memory is consolidated.
type Entity ¶
type Entity struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Name string `json:"name"`
EntityType string `json:"entity_type"`
Observations []string `json:"observations"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Entity is a named thing with a type and a list of observations. Modelled after the MCP memory server reference (servers/src/memory).
type EntityInput ¶
type EntityInput struct {
Name string `json:"name"`
EntityType string `json:"entity_type"`
Observations []string `json:"observations"`
}
EntityInput is used for upsert operations.
func ParseLLMResponse ¶
func ParseLLMResponse(raw string) ([]EntityInput, error)
ParseLLMResponse extracts EntityInputs from a JSON string that may be wrapped in markdown fences or surrounded by explanatory prose.
func ScreenEntities ¶ added in v1.10.1
func ScreenEntities(in []EntityInput) []EntityInput
ScreenEntities applies the limits to what a model proposed to remember: it drops an observation that holds a credential, shortens an overlong one, and keeps at most maxEntitiesPerExtraction entities with maxObservationsPerEntity observations each. An entity left with nothing to say is dropped.
type Extractor ¶
type Extractor struct {
// contains filtered or unexported fields
}
Extractor runs LLM-powered extraction of entities and observations from session transcripts and persists them in the long-term memory graph.
Extraction is designed to be asynchronous and best-effort: any error is logged at DEBUG level and not surfaced to the user.
func NewExtractor ¶
func NewExtractor(store Store, caller LLMCaller, cfg ExtractorConfig) *Extractor
NewExtractor creates an Extractor. Zero-value config fields are replaced with defaults.
func (*Extractor) Extract ¶
Extract analyses the conversation and upserts any extracted entities into the knowledge graph for userID.
It is safe to call from a goroutine: all errors are logged and the function always returns nil to the caller (extraction failures are non-fatal).
func (*Extractor) SetConsolidator ¶ added in v1.10.1
func (e *Extractor) SetConsolidator(c *Consolidator)
SetConsolidator makes the extractor consolidate the user's memory after each extraction, when the consolidator finds it due. Nil (the default) never consolidates.
type ExtractorConfig ¶
type ExtractorConfig struct {
// MinTurns is the minimum number of user turns required before extraction
// runs. Short sessions rarely contain facts worth persisting.
// Default: 3.
MinTurns int
// MaxTranscriptChars caps the conversation snippet fed to the LLM.
// Keeps extraction cheap; recent turns are preferred when truncating.
// Default: 8000.
MaxTranscriptChars int
// Model is the LLM model used for extraction. Haiku-class recommended.
// When Model.Model is empty the request is sent without an explicit model
// field, relying on the caller's default.
Model types.ModelIdentifier
// ExtractionTimeout is the per-call deadline passed to CreateMessage.
// Default: 45 s.
ExtractionTimeout time.Duration
}
ExtractorConfig controls extraction behaviour.
func DefaultExtractorConfig ¶
func DefaultExtractorConfig() ExtractorConfig
DefaultExtractorConfig returns sensible defaults.
type LLMCaller ¶
type LLMCaller interface {
CreateMessage(ctx context.Context, req types.APIRequest) (*types.APIResponse, error)
}
LLMCaller is the minimal interface for calling the provider API. *providers.Client satisfies this — defined here as an interface so the extractor can be tested without a live network call.
type Lister ¶ added in v1.10.2
Lister is a Store that can list a user's entities, which recall needs: the plain Store only searches by substring.
type ObservationInput ¶
type ObservationInput struct {
EntityName string `json:"entity_name"`
Contents []string `json:"contents"`
}
ObservationInput appends observations to an existing entity.
type ObservationResult ¶
type ObservationResult struct {
EntityName string `json:"entity_name"`
AddedObservations []string `json:"added_observations"`
}
ObservationResult reports what was actually added after deduplication.
type Recaller ¶ added in v1.10.2
type Recaller struct {
// contains filtered or unexported fields
}
Recaller turns a user's memory into the block of text placed in the system prompt of a new session, so the agent starts knowing what earlier conversations taught instead of having to think of searching for it.
func NewRecaller ¶ added in v1.10.2
NewRecaller creates a Recaller. maxChars <= 0 uses DefaultRecallChars.
func (*Recaller) Recall ¶ added in v1.10.2
Recall returns the memory block for a conversation that starts with query, or an empty string when memory holds nothing relevant. Entities of the always-recalled types come first, then those that share words with the query, best match first, until the block is full.
type Relation ¶
type Relation struct {
ID string `json:"id"`
UserID string `json:"user_id"`
From string `json:"from"` // entity name
To string `json:"to"` // entity name
RelationType string `json:"relation_type"`
CreatedAt time.Time `json:"created_at"`
}
Relation is a directed edge between two entities (active-voice convention).
type RelationInput ¶
type RelationInput struct {
From string `json:"from"`
To string `json:"to"`
RelationType string `json:"relation_type"`
}
RelationInput is used for create operations.
type SQLiteStore ¶
type SQLiteStore struct {
// contains filtered or unexported fields
}
SQLiteStore implements Store using a SQLite database. Schema is initialized via migration 20260615_012_longterm_memory.
func NewSQLiteStore ¶
func NewSQLiteStore(db *sql.DB) *SQLiteStore
NewSQLiteStore wraps an open *sql.DB as a longterm memory Store. The caller is responsible for closing the underlying DB.
func (*SQLiteStore) AddObservations ¶
func (s *SQLiteStore) AddObservations(ctx context.Context, userID string, inputs []ObservationInput) ([]ObservationResult, error)
AddObservations appends new observations to existing entities. Duplicate observation strings (per entity) are silently skipped.
func (*SQLiteStore) ListEntities ¶ added in v1.10.1
ListEntities returns every entity of the user, ordered by name.
func (*SQLiteStore) ListSnapshots ¶ added in v1.10.2
func (s *SQLiteStore) ListSnapshots(ctx context.Context, userID string) ([]SnapshotInfo, error)
ListSnapshots implements Snapshotter.
func (*SQLiteStore) OpenNodes ¶
OpenNodes retrieves specific entities by exact name and their relations.
func (*SQLiteStore) ReplaceEntities ¶ added in v1.10.1
func (s *SQLiteStore) ReplaceEntities(ctx context.Context, userID string, snapshot []Entity, entities []EntityInput, renames map[string]string) error
ReplaceEntities implements Consolidatable.
func (*SQLiteStore) RestoreSnapshot ¶ added in v1.10.2
func (s *SQLiteStore) RestoreSnapshot(ctx context.Context, userID, snapshotID string) error
RestoreSnapshot implements Snapshotter.
func (*SQLiteStore) RetrieveForContext ¶
func (s *SQLiteStore) RetrieveForContext(ctx context.Context, userID, query string, maxTokens int) (string, error)
RetrieveForContext searches memory and formats results as a Markdown block.
func (*SQLiteStore) SaveSnapshot ¶ added in v1.10.2
func (s *SQLiteStore) SaveSnapshot(ctx context.Context, userID, reason string) (SnapshotInfo, error)
SaveSnapshot implements Snapshotter.
func (*SQLiteStore) SearchNodes ¶
SearchNodes performs case-insensitive substring search across entity names, types, and observation content.
func (*SQLiteStore) UpsertEntities ¶
func (s *SQLiteStore) UpsertEntities(ctx context.Context, userID string, inputs []EntityInput) ([]Entity, error)
UpsertEntities creates entities that do not yet exist (deduplicates by name per user). Returns only the entities that were actually inserted.
type SnapshotInfo ¶ added in v1.10.2
type SnapshotInfo struct {
ID string `json:"id"`
Reason string `json:"reason"`
CreatedAt time.Time `json:"created_at"`
Entities int `json:"entities"`
Observations int `json:"observations"`
}
SnapshotInfo describes a copy of a user's memory.
type Snapshotter ¶ added in v1.10.2
type Snapshotter interface {
// SaveSnapshot copies the user's entities and relations and returns the copy's description.
SaveSnapshot(ctx context.Context, userID, reason string) (SnapshotInfo, error)
// ListSnapshots returns the user's copies, newest first.
ListSnapshots(ctx context.Context, userID string) ([]SnapshotInfo, error)
// RestoreSnapshot replaces the user's memory with the copy. It first copies the memory as it is, so that a restore
// can be undone too.
RestoreSnapshot(ctx context.Context, userID, snapshotID string) error
}
Snapshotter is a store that can copy a user's memory and put the copy back. SQLiteStore implements it; a host with its own Consolidatable store implements it too to allow consolidation, which refuses to run without a way back.
type Store ¶
type Store interface {
// UpsertEntities creates entities that do not exist yet (deduplicates by name).
// Returns only the entities that were actually inserted.
UpsertEntities(ctx context.Context, userID string, inputs []EntityInput) ([]Entity, error)
// AddObservations appends new observations to existing entities.
// Duplicate observation strings are silently skipped.
AddObservations(ctx context.Context, userID string, inputs []ObservationInput) ([]ObservationResult, error)
// SearchNodes performs case-insensitive substring search across entity names,
// entity types, and observation content. Returns matching entities plus all
// relations where at least one endpoint is in the result set.
SearchNodes(ctx context.Context, userID, query string) (*Graph, error)
// OpenNodes returns specific entities by exact name and the relations that
// touch them (at least one endpoint in the requested set).
OpenNodes(ctx context.Context, userID string, names []string) (*Graph, error)
// RetrieveForContext retrieves memory relevant to the current query and
// formats it as a Markdown block capped to approximately maxTokens.
// Returns an empty string when there is nothing relevant.
RetrieveForContext(ctx context.Context, userID, query string, maxTokens int) (string, error)
}
Store is the persistence interface for the long-term memory knowledge graph. Concrete persistence implementations are injected by the embedding runtime. All operations are scoped to a single user via userID.