Files
llmgw/internal/router/guardmetrics.go
T

60 lines
2.1 KiB
Go

package router
import (
"github.com/prometheus/client_golang/prometheus"
"gitea.mynodes.xyz/ilker/llmgw/internal/guard"
)
// guardMetrics count what the guardrails did, by name: never what they read.
type guardMetrics struct {
checks *prometheus.CounterVec
latency *prometheus.HistogramVec
stops prometheus.Counter
blocks *prometheus.CounterVec
}
func newGuardMetrics() *guardMetrics {
return &guardMetrics{
checks: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "llmgw_guard_checks_total",
Help: "Requests and answers judged by the guardrails, by stage (input, output), policy, action " +
"(allowed, flagged, redacted, blocked), category, and mode (enforce, shadow).",
}, []string{"stage", "policy", "action", "category", "mode"}),
latency: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "llmgw_guard_latency_seconds",
Help: "Time the guardrails took to judge a request or an answer, judge model included.",
Buckets: []float64{.0005, .001, .0025, .005, .01, .025, .05, .1, .25, .5, 1, 2.5},
}, []string{"stage"}),
stops: prometheus.NewCounter(prometheus.CounterOpts{
Name: "llmgw_guard_stream_stops_total",
Help: "Streamed answers the guardrails stopped part way.",
}),
blocks: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "llmgw_tenant_guard_blocks_total",
Help: "Requests and answers the guardrails blocked, by tenant (the level router.tenant_label names).",
}, []string{"tenant"}),
}
}
func (g *guardMetrics) collectors() []prometheus.Collector {
return []prometheus.Collector{g.checks, g.latency, g.stops, g.blocks}
}
// observe counts one verdict; tenant is "" where tenants are not labelled.
func (g *guardMetrics) observe(v guard.Verdict, stage guard.Stage, tenant string) {
action := string(v.Action)
if action == "" {
action = "allowed"
}
mode := "enforce"
if v.Shadow {
mode = "shadow"
}
g.checks.WithLabelValues(string(stage), v.Policy, action, v.Category, mode).Inc()
g.latency.WithLabelValues(string(stage)).Observe(v.Took.Seconds())
if v.Action == guard.Block && !v.Shadow && tenant != "" {
g.blocks.WithLabelValues(tenant).Inc()
}
}