Documentation
¶
Overview ¶
Package memory implements cross-session long-term memory: what the system remembers about one principal inside one workspace, independently of any single chat session.
Index ¶
- Variables
- func NewMemoryService(repo interfaces.MemoryRepository, tenantRepo interfaces.TenantRepository, ...) interfaces.MemoryService
- func ResolveScope(ctx context.Context) (interfaces.MemoryScope, error)
- type Service
- func (s *Service) Clear(ctx context.Context) (int64, error)
- func (s *Service) ConfirmItem(ctx context.Context, id string) (*types.MemoryItem, error)
- func (s *Service) ConsolidateNow(ctx context.Context) (*types.MemoryConsolidationResult, error)
- func (s *Service) CreateItem(ctx context.Context, kind, content string, importance int) (*types.MemoryItem, error)
- func (s *Service) DeleteDocument(ctx context.Context, id string) error
- func (s *Service) DeleteItem(ctx context.Context, id string) error
- func (s *Service) DeleteTopic(ctx context.Context, id string) error
- func (s *Service) DocumentAffinity(ctx context.Context, knowledgeIDs []string) map[string]int
- func (s *Service) FamiliarKnowledgeIDs(ctx context.Context) []string
- func (s *Service) GetSettings(ctx context.Context) (*types.MemorySettings, error)
- func (s *Service) Handle(ctx context.Context, task *asynq.Task) error
- func (s *Service) ListDocuments(ctx context.Context, limit, offset int) ([]*types.MemoryDocView, int64, error)
- func (s *Service) ListItems(ctx context.Context, status string, limit, offset int) ([]*types.MemoryItem, int64, error)
- func (s *Service) ListTopics(ctx context.Context, limit, offset int) ([]*types.MemoryTopicView, int64, error)
- func (s *Service) MemoryAvailable(ctx context.Context) bool
- func (s *Service) ObserveQuestionTopics(ctx context.Context, topics []string) []string
- func (s *Service) PromoteTopic(ctx context.Context, id string) (*types.MemoryItem, error)
- func (s *Service) Recall(ctx context.Context, query string) interfaces.MemoryRecall
- func (s *Service) RecordAnswerSources(ctx context.Context, refs []types.MemoryDocAffinity)
- func (s *Service) RejectItem(ctx context.Context, id string) error
- func (s *Service) Remember(ctx context.Context, item types.MemoryItem) (*types.MemoryItem, error)
- func (s *Service) RetrievalContextFor(ctx context.Context) interfaces.RetrievalContext
- func (s *Service) ScheduleExtraction(ctx context.Context, sessionID, messageID, chatModelID string)
- func (s *Service) SearchMemory(ctx context.Context, query string, limit int) interfaces.MemorySearchResult
- func (s *Service) SetEnabled(ctx context.Context, enabled bool) error
- func (s *Service) UpdateItem(ctx context.Context, id, content string, importance int) (*types.MemoryItem, error)
Constants ¶
This section is empty.
Variables ¶
var ErrItemNotFound = errors.New("memory: item not found")
ErrItemNotFound is returned when an item id does not exist in the caller's own memory space. Scope mismatch and genuine absence deliberately produce the same error so an id cannot be probed for existence across users.
var ErrMemoryDisabled = errors.New("memory: disabled for this scope")
ErrMemoryDisabled is returned by write operations when memory is off at the workspace or user level.
var ErrNoMemoryScope = errors.New("memory: no principal in context")
ErrNoMemoryScope means the request carries no principal we can attribute memory to. Callers on the read path treat it as "no memory"; callers on the API path turn it into an error, because a memory manager with no owner is a bug rather than an empty state.
var ErrPreviouslyForgotten = errors.New("memory: previously forgotten by the user")
ErrPreviouslyForgotten means the statement matches one the user deleted. Callers on the write path treat it as "nothing to do", not as a failure.
var ErrSensitiveContent = errors.New("memory: statement was sensitive material")
ErrSensitiveContent means the statement was almost entirely credentials or identity numbers, so redacting it left nothing worth remembering.
Functions ¶
func NewMemoryService ¶
func NewMemoryService( repo interfaces.MemoryRepository, tenantRepo interfaces.TenantRepository, messageRepo interfaces.MessageRepository, modelService interfaces.ModelService, enqueuer interfaces.TaskEnqueuer, cfg *config.Config, ) interfaces.MemoryService
NewMemoryService builds the long-term memory service.
func ResolveScope ¶
func ResolveScope(ctx context.Context) (interfaces.MemoryScope, error)
ResolveScope derives the memory space from the request context alone.
Deriving rather than accepting a scope is the whole isolation model: there is no code path where a client-supplied id can select a memory space, so no endpoint has to be audited for that. The subject is Principal.StorageID(), which covers web users, IM users, API external users and embed visitors alike, and it is paired with the workspace so the same person's memories do not leak between workspaces.
The workspace is the caller's own, not the execution tenant: a shared agent runs under its owner's tenant so its models and knowledge resolve there, and WithExecutionTenant keeps Caller intact precisely so identity-scoped data (memory) still follows the person who is chatting. Reading TenantIDContextKey here would look up the owner's workspace and never find the caller's memories.
Types ¶
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service implements interfaces.MemoryService.
func (*Service) ConfirmItem ¶
ConfirmItem accepts something the system inferred, moving it out of the pending inbox and into use.
func (*Service) ConsolidateNow ¶
ConsolidateNow reviews the caller's store immediately, without waiting for the daily maintenance pass that rides along on distillation.
func (*Service) CreateItem ¶
func (s *Service) CreateItem( ctx context.Context, kind, content string, importance int, ) (*types.MemoryItem, error)
CreateItem adds a memory the user typed themselves. It goes through the same write path as everything else, so a hand-written memory can supersede an extracted one about the same topic rather than sitting next to it.
func (*Service) DeleteDocument ¶
DeleteDocument stops using one document as a personal retrieval signal.
func (*Service) DeleteItem ¶
DeleteItem forgets one memory permanently.
func (*Service) DeleteTopic ¶
DeleteTopic stops tracking a subject and remembers the refusal so automatic promotion cannot bring the same label back.
func (*Service) DocumentAffinity ¶
DocumentAffinity scores documents by how much this person has relied on them.
func (*Service) FamiliarKnowledgeIDs ¶
FamiliarKnowledgeIDs returns document ids this person keeps citing.
func (*Service) GetSettings ¶
GetSettings returns the merged view the settings UI renders.
func (*Service) ListDocuments ¶
func (s *Service) ListDocuments( ctx context.Context, limit, offset int, ) ([]*types.MemoryDocView, int64, error)
ListDocuments returns documents cited often enough to count as a habit.
func (*Service) ListItems ¶
func (s *Service) ListItems( ctx context.Context, status string, limit, offset int, ) ([]*types.MemoryItem, int64, error)
ListItems backs the memory manager list.
func (*Service) ListTopics ¶
func (s *Service) ListTopics( ctx context.Context, limit, offset int, ) ([]*types.MemoryTopicView, int64, error)
ListTopics returns subjects that have been counted but not yet promoted.
func (*Service) MemoryAvailable ¶
MemoryAvailable reports whether this request may read memory at all.
It is deliberately the same predicate SearchMemory itself applies, rather than a second reading of the three switches. A caller that decides whether to offer a memory feature and the code that answers when it is used must never be able to disagree about whether memory is on.
func (*Service) ObserveQuestionTopics ¶
ObserveQuestionTopics counts what a person asked about and promotes a subject into memory once it recurs.
This is the answer to "a knowledge-base question is not about the user, so it produces nothing". A single question really is noise — recording it would fill the profile with every passing curiosity. But the same subject across several conversations says something durable about the person, and counting first is how MemoryOS separates the two without a rule that throws away every question. Returns the interests promoted by this call.
func (*Service) PromoteTopic ¶
PromoteTopic turns a counted subject into an interest without waiting.
func (*Service) Recall ¶
func (s *Service) Recall(ctx context.Context, query string) interfaces.MemoryRecall
Recall assembles the memory to inject for one turn. It never calls a model and never returns an error: memory is an enhancement, so any failure has to degrade into an ordinary answer rather than into a failed request.
func (*Service) RecordAnswerSources ¶
func (s *Service) RecordAnswerSources(ctx context.Context, refs []types.MemoryDocAffinity)
RecordAnswerSources notes which documents an answer drew on.
The references attached to an answer are a weaker signal than an explicit thumbs-up, but they are the only one available without asking the user anything, and they are what makes the reranker able to prefer the material this person keeps coming back to.
func (*Service) RejectItem ¶
RejectItem declines an inference. It deletes rather than archives, so the tombstone stops the same guess from being proposed again next week.
func (*Service) Remember ¶
func (s *Service) Remember(ctx context.Context, item types.MemoryItem) (*types.MemoryItem, error)
Remember stores one statement, resolving any contradiction with what is already known about the same topic.
func (*Service) RetrievalContextFor ¶
func (s *Service) RetrievalContextFor(ctx context.Context) interfaces.RetrievalContext
RetrievalContextFor returns what memory contributes to retrieval.
Like Recall this makes no model call: it is two indexed reads plus string assembly, because it runs before the first token of every retrieval turn.
func (*Service) ScheduleExtraction ¶
ScheduleExtraction records that a turn needs distilling and, when nobody else has already done so, queues the run.
The important property is that a turn is never dropped. Earlier this method compared the current time against the last run and returned early inside the interval, which silently discarded every turn in that window. Now the turn is always recorded against the subject; the timers only decide *when* a run happens, never *whether* a message is considered.
Everything the handler needs travels in the payload. Both asynq and the Lite executor hand the handler a bare context, so any scope the request knew and the payload does not carry is gone by the time the task runs.
func (*Service) SearchMemory ¶
func (s *Service) SearchMemory( ctx context.Context, query string, limit int, ) interfaces.MemorySearchResult
SearchMemory ranks this user's stored memories against an arbitrary query.
Recall runs once per turn, against the question the user opened with, and what it admits is capped hard: five situational items inside a 600-rune budget. Two things fall outside that. An agent loop that has spent ten iterations moving away from the opening question is now working on something that shares no wording with what recall was ranked against; and a subject with dozens of stored facts has most of them sitting below the cut with no way to reach them. Neither is fixable by enlarging the per-turn budget, because that budget is paid on every turn including the ones that need none of it.
Only active items are searched. Superseded and archived memories stay out of reach on purpose: a statement that a newer one replaced is precisely what the supersede machinery exists to keep out of an answer, and surfacing it through a side door would undo that.
func (*Service) SetEnabled ¶
SetEnabled flips the caller's own opt out.
func (*Service) UpdateItem ¶
func (s *Service) UpdateItem( ctx context.Context, id, content string, importance int, ) (*types.MemoryItem, error)
UpdateItem edits one item from the memory manager. Edited items become manual so a later extraction does not quietly undo a user's correction.