picoclaw/pkg/swarm/memory/sqlite_store.go

174 lines
No EOL
5.4 KiB
Go

package memory
import (
"context"
"database/sql"
"encoding/json"
"github.com/sipeed/picoclaw/pkg/swarm/core"
_ "modernc.org/sqlite"
)
type SQLiteStore struct {
db *sql.DB
}
func NewSQLiteStore(dbPath string) (*SQLiteStore, error) {
db, err := sql.Open("sqlite", dbPath)
if err != nil { return nil, err }
s := &SQLiteStore{db: db}
if err := s.init(); err != nil {
return nil, err
}
return s, nil
}
func (s *SQLiteStore) init() error {
if _, err := s.db.Exec(`CREATE TABLE IF NOT EXISTS swarms (id TEXT PRIMARY KEY, goal TEXT, status TEXT, created_at DATETIME);`); err != nil {
return err
}
if _, err := s.db.Exec(`CREATE TABLE IF NOT EXISTS nodes (id TEXT PRIMARY KEY, swarm_id TEXT, parent_id TEXT, role JSON, task TEXT, status TEXT, output TEXT, stats JSON);`); err != nil {
return err
}
if _, err := s.db.Exec(`CREATE TABLE IF NOT EXISTS facts (swarm_id TEXT, content TEXT, confidence REAL, source TEXT, metadata JSON);`); err != nil {
return err
}
return nil
}
func (s *SQLiteStore) CreateSwarm(ctx context.Context, sw *core.Swarm) error {
_, err := s.db.ExecContext(ctx, "INSERT INTO swarms (id, goal, status, created_at) VALUES (?, ?, ?, ?)", sw.ID, sw.Goal, sw.Status, sw.CreatedAt)
return err
}
func (s *SQLiteStore) GetSwarm(ctx context.Context, id core.SwarmID) (*core.Swarm, error) {
row := s.db.QueryRowContext(ctx, "SELECT id, goal, status, created_at FROM swarms WHERE id=?", id)
var sw core.Swarm
if err := row.Scan(&sw.ID, &sw.Goal, &sw.Status, &sw.CreatedAt); err != nil {
return nil, err
}
return &sw, nil
}
func (s *SQLiteStore) UpdateSwarm(ctx context.Context, sw *core.Swarm) error {
_, err := s.db.ExecContext(ctx, "UPDATE swarms SET status=? WHERE id=?", sw.Status, sw.ID)
return err
}
func (s *SQLiteStore) ListSwarms(ctx context.Context, status core.SwarmStatus) ([]*core.Swarm, error) {
rows, err := s.db.QueryContext(ctx, "SELECT id, goal, status, created_at FROM swarms WHERE status=?", status)
if err != nil {
return nil, err
}
defer rows.Close()
var list []*core.Swarm
for rows.Next() {
var sw core.Swarm
if err := rows.Scan(&sw.ID, &sw.Goal, &sw.Status, &sw.CreatedAt); err != nil {
return nil, err
}
list = append(list, &sw)
}
return list, nil
}
func (s *SQLiteStore) CreateNode(ctx context.Context, n *core.Node) error {
role, err := json.Marshal(n.Role)
if err != nil {
return err
}
stats, err := json.Marshal(n.Stats)
if err != nil {
return err
}
_, err = s.db.ExecContext(ctx, "INSERT INTO nodes (id, swarm_id, parent_id, role, task, status, output, stats) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", n.ID, n.SwarmID, n.ParentID, role, n.Task, n.Status, n.Output, stats)
return err
}
func (s *SQLiteStore) GetNode(ctx context.Context, id core.NodeID) (*core.Node, error) {
row := s.db.QueryRowContext(ctx, "SELECT id, swarm_id, parent_id, role, task, status, output, stats FROM nodes WHERE id=?", id)
var n core.Node
var role, stats []byte
if err := row.Scan(&n.ID, &n.SwarmID, &n.ParentID, &role, &n.Task, &n.Status, &n.Output, &stats); err != nil {
return nil, err
}
if err := json.Unmarshal(role, &n.Role); err != nil {
return nil, err
}
if err := json.Unmarshal(stats, &n.Stats); err != nil {
return nil, err
}
return &n, nil
}
func (s *SQLiteStore) UpdateNode(ctx context.Context, n *core.Node) error {
stats, err := json.Marshal(n.Stats)
if err != nil {
return err
}
_, err = s.db.ExecContext(ctx, "UPDATE nodes SET status=?, output=?, stats=? WHERE id=?", n.Status, n.Output, stats, n.ID)
return err
}
func (s *SQLiteStore) GetSwarmNodes(ctx context.Context, id core.SwarmID) ([]*core.Node, error) {
rows, err := s.db.QueryContext(ctx, "SELECT id, swarm_id, parent_id, role, task, status, output, stats FROM nodes WHERE swarm_id=?", id)
if err != nil {
return nil, err
}
defer rows.Close()
var list []*core.Node
for rows.Next() {
var n core.Node
var role, stats []byte
if err := rows.Scan(&n.ID, &n.SwarmID, &n.ParentID, &role, &n.Task, &n.Status, &n.Output, &stats); err != nil {
return nil, err
}
if err := json.Unmarshal(role, &n.Role); err != nil {
return nil, err
}
if err := json.Unmarshal(stats, &n.Stats); err != nil {
return nil, err
}
list = append(list, &n)
}
return list, nil
}
func (s *SQLiteStore) SaveFact(ctx context.Context, f core.Fact) error {
meta, err := json.Marshal(f.Metadata)
if err != nil {
return err
}
_, err = s.db.ExecContext(ctx, "INSERT INTO facts (swarm_id, content, confidence, source, metadata) VALUES (?, ?, ?, ?, ?)", f.SwarmID, f.Content, f.Confidence, f.Source, meta)
return err
}
func (s *SQLiteStore) SearchFacts(ctx context.Context, id core.SwarmID, q string, limit int, global bool) ([]core.FactResult, error) {
query := "SELECT content FROM facts WHERE swarm_id=? AND content LIKE ? LIMIT ?"
args := []any{id, "%" + q + "%", limit}
if global {
query = "SELECT content FROM facts WHERE content LIKE ? LIMIT ?"
args = []any{"%" + q + "%", limit}
}
rows, err := s.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var res []core.FactResult
for rows.Next() {
var content string
if err := rows.Scan(&content); err != nil {
return nil, err
}
res = append(res, core.FactResult{Content: content, Score: 1.0})
}
return res, nil
}
func (s *SQLiteStore) ClearMemory(ctx context.Context, id core.SwarmID) error {
_, err := s.db.ExecContext(ctx, "DELETE FROM facts WHERE swarm_id=?", id)
return err
}