Phase 2 of Pulse AI differentiation: - Create internal/ai/baseline package for learned baselines - Implement statistical baseline learning with mean, stddev, percentiles - Add z-score based anomaly detection with severity classification (low, medium, high, critical based on standard deviations) - Integrate baseline provider into context builder - Wire baseline store into patrol service with adapters - Add anomaly enrichment to resource contexts Key features: - Learn computes baseline from historical metric data points - IsAnomaly and CheckAnomaly detect deviations from normal - Persists baselines to disk as JSON for durability - Formatted anomaly descriptions for AI consumption Example: 'Memory is high above normal (85.2% vs typical 42.1% ± 8.3%)' The baseline store needs to be initialized and triggered to learn from metrics history. Next step is adding the learning loop. All tests passing.
46 lines
1.2 KiB
Go
46 lines
1.2 KiB
Go
package ai
|
|
|
|
import (
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/ai/baseline"
|
|
)
|
|
|
|
// BaselineStoreAdapter adapts baseline.Store to the context.BaselineProvider interface
|
|
type BaselineStoreAdapter struct {
|
|
store *baseline.Store
|
|
}
|
|
|
|
// NewBaselineStoreAdapter creates an adapter for baseline.Store
|
|
func NewBaselineStoreAdapter(store *baseline.Store) *BaselineStoreAdapter {
|
|
if store == nil {
|
|
return nil
|
|
}
|
|
return &BaselineStoreAdapter{store: store}
|
|
}
|
|
|
|
// CheckAnomaly implements context.BaselineProvider
|
|
func (a *BaselineStoreAdapter) CheckAnomaly(resourceID, metric string, value float64) (severity string, zScore float64, mean float64, stddev float64, ok bool) {
|
|
if a.store == nil {
|
|
return "", 0, 0, 0, false
|
|
}
|
|
|
|
s, z, b := a.store.CheckAnomaly(resourceID, metric, value)
|
|
if b == nil {
|
|
return "", 0, 0, 0, false
|
|
}
|
|
|
|
return string(s), z, b.Mean, b.StdDev, true
|
|
}
|
|
|
|
// GetBaseline implements context.BaselineProvider
|
|
func (a *BaselineStoreAdapter) GetBaseline(resourceID, metric string) (mean float64, stddev float64, sampleCount int, ok bool) {
|
|
if a.store == nil {
|
|
return 0, 0, 0, false
|
|
}
|
|
|
|
b, exists := a.store.GetBaseline(resourceID, metric)
|
|
if !exists || b == nil {
|
|
return 0, 0, 0, false
|
|
}
|
|
|
|
return b.Mean, b.StdDev, b.SampleCount, true
|
|
}
|