feat: Add auto-trigger for LLM analysis
Introduces a new module `auto_trigger` responsible for opportunistically initiating LLM analysis jobs. This mechanism scans case directories for new or updated recordings and transcripts, automatically creating and queuing analysis jobs when appropriate. Key components: - `evaluate_case`: Pure function to determine if a case is ready for analysis based on recording status, transcriptions, document modification times, and existing job/failure markers. - `try_enqueue`: Orchestrates the analysis job creation and queuing process if `evaluate_case` returns `AutoDecision::Enqueue`. - `try_enqueue_all_for_user`: Iterates through all user cases to trigger `try_enqueue` for eligible ones. - `write_failure_marker`/`remove_failure_marker`: Handles persistent recording of analysis failures and their cleanup. This feature aims to reduce manual intervention by automatically analyzing new or changed case data.
This commit is contained in:
@@ -10,11 +10,14 @@ use tracing::{info, warn};
|
||||
use time::{Date, OffsetDateTime, format_description::well_known::Rfc3339};
|
||||
|
||||
use crate::PipelineState;
|
||||
use crate::analyze::{ANALYSIS_INPUT_FILE, DOCUMENT_FILE, recovery as analyze_recovery};
|
||||
use crate::analyze::{
|
||||
ANALYSIS_INPUT_FILE, DOCUMENT_FILE, auto_trigger, recovery as analyze_recovery,
|
||||
};
|
||||
use crate::auth::AuthenticatedWebUser;
|
||||
use crate::case_id::{CaseId, CaseIdPath};
|
||||
use crate::config::Config;
|
||||
use crate::error::AppError;
|
||||
use crate::events::EventSender;
|
||||
use crate::routes::case_actions::read_document;
|
||||
use crate::routes::web::{RecordingView, scan_recordings};
|
||||
use crate::transcribe::recovery as transcribe_recovery;
|
||||
@@ -228,9 +231,16 @@ pub async fn handle_my_cases(
|
||||
user: AuthenticatedWebUser,
|
||||
State(config): State<Arc<Config>>,
|
||||
State(pipeline): State<PipelineState>,
|
||||
State(events_tx): State<EventSender>,
|
||||
) -> Result<Html<String>, AppError> {
|
||||
let user_root = config.data_path.join(&user.slug);
|
||||
pipeline.heal_orphans_if_idle(&user_root, &user.slug).await;
|
||||
// Auto-analysis: hand every eligible case to the worker before we
|
||||
// render. The common case is "nothing to do" and costs a handful of
|
||||
// stat-calls per case; actual enqueues happen only when pre-conditions
|
||||
// flip (new transcripts in, stale document, ...).
|
||||
auto_trigger::try_enqueue_all_for_user(&user_root, &pipeline.analyze_tx, &config, &events_tx)
|
||||
.await;
|
||||
|
||||
let busy = pipeline.analyze_busy.0.load(Ordering::Acquire);
|
||||
let cases = scan_user_cases(&config, &user.slug, busy).await;
|
||||
@@ -263,6 +273,7 @@ pub async fn handle_case_page(
|
||||
user: AuthenticatedWebUser,
|
||||
State(config): State<Arc<Config>>,
|
||||
State(pipeline): State<PipelineState>,
|
||||
State(events_tx): State<EventSender>,
|
||||
CaseIdPath(case_id): CaseIdPath,
|
||||
) -> Result<Html<String>, AppError> {
|
||||
let user_root = config.data_path.join(&user.slug);
|
||||
@@ -275,6 +286,10 @@ pub async fn handle_case_page(
|
||||
"case page (possible IDOR probe)",
|
||||
)
|
||||
.await?;
|
||||
// Auto-analysis trigger for deep-link navigation: same evaluation as
|
||||
// the list view. Document render below still wins the race if the
|
||||
// worker happens to finish synchronously.
|
||||
auto_trigger::try_enqueue(&case_dir, &pipeline.analyze_tx, &config, &events_tx).await;
|
||||
let case_id_str = case_id.to_string();
|
||||
info!(slug = %user.slug, case_id = %case_id, "case page viewed");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user