Ny feature: highlight_extract-jobb som analyserer fullstendig transkripsjon etter innspilling og finner 5-10 klippverdige øyeblikk (humor, emosjon, sterke meninger, punchlines, narrative høydepunkter). Komponenter: - synops-highlight CLI: henter segmenter, kaller AI, oppretter klipp-noder - maskinrommet/highlight.rs: jobbdispatcher med modellrouting - Registrert i jobbkø-dispatcher som "highlight_extract" Hvert klipp blir en content-node med metadata (tidsstempler, score, foreslått teksting, thumbnail-sitat, hashtags) og derived_from-edge til episoden. Bruker synops/high-modell via AI Gateway. Ref: docs/proposals/auto_highlight_reel.md
100 lines
3.2 KiB
Rust
100 lines
3.2 KiB
Rust
// Highlight-reel dispatcher — delegerer til synops-highlight CLI.
|
|
//
|
|
// Maskinrommet orkestrerer, CLI-verktøyet gjør jobben:
|
|
// henter transkripsjon, kaller AI for analyse, oppretter klipp-noder.
|
|
//
|
|
// Jobbtype: "highlight_extract"
|
|
// Payload: { "media_node_id": "<uuid>", "requested_by": "<uuid>",
|
|
// "collection_id": "<uuid>" (valgfri) }
|
|
//
|
|
// Ref: docs/proposals/auto_highlight_reel.md
|
|
// docs/retninger/unix_filosofi.md
|
|
|
|
use uuid::Uuid;
|
|
|
|
use crate::ai_admin;
|
|
use crate::cli_dispatch;
|
|
use crate::jobs::JobRow;
|
|
|
|
/// Synops-highlight binary path.
|
|
fn highlight_bin() -> String {
|
|
std::env::var("SYNOPS_HIGHLIGHT_BIN")
|
|
.unwrap_or_else(|_| "synops-highlight".to_string())
|
|
}
|
|
|
|
/// Handler for highlight_extract-jobber.
|
|
///
|
|
/// Spawner synops-highlight med --write for å gjøre alt arbeidet:
|
|
/// transkripsjonshenting, AI-analyse, klipp-node-opprettelse.
|
|
///
|
|
/// Payload forventer:
|
|
/// - media_node_id: UUID — episodenoden med transkripsjon
|
|
/// - requested_by: UUID — brukeren som utløste highlight-analysen
|
|
/// - collection_id: UUID (valgfri) — podcast-samling for belongs_to-edge
|
|
pub async fn handle_highlight_extract(
|
|
job: &JobRow,
|
|
db: &sqlx::PgPool,
|
|
) -> Result<serde_json::Value, String> {
|
|
let media_node_id: Uuid = job
|
|
.payload
|
|
.get("media_node_id")
|
|
.and_then(|v| v.as_str())
|
|
.and_then(|s| s.parse().ok())
|
|
.ok_or("Mangler gyldig media_node_id i payload")?;
|
|
|
|
let requested_by: Uuid = job
|
|
.payload
|
|
.get("requested_by")
|
|
.and_then(|v| v.as_str())
|
|
.and_then(|s| s.parse().ok())
|
|
.ok_or("Mangler gyldig requested_by i payload")?;
|
|
|
|
let collection_id: Option<Uuid> = job
|
|
.payload
|
|
.get("collection_id")
|
|
.and_then(|v| v.as_str())
|
|
.and_then(|s| s.parse().ok());
|
|
|
|
// Bygg kommando
|
|
let bin = highlight_bin();
|
|
let mut cmd = tokio::process::Command::new(&bin);
|
|
|
|
cmd.arg("--media-node-id")
|
|
.arg(media_node_id.to_string())
|
|
.arg("--requested-by")
|
|
.arg(requested_by.to_string())
|
|
.arg("--write");
|
|
|
|
if let Some(coll_id) = collection_id {
|
|
cmd.arg("--collection-id").arg(coll_id.to_string());
|
|
}
|
|
|
|
// Sett miljøvariabler CLI-verktøyet trenger
|
|
cli_dispatch::set_database_url(&mut cmd)?;
|
|
cli_dispatch::forward_env(&mut cmd, "AI_GATEWAY_URL");
|
|
cli_dispatch::forward_env(&mut cmd, "LITELLM_MASTER_KEY");
|
|
|
|
// Modellalias fra ai_job_routing — admin kan endre uten redeploy
|
|
let model_alias = ai_admin::resolve_routing_or_default(db, "highlight").await;
|
|
cmd.env("AI_HIGHLIGHT_MODEL", &model_alias);
|
|
|
|
tracing::info!(
|
|
media_node_id = %media_node_id,
|
|
requested_by = %requested_by,
|
|
collection_id = ?collection_id,
|
|
bin = %bin,
|
|
"Starter synops-highlight"
|
|
);
|
|
|
|
let result = cli_dispatch::run_cli_tool(&bin, &mut cmd).await?;
|
|
|
|
tracing::info!(
|
|
media_node_id = %media_node_id,
|
|
clips_created = result["clips_created"].as_u64().unwrap_or(0),
|
|
highlights_found = result["highlights_found"].as_u64().unwrap_or(0),
|
|
status = result["status"].as_str().unwrap_or("unknown"),
|
|
"synops-highlight fullført"
|
|
);
|
|
|
|
Ok(result)
|
|
}
|