1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
4#[serde(rename_all = "camelCase", deny_unknown_fields)]
5pub struct ModelSettings {
6 #[serde(default, skip_serializing_if = "Option::is_none")]
8 pub temperature: Option<f32>,
9 #[serde(default, skip_serializing_if = "Option::is_none")]
11 pub top_p: Option<f32>,
12 #[serde(default, skip_serializing_if = "Option::is_none")]
14 pub max_tokens: Option<u32>,
15}
16
17impl ModelSettings {
18 pub fn is_empty(&self) -> bool {
20 self.temperature.is_none() && self.top_p.is_none() && self.max_tokens.is_none()
21 }
22}
23
24#[cfg(test)]
25mod tests {
26 use serde_json::from_str;
27
28 use super::*;
29
30 #[test]
31 fn json_round_trips_in_camel_case_and_rejects_unknown_fields() {
32 assert!(ModelSettings::default().is_empty());
33 assert_eq!(serde_json::to_value(ModelSettings::default()).unwrap(), serde_json::json!({}));
34
35 let settings = ModelSettings { temperature: Some(0.0), top_p: None, max_tokens: Some(1024) };
36 let json = serde_json::to_value(&settings).unwrap();
37 let parsed: ModelSettings =
38 serde_json::from_str(r#"{ "temperature": 0.2, "topP": 0.9, "maxTokens": 64 }"#).unwrap();
39
40 assert!(!settings.is_empty());
41 assert_eq!(json["temperature"], 0.0);
42 assert_eq!(json["maxTokens"], 1024);
43 assert!(json.get("topP").is_none());
44 assert_eq!(parsed, ModelSettings { temperature: Some(0.2), top_p: Some(0.9), max_tokens: Some(64) });
45 assert!(from_str::<ModelSettings>(r#"{ "frequencyPenalty": 1.0 }"#).is_err());
46 }
47}