use serde::{Deserialize, Serialize};
use crate::{
generation::{
parameters::{FormatType, KeepAlive, ThinkType},
tools::{Tool, ToolInfo},
},
models::ModelOptions,
};
use super::ChatMessage;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessageRequest {
#[serde(rename = "model")]
pub model_name: String,
pub messages: Vec<ChatMessage>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<ToolInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<ModelOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub template: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<FormatType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keep_alive: Option<KeepAlive>,
#[serde(default)]
pub(crate) stream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub think: Option<ThinkType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logprobs: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_logprobs: Option<u32>,
}
impl ChatMessageRequest {
pub fn new(model_name: String, messages: Vec<ChatMessage>) -> Self {
Self {
model_name,
messages,
options: None,
template: None,
format: None,
keep_alive: None,
stream: false,
tools: vec![],
think: None,
logprobs: None,
top_logprobs: None,
}
}
pub fn options(mut self, options: ModelOptions) -> Self {
self.options = Some(options);
self
}
pub fn template(mut self, template: String) -> Self {
self.template = Some(template);
self
}
pub fn format(mut self, format: FormatType) -> Self {
self.format = Some(format);
self
}
pub fn keep_alive(mut self, keep_alive: KeepAlive) -> Self {
self.keep_alive = Some(keep_alive);
self
}
pub fn tools(mut self, tools: Vec<ToolInfo>) -> Self {
self.tools = tools;
self
}
pub fn add_tool<T: Tool>(mut self, _tool: T) -> Self {
self.tools.push(ToolInfo::new::<_, T>());
self
}
pub fn think(mut self, think: impl Into<ThinkType>) -> Self {
self.think = Some(think.into());
self
}
pub fn logprobs(mut self, logprobs: bool) -> Self {
self.logprobs = Some(logprobs);
self
}
pub fn top_logprobs(mut self, top_logprobs: u32) -> Self {
self.top_logprobs = Some(top_logprobs);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::generation::parameters::{FormatType, JsonSchema, JsonStructure};
use serde::Deserialize;
use serde_json::Value;
#[allow(dead_code)]
#[derive(Debug, JsonSchema)]
struct StructuredReply {
answer: String,
}
#[test]
fn serializes_structured_json_format() {
let request = ChatMessageRequest::new(
"model".to_string(),
vec![ChatMessage::user("hello".to_string())],
)
.format(FormatType::StructuredJson(Box::new(JsonStructure::new::<
StructuredReply,
>())));
let value = serde_json::to_value(&request).expect("serialize request");
let format = value
.get("format")
.expect("format field present")
.as_object()
.expect("format serialized as object");
assert_eq!(
format
.get("type")
.expect("schema type present")
.as_str()
.expect("type is string"),
"object"
);
let properties = format
.get("properties")
.expect("schema has properties")
.as_object()
.expect("properties serialized as object");
assert!(properties.contains_key("answer"));
assert_eq!(
value.get("stream").expect("stream field present"),
&Value::Bool(false)
);
}
#[allow(dead_code)]
#[derive(Debug, Deserialize, JsonSchema)]
struct TestToolParams {
city: String,
}
struct TestTool;
impl Tool for TestTool {
type Params = TestToolParams;
fn name() -> &'static str {
"test_weather"
}
fn description() -> &'static str {
"Gets test weather"
}
async fn call(
&mut self,
_parameters: Self::Params,
) -> crate::generation::tools::Result<String> {
Ok("sunny".to_string())
}
}
#[test]
fn add_tool_serializes_tool_schema_for_streaming_request() {
let mut request = ChatMessageRequest::new(
"model".to_string(),
vec![ChatMessage::user("hello".to_string())],
)
.add_tool(TestTool);
request.stream = true;
let value = serde_json::to_value(&request).expect("serialize request");
assert_eq!(
value.get("stream").expect("stream field present"),
&Value::Bool(true)
);
let tools = value
.get("tools")
.expect("tools field present")
.as_array()
.expect("tools serialized as array");
assert_eq!(tools.len(), 1);
let function = tools[0]
.get("function")
.expect("function field present")
.as_object()
.expect("function serialized as object");
assert_eq!(
function.get("name").expect("name present"),
&Value::String("test_weather".to_string())
);
assert_eq!(
function.get("description").expect("description present"),
&Value::String("Gets test weather".to_string())
);
assert!(function.contains_key("parameters"));
}
}