Sentinel

Plugin System

Lifecycle hooks for metrics, audit trails, and custom processing.

Sentinel uses an opt-in plugin system where extensions subscribe to lifecycle events by implementing specific interfaces. The base interface requires only a Name() method — all hooks are optional.

Base interface

type Extension interface {
    Name() string
}

Lifecycle hooks

There are 16 hook interfaces organized by category. Extensions implement only the hooks they need.

Eval lifecycle

InterfaceMethodWhen it fires
EvalRunStartedOnEvalRunStarted(ctx, suiteID, runID, model)Evaluation run begins
EvalRunCompletedOnEvalRunCompleted(ctx, suiteID, runID, passRate, elapsed)Run finishes successfully
EvalRunFailedOnEvalRunFailed(ctx, suiteID, runID, err)Run fails with error

Case lifecycle

InterfaceMethodWhen it fires
CaseStartedOnCaseStarted(ctx, runID, caseID)Case evaluation begins
CaseCompletedOnCaseCompleted(ctx, runID, caseID, score, elapsed)Case evaluation finishes
CaseFailedOnCaseFailed(ctx, runID, caseID, err)Case evaluation fails

Regression

InterfaceMethodWhen it fires
RegressionDetectedOnRegressionDetected(ctx, suiteID, baselineID, delta)Performance regression found

Baseline

InterfaceMethodWhen it fires
BaselineSavedOnBaselineSaved(ctx, suiteID, baselineID)Baseline saved

Red team

InterfaceMethodWhen it fires
RedTeamStartedOnRedTeamStarted(ctx, suiteID, attackCount)Red team evaluation begins
RedTeamCompletedOnRedTeamCompleted(ctx, suiteID, bypassCount, elapsed)Red team evaluation finishes

Persona evaluation

InterfaceMethodWhen it fires
PersonaEvalStartedOnPersonaEvalStarted(ctx, runID, personaName)Persona-aware evaluation begins
PersonaEvalCompletedOnPersonaEvalCompleted(ctx, runID, personaName, dimensions)Persona-aware evaluation finishes

Prompt versioning

InterfaceMethodWhen it fires
PromptVersionCreatedOnPromptVersionCreated(ctx, suiteID, pvID, version)Prompt version created

Comparison

InterfaceMethodWhen it fires
ComparisonCompletedOnComparisonCompleted(ctx, suiteID, models, elapsed)Multi-model comparison finishes

Shutdown

InterfaceMethodWhen it fires
ShutdownOnShutdown(ctx)Graceful shutdown

Registry

The plugin.Registry type-caches extensions at registration time:

registry := plugin.NewRegistry(logger)
registry.Register(metricsExt)
registry.Register(auditExt)

When events are emitted, only extensions implementing the relevant hook are called:

registry.EmitEvalRunStarted(ctx, suiteID, runID, model)
// Only calls extensions that implement EvalRunStarted

Error handling

Hook errors are logged but never propagated. Hooks must not block the evaluation pipeline.

Built-in extensions

  • observability.MetricsExtension — Counters for all lifecycle events
  • audithook.Extension — Bridges events to an audit trail backend

Writing a custom extension

type SlackNotifier struct {
    webhookURL string
}

func (s *SlackNotifier) Name() string { return "slack-notifier" }

func (s *SlackNotifier) OnRegressionDetected(ctx context.Context, suiteID id.SuiteID, baselineID id.BaselineID, delta float64) error {
    return postToSlack(s.webhookURL, fmt.Sprintf("Regression detected in suite %s: %.1f%% drop", suiteID, delta*100))
}

// Register:
eng, _ := engine.New(
    engine.WithExtension(&SlackNotifier{webhookURL: "https://hooks.slack.com/..."}),
)

On this page