diff options
| author | Sam Scholten | 2026-08-30 20:34:38 +1000 |
|---|---|---|
| committer | Sam Scholten | 2026-08-30 20:34:38 +1000 |
| commit | eff960c54620bfba47d40bc3792ba805dea3dab3 (patch) | |
| tree | a04fd365bb08a5a043b99665f8d7435ba1e989e0 /cmds/serve.go | |
| parent | 25f47ca899527ed2b16d19209c0546413e057126 (diff) | |
| download | fluxrec-eff960c54620bfba47d40bc3792ba805dea3dab3.tar.gz fluxrec-eff960c54620bfba47d40bc3792ba805dea3dab3.zip | |
rss: provenance + score calibration in items
Item titles gain a rune-capped source-feed suffix so list views (which
only ever show the channel title) display provenance inline. Item bodies
open with feed, model score, and the batch-wide score median; the median
is computed over every scored entry at poll time and stored on the run
row as a nil-safe pointer, so old rows render without it. /api/status
exposes the median too.
Diffstat (limited to 'cmds/serve.go')
| -rw-r--r-- | cmds/serve.go | 23 |
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") |
