Replace passthrough() + stdlib flag.FlagSet anti-pattern with proper cobra integration. Every Run* function now takes a typed *Opts struct and returns error. Flags registered via cli.StringFlag/IntFlag/etc. Commands participate in Core lifecycle with full cobra flag parsing. - 6 command groups: gen, score, data, export, infra, mon - 25 commands converted, 0 passthrough() calls remain - Delete passthrough() helper from lem.go - Update export_test.go to use ExportOpts struct Co-Authored-By: Virgil <virgil@lethean.io>
121 lines
3.1 KiB
Go
121 lines
3.1 KiB
Go
package lem
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
const targetTotal = 15000
|
|
|
|
// MetricsOpts holds configuration for the metrics command.
|
|
type MetricsOpts struct {
|
|
DB string // DuckDB database path (defaults to LEM_DB env)
|
|
Influx string // InfluxDB URL
|
|
InfluxDB string // InfluxDB database name
|
|
}
|
|
|
|
// RunMetrics reads golden set stats from DuckDB and pushes them to InfluxDB as
|
|
// golden_set_stats, golden_set_domain, and golden_set_voice measurements.
|
|
func RunMetrics(cfg MetricsOpts) error {
|
|
if cfg.DB == "" {
|
|
cfg.DB = os.Getenv("LEM_DB")
|
|
}
|
|
if cfg.DB == "" {
|
|
return fmt.Errorf("--db or LEM_DB required (path to DuckDB file)")
|
|
}
|
|
|
|
db, err := OpenDB(cfg.DB)
|
|
if err != nil {
|
|
return fmt.Errorf("open db: %w", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Query overall stats.
|
|
var total, domains, voices int
|
|
var avgGenTime, avgChars float64
|
|
|
|
err = db.conn.QueryRow(`
|
|
SELECT count(*), count(DISTINCT domain), count(DISTINCT voice),
|
|
coalesce(avg(gen_time), 0), coalesce(avg(char_count), 0)
|
|
FROM golden_set
|
|
`).Scan(&total, &domains, &voices, &avgGenTime, &avgChars)
|
|
if err != nil {
|
|
return fmt.Errorf("query golden_set stats: %w", err)
|
|
}
|
|
|
|
if total == 0 {
|
|
fmt.Println("No golden set data in DuckDB.")
|
|
return nil
|
|
}
|
|
|
|
nowNs := time.Now().UTC().UnixNano()
|
|
pct := float64(total) / float64(targetTotal) * 100.0
|
|
|
|
var lines []string
|
|
|
|
// Overall stats measurement.
|
|
lines = append(lines, fmt.Sprintf(
|
|
"golden_set_stats total_examples=%di,domains=%di,voices=%di,avg_gen_time=%.2f,avg_response_chars=%.0f,completion_pct=%.1f %d",
|
|
total, domains, voices, avgGenTime, avgChars, pct, nowNs,
|
|
))
|
|
|
|
// Per-domain stats.
|
|
domainRows, err := db.conn.Query(`
|
|
SELECT domain, count(*) AS n, avg(gen_time) AS avg_t
|
|
FROM golden_set GROUP BY domain
|
|
`)
|
|
if err != nil {
|
|
return fmt.Errorf("query domains: %w", err)
|
|
}
|
|
domainCount := 0
|
|
for domainRows.Next() {
|
|
var domain string
|
|
var n int
|
|
var avgT float64
|
|
if err := domainRows.Scan(&domain, &n, &avgT); err != nil {
|
|
return fmt.Errorf("scan domain row: %w", err)
|
|
}
|
|
lines = append(lines, fmt.Sprintf(
|
|
"golden_set_domain,domain=%s count=%di,avg_gen_time=%.2f %d",
|
|
escapeLp(domain), n, avgT, nowNs,
|
|
))
|
|
domainCount++
|
|
}
|
|
domainRows.Close()
|
|
|
|
// Per-voice stats.
|
|
voiceRows, err := db.conn.Query(`
|
|
SELECT voice, count(*) AS n, avg(char_count) AS avg_c, avg(gen_time) AS avg_t
|
|
FROM golden_set GROUP BY voice
|
|
`)
|
|
if err != nil {
|
|
return fmt.Errorf("query voices: %w", err)
|
|
}
|
|
voiceCount := 0
|
|
for voiceRows.Next() {
|
|
var voice string
|
|
var n int
|
|
var avgC, avgT float64
|
|
if err := voiceRows.Scan(&voice, &n, &avgC, &avgT); err != nil {
|
|
return fmt.Errorf("scan voice row: %w", err)
|
|
}
|
|
lines = append(lines, fmt.Sprintf(
|
|
"golden_set_voice,voice=%s count=%di,avg_chars=%.0f,avg_gen_time=%.2f %d",
|
|
escapeLp(voice), n, avgC, avgT, nowNs,
|
|
))
|
|
voiceCount++
|
|
}
|
|
voiceRows.Close()
|
|
|
|
// Write to InfluxDB.
|
|
influx := NewInfluxClient(cfg.Influx, cfg.InfluxDB)
|
|
if err := influx.WriteLp(lines); err != nil {
|
|
return fmt.Errorf("write metrics: %w", err)
|
|
}
|
|
|
|
fmt.Printf("Wrote metrics to InfluxDB: %d examples, %d domains, %d voices (%d points)\n",
|
|
total, domainCount, voiceCount, len(lines))
|
|
|
|
return nil
|
|
}
|