feat(onboard): burst picks likely-good videos, summarizes with stronger model (ADR-028)
Wire the onboarding burst to its quality-aware selection and a stronger model: - main.go onboard uses OnboardBurstVideoIDs (junk-avoiding) instead of pure newest-first, bounded by MinVideoSeconds / OnboardMaxVideoSeconds. - buildBurstProcessor builds a burst-only summarizer chain led by the onboard model (burstChainModels: onboard -> standard ADR-022 chain, deduped, NDA lever intact), over the same store/cache/sink. Collapses onto the shared Processor when the onboard model is empty/equal-to-primary or config is incomplete. - summarizerEndpoint + newYouTubeSource extracted so standard and burst wiring share one definition. - Remove now-superseded NewestUnsummarizedVideoIDs: OnboardBurstVideoIDs(.,0,0) is identical pure-newest behaviour and its test covers RLS + ordering.
This commit is contained in:
+23
-10
@@ -262,23 +262,36 @@ func cmdServe(ctx context.Context, log *slog.Logger) error {
|
||||
// One lock shared by the scheduler and connect-triggered passes (#6) so
|
||||
// they never fetch concurrently — the single-fetcher invariant (ADR-018).
|
||||
runUser := serialize(&sync.Mutex{}, rawRunUser)
|
||||
// Onboarding burst (Feature 1): after the connect-triggered discovery pass,
|
||||
// summarize up to OnboardSummarizeCount of the user's NEWEST unsummarized
|
||||
// videos so a fresh account gets real summaries in its first session. Hard
|
||||
// cap; explicit, so it bypasses the recency window — but every fetch still
|
||||
// goes through globalFetchGate via the Processor. No-op when disabled
|
||||
// (count 0) or queue-only (no Processor).
|
||||
// Onboarding burst (Feature 1, refined by ADR-028): after the connect-triggered
|
||||
// discovery pass, summarize up to OnboardSummarizeCount of the user's newest
|
||||
// LIKELY-GOOD unsummarized videos so a fresh account gets a strong first
|
||||
// session. Selection avoids known-junk (Shorts/over-long/livestream VODs via
|
||||
// the persisted duration); the burst leads its chain with the stronger onboard
|
||||
// model. Hard cap; explicit, so it bypasses the recency window — but every
|
||||
// fetch still goes through globalFetchGate. No-op when disabled (count 0) or
|
||||
// queue-only (no processor).
|
||||
//
|
||||
// burstProcessor leads with the stronger model (ADR-028); it collapses onto the
|
||||
// shared Processor when the onboard model is empty/equal-to-primary or the
|
||||
// engine config is incomplete.
|
||||
burstProcessor := app.Processor
|
||||
if burstEngine, berr := buildBurstProcessor(cfg, st); berr != nil {
|
||||
return berr
|
||||
} else if burstEngine != nil {
|
||||
burstProcessor = &engineProcessor{engine: burstEngine, store: st}
|
||||
log.Info("onboarding burst uses a stronger model", "onboard_model", cfg.OnboardSummarizerModel)
|
||||
}
|
||||
onboard := func(ctx context.Context, userID string) {
|
||||
if cfg.OnboardSummarizeCount <= 0 || app.Processor == nil {
|
||||
if cfg.OnboardSummarizeCount <= 0 || burstProcessor == nil {
|
||||
return
|
||||
}
|
||||
ids, err := st.NewestUnsummarizedVideoIDs(ctx, userID, cfg.OnboardSummarizeCount)
|
||||
ids, err := st.OnboardBurstVideoIDs(ctx, userID, cfg.OnboardSummarizeCount, cfg.MinVideoSeconds, cfg.OnboardMaxVideoSeconds)
|
||||
if err != nil {
|
||||
log.Warn("onboarding: list newest unsummarized", "user", userID, "err", err)
|
||||
log.Warn("onboarding: list burst candidates", "user", userID, "err", err)
|
||||
return
|
||||
}
|
||||
for _, id := range ids {
|
||||
if err := app.Processor.ProcessVideo(ctx, userID, id); err != nil {
|
||||
if err := burstProcessor.ProcessVideo(ctx, userID, id); err != nil {
|
||||
log.Warn("onboarding: summarize", "user", userID, "video", id, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user