fix: audit fixes for async insight jobs + persist generation params

- Fix query param mismatch: rename GenerationStatusQuery.file_path to
  path so the client's app-resume buildQuery({ path: ... }) resolves
  correctly instead of always getting 400
- Remove dead _lib_id bindings from both generate handlers
- Return 202 Accepted instead of 200 from generate endpoints
- Restore OpenTelemetry span instrumentation on generate handlers
- Remove stale UNIQUE constraint from initial migration (incompatible
  with plain-INSERT DAO)
- Add tests for status guard: complete_job/fail_job are no-ops when
  job is already cancelled, and cancel_job by id
- Persist generation params (num_ctx, temperature, top_p, top_k, min_p,
  system_prompt, persona_id) on the photo_insights table for auditing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cameron Cordes
2026-05-27 13:02:15 -04:00
parent b87eb4e690
commit 2818936739
14 changed files with 786 additions and 194 deletions
+10
View File
@@ -19,6 +19,7 @@ use crate::video::actors::{
PlaylistGenerator, PreviewClipGenerator, StreamActor, VideoPlaylistManager,
};
use actix::{Actor, Addr};
use std::collections::HashMap;
use std::env;
use std::sync::{Arc, Mutex, RwLock};
@@ -88,6 +89,9 @@ pub struct AppState {
pub clip_client: ClipClient,
/// Tracks async insight generation jobs (spawned by generate endpoints).
pub insight_job_dao: Arc<Mutex<Box<dyn InsightGenerationJobDao>>>,
/// In-memory map from job_id → tokio AbortHandle for running tasks.
/// Used to abort server-side tasks on cancel or regenerate.
pub insight_job_handles: Arc<Mutex<HashMap<i32, tokio::task::AbortHandle>>>,
}
impl AppState {
@@ -127,6 +131,7 @@ impl AppState {
face_client: FaceClient,
clip_client: ClipClient,
insight_job_dao: Arc<Mutex<Box<dyn InsightGenerationJobDao>>>,
insight_job_handles: Arc<Mutex<HashMap<i32, tokio::task::AbortHandle>>>,
) -> Self {
assert!(
!libraries_vec.is_empty(),
@@ -169,6 +174,7 @@ impl AppState {
face_client,
clip_client,
insight_job_dao,
insight_job_handles,
}
}
@@ -260,6 +266,8 @@ impl Default for AppState {
// Initialize insight generation job DAO (async generation tracking)
let insight_job_dao: Arc<Mutex<Box<dyn InsightGenerationJobDao>>> =
Arc::new(Mutex::new(Box::new(SqliteInsightGenerationJobDao::new())));
let insight_job_handles: Arc<Mutex<HashMap<i32, tokio::task::AbortHandle>>> =
Arc::new(Mutex::new(HashMap::new()));
// Load base path and ensure the primary library row reflects it.
let base_path = env::var("BASE_PATH").expect("BASE_PATH was not set in the env");
@@ -328,6 +336,7 @@ impl Default for AppState {
face_client,
clip_client,
insight_job_dao,
insight_job_handles,
)
}
}
@@ -513,6 +522,7 @@ impl AppState {
FaceClient::new(None), // disabled in test
ClipClient::new(None), // disabled in test
Arc::new(Mutex::new(Box::new(SqliteInsightGenerationJobDao::new()))), // placeholder for test
Arc::new(Mutex::new(HashMap::new())), // placeholder for test
)
}
}