harden article generation reliability
This commit is contained in:
@@ -332,16 +332,20 @@ type CacheConfig struct {
|
||||
}
|
||||
|
||||
type GenerationConfig struct {
|
||||
QueueSize int `mapstructure:"queue_size"`
|
||||
WorkerConcurrency int `mapstructure:"worker_concurrency"`
|
||||
StreamEnabled bool `mapstructure:"stream_enabled"`
|
||||
ArticleTimeout time.Duration `mapstructure:"article_timeout"`
|
||||
TaskLeaseTTL time.Duration `mapstructure:"task_lease_ttl"`
|
||||
TaskRecoveryInterval time.Duration `mapstructure:"task_recovery_interval"`
|
||||
TaskRecoveryTimeout time.Duration `mapstructure:"task_recovery_timeout"`
|
||||
TaskRecoveryBatchSize int `mapstructure:"task_recovery_batch_size"`
|
||||
TaskQueuedStaleAfter time.Duration `mapstructure:"task_queued_stale_after"`
|
||||
TaskMaxAttempts int `mapstructure:"task_max_attempts"`
|
||||
QueueSize int `mapstructure:"queue_size"`
|
||||
WorkerConcurrency int `mapstructure:"worker_concurrency"`
|
||||
StreamEnabled bool `mapstructure:"stream_enabled"`
|
||||
ArticleTimeout time.Duration `mapstructure:"article_timeout"`
|
||||
TaskLeaseTTL time.Duration `mapstructure:"task_lease_ttl"`
|
||||
TaskRecoveryInterval time.Duration `mapstructure:"task_recovery_interval"`
|
||||
TaskRecoveryTimeout time.Duration `mapstructure:"task_recovery_timeout"`
|
||||
TaskRecoveryBatchSize int `mapstructure:"task_recovery_batch_size"`
|
||||
TaskQueuedStaleAfter time.Duration `mapstructure:"task_queued_stale_after"`
|
||||
TaskMaxAttempts int `mapstructure:"task_max_attempts"`
|
||||
TaskStateCheckInterval time.Duration `mapstructure:"task_state_check_interval"`
|
||||
TaskStateCheckTimeout time.Duration `mapstructure:"task_state_check_timeout"`
|
||||
TaskStateCheckBatchSize int `mapstructure:"task_state_check_batch_size"`
|
||||
TaskStateCheckLookback time.Duration `mapstructure:"task_state_check_lookback"`
|
||||
}
|
||||
|
||||
func Load(configPath string) (*Config, error) {
|
||||
@@ -625,6 +629,18 @@ func NormalizeGenerationConfig(cfg *GenerationConfig) {
|
||||
if cfg.TaskMaxAttempts <= 0 {
|
||||
cfg.TaskMaxAttempts = 3
|
||||
}
|
||||
if cfg.TaskStateCheckInterval <= 0 {
|
||||
cfg.TaskStateCheckInterval = 5 * time.Minute
|
||||
}
|
||||
if cfg.TaskStateCheckTimeout <= 0 {
|
||||
cfg.TaskStateCheckTimeout = 10 * time.Second
|
||||
}
|
||||
if cfg.TaskStateCheckBatchSize <= 0 {
|
||||
cfg.TaskStateCheckBatchSize = 100
|
||||
}
|
||||
if cfg.TaskStateCheckLookback <= 0 {
|
||||
cfg.TaskStateCheckLookback = 24 * time.Hour
|
||||
}
|
||||
}
|
||||
|
||||
func NormalizeComplianceConfig(cfg *ComplianceConfig) {
|
||||
@@ -731,6 +747,18 @@ func applyEnvOverrides(cfg *Config) {
|
||||
if n, ok := lookupIntEnv("COMPLIANCE_REVIEW_MAX_ATTEMPTS"); ok {
|
||||
cfg.Compliance.ReviewMaxAttempts = n
|
||||
}
|
||||
if d, ok := lookupDurationEnv("GENERATION_TASK_STATE_CHECK_INTERVAL"); ok {
|
||||
cfg.Generation.TaskStateCheckInterval = d
|
||||
}
|
||||
if d, ok := lookupDurationEnv("GENERATION_TASK_STATE_CHECK_TIMEOUT"); ok {
|
||||
cfg.Generation.TaskStateCheckTimeout = d
|
||||
}
|
||||
if n, ok := lookupIntEnv("GENERATION_TASK_STATE_CHECK_BATCH_SIZE"); ok {
|
||||
cfg.Generation.TaskStateCheckBatchSize = n
|
||||
}
|
||||
if d, ok := lookupDurationEnv("GENERATION_TASK_STATE_CHECK_LOOKBACK"); ok {
|
||||
cfg.Generation.TaskStateCheckLookback = d
|
||||
}
|
||||
if apiKey, ok := lookupNonEmptyEnv("QDRANT_API_KEY"); ok {
|
||||
cfg.Qdrant.APIKey = apiKey
|
||||
}
|
||||
|
||||
@@ -480,6 +480,18 @@ generation:
|
||||
if cfg.Generation.TaskMaxAttempts != 3 {
|
||||
t.Fatalf("expected default task max attempts 3, got %d", cfg.Generation.TaskMaxAttempts)
|
||||
}
|
||||
if cfg.Generation.TaskStateCheckInterval != 5*time.Minute {
|
||||
t.Fatalf("expected default task state check interval 5m, got %s", cfg.Generation.TaskStateCheckInterval)
|
||||
}
|
||||
if cfg.Generation.TaskStateCheckTimeout != 10*time.Second {
|
||||
t.Fatalf("expected default task state check timeout 10s, got %s", cfg.Generation.TaskStateCheckTimeout)
|
||||
}
|
||||
if cfg.Generation.TaskStateCheckBatchSize != 100 {
|
||||
t.Fatalf("expected default task state check batch size 100, got %d", cfg.Generation.TaskStateCheckBatchSize)
|
||||
}
|
||||
if cfg.Generation.TaskStateCheckLookback != 24*time.Hour {
|
||||
t.Fatalf("expected default task state check lookback 24h, got %s", cfg.Generation.TaskStateCheckLookback)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAppliesGenerationRecoveryOverrides(t *testing.T) {
|
||||
@@ -494,6 +506,10 @@ generation:
|
||||
task_recovery_batch_size: 12
|
||||
task_queued_stale_after: 45s
|
||||
task_max_attempts: 5
|
||||
task_state_check_interval: 9m
|
||||
task_state_check_timeout: 11s
|
||||
task_state_check_batch_size: 13
|
||||
task_state_check_lookback: 2h
|
||||
`)
|
||||
|
||||
cfg, err := Load(configPath)
|
||||
@@ -509,11 +525,38 @@ generation:
|
||||
cfg.Generation.TaskRecoveryTimeout != 10*time.Second ||
|
||||
cfg.Generation.TaskRecoveryBatchSize != 12 ||
|
||||
cfg.Generation.TaskQueuedStaleAfter != 45*time.Second ||
|
||||
cfg.Generation.TaskMaxAttempts != 5 {
|
||||
cfg.Generation.TaskMaxAttempts != 5 ||
|
||||
cfg.Generation.TaskStateCheckInterval != 9*time.Minute ||
|
||||
cfg.Generation.TaskStateCheckTimeout != 11*time.Second ||
|
||||
cfg.Generation.TaskStateCheckBatchSize != 13 ||
|
||||
cfg.Generation.TaskStateCheckLookback != 2*time.Hour {
|
||||
t.Fatalf("unexpected generation recovery config: %#v", cfg.Generation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAppliesGenerationStateCheckEnvOverrides(t *testing.T) {
|
||||
t.Setenv("GENERATION_TASK_STATE_CHECK_INTERVAL", "3m")
|
||||
t.Setenv("GENERATION_TASK_STATE_CHECK_TIMEOUT", "4s")
|
||||
t.Setenv("GENERATION_TASK_STATE_CHECK_BATCH_SIZE", "17")
|
||||
t.Setenv("GENERATION_TASK_STATE_CHECK_LOOKBACK", "6h")
|
||||
|
||||
configPath := writeTestConfig(t, `
|
||||
generation: {}
|
||||
`)
|
||||
|
||||
cfg, err := Load(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error = %v", err)
|
||||
}
|
||||
|
||||
if cfg.Generation.TaskStateCheckInterval != 3*time.Minute ||
|
||||
cfg.Generation.TaskStateCheckTimeout != 4*time.Second ||
|
||||
cfg.Generation.TaskStateCheckBatchSize != 17 ||
|
||||
cfg.Generation.TaskStateCheckLookback != 6*time.Hour {
|
||||
t.Fatalf("unexpected generation state check env config: %#v", cfg.Generation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreReloadsConfigAndKeepsPreviousOnInvalidYAML(t *testing.T) {
|
||||
configPath := writeTestConfig(t, `
|
||||
jwt:
|
||||
|
||||
Reference in New Issue
Block a user