use serde::{Deserialize, Serialize};
use super::*;
use crate::api::inference_types::RepetitionDetectionParams;
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
pub struct CompletionRequest {
pub model: String,
#[serde(default)]
pub adapter: Option<String>,
#[serde(default)]
pub src_lang: Option<String>,
#[serde(default)]
pub tgt_lang: Option<String>,
#[serde(default)]
pub num_beams: Option<u32>,
#[serde(default)]
pub length_penalty: Option<f32>,
#[serde(default)]
pub early_stopping: Option<bool>,
pub prompt: PromptInput,
#[serde(default = "default_max_tokens")]
pub max_tokens: usize,
#[serde(default)]
pub min_tokens: usize,
pub temperature: Option<f32>,
pub top_k: Option<u32>,
pub top_p: Option<f32>,
pub top_n_sigma: Option<f32>,
pub min_p: Option<f32>,
pub repetition_penalty: Option<f32>,
#[serde(default)]
pub presence_penalty: Option<f32>,
#[serde(default)]
pub frequency_penalty: Option<f32>,
#[serde(default)]
pub logit_bias: Option<std::collections::HashMap<String, f32>>,
#[serde(default)]
pub stream: bool,
#[serde(default, deserialize_with = "deserialize_stop")]
pub stop: Vec<String>,
pub seed: Option<u64>,
#[serde(default)]
pub repetition_detection: Option<RepetitionDetectionParams>,
#[serde(default)]
pub echo: bool,
pub logprobs: Option<u8>,
#[serde(default = "default_n")]
pub n: usize,
pub stream_options: Option<StreamOptions>,
#[allow(dead_code)]
pub user: Option<String>,
#[allow(dead_code)]
pub suffix: Option<String>,
#[allow(dead_code)]
pub best_of: Option<usize>,
}
#[derive(Debug, Serialize)]
pub struct CompletionLogprobs {
pub tokens: Vec<String>,
pub token_logprobs: Vec<Option<f32>>,
pub top_logprobs: Vec<Option<std::collections::HashMap<String, f32>>>,
pub text_offset: Vec<usize>,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum PromptInput {
Text(String),
TokenIds(Vec<u32>),
TokenIdBatch(Vec<Vec<u32>>),
TextArray(Vec<String>),
}
#[derive(Debug, Serialize)]
pub struct CompletionResponse {
pub id: String,
pub object: String,
pub created: u64,
pub model: String,
pub choices: Vec<CompletionChoice>,
pub usage: Usage,
pub system_fingerprint: String,
}
#[derive(Debug, Serialize)]
pub struct CompletionChoice {
pub index: usize,
pub text: String,
pub finish_reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub logprobs: Option<CompletionLogprobs>,
}
impl CompletionResponse {
pub fn new(model: &str, text: String, usage: Usage, finish_reason: &str) -> Self {
Self::from_choices(
model,
vec![CompletionChoice {
index: 0,
text,
finish_reason: finish_reason.to_string(),
logprobs: None,
}],
usage,
)
}
pub fn from_choices(model: &str, choices: Vec<CompletionChoice>, usage: Usage) -> Self {
Self {
id: format!("cmpl-{}", uuid_v4()),
object: "text_completion".to_string(),
created: unix_timestamp(),
model: model.to_string(),
choices,
usage,
system_fingerprint: "fp_metrale".to_string(),
}
}
}
#[derive(Debug, Serialize)]
pub struct CompletionChunk {
pub id: String,
pub object: String,
pub created: u64,
pub model: String,
pub choices: Vec<CompletionChunkChoice>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
#[derive(Debug, Serialize)]
pub struct CompletionChunkChoice {
pub index: usize,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logprobs: Option<CompletionLogprobs>,
}
impl CompletionChunk {
pub fn text_chunk(model: &str, id: &str, text: String) -> Self {
Self {
id: id.to_string(),
object: "text_completion".to_string(),
created: unix_timestamp(),
model: model.to_string(),
choices: vec![CompletionChunkChoice {
index: 0,
text,
finish_reason: None,
logprobs: None,
}],
usage: None,
}
}
pub fn echo_chunk(
model: &str,
id: &str,
text: String,
logprobs: Option<CompletionLogprobs>,
) -> Self {
Self {
id: id.to_string(),
object: "text_completion".to_string(),
created: unix_timestamp(),
model: model.to_string(),
choices: vec![CompletionChunkChoice {
index: 0,
text,
finish_reason: None,
logprobs,
}],
usage: None,
}
}
pub fn finish_chunk_no_usage(model: &str, id: &str, finish_reason: &str) -> Self {
Self {
id: id.to_string(),
object: "text_completion".to_string(),
created: unix_timestamp(),
model: model.to_string(),
choices: vec![CompletionChunkChoice {
index: 0,
text: String::new(),
finish_reason: Some(finish_reason.to_string()),
logprobs: None,
}],
usage: None,
}
}
pub fn usage_only_chunk(model: &str, id: &str, usage: Usage) -> Self {
Self {
id: id.to_string(),
object: "text_completion".to_string(),
created: unix_timestamp(),
model: model.to_string(),
choices: Vec::new(),
usage: Some(usage),
}
}
pub fn done_chunk(model: &str, id: &str, finish_reason: &str, usage: Usage) -> Self {
Self {
id: id.to_string(),
object: "text_completion".to_string(),
created: unix_timestamp(),
model: model.to_string(),
choices: vec![CompletionChunkChoice {
index: 0,
text: String::new(),
finish_reason: Some(finish_reason.to_string()),
logprobs: None,
}],
usage: Some(usage),
}
}
}
#[derive(Debug, Deserialize)]
pub struct TokenizeRequest {
#[allow(dead_code)]
pub model: Option<String>,
pub prompt: Option<String>,
pub messages: Option<Vec<IncomingMessage>>,
}
#[derive(Debug, Serialize)]
pub struct TokenizeResponse {
pub tokens: Vec<u32>,
pub count: usize,
}
#[cfg(test)]
mod min_tokens_tests {
use super::*;
fn req_with(json: serde_json::Value) -> CompletionRequest {
serde_json::from_value(json).expect("valid CompletionRequest JSON")
}
#[test]
fn min_tokens_defaults_to_zero() {
let req = req_with(serde_json::json!({ "model": "m", "prompt": "hi" }));
assert_eq!(req.min_tokens, 0);
}
#[test]
fn min_tokens_explicit_deserialization() {
let req = req_with(serde_json::json!({
"model": "m",
"prompt": "hi",
"min_tokens": 2048
}));
assert_eq!(req.min_tokens, 2048);
}
#[test]
fn min_tokens_zero_is_valid_explicit_value() {
let req = req_with(serde_json::json!({
"model": "m",
"prompt": "hi",
"min_tokens": 0
}));
assert_eq!(req.min_tokens, 0);
}
}