use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Content {
#[serde(rename = "text")]
Text(String),
#[serde(rename = "json")]
Json(JsonValue),
#[serde(rename = "image")]
Image {
url: Option<String>,
mime: Option<String>,
name: Option<String>,
},
#[serde(rename = "audio")]
Audio {
url: Option<String>,
mime: Option<String>,
},
}
impl Content {
pub fn as_text(&self) -> String {
match self {
Content::Text(s) => s.clone(),
Content::Json(v) => v.to_string(),
Content::Image { url, .. } => url.clone().unwrap_or_default(),
Content::Audio { url, .. } => url.clone().unwrap_or_default(),
}
}
pub fn new_text<S: Into<String>>(s: S) -> Self {
Content::Text(s.into())
}
pub fn new_json(v: JsonValue) -> Self {
Content::Json(v)
}
pub fn new_image(url: Option<String>, mime: Option<String>, name: Option<String>) -> Self {
Content::Image { url, mime, name }
}
pub fn new_audio(url: Option<String>, mime: Option<String>) -> Self {
Content::Audio { url, mime }
}
pub fn from_image_file<P: AsRef<std::path::Path>>(path: P) -> Self {
let path = path.as_ref();
let name = path
.file_name()
.and_then(|n| n.to_str())
.map(|s| s.to_string());
let mime = path
.extension()
.and_then(|ext| ext.to_str())
.and_then(|ext| match ext.to_lowercase().as_str() {
"png" => Some("image/png"),
"jpg" | "jpeg" => Some("image/jpeg"),
"gif" => Some("image/gif"),
"webp" => Some("image/webp"),
"svg" => Some("image/svg+xml"),
_ => None,
})
.map(|s| s.to_string());
Content::Image {
url: None, mime,
name,
}
}
pub fn from_audio_file<P: AsRef<std::path::Path>>(path: P) -> Self {
let path = path.as_ref();
let mime = path
.extension()
.and_then(|ext| ext.to_str())
.and_then(|ext| match ext.to_lowercase().as_str() {
"mp3" => Some("audio/mpeg"),
"wav" => Some("audio/wav"),
"ogg" => Some("audio/ogg"),
"m4a" => Some("audio/mp4"),
"flac" => Some("audio/flac"),
_ => None,
})
.map(|s| s.to_string());
Content::Audio {
url: None, mime,
}
}
pub fn from_data_url(data_url: String, mime: Option<String>, name: Option<String>) -> Self {
Content::Image {
url: Some(data_url),
mime,
name,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
pub content: Content,
pub function_call: Option<crate::types::function_call::FunctionCall>,
}
impl Message {
pub fn new(role: Role, content: impl Into<Content>) -> Self {
Self {
role,
content: content.into(),
function_call: None,
}
}
pub fn user<S: Into<String>>(text: S) -> Self {
Self {
role: Role::User,
content: Content::Text(text.into()),
function_call: None,
}
}
pub fn assistant<S: Into<String>>(text: S) -> Self {
Self {
role: Role::Assistant,
content: Content::Text(text.into()),
function_call: None,
}
}
pub fn system<S: Into<String>>(text: S) -> Self {
Self {
role: Role::System,
content: Content::Text(text.into()),
function_call: None,
}
}
pub fn user_with_content(content: Content) -> Self {
Self {
role: Role::User,
content,
function_call: None,
}
}
pub fn assistant_with_function_call(
text: impl Into<String>,
function_call: crate::types::function_call::FunctionCall,
) -> Self {
Self {
role: Role::Assistant,
content: Content::Text(text.into()),
function_call: Some(function_call),
}
}
}
impl From<String> for Content {
fn from(s: String) -> Self {
Content::Text(s)
}
}
impl From<&str> for Content {
fn from(s: &str) -> Self {
Content::Text(s.to_string())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Role {
#[serde(rename = "system")]
System,
#[serde(rename = "user")]
User,
#[serde(rename = "assistant")]
Assistant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Choice {
pub index: u32,
pub message: Message,
pub finish_reason: Option<String>,
}