Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
60 lines
2.1 KiB
Go
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()
|
|
}
|
|
}
|