Documentation
¶
Overview ¶
Package events provides temporal event storage with semantic search capabilities.
Index ¶
- type Event
- type EventStore
- func (s *EventStore) CountEvents(ctx context.Context) (int64, error)
- func (s *EventStore) DeleteEvent(ctx context.Context, id int64) error
- func (s *EventStore) DeleteMessageEvents(ctx context.Context, messageID, subject string) error
- func (s *EventStore) ListEvents(ctx context.Context, subject string, limit, offset int) ([]Event, error)
- func (s *EventStore) MessageEventMarkers(ctx context.Context, sessionID, subject string) (map[string]string, error)
- func (s *EventStore) RebuildFTS(ctx context.Context) error
- func (s *EventStore) ReplaceMessageEvents(ctx context.Context, sessionID, messageID, subject string, ...) error
- func (s *EventStore) ReplaceSessionEvents(ctx context.Context, sessionID, subject string, ...) error
- func (s *EventStore) SaveEvent(ctx context.Context, subject, content string, metadata map[string]interface{}) (int64, error)
- func (s *EventStore) SaveEventWithEmbedding(ctx context.Context, subject, content string, metadata map[string]interface{}, ...) (int64, error)
- func (s *EventStore) SearchEvents(ctx context.Context, opts SearchOptions) ([]SearchResult, error)
- func (s *EventStore) SessionHasLegacyRows(ctx context.Context, sessionID, subject string) (bool, error)
- func (s *EventStore) SetWriteProxy(proxy *dbproxy.DBProxy)
- type SearchOptions
- type SearchResult
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Event ¶
type Event struct {
// ID is the database row ID (set after insert, zero for new events).
ID int64
// Subject categorizes the event (e.g. user ID, session ID, topic).
Subject string
// Content is the event text content.
Content string
// Metadata is an arbitrary JSON string stored alongside the event.
Metadata map[string]interface{}
// EventAt is when the event occurred (defaults to creation time).
EventAt time.Time
// CreatedAt is when the event was stored in the database.
CreatedAt time.Time
}
Event represents a temporal event with optional semantic search.
type EventStore ¶
type EventStore struct {
// contains filtered or unexported fields
}
EventStore manages temporal events with semantic search capabilities.
func NewEventStore ¶
func NewEventStore(db *sql.DB, embedder embeddings.Embedder) *EventStore
NewEventStore creates a new EventStore backed by db. The embedder is used to generate embeddings for event content.
func (*EventStore) CountEvents ¶
func (s *EventStore) CountEvents(ctx context.Context) (int64, error)
CountEvents returns the total number of events.
func (*EventStore) DeleteEvent ¶
func (s *EventStore) DeleteEvent(ctx context.Context, id int64) error
DeleteEvent removes an event and its FTS entry. It is a no-op when the event does not exist.
func (*EventStore) DeleteMessageEvents ¶ added in v0.710.6
func (s *EventStore) DeleteMessageEvents(ctx context.Context, messageID, subject string) error
DeleteMessageEvents removes every indexed chunk for one message (e.g. when the message no longer exists in the session, such as after history truncation). No-op — not an error — when the message has no indexed rows.
func (*EventStore) ListEvents ¶
func (s *EventStore) ListEvents(ctx context.Context, subject string, limit, offset int) ([]Event, error)
ListEvents returns paginated events filtered by subject (all when subject="").
func (*EventStore) MessageEventMarkers ¶ added in v0.710.6
func (s *EventStore) MessageEventMarkers(ctx context.Context, sessionID, subject string) (map[string]string, error)
MessageEventMarkers returns, for every message currently indexed under sessionID (per-message rows only — see SessionHasLegacyRows for legacy detection), a map of message_id to its stored content_hash. The incremental indexer reads this once per run (a single read-only query, no lock held) to decide which messages are unchanged (hash matches, skip re-embedding), changed (hash differs, re-embed and replace), or removed (message_id present here but not among the session's current messages, delete). A message's chunk rows all carry the same content_hash (set once by the caller before ReplaceMessageEvents), so picking any one row per message_id is sufficient; GROUP BY guarantees exactly one row is returned per message.
func (*EventStore) RebuildFTS ¶
func (s *EventStore) RebuildFTS(ctx context.Context) error
RebuildFTS rebuilds the FTS5 index from the events content table. Use this to recover from index corruption or after bulk inserts that bypassed the normal SaveEvent path.
func (*EventStore) ReplaceMessageEvents ¶ added in v0.710.6
func (s *EventStore) ReplaceMessageEvents(ctx context.Context, sessionID, messageID, subject string, metadata map[string]interface{}, chunks []string, embeddings [][]float32) error
ReplaceMessageEvents replaces all indexed chunks for exactly one message (identified by messageID) atomically, without touching any other message's rows — including other messages in the same session. This is the per-message counterpart to ReplaceSessionEvents used by the incremental session indexer (internal/app/remembrances_indexer.go): only messages that are new or whose content changed need to go through this path, so a session-wide replace-all (O(n) re-embedding, O(n) write tx) is no longer required on every index run. sessionID is carried for logging/API parity with ReplaceSessionEvents and is expected in metadata (as "session_id") for search-time filtering; the delete scope itself is keyed by messageID alone, which is safe because message IDs (internal/message.Message.ID) are globally unique UUIDs, not just unique within a session. Embeddings must already be computed; if embedding generation fails before this method is called, the previous indexed version of this message remains untouched.
func (*EventStore) ReplaceSessionEvents ¶ added in v0.330.0
func (s *EventStore) ReplaceSessionEvents(ctx context.Context, sessionID, subject string, metadata map[string]interface{}, chunks []string, embeddings [][]float32) error
ReplaceSessionEvents replaces all indexed chunks for a session atomically. Embeddings must already be computed; if embedding generation fails before this method is called, the previous indexed version remains untouched.
func (*EventStore) SaveEvent ¶
func (s *EventStore) SaveEvent(ctx context.Context, subject, content string, metadata map[string]interface{}) (int64, error)
SaveEvent stores a new event with its embedding and updates the FTS index. The embedding is generated from the content using the configured embedder. Returns the auto-assigned event ID.
func (*EventStore) SaveEventWithEmbedding ¶ added in v0.326.0
func (s *EventStore) SaveEventWithEmbedding(ctx context.Context, subject, content string, metadata map[string]interface{}, embedding []float32) (int64, error)
SaveEventWithEmbedding inserts an event using a pre-computed embedding. Called by the primary IPC dispatcher when a secondary forwards a SaveEvent write. No embedding generation is performed; the provided embedding is stored directly.
func (*EventStore) SearchEvents ¶
func (s *EventStore) SearchEvents(ctx context.Context, opts SearchOptions) ([]SearchResult, error)
SearchEvents performs hybrid search with temporal filters. The search combines vector similarity and FTS using RRF, then applies time filters.
func (*EventStore) SessionHasLegacyRows ¶ added in v0.710.6
func (s *EventStore) SessionHasLegacyRows(ctx context.Context, sessionID, subject string) (bool, error)
SessionHasLegacyRows reports whether sessionID still has any indexed rows written by the old whole-transcript replace path (metadata with no "message_id" key) rather than the per-message path. It is a cheap read-only lookup: the (subject, session_id) equality narrows to this session's rows via idx_events_session before the IS NULL filter is evaluated, so cost scales with the size of one session, not the whole events table. The incremental indexer uses this to lazily migrate a session on its first post-upgrade run: when true, it clears every row for the session (a plain ReplaceSessionEvents with no chunks) before writing any per-message rows, so legacy and per-message rows for the same session never coexist.
func (*EventStore) SetWriteProxy ¶ added in v0.326.0
func (s *EventStore) SetWriteProxy(proxy *dbproxy.DBProxy)
SetWriteProxy configures a DB proxy for mutating operations.
type SearchOptions ¶
type SearchOptions struct {
// Query is the search text (used for both vector and FTS search).
Query string
// Subject restricts results to events with this subject (empty means all).
Subject string
// FromDate filters events that occurred on or after this time (inclusive).
FromDate *time.Time
// ToDate filters events that occurred on or before this time (inclusive).
ToDate *time.Time
// LastHours filters events from the last N hours (overrides FromDate).
LastHours int
// LastDays filters events from the last N days (overrides FromDate).
LastDays int
// Limit is the maximum number of results to return (defaults to 10).
Limit int
}
SearchOptions controls event search behaviour.
type SearchResult ¶
type SearchResult struct {
Event Event
// Score is a normalised relevance score in [0, 1] where higher is better.
// For vector search: cosine similarity.
// For FTS search: normalised BM25.
// For hybrid search: Reciprocal Rank Fusion score.
Score float64
// Rank is the 1-based position in the result list.
Rank int
}
SearchResult is a ranked event returned from search.