1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::{Map, Value};
5use validator::Validate;
6
7use super::{
8 common::{default_true, deserialize_null_as_false, is_false, GenerationRequest, InputIds},
9 sampling_params::SamplingParams,
10};
11use crate::validated::Normalizable;
12
13#[derive(Clone, Debug, Serialize, Deserialize, Validate, schemars::JsonSchema)]
18#[validate(schema(function = "validate_generate_request"))]
19pub struct GenerateRequest {
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub text: Option<String>,
23
24 #[serde(default = "super::common::default_unknown_model")]
25 pub model: String,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub input_ids: Option<InputIds>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
35 pub input_embeds: Option<Value>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
42 pub image_data: Option<Value>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
49 pub video_data: Option<Value>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
56 pub audio_data: Option<Value>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub sampling_params: Option<SamplingParams>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub return_logprob: Option<bool>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub logprob_start_len: Option<i32>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub top_logprobs_num: Option<i32>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub token_ids_logprob: Option<Vec<u32>>,
77
78 #[serde(default)]
80 pub return_text_in_logprobs: bool,
81
82 #[serde(default, deserialize_with = "deserialize_null_as_false")]
84 pub stream: bool,
85
86 #[serde(default = "default_true")]
88 pub log_metrics: bool,
89
90 #[serde(default, skip_serializing_if = "is_false")]
92 pub return_hidden_states: bool,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub modalities: Option<Vec<String>>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub session_params: Option<HashMap<String, Value>>,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub lora_path: Option<String>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub lora_id: Option<String>,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
114 pub custom_logit_processor: Option<String>,
115
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub bootstrap_host: Option<String>,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub bootstrap_port: Option<i32>,
123
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub bootstrap_room: Option<i32>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub bootstrap_pair_key: Option<String>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub data_parallel_rank: Option<i32>,
135
136 #[serde(default)]
138 pub background: bool,
139
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub conversation_id: Option<String>,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub priority: Option<i32>,
147
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub extra_key: Option<String>,
151
152 #[serde(default)]
154 pub no_logs: bool,
155
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub custom_labels: Option<HashMap<String, String>>,
159
160 #[serde(default)]
162 pub return_bytes: bool,
163
164 #[serde(default)]
166 pub return_entropy: bool,
167
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub rid: Option<String>,
171
172 #[serde(flatten)]
174 pub other: Map<String, Value>,
175}
176
177impl Normalizable for GenerateRequest {
178 }
180
181fn validate_generate_request(req: &GenerateRequest) -> Result<(), validator::ValidationError> {
183 let has_text = req.text.is_some();
186 let has_input_ids = req.input_ids.is_some();
187
188 let count = [has_text, has_input_ids].iter().filter(|&&x| x).count();
189
190 if count == 0 {
191 return Err(validator::ValidationError::new(
192 "Either text or input_ids should be provided.",
193 ));
194 }
195
196 if count > 1 {
197 return Err(validator::ValidationError::new(
198 "Either text or input_ids should be provided.",
199 ));
200 }
201
202 Ok(())
203}
204
205impl GenerationRequest for GenerateRequest {
206 fn rid(&self) -> Option<&str> {
207 self.rid.as_deref()
208 }
209
210 fn is_stream(&self) -> bool {
211 self.stream
212 }
213
214 fn get_model(&self) -> Option<&str> {
215 Some(self.model.as_str())
216 }
217
218 fn extract_text_for_routing(&self) -> String {
219 if let Some(ref text) = self.text {
221 return text.clone();
222 }
223
224 if let Some(ref input_ids) = self.input_ids {
225 return match input_ids {
226 InputIds::Single(ids) => ids
227 .iter()
228 .map(|&id| id.to_string())
229 .collect::<Vec<String>>()
230 .join(" "),
231 InputIds::Batch(batches) => batches
232 .iter()
233 .flat_map(|batch| batch.iter().map(|&id| id.to_string()))
234 .collect::<Vec<String>>()
235 .join(" "),
236 };
237 }
238
239 String::new()
241 }
242
243 fn routing_tokens(&self) -> Option<&[i32]> {
244 match &self.input_ids {
247 Some(InputIds::Single(ids)) if !ids.is_empty() => Some(ids),
248 Some(InputIds::Batch(seqs)) => seqs
251 .first()
252 .map(Vec::as_slice)
253 .filter(|ids| !ids.is_empty()),
254 _ => None,
255 }
256 }
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
282pub struct GenerateResponse {
283 pub text: String,
284 pub output_ids: Vec<u32>,
285 pub meta_info: GenerateMetaInfo,
286}
287
288#[serde_with::skip_serializing_none]
290#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
291pub struct GenerateMetaInfo {
292 pub id: String,
293 pub finish_reason: GenerateFinishReason,
294 pub prompt_tokens: u32,
295 pub weight_version: String,
296 pub input_token_logprobs: Option<Vec<Vec<Option<f64>>>>,
297 pub output_token_logprobs: Option<Vec<Vec<Option<f64>>>>,
298 pub completion_tokens: u32,
299 pub cached_tokens: u32,
300 pub reasoning_tokens: Option<u32>,
301 pub e2e_latency: f64,
302 pub matched_stop: Option<Value>,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
307#[serde(untagged)]
308pub enum GenerateFinishReason {
309 Length {
310 #[serde(rename = "type")]
311 finish_type: GenerateFinishType,
312 length: u32,
313 },
314 Stop {
315 #[serde(rename = "type")]
316 finish_type: GenerateFinishType,
317 },
318 Other(Value),
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
322#[serde(rename_all = "lowercase")]
323pub enum GenerateFinishType {
324 Length,
325 Stop,
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 fn req() -> GenerateRequest {
333 serde_json::from_value(serde_json::json!({"model": "m"})).expect("minimal request")
334 }
335
336 #[test]
337 fn return_hidden_states_false_is_omitted_and_absent_reads_false() {
338 let r = req();
339 let v = serde_json::to_value(&r).expect("serialize");
340 assert!(v.get("return_hidden_states").is_none());
341
342 let back: GenerateRequest = serde_json::from_value(v).expect("roundtrip");
343 assert!(!back.return_hidden_states);
344 }
345
346 #[test]
347 fn return_hidden_states_true_round_trips() {
348 let mut r = req();
349 r.return_hidden_states = true;
350 let v = serde_json::to_value(&r).expect("serialize");
351 assert_eq!(v["return_hidden_states"], true);
352
353 let back: GenerateRequest = serde_json::from_value(v).expect("roundtrip");
354 assert!(back.return_hidden_states);
355 }
356
357 #[test]
358 fn routing_tokens_from_single_input_ids() {
359 let mut r = req();
360 r.input_ids = Some(InputIds::Single(vec![1, 2, 3]));
361 assert_eq!(r.routing_tokens(), Some(&[1, 2, 3][..]));
362 }
363
364 #[test]
365 fn routing_tokens_prefer_input_ids_over_text() {
366 let mut r = req();
367 r.text = Some("hello".to_string());
368 r.input_ids = Some(InputIds::Single(vec![1, 2, 3]));
369 assert_eq!(r.routing_tokens(), Some(&[1, 2, 3][..]));
370
371 r.input_ids = Some(InputIds::Batch(vec![vec![4, 5], vec![6]]));
372 assert_eq!(r.routing_tokens(), Some(&[4, 5][..]));
373 }
374
375 #[test]
376 fn routing_tokens_none_for_empty_input_ids_with_text() {
377 let mut r = req();
378 r.text = Some("hello".to_string());
379 r.input_ids = Some(InputIds::Single(vec![]));
380 assert_eq!(r.routing_tokens(), None);
381 assert_eq!(r.extract_text_for_routing(), "hello");
382 }
383
384 #[test]
385 fn routing_tokens_from_batch_first_sequence() {
386 let mut r = req();
387 r.input_ids = Some(InputIds::Batch(vec![vec![1, 2], vec![3, 4]]));
388 assert_eq!(r.routing_tokens(), Some(&[1, 2][..]));
389 }
390
391 #[test]
392 fn routing_tokens_none_for_empty_inputs() {
393 let mut r = req();
394 r.input_ids = Some(InputIds::Batch(vec![]));
395 assert_eq!(r.routing_tokens(), None);
396
397 let mut r = req();
398 r.input_ids = Some(InputIds::Batch(vec![vec![], vec![1]]));
399 assert_eq!(r.routing_tokens(), None);
400
401 let r = req();
402 assert_eq!(r.routing_tokens(), None);
403 }
404}