diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs index 91fe69e0bb..af2ce866c8 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/omni_gen_video_generate_handler.rs @@ -176,12 +176,14 @@ pub async fn omni_gen_video_generate_handler( kinovi_character_id_map: &kinovi_character_id_map, }).await? } else { + let empty_map = HashMap::new(); + let media_url_map = media_file_hydration_map.as_ref().unwrap_or(&empty_map); run_pipeline_v1(RunPipelineV1Args { request: &request, server_state: &server_state, mysql_connection: &mut mysql_connection, user_token, - media_file_hydration_map: &media_file_hydration_map, + media_url_map, kinovi_character_id_map: &kinovi_character_id_map, }).await? }; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/pipeline_v1/run_pipeline_v1.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/pipeline_v1/run_pipeline_v1.rs index 2af5868f21..249424c1ce 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/pipeline_v1/run_pipeline_v1.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/pipeline_v1/run_pipeline_v1.rs @@ -32,7 +32,7 @@ pub struct RunPipelineV1Args<'a> { pub server_state: &'a ServerState, pub mysql_connection: &'a mut sqlx::pool::PoolConnection, pub user_token: &'a UserToken, - pub media_file_hydration_map: &'a Option>, + pub media_url_map: &'a HashMap, pub kinovi_character_id_map: &'a Option>, } @@ -64,7 +64,7 @@ pub async fn run_pipeline_v1(args: RunPipelineV1Args<'_>) -> Result) -> Result Provider::Fal, }; - // ── Distill: cost estimate (Artcraft provider — what we bill on) ── + // ── Cost estimate (Artcraft provider — what we bill on) ── let initial = hydrate_to_router_request(request)?; @@ -95,38 +95,18 @@ pub async fn run_pipeline_v1(args: RunPipelineV1Args<'_>) -> Result) -> Result { execute_generation_kinovi( request, server_state, - media_file_hydration_map.as_ref(), kinovi_character_ids, + Some(media_url_map), kinovi_character_ids, billing.maybe_wallet_ledger_entry_token.as_ref(), mysql_connection, ).await? } @@ -167,16 +147,27 @@ pub async fn run_pipeline_v1(args: RunPipelineV1Args<'_>) -> Result, token: Option<&MediaFileToken>) -> Option { + token.and_then(|t| map.get(t)).map(|u| u.to_string()) +} + +fn lookup_urls(map: &HashMap, tokens: Option<&Vec>) -> Option> { + tokens + .filter(|v| !v.is_empty()) + .map(|tokens| tokens.iter().filter_map(|t| map.get(t).map(|u| u.to_string())).collect()) +} + // ── Distillation (kept public for tests) ── pub fn distill_video_request( request: &OmniGenVideoCostAndGenerateRequest, - media_file_hydration_map: Option<&HashMap>, + media_url_map: &HashMap, execution_provider: Provider, ) -> Result { let initial = hydrate_to_router_request(request)?; - // Cost estimate (Artcraft provider — what we bill on). let cost: VideoGenerationCostEstimate = { let cost_request = GenerateVideoRequestBuilder { provider: Provider::Artcraft, @@ -189,34 +180,16 @@ pub fn distill_video_request( cost_plan.estimate_costs() }; - // Resolve media tokens to URLs for the execution request. - let start_frame_url = resolve_single_media_token( - request.start_frame_image_media_token.as_ref(), media_file_hydration_map, - )?; - let end_frame_url = resolve_single_media_token( - request.end_frame_image_media_token.as_ref(), media_file_hydration_map, - )?; - let reference_image_urls = resolve_media_token_list( - request.reference_image_media_tokens.as_ref(), media_file_hydration_map, - )?; - let reference_video_urls = resolve_media_token_list( - request.reference_video_media_tokens.as_ref(), media_file_hydration_map, - )?; - let reference_audio_urls = resolve_media_token_list( - request.reference_audio_media_tokens.as_ref(), media_file_hydration_map, - )?; - - // Build the execution request with resolved URLs. let exec_request = GenerateVideoRequestBuilder { model: initial.model, provider: execution_provider, prompt: initial.prompt, negative_prompt: initial.negative_prompt, - start_frame: start_frame_url.map(ImageRef::Url), - end_frame: end_frame_url.map(ImageRef::Url), - reference_images: reference_image_urls.map(ImageListRef::Urls), - reference_videos: reference_video_urls.map(VideoListRef::Urls), - reference_audio: reference_audio_urls.map(AudioListRef::Urls), + start_frame: lookup_url(media_url_map, request.start_frame_image_media_token.as_ref()).map(ImageRef::Url), + end_frame: lookup_url(media_url_map, request.end_frame_image_media_token.as_ref()).map(ImageRef::Url), + reference_images: lookup_urls(media_url_map, request.reference_image_media_tokens.as_ref()).map(ImageListRef::Urls), + reference_videos: lookup_urls(media_url_map, request.reference_video_media_tokens.as_ref()).map(VideoListRef::Urls), + reference_audio: lookup_urls(media_url_map, request.reference_audio_media_tokens.as_ref()).map(AudioListRef::Urls), reference_character_tokens: None, resolution: initial.resolution, aspect_ratio: initial.aspect_ratio, @@ -239,58 +212,3 @@ pub fn distill_video_request( execution_provider, }) } - -// ── Token resolution helpers ── - -fn resolve_single_media_token( - token: Option<&MediaFileToken>, - hydration_map: Option<&HashMap>, -) -> Result, AdvancedCommonWebError> { - let token = match token { - Some(t) => t, - None => return Ok(None), - }; - - let map = hydration_map.ok_or_else(|| { - AdvancedCommonWebError::BadInputWithSimpleMessage( - "media token supplied but no hydration map was provided".to_string(), - ) - })?; - - match map.get(token) { - Some(url) => Ok(Some(url.to_string())), - None => Err(AdvancedCommonWebError::BadInputWithSimpleMessage(format!( - "Media token not found in hydration map: {:?}", token - ))), - } -} - -fn resolve_media_token_list( - tokens: Option<&Vec>, - hydration_map: Option<&HashMap>, -) -> Result>, AdvancedCommonWebError> { - let tokens = match tokens { - Some(tokens) if !tokens.is_empty() => tokens, - _ => return Ok(None), - }; - - let map = hydration_map.ok_or_else(|| { - AdvancedCommonWebError::BadInputWithSimpleMessage( - "media tokens supplied but no hydration map was provided".to_string(), - ) - })?; - - let mut urls: Vec = Vec::with_capacity(tokens.len()); - for token in tokens { - match map.get(token) { - Some(url) => urls.push(url.to_string()), - None => { - return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(format!( - "Media token not found in hydration map: {:?}", token - ))); - } - } - } - - Ok(Some(urls)) -} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs index e9a03d92be..e259e4df66 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs @@ -83,7 +83,7 @@ mod tests { fn distill_text(request: &OmniGenVideoCostAndGenerateRequest) -> DistilledVideoRequest { let empty: HashMap = HashMap::new(); - distill_video_request(request, Some(&empty), Provider::Seedance2Pro) + distill_video_request(request, &empty, Provider::Seedance2Pro) .expect("distill should succeed for Seedance 2.0 (text)") } @@ -91,7 +91,7 @@ mod tests { request: &OmniGenVideoCostAndGenerateRequest, hydration: &HashMap, ) -> DistilledVideoRequest { - distill_video_request(request, Some(hydration), Provider::Seedance2Pro) + distill_video_request(request, hydration, Provider::Seedance2Pro) .expect("distill should succeed for Seedance 2.0 (with media)") } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_seedance_1p5_pro.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_seedance_1p5_pro.rs index b3c7fc5a23..43f99d9cd8 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_seedance_1p5_pro.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_seedance_1p5_pro.rs @@ -76,7 +76,7 @@ mod tests { fn distill_text(request: &OmniGenVideoCostAndGenerateRequest) -> DistilledVideoRequest { let empty: HashMap = HashMap::new(); - distill_video_request(request, Some(&empty), Provider::Fal) + distill_video_request(request, &empty, Provider::Fal) .expect("distill_video_request should succeed for Seedance 1.5 Pro (text)") } @@ -84,7 +84,7 @@ mod tests { request: &OmniGenVideoCostAndGenerateRequest, hydration: &HashMap, ) -> DistilledVideoRequest { - distill_video_request(request, Some(hydration), Provider::Fal) + distill_video_request(request, hydration, Provider::Fal) .expect("distill_video_request should succeed for Seedance 1.5 Pro (image)") } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_2.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_2.rs index 950819639f..15c3e0308c 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_2.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_2.rs @@ -63,7 +63,7 @@ mod tests { fn distill_text(request: &OmniGenVideoCostAndGenerateRequest) -> DistilledVideoRequest { let empty: HashMap = HashMap::new(); - distill_video_request(request, Some(&empty), Provider::Fal) + distill_video_request(request, &empty, Provider::Fal) .expect("distill_video_request should succeed for Veo 2 (text)") } @@ -71,7 +71,7 @@ mod tests { request: &OmniGenVideoCostAndGenerateRequest, hydration: &HashMap, ) -> DistilledVideoRequest { - distill_video_request(request, Some(hydration), Provider::Fal) + distill_video_request(request, hydration, Provider::Fal) .expect("distill_video_request should succeed for Veo 2 (image)") } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_3.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_3.rs index 5cd0b653aa..5a7243416f 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_3.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/test_veo_3.rs @@ -69,7 +69,7 @@ mod tests { fn distill_text(request: &OmniGenVideoCostAndGenerateRequest) -> DistilledVideoRequest { let empty: HashMap = HashMap::new(); - distill_video_request(request, Some(&empty), Provider::Fal) + distill_video_request(request, &empty, Provider::Fal) .expect("distill should succeed for Veo 3 (text)") } @@ -77,7 +77,7 @@ mod tests { request: &OmniGenVideoCostAndGenerateRequest, hydration: &HashMap, ) -> DistilledVideoRequest { - distill_video_request(request, Some(hydration), Provider::Fal) + distill_video_request(request, hydration, Provider::Fal) .expect("distill should succeed for Veo 3 (image)") }