feat: implement memory management system with SQLite persistence, including conversation windows and episodic facts

This commit is contained in:
2026-03-06 00:39:22 +00:00
parent d26be78c46
commit cb9489e633
7 changed files with 642 additions and 8 deletions
+20
View File
@@ -0,0 +1,20 @@
package memory
import "context"
// Store is the interface for persistent memory operations.
// Defined in the pure package; implemented by shell/memory.
type Store interface {
// Facts
SaveFact(ctx context.Context, fact Fact) error
RecallFacts(ctx context.Context, agentID, subject string, key *string) ([]Fact, error)
DeleteFacts(ctx context.Context, agentID, subject string, key *string) error
// Message history
SaveMessage(ctx context.Context, msg HistoryMessage) error
LoadMessages(ctx context.Context, agentID, roomID string, limit int) ([]HistoryMessage, error)
DeleteMessages(ctx context.Context, agentID string, roomID *string) error
// Lifecycle
Close() error
}
+26
View File
@@ -0,0 +1,26 @@
// Package memory provides pure types for agent memory: conversation windows and episodic facts.
package memory
import (
"time"
"github.com/enmanuel/agents/pkg/llm"
)
// Fact is a single episodic fact: a key-value pair scoped to a subject.
type Fact struct {
AgentID string
Subject string
Key string
Value string
UpdatedAt time.Time
}
// HistoryMessage is a persisted conversation message.
type HistoryMessage struct {
AgentID string
RoomID string
Role llm.Role
Content string
CreatedAt time.Time
}
+43
View File
@@ -0,0 +1,43 @@
package memory
import "github.com/enmanuel/agents/pkg/llm"
// Window is an immutable sliding window of conversation messages for a single room.
type Window struct {
messages []llm.Message
maxSize int
}
// NewWindow creates an empty window with the given capacity.
func NewWindow(maxSize int) Window {
return Window{maxSize: maxSize}
}
// Append returns a new Window with the message added, dropping the oldest
// messages if capacity is exceeded.
func (w Window) Append(msg llm.Message) Window {
msgs := make([]llm.Message, len(w.messages), len(w.messages)+1)
copy(msgs, w.messages)
msgs = append(msgs, msg)
if len(msgs) > w.maxSize {
msgs = msgs[len(msgs)-w.maxSize:]
}
return Window{messages: msgs, maxSize: w.maxSize}
}
// ToLLMMessages returns a copy of the window contents as []llm.Message.
func (w Window) ToLLMMessages() []llm.Message {
out := make([]llm.Message, len(w.messages))
copy(out, w.messages)
return out
}
// Len returns the number of messages in the window.
func (w Window) Len() int {
return len(w.messages)
}
// Clear returns an empty window with the same capacity.
func (w Window) Clear() Window {
return NewWindow(w.maxSize)
}