diff --git a/shell/memory/migrations/001_init.sql b/shell/memory/migrations/001_init.sql new file mode 100644 index 0000000..027b878 --- /dev/null +++ b/shell/memory/migrations/001_init.sql @@ -0,0 +1,22 @@ +-- Memoria persistente del agente: facts (clave/valor por sujeto) + messages (historial chat). + +CREATE TABLE IF NOT EXISTS facts ( + agent_id TEXT NOT NULL, + subject TEXT NOT NULL, + key TEXT NOT NULL, + value TEXT NOT NULL, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (agent_id, subject, key) +); + +CREATE TABLE IF NOT EXISTS messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + agent_id TEXT NOT NULL, + room_id TEXT NOT NULL, + role TEXT NOT NULL, + content TEXT NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_messages_room ON messages(agent_id, room_id, created_at DESC); +CREATE INDEX IF NOT EXISTS idx_facts_subject ON facts(agent_id, subject); diff --git a/shell/memory/sqlite.go b/shell/memory/sqlite.go index cfb0720..2a2a6e1 100644 --- a/shell/memory/sqlite.go +++ b/shell/memory/sqlite.go @@ -4,38 +4,43 @@ package shellmem import ( "context" "database/sql" + "embed" "fmt" + "io/fs" "log/slog" "os" "path/filepath" + "sort" + "strings" "time" "github.com/enmanuel/agents/pkg/llm" "github.com/enmanuel/agents/pkg/memory" ) -const schema = ` -CREATE TABLE IF NOT EXISTS facts ( - agent_id TEXT NOT NULL, - subject TEXT NOT NULL, - key TEXT NOT NULL, - value TEXT NOT NULL, - updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (agent_id, subject, key) -); +//go:embed migrations/*.sql +var migrationsFS embed.FS -CREATE TABLE IF NOT EXISTS messages ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - room_id TEXT NOT NULL, - role TEXT NOT NULL, - content TEXT NOT NULL, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE INDEX IF NOT EXISTS idx_messages_room ON messages(agent_id, room_id, created_at DESC); -CREATE INDEX IF NOT EXISTS idx_facts_subject ON facts(agent_id, subject); -` +func applyMigrations(db *sql.DB) error { + files, err := fs.Glob(migrationsFS, "migrations/*.sql") + if err != nil { + return err + } + sort.Strings(files) + for _, f := range files { + b, err := migrationsFS.ReadFile(f) + if err != nil { + return err + } + if _, err := db.Exec(string(b)); err != nil { + if strings.Contains(err.Error(), "duplicate column") || strings.Contains(err.Error(), "already exists") { + continue + } + return fmt.Errorf("migrate %s: %w", f, err) + } + } + return nil +} // SQLiteStore implements memory.Store using SQLite. type SQLiteStore struct { @@ -54,7 +59,7 @@ func New(dbPath string, logger *slog.Logger) (*SQLiteStore, error) { if err != nil { return nil, fmt.Errorf("open memory db: %w", err) } - if _, err := db.Exec(schema); err != nil { + if err := applyMigrations(db); err != nil { db.Close() return nil, fmt.Errorf("migrate memory db: %w", err) }