Wait out TTS GPU hold before the insight job timeout starts

The GPU lease keeps per-request reqwest budgets from burning behind a
cross-model swap, but the job-level INSIGHT_GENERATION_TIMEOUT_SECS
wall-clock started at spawn — an insight queued behind a running TTS
synthesis parked its first chat call on the lease and timed out
("timeout after 180s") before chatterbox even finished loading.

Acquire-and-drop an LLM read lease before starting the job clock in
both insight handlers: the wait for the GPU happens before the
timeout begins, mirroring the per-request lease semantics. Dropped
immediately — holding it across the generation would deadlock the
chat calls' own lease acquisitions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cameron Cordes
2026-06-11 19:15:38 -04:00
parent a022a3d15d
commit b1493f5aca
+10
View File
@@ -468,6 +468,13 @@ pub async fn generate_insight_handler(
let path_for_task = path.clone(); let path_for_task = path.clone();
let generator_for_task = generator.clone(); let generator_for_task = generator.clone();
let result = tokio::task::spawn(async move { let result = tokio::task::spawn(async move {
// Cross-model barrier: if a TTS synthesis holds the GPU, wait it
// out BEFORE the generation wall-clock starts. The per-request
// lease keeps reqwest budgets honest, but this job-level timeout
// would otherwise burn while the first chat call queues behind a
// multi-minute synthesis. Dropped immediately — holding it across
// the generation would deadlock the chat calls' own leases.
drop(crate::ai::gpu::llm_lease().await);
tokio::time::timeout( tokio::time::timeout(
std::time::Duration::from_secs(timeout_secs), std::time::Duration::from_secs(timeout_secs),
generator_for_task.generate_insight_for_photo_with_config( generator_for_task.generate_insight_for_photo_with_config(
@@ -846,6 +853,9 @@ pub async fn generate_agentic_insight_handler(
let path_for_task = path.clone(); let path_for_task = path.clone();
let generator_for_task = generator.clone(); let generator_for_task = generator.clone();
let result = tokio::task::spawn(async move { let result = tokio::task::spawn(async move {
// Cross-model barrier — see generate_insight_handler: wait out any
// running TTS synthesis before the generation wall-clock starts.
drop(crate::ai::gpu::llm_lease().await);
tokio::time::timeout( tokio::time::timeout(
std::time::Duration::from_secs(timeout_secs), std::time::Duration::from_secs(timeout_secs),
generator_for_task.generate_agentic_insight_for_photo( generator_for_task.generate_agentic_insight_for_photo(