aboutsummaryrefslogtreecommitdiff
path: root/cmds/serve.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/serve.go')
-rw-r--r--cmds/serve.go23
1 files changed, 23 insertions, 0 deletions
diff --git a/cmds/serve.go b/cmds/serve.go
index 9d82d38..0df5003 100644
--- a/cmds/serve.go
+++ b/cmds/serve.go
@@ -257,6 +257,7 @@ func (c *ServeCommand) pollOnce(client *miniflux.Client, vec *core.TFIDFVectoriz
Kept: assembleBatch(scored, c.TopN, c.ExploreFrac, rng),
DurationMs: time.Since(started).Milliseconds(),
ModelCreatedAt: model.CreatedAt,
+ ScoreMedian: medianScore(scored),
}
if err := core.AppendRun(c.Runs, row); err != nil {
return err
@@ -275,6 +276,27 @@ type scoredEntry struct {
score float64
}
+// medianScore returns the median score across every scored entry in a
+// poll — the batch-wide background (typical-entry) calibration rendered
+// alongside kept items' scores in the RSS body. Nil when nothing was
+// scored, so the run row omits the field rather than logging a fake 0.00.
+func medianScore(entries []scoredEntry) *float64 {
+ if len(entries) == 0 {
+ return nil
+ }
+ scores := make([]float64, len(entries))
+ for i, e := range entries {
+ scores[i] = e.score
+ }
+ sort.Float64s(scores)
+ n := len(scores)
+ median := scores[n/2]
+ if n%2 == 0 {
+ median = (scores[n/2-1] + scores[n/2]) / 2
+ }
+ return &median
+}
+
// assembleBatch ranks scored entries by score descending and selects the
// batch: the top (topN minus the exploration budget) are ranked; the
// budget splits at a fixed 1/3 : 2/3 into runner-ups (highest-scored just
@@ -443,6 +465,7 @@ func (c *ServeCommand) routes(mins []int, modelCreatedAt time.Time) http.Handler
"kept": len(last.Kept),
"duration_ms": last.DurationMs,
"model_created_at": last.ModelCreatedAt,
+ "score_median": last.ScoreMedian,
}
}
w.Header().Set("Content-Type", "application/json")