use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use validator::Validate;
use crate::protocols::common::GuidedDecodingOptions;
#[derive(ToSchema, Serialize, Deserialize, Builder, Validate, Debug, Clone, Default)]
pub struct CommonExt {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub ignore_eos: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub min_tokens: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub top_k: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub min_p: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub repetition_penalty: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub include_stop_str_in_output: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub guided_json: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub guided_regex: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub guided_grammar: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub guided_choice: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub guided_decoding_backend: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub guided_whitespace_pattern: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub skip_special_tokens: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub prompt_logprobs: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schema(ignore)]
#[builder(default, setter(strip_option))]
pub add_generation_prompt: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schema(ignore)]
#[builder(default, setter(strip_option))]
pub continue_final_message: Option<bool>,
}
pub(crate) fn extract_guided_decoding_options(
request: &impl CommonExtProvider,
) -> anyhow::Result<Option<GuidedDecodingOptions>> {
let guided_whitespace_pattern = request.get_guided_whitespace_pattern();
GuidedDecodingOptions::from_optional(
request.get_guided_json(),
request.get_guided_regex(),
request.get_guided_choice(),
request.get_guided_grammar(),
request.get_guided_decoding_backend(),
guided_whitespace_pattern.clone(),
None,
)
}
impl CommonExt {
pub fn builder() -> CommonExtBuilder {
CommonExtBuilder::default()
}
}
pub trait CommonExtProvider {
fn common_ext(&self) -> Option<&CommonExt>;
fn get_guided_json(&self) -> Option<serde_json::Value>;
fn get_guided_regex(&self) -> Option<String>;
fn get_guided_grammar(&self) -> Option<String>;
fn get_guided_choice(&self) -> Option<Vec<String>>;
fn get_guided_decoding_backend(&self) -> Option<String>;
#[allow(unused)] fn get_guided_whitespace_pattern(&self) -> Option<String>;
fn get_top_k(&self) -> Option<i32>;
fn get_min_p(&self) -> Option<f32>;
fn get_repetition_penalty(&self) -> Option<f32>;
fn get_include_stop_str_in_output(&self) -> Option<bool>;
fn get_skip_special_tokens(&self) -> Option<bool>;
fn get_prompt_logprobs_count(&self) -> Option<u32> {
None
}
fn get_continue_final_message(&self) -> Option<bool> {
self.common_ext()
.and_then(|common| common.continue_final_message)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_validation_min_tokens() {
let common_ext = CommonExt {
ignore_eos: None,
min_tokens: Some(0), top_k: None,
min_p: None,
repetition_penalty: None,
include_stop_str_in_output: None,
guided_json: None,
guided_regex: None,
guided_grammar: None,
guided_choice: None,
guided_decoding_backend: None,
guided_whitespace_pattern: None,
skip_special_tokens: None,
prompt_logprobs: None,
add_generation_prompt: None,
continue_final_message: None,
};
assert!(common_ext.validate().is_ok());
}
#[test]
fn test_skip_special_tokens_serialization() {
let common_ext = CommonExt::builder()
.skip_special_tokens(true)
.build()
.unwrap();
let json = serde_json::to_string(&common_ext).unwrap();
let deserialized: CommonExt = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.skip_special_tokens, Some(true));
let common_ext = CommonExt::builder()
.skip_special_tokens(false)
.build()
.unwrap();
let json = serde_json::to_string(&common_ext).unwrap();
let deserialized: CommonExt = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.skip_special_tokens, Some(false));
let common_ext = CommonExt::builder().build().unwrap();
let json = serde_json::to_string(&common_ext).unwrap();
assert!(!json.contains("skip_special_tokens"));
}
}