use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum Role {
System,
User,
Assistant,
Tool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCall>>,
}
impl Message {
pub fn system(content: impl Into<String>) -> Self {
Self {
role: Role::System,
content: content.into(),
tool_calls: None,
}
}
pub fn user(content: impl Into<String>) -> Self {
Self {
role: Role::User,
content: content.into(),
tool_calls: None,
}
}
pub fn assistant(content: impl Into<String>) -> Self {
Self {
role: Role::Assistant,
content: content.into(),
tool_calls: None,
}
}
pub fn assistant_with_tool_calls(tool_calls: Vec<ToolCall>) -> Self {
Self {
role: Role::Assistant,
content: String::new(),
tool_calls: Some(tool_calls),
}
}
pub fn tool(content: impl Into<String>) -> Self {
Self {
role: Role::Tool,
content: content.into(),
tool_calls: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub function: FunctionCall,
}
impl ToolCall {
pub fn new(name: impl Into<String>, arguments: serde_json::Value) -> Self {
Self {
function: FunctionCall {
name: name.into(),
arguments,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCall {
pub name: String,
pub arguments: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tool {
#[serde(rename = "type")]
pub tool_type: String,
pub function: FunctionDefinition,
}
impl Tool {
pub fn function(
name: impl Into<String>,
description: impl Into<String>,
parameters: serde_json::Value,
) -> Self {
Self {
tool_type: "function".to_string(),
function: FunctionDefinition {
name: name.into(),
description: description.into(),
parameters,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionDefinition {
pub name: String,
pub description: String,
pub parameters: serde_json::Value,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Options {
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub num_predict: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seed: Option<i64>,
}
impl Options {
pub fn with_temperature(mut self, temperature: f32) -> Self {
self.temperature = Some(temperature);
self
}
pub fn with_top_p(mut self, top_p: f32) -> Self {
self.top_p = Some(top_p);
self
}
pub fn with_num_predict(mut self, num_predict: i32) -> Self {
self.num_predict = Some(num_predict);
self
}
pub fn with_stop(mut self, stop: Vec<String>) -> Self {
self.stop = Some(stop);
self
}
}
#[derive(Debug, Clone, Serialize)]
pub struct ChatRequest {
pub model: String,
pub messages: Vec<Message>,
pub stream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Options>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<Tool>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
}
impl ChatRequest {
pub fn new(model: impl Into<String>, messages: Vec<Message>) -> Self {
Self {
model: model.into(),
messages,
stream: false,
options: None,
tools: None,
format: None,
}
}
pub fn with_options(mut self, options: Options) -> Self {
self.options = Some(options);
self
}
pub fn with_tools(mut self, tools: Vec<Tool>) -> Self {
self.tools = Some(tools);
self
}
pub fn with_format(mut self, format: impl Into<String>) -> Self {
self.format = Some(format.into());
self
}
pub fn with_stream(mut self, stream: bool) -> Self {
self.stream = stream;
self
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct ChatResponse {
pub model: String,
pub created_at: String,
pub message: Message,
pub done: bool,
pub done_reason: Option<String>,
pub total_duration: Option<u64>,
pub load_duration: Option<u64>,
pub prompt_eval_count: Option<usize>,
pub prompt_eval_duration: Option<u64>,
pub eval_count: Option<usize>,
pub eval_duration: Option<u64>,
}
impl ChatResponse {
pub fn content(&self) -> &str {
&self.message.content
}
pub fn tool_calls(&self) -> Option<&Vec<ToolCall>> {
self.message.tool_calls.as_ref()
}
pub fn has_tool_calls(&self) -> bool {
self.message.tool_calls.is_some()
}
}
#[derive(Debug, Clone, Serialize)]
pub struct GenerateRequest {
pub model: String,
pub prompt: String,
pub stream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Options>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
}
impl GenerateRequest {
pub fn new(model: impl Into<String>, prompt: impl Into<String>) -> Self {
Self {
model: model.into(),
prompt: prompt.into(),
stream: false,
options: None,
system: None,
format: None,
}
}
pub fn with_options(mut self, options: Options) -> Self {
self.options = Some(options);
self
}
pub fn with_system(mut self, system: impl Into<String>) -> Self {
self.system = Some(system.into());
self
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct GenerateResponse {
pub model: String,
pub created_at: String,
pub response: String,
pub done: bool,
pub total_duration: Option<u64>,
pub prompt_eval_count: Option<usize>,
pub eval_count: Option<usize>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ModelInfo {
pub name: String,
pub modified_at: String,
pub size: u64,
pub digest: String,
pub details: Option<ModelDetails>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ModelDetails {
pub format: Option<String>,
pub family: Option<String>,
pub parameter_size: Option<String>,
pub quantization_level: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ModelList {
pub models: Vec<ModelInfo>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ErrorResponse {
pub error: String,
}