gproxy_protocol/transform/video/
native.rs1use super::ResolvedVideoResource;
5use crate::{
6 transform::{Converted, Report, TransformError},
7 wire::{DeclaredFields, gemini::video as g, openai::video as o},
8};
9use std::collections::BTreeMap;
10
11#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
12pub struct NativeVideoDefaults {
13 pub seconds: o::NativeVideoSeconds,
16 pub size: o::NativeVideoSize,
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct PreparedNativeVeoRequest {
21 pub body: g::PredictLongRunningRequestBody,
22 pub original: o::NativeCreateVideoRequestBody,
23 pub effective: NativeVideoDefaults,
24 pub target_model: String,
25}
26
27pub fn native_to_gemini_request(
28 input: o::NativeCreateVideoRequestBody,
29 target_model: &str,
30 defaults: Option<NativeVideoDefaults>,
31 resources: &BTreeMap<String, ResolvedVideoResource>,
32) -> Result<Converted<PreparedNativeVeoRequest>, TransformError> {
33 let input = input.into_declared();
34
35 let seconds = input
36 .seconds
37 .or(defaults.as_ref().map(|v| v.seconds))
38 .ok_or_else(|| TransformError::missing_metadata("native.effective_seconds"))?;
39 let size = input
40 .size
41 .or(defaults.as_ref().map(|v| v.size))
42 .ok_or_else(|| TransformError::missing_metadata("native.effective_size"))?;
43 let aspect = match size {
44 o::NativeVideoSize::Portrait720 => Some("9:16"),
45 o::NativeVideoSize::Landscape720 => Some("16:9"),
46 _ => None,
47 };
48 let seconds_value = match seconds {
49 o::NativeVideoSeconds::Four => 4,
50 o::NativeVideoSeconds::Eight => 8,
51 o::NativeVideoSeconds::Twelve => 12,
52 };
53 let image = input
54 .input_reference
55 .as_ref()
56 .map(|reference| {
57 let key = match reference {
58 o::NativeInputReference::File(v) => &v.file_id,
59 o::NativeInputReference::Url(v) => &v.image_url,
60 };
61 super::resources::image_resource(key, resources, super::VideoResourceRole::FirstFrame)
62 })
63 .map(crate::transform::optional)
64 .transpose()?
65 .flatten();
66 let mut instance = g::VideoGenerationInstance::builder()
67 .prompt(input.prompt.clone())
68 .build();
69 instance.image = image;
70 let mut parameters = g::VideoGenerationParameters::builder()
71 .sample_count(1)
72 .duration_seconds(seconds_value)
73 .build();
74 parameters.aspect_ratio = aspect.map(str::to_owned);
75 parameters.resolution = aspect.map(|_| "720p".to_owned());
76 let body = g::PredictLongRunningRequestBody::builder(vec![instance])
77 .parameters(parameters)
78 .build();
79 Ok(Converted {
80 value: PreparedNativeVeoRequest {
81 body,
82 original: input,
83 effective: NativeVideoDefaults { seconds, size },
84 target_model: target_model.into(),
85 },
86 report: Report::default(),
87 })
88}
89
90#[derive(Debug, Clone, PartialEq)]
91pub struct PreparedVeoNativeRequest {
92 pub body: o::NativeCreateVideoRequestBody,
93 pub original: g::PredictLongRunningRequestBody,
94 pub target_model: String,
95}
96
97pub fn gemini_to_native_request(
100 input: g::PredictLongRunningRequestBody,
101 target_model: &str,
102 defaults: Option<NativeVideoDefaults>,
103 resources: &BTreeMap<String, ResolvedVideoResource>,
104) -> Result<Converted<PreparedVeoNativeRequest>, TransformError> {
105 let input = input.into_declared();
106 if target_model.trim().is_empty() {
107 return Err(TransformError::missing_metadata("target_model"));
108 }
109
110 let instance = input
111 .instances
112 .first()
113 .ok_or_else(|| TransformError::missing_metadata("video.instance"))?;
114
115 let prompt = instance
116 .prompt
117 .as_ref()
118 .filter(|v| !v.trim().is_empty())
119 .ok_or_else(|| TransformError::missing_metadata("native.prompt"))?
120 .clone();
121 let parameters = input.parameters.as_ref();
122
123 let seconds = match parameters.and_then(|p| p.duration_seconds) {
124 Some(4) => Some(o::NativeVideoSeconds::Four),
125 Some(8) => Some(o::NativeVideoSeconds::Eight),
126 Some(12) => Some(o::NativeVideoSeconds::Twelve),
127 Some(_) => None,
128 None => defaults.as_ref().map(|v| v.seconds),
129 };
130 let aspect = parameters.and_then(|p| p.aspect_ratio.as_deref());
131 let resolution = parameters.and_then(|p| p.resolution.as_deref());
132 let size = match (aspect, resolution) {
133 (Some("16:9"), Some("720p")) => Some(o::NativeVideoSize::Landscape720),
134 (Some("9:16"), Some("720p")) => Some(o::NativeVideoSize::Portrait720),
135 (None, None) => defaults.as_ref().map(|v| v.size),
136 _ => None,
137 };
138 let reference = instance
139 .image
140 .as_ref()
141 .map(|image| {
142 super::resources::published_url(image, resources).map(|url| {
143 o::NativeInputReference::Url(o::NativeUrlReference::builder(url).build())
144 })
145 })
146 .map(crate::transform::optional)
147 .transpose()?
148 .flatten();
149 let mut body = o::NativeCreateVideoRequestBody::builder(prompt)
150 .model(target_model)
151 .build();
152 body.seconds = seconds;
153 body.size = size;
154 body.input_reference = reference;
155 Ok(Converted {
156 value: PreparedVeoNativeRequest {
157 body,
158 original: input,
159 target_model: target_model.into(),
160 },
161 report: Report::default(),
162 })
163}