feat(ai): Wire change detection into patrol service
Integrate operational memory into patrol context: - Add changeDetector and remediationLog fields to PatrolService - Add SetChangeDetector and SetRemediationLog methods - Integrate change detection into buildEnrichedContext - Convert state to ResourceSnapshots for change tracking - Append recent changes summary to AI context The AI now sees a 'Recent Infrastructure Changes (24h)' section showing events like: - VM 'web-server' status changed: running → stopped (2h ago) - 'db-server' migrated from node1 to node2 (4h ago) - 'web-server' memory increased: 4 GB → 8 GB (1d ago) All tests passing.
This commit is contained in:
parent
286edee8aa
commit
a76c254ff5
1 changed files with 97 additions and 0 deletions
|
|
@ -212,6 +212,8 @@ type PatrolService struct {
|
||||||
knowledgeStore *knowledge.Store // For per-resource notes in patrol context
|
knowledgeStore *knowledge.Store // For per-resource notes in patrol context
|
||||||
metricsHistory MetricsHistoryProvider // For trend analysis and predictions
|
metricsHistory MetricsHistoryProvider // For trend analysis and predictions
|
||||||
baselineStore *baseline.Store // For anomaly detection via learned baselines
|
baselineStore *baseline.Store // For anomaly detection via learned baselines
|
||||||
|
changeDetector *ChangeDetector // For tracking infrastructure changes
|
||||||
|
remediationLog *RemediationLog // For tracking remediation actions
|
||||||
|
|
||||||
// Cached thresholds (recalculated when thresholdProvider changes)
|
// Cached thresholds (recalculated when thresholdProvider changes)
|
||||||
thresholds PatrolThresholds
|
thresholds PatrolThresholds
|
||||||
|
|
@ -358,6 +360,29 @@ func (p *PatrolService) GetBaselineStore() *baseline.Store {
|
||||||
return p.baselineStore
|
return p.baselineStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChangeDetector sets the change detector for tracking infrastructure changes
|
||||||
|
func (p *PatrolService) SetChangeDetector(detector *ChangeDetector) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
p.changeDetector = detector
|
||||||
|
log.Info().Msg("AI Patrol: Change detector set for operational memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRemediationLog sets the remediation log for tracking fix attempts
|
||||||
|
func (p *PatrolService) SetRemediationLog(remLog *RemediationLog) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
p.remediationLog = remLog
|
||||||
|
log.Info().Msg("AI Patrol: Remediation log set for operational memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRemediationLog returns the remediation log (for logging actions)
|
||||||
|
func (p *PatrolService) GetRemediationLog() *RemediationLog {
|
||||||
|
p.mu.RLock()
|
||||||
|
defer p.mu.RUnlock()
|
||||||
|
return p.remediationLog
|
||||||
|
}
|
||||||
|
|
||||||
// GetConfig returns the current patrol configuration
|
// GetConfig returns the current patrol configuration
|
||||||
func (p *PatrolService) GetConfig() PatrolConfig {
|
func (p *PatrolService) GetConfig() PatrolConfig {
|
||||||
p.mu.RLock()
|
p.mu.RLock()
|
||||||
|
|
@ -1703,6 +1728,7 @@ func (p *PatrolService) buildEnrichedContext(state models.StateSnapshot) string
|
||||||
metricsHistory := p.metricsHistory
|
metricsHistory := p.metricsHistory
|
||||||
knowledgeStore := p.knowledgeStore
|
knowledgeStore := p.knowledgeStore
|
||||||
baselineStore := p.baselineStore
|
baselineStore := p.baselineStore
|
||||||
|
changeDetector := p.changeDetector
|
||||||
p.mu.RUnlock()
|
p.mu.RUnlock()
|
||||||
|
|
||||||
// If no metrics history, fall back to basic summary
|
// If no metrics history, fall back to basic summary
|
||||||
|
|
@ -1737,6 +1763,25 @@ func (p *PatrolService) buildEnrichedContext(state models.StateSnapshot) string
|
||||||
|
|
||||||
// Format for AI consumption
|
// Format for AI consumption
|
||||||
formatted := aicontext.FormatInfrastructureContext(infraCtx)
|
formatted := aicontext.FormatInfrastructureContext(infraCtx)
|
||||||
|
|
||||||
|
// Append recent changes if change detector is available
|
||||||
|
if changeDetector != nil {
|
||||||
|
// Detect any new changes from current state
|
||||||
|
snapshots := stateToSnapshots(state)
|
||||||
|
newChanges := changeDetector.DetectChanges(snapshots)
|
||||||
|
|
||||||
|
// Get summary of recent changes (last 24 hours)
|
||||||
|
since := time.Now().Add(-24 * time.Hour)
|
||||||
|
changesSummary := changeDetector.GetChangesSummary(since, 20)
|
||||||
|
|
||||||
|
if changesSummary != "" {
|
||||||
|
formatted += "\n## Recent Infrastructure Changes (24h)\n\n" + changesSummary
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newChanges) > 0 {
|
||||||
|
log.Debug().Int("new_changes", len(newChanges)).Msg("AI Patrol: Detected infrastructure changes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Int("resources", infraCtx.TotalResources).
|
Int("resources", infraCtx.TotalResources).
|
||||||
|
|
@ -1747,6 +1792,58 @@ func (p *PatrolService) buildEnrichedContext(state models.StateSnapshot) string
|
||||||
return formatted
|
return formatted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stateToSnapshots converts state to resource snapshots for change detection
|
||||||
|
func stateToSnapshots(state models.StateSnapshot) []ResourceSnapshot {
|
||||||
|
var snapshots []ResourceSnapshot
|
||||||
|
|
||||||
|
for _, node := range state.Nodes {
|
||||||
|
snapshots = append(snapshots, ResourceSnapshot{
|
||||||
|
ID: node.ID,
|
||||||
|
Name: node.Name,
|
||||||
|
Type: "node",
|
||||||
|
Status: node.Status,
|
||||||
|
CPUCores: node.CPUInfo.Cores,
|
||||||
|
MemoryBytes: node.Memory.Total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, vm := range state.VMs {
|
||||||
|
if vm.Template {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
snapshots = append(snapshots, ResourceSnapshot{
|
||||||
|
ID: vm.ID,
|
||||||
|
Name: vm.Name,
|
||||||
|
Type: "vm",
|
||||||
|
Status: vm.Status,
|
||||||
|
Node: vm.Node,
|
||||||
|
CPUCores: vm.CPUs,
|
||||||
|
MemoryBytes: vm.Memory.Total,
|
||||||
|
DiskBytes: vm.Disk.Total,
|
||||||
|
LastBackup: vm.LastBackup,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ct := range state.Containers {
|
||||||
|
if ct.Template {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
snapshots = append(snapshots, ResourceSnapshot{
|
||||||
|
ID: ct.ID,
|
||||||
|
Name: ct.Name,
|
||||||
|
Type: "container",
|
||||||
|
Status: ct.Status,
|
||||||
|
Node: ct.Node,
|
||||||
|
CPUCores: ct.CPUs,
|
||||||
|
MemoryBytes: ct.Memory.Total,
|
||||||
|
DiskBytes: ct.Disk.Total,
|
||||||
|
LastBackup: ct.LastBackup,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshots
|
||||||
|
}
|
||||||
|
|
||||||
// metricsHistoryShim adapts ai.MetricsHistoryProvider to aicontext.MetricsHistoryProvider
|
// metricsHistoryShim adapts ai.MetricsHistoryProvider to aicontext.MetricsHistoryProvider
|
||||||
type metricsHistoryShim struct {
|
type metricsHistoryShim struct {
|
||||||
provider MetricsHistoryProvider
|
provider MetricsHistoryProvider
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue