use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ChatMessage {
pub role: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCall>>,
}
impl ChatMessage {
pub fn system(content: impl Into<String>) -> Self {
Self {
role: "system".to_string(),
content: Some(content.into()),
name: None,
tool_call_id: None,
tool_calls: None,
}
}
pub fn user(content: impl Into<String>) -> Self {
Self {
role: "user".to_string(),
content: Some(content.into()),
name: None,
tool_call_id: None,
tool_calls: None,
}
}
pub fn assistant(content: impl Into<String>) -> Self {
Self {
role: "assistant".to_string(),
content: Some(content.into()),
name: None,
tool_call_id: None,
tool_calls: None,
}
}
pub fn tool_result(content: impl Into<String>, tool_name: impl Into<String>, tool_call_id: impl Into<String>) -> Self {
Self {
role: "tool".to_string(),
content: Some(content.into()),
name: Some(tool_name.into()),
tool_call_id: Some(tool_call_id.into()),
tool_calls: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub parameters: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub strict: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ToolSpec {
#[serde(rename = "type")]
pub r#type: String,
pub function: ToolDefinition,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Default)]
pub(crate) struct FunctionCall {
#[serde(default)]
pub name: String,
#[serde(default)]
pub arguments: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ToolCall {
#[serde(default)]
pub id: String,
#[serde(rename = "type", default)]
pub call_type: String,
#[serde(default)]
pub function: FunctionCall,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<String>,
}
impl ToolCall {
pub fn get_name(&self) -> String {
self.name.clone().unwrap_or_else(|| self.function.name.clone())
}
pub fn get_arguments(&self) -> String {
self.arguments.clone().unwrap_or_else(|| self.function.arguments.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ChatRequest {
pub model: String,
pub messages: Vec<ChatMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<ToolSpec>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ChatChoice {
pub index: usize,
pub message: ChatMessage,
#[serde(default)]
pub tool_calls: Vec<ToolCall>,
pub finish_reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ChatResponse {
pub id: String,
pub object: String,
pub created: u64,
pub model: String,
pub choices: Vec<ChatChoice>,
pub usage: Option<Usage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct Usage {
pub prompt_tokens: usize,
pub completion_tokens: usize,
pub total_tokens: usize,
}
#[cfg(feature = "experimental")]
#[derive(Debug, Clone)]
pub struct Message {
inner: ChatMessage
}
#[cfg(feature = "experimental")]
impl Message {
pub fn system(content: impl Into<String>) -> Self {
Self { inner: ChatMessage::system(content) }
}
pub fn user(content: impl Into<String>) -> Self {
Self { inner: ChatMessage::user(content) }
}
pub fn assistant(content: impl Into<String>) -> Self {
Self { inner: ChatMessage::assistant(content) }
}
pub fn role(&self) -> &str {
&self.inner.role
}
pub fn content(&self) -> Option<&str> {
self.inner.content.as_deref()
}
}