feat(config): add TAPIR_ONBOARD_SUMMARIZE_COUNT (default 3, hard cap 5)
Bounds the connect-time onboarding summary burst (Feature 1). Hard-capped at 5 and clamped (negative->0, >cap->cap) so onboarding can never bulk-fetch; 0 disables. The cap bounds COUNT only — every fetch still flows through the shared caption rate gate (ADR-014). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+45
-11
@@ -13,6 +13,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -79,6 +80,13 @@ type Config struct {
|
|||||||
// pre-recency behaviour). Default ~7 days.
|
// pre-recency behaviour). Default ~7 days.
|
||||||
AutoSummarizeWindow time.Duration
|
AutoSummarizeWindow time.Duration
|
||||||
|
|
||||||
|
// OnboardSummarizeCount caps how many of a freshly-connected user's newest
|
||||||
|
// videos are summarized immediately on connect (the onboarding "it works"
|
||||||
|
// burst). HARD-capped at maxOnboardSummarizeCount so onboarding can never
|
||||||
|
// bulk-fetch; 0 disables the burst. Every fetch still flows through the shared
|
||||||
|
// caption rate gate (ADR-014) — the cap bounds count, never the pacing. Default 3.
|
||||||
|
OnboardSummarizeCount int
|
||||||
|
|
||||||
// DiscoveryInterval, when > 0, makes `serve` run in-process scheduled discovery
|
// DiscoveryInterval, when > 0, makes `serve` run in-process scheduled discovery
|
||||||
// for ALL users on that cadence (ADR-018). Zero/unset = disabled, so dev and
|
// for ALL users on that cadence (ADR-018). Zero/unset = disabled, so dev and
|
||||||
// tests never auto-fetch. Single-replica assumption — see cmdServe.
|
// tests never auto-fetch. Single-replica assumption — see cmdServe.
|
||||||
@@ -108,17 +116,19 @@ func (c Config) DexConfigured() bool { return strings.TrimSpace(c.OIDCIssuer) !=
|
|||||||
|
|
||||||
// Defaults (see docs/homelab-integration.md). All overridable via env.
|
// Defaults (see docs/homelab-integration.md). All overridable via env.
|
||||||
const (
|
const (
|
||||||
defaultGatewayURL = "http://koala:30401/v1"
|
defaultGatewayURL = "http://koala:30401/v1"
|
||||||
defaultSummarizerModel = "koala/phi4-mini"
|
defaultSummarizerModel = "koala/phi4-mini"
|
||||||
defaultSummarizerTimeout = 5 * time.Minute
|
defaultSummarizerTimeout = 5 * time.Minute
|
||||||
defaultYTTokenRef = "youtube/refresh_token"
|
defaultYTTokenRef = "youtube/refresh_token"
|
||||||
defaultYTConnectRedirectURL = "https://tapir.d-ma.be/oauth/youtube/callback"
|
defaultYTConnectRedirectURL = "https://tapir.d-ma.be/oauth/youtube/callback"
|
||||||
defaultOAuthRedirectAddr = "localhost:8080"
|
defaultOAuthRedirectAddr = "localhost:8080"
|
||||||
defaultHTTPAddr = ":8080"
|
defaultHTTPAddr = ":8080"
|
||||||
defaultFetchBackoff = time.Hour
|
defaultFetchBackoff = time.Hour
|
||||||
defaultFetchRate = 2 * time.Second
|
defaultFetchRate = 2 * time.Second
|
||||||
defaultPublicURL = "https://tapir.d-ma.be"
|
defaultPublicURL = "https://tapir.d-ma.be"
|
||||||
defaultAutoSummarizeWindow = 7 * 24 * time.Hour
|
defaultAutoSummarizeWindow = 7 * 24 * time.Hour
|
||||||
|
defaultOnboardSummarizeCount = 3
|
||||||
|
maxOnboardSummarizeCount = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load reads the environment into a Config, applying defaults. It does not
|
// Load reads the environment into a Config, applying defaults. It does not
|
||||||
@@ -183,6 +193,18 @@ func Load() (Config, error) {
|
|||||||
}
|
}
|
||||||
c.AutoSummarizeWindow = autoWindow
|
c.AutoSummarizeWindow = autoWindow
|
||||||
|
|
||||||
|
onboard, err := intOr("TAPIR_ONBOARD_SUMMARIZE_COUNT", defaultOnboardSummarizeCount)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
if onboard < 0 {
|
||||||
|
onboard = 0
|
||||||
|
}
|
||||||
|
if onboard > maxOnboardSummarizeCount {
|
||||||
|
onboard = maxOnboardSummarizeCount
|
||||||
|
}
|
||||||
|
c.OnboardSummarizeCount = onboard
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,6 +269,18 @@ func envOr(key, fallback string) string {
|
|||||||
return fallback
|
return fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func intOr(key string, fallback int) (int, error) {
|
||||||
|
v := os.Getenv(key)
|
||||||
|
if v == "" {
|
||||||
|
return fallback, nil
|
||||||
|
}
|
||||||
|
n, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("config: %s=%q: %w", key, v, err)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
func durationOr(key string, fallback time.Duration) (time.Duration, error) {
|
func durationOr(key string, fallback time.Duration) (time.Duration, error) {
|
||||||
v := os.Getenv(key)
|
v := os.Getenv(key)
|
||||||
if v == "" {
|
if v == "" {
|
||||||
|
|||||||
@@ -53,6 +53,38 @@ func TestLoad_AppliesDefaults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoad_OnboardSummarizeCount(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name, env string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{"default", "", defaultOnboardSummarizeCount},
|
||||||
|
{"explicit", "4", 4},
|
||||||
|
{"zero disables", "0", 0},
|
||||||
|
{"clamped to hard cap", "50", maxOnboardSummarizeCount},
|
||||||
|
{"negative clamps to zero", "-3", 0},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
setEnv(t, map[string]string{"TAPIR_ONBOARD_SUMMARIZE_COUNT": c.env})
|
||||||
|
cfg, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load: %v", err)
|
||||||
|
}
|
||||||
|
if cfg.OnboardSummarizeCount != c.want {
|
||||||
|
t.Fatalf("OnboardSummarizeCount = %d, want %d", cfg.OnboardSummarizeCount, c.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad_OnboardSummarizeCountInvalid(t *testing.T) {
|
||||||
|
setEnv(t, map[string]string{"TAPIR_ONBOARD_SUMMARIZE_COUNT": "three"})
|
||||||
|
if _, err := Load(); err == nil {
|
||||||
|
t.Fatal("Load: want error for non-numeric TAPIR_ONBOARD_SUMMARIZE_COUNT")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoad_ParsesValues(t *testing.T) {
|
func TestLoad_ParsesValues(t *testing.T) {
|
||||||
setEnv(t, map[string]string{
|
setEnv(t, map[string]string{
|
||||||
"TAPIR_USER_ID": "11111111-1111-1111-1111-111111111111",
|
"TAPIR_USER_ID": "11111111-1111-1111-1111-111111111111",
|
||||||
|
|||||||
Reference in New Issue
Block a user