Sentinel

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:

BackendPackageLinesNotes
Memorystore/memory~557Hash maps with RWMutex, simplest reference
SQLitestore/sqlite~511Bun ORM, creates tables on Migrate()
PostgreSQLstore/postgres~567Bun ORM, embedded SQL migrations

On this page