Custom Store
Implement a custom storage backend by satisfying the composite store.Store interface.
Every Sentinel storage backend is a Go interface. This guide shows how to implement a custom store.
The composite interface
Your store must implement the composite store.Store interface, which embeds 5 subsystem stores:
import "github.com/xraph/sentinel/store"
type Store interface {
suite.Store
testcase.Store
evalrun.Store
baseline.Store
promptversion.Store
Migrate(ctx context.Context) error
Ping(ctx context.Context) error
Close() error
}Subsystem interfaces
suite.Store
type Store interface {
CreateSuite(ctx context.Context, s *Suite) error
GetSuite(ctx context.Context, suiteID id.SuiteID) (*Suite, error)
GetSuiteByName(ctx context.Context, appID, name string) (*Suite, error)
UpdateSuite(ctx context.Context, s *Suite) error
DeleteSuite(ctx context.Context, suiteID id.SuiteID) error
ListSuites(ctx context.Context, filter *ListFilter) ([]*Suite, error)
}testcase.Store
type Store interface {
CreateCase(ctx context.Context, tc *Case) error
CreateCaseBatch(ctx context.Context, cases []*Case) error
GetCase(ctx context.Context, caseID id.CaseID) (*Case, error)
UpdateCase(ctx context.Context, tc *Case) error
DeleteCase(ctx context.Context, caseID id.CaseID) error
ListCases(ctx context.Context, suiteID id.SuiteID) ([]*Case, error)
CountCases(ctx context.Context, suiteID id.SuiteID) (int64, error)
ImportCases(ctx context.Context, suiteID id.SuiteID, format string, data []byte) (int64, error)
}evalrun.Store
type Store interface {
CreateRun(ctx context.Context, r *Run) error
GetRun(ctx context.Context, runID id.EvalRunID) (*Run, error)
UpdateRun(ctx context.Context, r *Run) error
ListRuns(ctx context.Context, filter *ListFilter) ([]*Run, error)
ListRunsBySuite(ctx context.Context, suiteID id.SuiteID) ([]*Run, error)
CreateResult(ctx context.Context, result *Result) error
CreateResultBatch(ctx context.Context, results []*Result) error
ListResults(ctx context.Context, runID id.EvalRunID) ([]*Result, error)
GetResultStats(ctx context.Context, runID id.EvalRunID) (*ResultStats, error)
}baseline.Store
type Store interface {
SaveBaseline(ctx context.Context, b *Baseline) error
GetBaseline(ctx context.Context, baselineID id.BaselineID) (*Baseline, error)
GetLatestBaseline(ctx context.Context, suiteID id.SuiteID) (*Baseline, error)
ListBaselines(ctx context.Context, suiteID id.SuiteID) ([]*Baseline, error)
DeleteBaseline(ctx context.Context, baselineID id.BaselineID) error
}promptversion.Store
type Store interface {
CreatePromptVersion(ctx context.Context, pv *PromptVersion) error
GetPromptVersion(ctx context.Context, pvID id.PromptVersionID) (*PromptVersion, error)
ListPromptVersions(ctx context.Context, suiteID id.SuiteID) ([]*PromptVersion, error)
GetCurrentPromptVersion(ctx context.Context, suiteID id.SuiteID) (*PromptVersion, error)
SetCurrentPromptVersion(ctx context.Context, suiteID id.SuiteID, pvID id.PromptVersionID) error
}Compile-time check
Use a compile-time assertion to verify your implementation satisfies all interfaces:
var _ store.Store = (*MyStore)(nil)Registering with the engine
eng, _ := engine.New(engine.WithStore(&MyStore{}))Reference implementations
Study the existing implementations:
| Backend | Package | Lines | Notes |
|---|---|---|---|
| Memory | store/memory | ~557 | Hash maps with RWMutex, simplest reference |
| SQLite | store/sqlite | ~511 | Bun ORM, creates tables on Migrate() |
| PostgreSQL | store/postgres | ~567 | Bun ORM, embedded SQL migrations |