Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 42s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
93 lines
2.4 KiB
Plaintext
93 lines
2.4 KiB
Plaintext
; feed/rank — scoring + ranking. Scorers are (activity -> number). Ranking is a
|
|
; stable two-pass grade-down: first by :at descending (the tiebreak), then by
|
|
; score descending — so ties resolve by recency, then by input order. Fully
|
|
; deterministic on ties.
|
|
;
|
|
; Requires: lib/apl/runtime.sx, lib/feed/normalize.sx, lib/feed/stream.sx.
|
|
|
|
; --- scorers ----------------------------------------------------------------
|
|
|
|
; recency: half-life decay. score = 0.5 ^ (age / half-life). at==now -> 1.0.
|
|
(define
|
|
feed/recency
|
|
(fn
|
|
(now half-life)
|
|
(fn (a) (expt 0.5 (/ (- now (get a :at)) half-life)))))
|
|
|
|
; velocity: how many of this actor's activities fall in (at-window, at] —
|
|
; a burst of recent activity scores higher.
|
|
(define
|
|
feed/velocity
|
|
(fn
|
|
(stream window)
|
|
(fn
|
|
(a)
|
|
(len
|
|
(filter
|
|
(fn
|
|
(b)
|
|
(and
|
|
(equal? (get b :actor) (get a :actor))
|
|
(<= (get b :at) (get a :at))
|
|
(> (get b :at) (- (get a :at) window))))
|
|
(feed/items stream))))))
|
|
|
|
; engagement: how many activities in the stream touch this activity's :object
|
|
(define
|
|
feed/engagement
|
|
(fn
|
|
(stream)
|
|
(fn
|
|
(a)
|
|
(len
|
|
(filter
|
|
(fn (b) (equal? (get b :object) (get a :object)))
|
|
(feed/items stream))))))
|
|
|
|
; composite: weighted sum. parts = (list (list weight scorer) ...)
|
|
(define
|
|
feed/composite
|
|
(fn
|
|
(parts)
|
|
(fn
|
|
(a)
|
|
(reduce
|
|
(fn (acc p) (+ acc (* (first p) ((nth p 1) a))))
|
|
0
|
|
parts))))
|
|
|
|
; --- ranking ----------------------------------------------------------------
|
|
|
|
; stable reorder of items by key-fn, descending (grade-down is stable)
|
|
(define
|
|
feed/-desc-by
|
|
(fn
|
|
(items key-fn)
|
|
(let
|
|
((keys (make-array (list (len items)) (map key-fn items))))
|
|
(let
|
|
((order (get (apl-grade-down keys) :ravel)))
|
|
(map (fn (i) (nth items (- i 1))) order)))))
|
|
|
|
; rank by score descending; ties -> :at descending -> input order
|
|
(define
|
|
feed/rank
|
|
(fn
|
|
(stream score-fn)
|
|
(let
|
|
((by-at (feed/-desc-by (feed/items stream) feed/at)))
|
|
(feed/stream (feed/-desc-by by-at score-fn)))))
|
|
|
|
; attach a :score to each activity (for inspection / debugging)
|
|
(define
|
|
feed/with-scores
|
|
(fn
|
|
(stream score-fn)
|
|
(feed/stream
|
|
(map (fn (a) (assoc a :score (score-fn a))) (feed/items stream)))))
|
|
|
|
; top-N ranked timeline
|
|
(define
|
|
feed/top
|
|
(fn (stream score-fn n) (feed/take (feed/rank stream score-fn) n)))
|