diff --git a/internal/config/uploads.go b/internal/config/uploads.go index 442ba26..2785455 100644 --- a/internal/config/uploads.go +++ b/internal/config/uploads.go @@ -28,6 +28,10 @@ type UploadConfig struct { // TranscribeTimeout bounds one job (conversion + transcription); a 90 minute // recording took ~7.5 minutes on iguana. TranscribeTimeout time.Duration + // TranscribeEngineTimeout bounds ONE engine call, so a wedged engine fails while the + // job is still alive and the fallback (or the other engine) can take over. The job + // timeout alone cannot do that (ADR-035 review). + TranscribeEngineTimeout time.Duration // UploadTmpDir is where uploads are spooled and converted. Audio is deleted // after transcription, so this holds at most the in-flight files. UploadTmpDir string @@ -62,6 +66,7 @@ const ( defaultTranscribeModel = "iguana/kb-whisper" defaultTranscribeFallbackModel = "berget/kb-whisper" defaultTranscribeTimeout = 60 * time.Minute + defaultTranscribeEngineTimeout = 25 * time.Minute defaultMaxUploadedChars = 90000 defaultUploadChatModel = "iguana/gemma4-26b" ) @@ -107,6 +112,15 @@ func loadUploadConfig() (UploadConfig, error) { } u.TranscribeTimeout = timeout + engineTimeout, err := durationOr("TAPIR_TRANSCRIBE_ENGINE_TIMEOUT", defaultTranscribeEngineTimeout) + if err != nil { + return UploadConfig{}, err + } + if engineTimeout <= 0 { + return UploadConfig{}, fmt.Errorf("config: TAPIR_TRANSCRIBE_ENGINE_TIMEOUT must be positive, got %v", engineTimeout) + } + u.TranscribeEngineTimeout = engineTimeout + if v := os.Getenv("TAPIR_RECONCILE_CHECK_ORDER"); v != "" { on, err := strconv.ParseBool(v) if err != nil {