Files
Maven/internal/store/events.go
T
kami 4c40a183cf feat: event store, pattern inference, and routine proposals
- Add events table (migration #4): stores normalized (action, object, ts)
  triples extracted from facts, indexed for recurrence detection.
- Add proposed_routines table: stores inferred recurring patterns with
  proposed/accepted/dismissed status and optional linked reminder.
- Add pattern package: Extractor normalizes fact text into (action, object)
  pairs with TTS normalization; Detector groups events to find recurring
  patterns and proposes routines.
- Add internal/ttsnorm: text normalization pipeline for Russian/English
  (lowercase, punctuation strip, number normalization, stopword removal).
- Add chat seed file for LLM phraser.
2026-07-10 15:49:03 +04:00

62 lines
1.9 KiB
Go

package store
import (
"context"
"fmt"
"time"
)
// Event — a normalized, derived observation from a fact. An event has an action
// (what happened, e.g. "refill") and an object (what it happened to, e.g.
// "cat_water_fountain"). Events are lossy — not every fact produces one.
// They are the input to the pattern detector.
type Event struct {
ID int64
FactID int64
Action string
Object string
Ts time.Time
}
// CreateEvent persists a derived event from a fact. Best-effort: the caller
// (the pattern extractor) decides whether the fact warrants an event; this
// just writes the row. Not every fact produces an event.
func (s *Store) CreateEvent(ctx context.Context, factID int64, action, object string, ts time.Time) (int64, error) {
res, err := s.db.ExecContext(ctx,
`INSERT INTO events (fact_id, action, object, ts) VALUES (?,?,?,?)`,
factID, action, object, ts.UnixMilli())
if err != nil {
return 0, fmt.Errorf("create event: %w", err)
}
id, err := res.LastInsertId()
if err != nil {
return 0, fmt.Errorf("create event: last insert id: %w", err)
}
return id, nil
}
// EventsFor returns all events matching action+object, ordered by ts ascending
// (oldest first — the order the pattern detector needs for interval computation).
func (s *Store) EventsFor(ctx context.Context, action, object string) ([]Event, error) {
rows, err := s.db.QueryContext(ctx, `
SELECT id, fact_id, action, object, ts
FROM events
WHERE action = ? AND object = ?
ORDER BY ts ASC, id ASC`, action, object)
if err != nil {
return nil, fmt.Errorf("events for %s/%s: %w", action, object, err)
}
defer rows.Close()
var out []Event
for rows.Next() {
var e Event
var tsMillis int64
if err := rows.Scan(&e.ID, &e.FactID, &e.Action, &e.Object, &tsMillis); err != nil {
return nil, err
}
e.Ts = time.UnixMilli(tsMillis).UTC()
out = append(out, e)
}
return out, rows.Err()
}