use serde::{Deserialize, Serialize};
use super::message::ChatMessage;
use super::tool::{RequestUsageConfig, ToolDefinition};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatRequest {
pub model: String,
pub messages: Vec<ChatMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<ToolDefinition>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<RequestUsageConfig>,
}
impl ChatRequest {
pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
Self {
model: model.into(),
messages,
temperature: None,
max_tokens: None,
tools: None,
tool_choice: None,
stop: None,
usage: None,
}
}
}
#[cfg(test)]
mod tests {
use super::super::tool::{FunctionDefinition, ToolDefinition};
use super::*;
use serde_json::json;
#[test]
fn minimal_request_omits_optionals() {
let req = ChatRequest::new("openai/gpt-4o-mini", vec![ChatMessage::user("hi")]);
let v: Value = serde_json::to_value(&req).expect("serialise");
assert_eq!(v["model"], "openai/gpt-4o-mini");
assert_eq!(v["messages"][0]["content"], "hi");
assert!(v.get("temperature").is_none() || v["temperature"].is_null());
assert!(v.get("tools").is_none() || v["tools"].is_null());
assert!(v.get("stop").is_none() || v["stop"].is_null());
assert!(v.get("usage").is_none() || v["usage"].is_null());
}
#[test]
fn request_with_tools_and_stop_serialises() {
let mut req = ChatRequest::new("openai/gpt-4o-mini", vec![ChatMessage::user("weather?")]);
req.temperature = Some(0.0);
req.max_tokens = Some(256);
req.tools = Some(vec![ToolDefinition::function(FunctionDefinition {
name: "get_weather".into(),
description: Some("weather".into()),
parameters: Some(json!({"type": "object"})),
cache_control: None,
})]);
req.tool_choice = Some(json!("auto"));
req.stop = Some(vec!["\n\n".into()]);
let v: Value = serde_json::to_value(&req).expect("serialise");
assert_eq!(v["tools"][0]["function"]["name"], "get_weather");
assert_eq!(v["tool_choice"], "auto");
assert_eq!(v["temperature"], 0.0_f32);
assert_eq!(v["max_tokens"], 256);
assert_eq!(v["stop"][0], "\n\n");
}
#[test]
fn detailed_usage_toggles() {
let mut req = ChatRequest::new("x", vec![ChatMessage::user("hi")]);
req.usage = Some(RequestUsageConfig::detailed());
let v: Value = serde_json::to_value(&req).expect("serialise");
assert_eq!(v["usage"], json!({"include": true}));
req.usage = None;
let v: Value = serde_json::to_value(&req).expect("serialise");
assert!(v.get("usage").is_none() || v["usage"].is_null());
}
}