Pulse/pkg/reporting/reporting.go
rcourtman e32d4ede44 Expose engine narrative entry points for non-rendering callers
The reporting engine's synthesis layer was reachable only through
Generate/GenerateMulti, which always rendered PDF or CSV. Pulse
Assistant needs the same retrospective synthesis (per-resource
summary, fleet outliers, period comparison) in a form it can present
in chat, not as a downloaded artifact.

Add two non-rendering entry points to the Engine interface:

  NarrativeFor(req MetricReportRequest) (*Narrative, error)
  FleetNarrativeFor(req MultiReportRequest) (*FleetNarrative, error)

Both run the same query path and the same narrator resolution as their
rendering counterparts (heuristic by default, AI when the request
supplies a narrator, fail-closed-to-heuristic on any narrator error)
and return the structured narrative without invoking the fpdf/csv
output stage. Test stubs in pkg/reporting and internal/api are
updated to implement the extended interface.

These are the seams the upcoming pulse_summarize Assistant tools wrap
to answer questions like "what's hot on pve1 this week" or "where
should I look across my fleet" without round-tripping through report
generation. Same synthesis layer, no PDF involved.

Also fixes a pre-existing flake in TestEngineGenerate_UsesSuppliedNarrator
(metrics writes are async; the first Generate sometimes ran before
the raw tier flushed). Wrapped in the same eventually-pattern used by
the prior-period and findings-provider tests.
2026-05-10 22:23:09 +01:00

178 lines
5.4 KiB
Go

package reporting
import (
"time"
)
// ReportFormat represents the output format of a report
type ReportFormat string
const (
FormatCSV ReportFormat = "csv"
FormatPDF ReportFormat = "pdf"
)
// MetricReportRequest defines the parameters for generating a report
type MetricReportRequest struct {
ResourceType string
ResourceID string
MetricType string // Optional, if empty all metrics for the resource are included
Start time.Time
End time.Time
Format ReportFormat
Title string
// Optional enrichment data (populated by handler from monitor state)
Resource *ResourceInfo // Details about the resource being reported on
Alerts []AlertInfo // Active and recently resolved alerts for this resource
Backups []BackupInfo // Backup information for VMs/containers
Storage []StorageInfo // Storage pools (for nodes)
Disks []DiskInfo // Physical disk health (for nodes)
// Optional narrative interpretation. When Narrator is non-nil the
// engine builds a NarrativeInput from the queried report data and asks
// it to produce the executive summary; on error or nil it falls back to
// the heuristic narrator. Findings are passed through to NarrativeInput
// so a narrator can reference Patrol activity in the period.
Narrator Narrator
FindingsProvider FindingsProvider
}
// ResourceInfo contains details about the resource being reported on
type ResourceInfo struct {
Name string
DisplayName string
Status string
Host string // URL for nodes
Node string // Parent node for VMs/containers
Instance string // Proxmox instance name
Uptime int64
KernelVersion string
PVEVersion string
OSName string
OSVersion string
IPAddresses []string
CPUModel string
CPUCores int
CPUSockets int
MemoryTotal int64
DiskTotal int64
LoadAverage []float64
Temperature *float64 // CPU temp if available
Tags []string
ClusterName string
IsCluster bool
}
// AlertInfo contains alert information for the report
type AlertInfo struct {
Type string
Level string // warning, critical
Message string
Value float64
Threshold float64
StartTime time.Time
ResolvedTime *time.Time // nil if still active
Acknowledged bool
}
// BackupInfo contains backup information for VMs/containers
type BackupInfo struct {
Type string // vzdump, pbs
Storage string
Timestamp time.Time
Size int64
Verified bool
Protected bool
VolID string
NextBackup *time.Time
}
// StorageInfo contains storage pool information
type StorageInfo struct {
Name string
Type string // lvm, zfs, dir, nfs, etc.
Status string
Total int64
Used int64
Available int64
UsagePerc float64
Content string // images, rootdir, backup, etc.
ZFSHealth string // For ZFS pools
ZFSErrors int // Checksum/read/write errors
}
// DiskInfo contains physical disk health information
type DiskInfo struct {
Device string
Model string
Serial string
Type string // nvme, ssd, hdd
Size int64
Health string // PASSED, FAILED, UNKNOWN
Temperature int // Celsius
WearLevel int // 0-100, percentage of life REMAINING (100 = healthy, 0 = end of life, -1 = unknown)
}
// MultiReportRequest defines the parameters for generating a multi-resource report.
type MultiReportRequest struct {
Resources []MetricReportRequest // One per resource, each with enrichment
Format ReportFormat
Start time.Time
End time.Time
Title string
MetricType string
// Optional fleet-level narrative interpretation. When FleetNarrator is
// non-nil the engine builds a FleetNarrativeInput from the queried
// per-resource report data and asks it to produce the cross-resource
// summary; on error or nil it falls back to the heuristic fleet
// narrator. FindingsProvider, when set, is consulted per-resource so
// patrol findings can flow into per-resource narratives.
FleetNarrator FleetNarrator
Narrator Narrator
FindingsProvider FindingsProvider
}
// MultiReportData holds the data for multi-resource report generation.
type MultiReportData struct {
Title string
Start time.Time
End time.Time
GeneratedAt time.Time
Resources []*ReportData // Reuse existing ReportData per resource
TotalPoints int
// Fleet-level narrative interpretation, populated by the engine when
// the request supplies a FleetNarrator (or always populated with the
// heuristic fallback). The renderer prefers this over recomputing
// observations inline.
FleetNarrative *FleetNarrative
}
// Engine defines the interface for report generation.
// This allows the enterprise version to provide PDF/CSV generation.
//
// NarrativeFor and FleetNarrativeFor return the structured narrative
// without rendering, for callers that want the synthesis layer in a
// non-PDF form (Pulse Assistant tool calls, programmatic consumers).
type Engine interface {
Generate(req MetricReportRequest) (data []byte, contentType string, err error)
GenerateMulti(req MultiReportRequest) (data []byte, contentType string, err error)
NarrativeFor(req MetricReportRequest) (*Narrative, error)
FleetNarrativeFor(req MultiReportRequest) (*FleetNarrative, error)
}
var (
globalEngine Engine
)
// SetEngine sets the global report engine.
func SetEngine(e Engine) {
globalEngine = e
}
// GetEngine returns the current global report engine.
func GetEngine() Engine {
return globalEngine
}