#![allow(clippy::enum_variant_names)]
use serde::{Deserialize, Serialize, de};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Role {
User,
Model,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum Part {
Text {
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
thought: Option<bool>,
#[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
thought_signature: Option<String>,
},
InlineData {
#[serde(rename = "inlineData")]
inline_data: Blob,
},
FunctionCall {
#[serde(rename = "functionCall")]
function_call: super::tools::FunctionCall,
#[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
thought_signature: Option<String>,
},
FunctionResponse {
#[serde(rename = "functionResponse")]
function_response: super::tools::FunctionResponse,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Blob {
pub mime_type: String,
pub data: String,
}
impl Blob {
pub fn new(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
Self { mime_type: mime_type.into(), data: data.into() }
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Content {
#[serde(skip_serializing_if = "Option::is_none")]
pub parts: Option<Vec<Part>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<Role>,
}
impl Content {
pub fn text(text: impl Into<String>) -> Self {
Self {
parts: Some(vec![Part::Text {
text: text.into(),
thought: None,
thought_signature: None,
}]),
role: None,
}
}
pub fn function_call(function_call: super::tools::FunctionCall) -> Self {
Self {
parts: Some(vec![Part::FunctionCall { function_call, thought_signature: None }]),
role: None,
}
}
pub fn function_call_with_thought(
function_call: super::tools::FunctionCall,
thought_signature: impl Into<String>,
) -> Self {
Self {
parts: Some(vec![Part::FunctionCall {
function_call,
thought_signature: Some(thought_signature.into()),
}]),
role: None,
}
}
pub fn text_with_thought_signature(
text: impl Into<String>,
thought_signature: impl Into<String>,
) -> Self {
Self {
parts: Some(vec![Part::Text {
text: text.into(),
thought: None,
thought_signature: Some(thought_signature.into()),
}]),
role: None,
}
}
pub fn thought_with_signature(
text: impl Into<String>,
thought_signature: impl Into<String>,
) -> Self {
Self {
parts: Some(vec![Part::Text {
text: text.into(),
thought: Some(true),
thought_signature: Some(thought_signature.into()),
}]),
role: None,
}
}
pub fn function_response(function_response: super::tools::FunctionResponse) -> Self {
Self { parts: Some(vec![Part::FunctionResponse { function_response }]), role: None }
}
pub fn function_response_json(name: impl Into<String>, response: serde_json::Value) -> Self {
Self {
parts: Some(vec![Part::FunctionResponse {
function_response: super::tools::FunctionResponse::new(name, response),
}]),
role: None,
}
}
pub fn inline_data(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
Self {
parts: Some(vec![Part::InlineData { inline_data: Blob::new(mime_type, data) }]),
role: None,
}
}
pub fn with_role(mut self, role: Role) -> Self {
self.role = Some(role);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub content: Content,
pub role: Role,
}
impl Message {
pub fn user(text: impl Into<String>) -> Self {
Self { content: Content::text(text).with_role(Role::User), role: Role::User }
}
pub fn model(text: impl Into<String>) -> Self {
Self { content: Content::text(text).with_role(Role::Model), role: Role::Model }
}
pub fn embed(text: impl Into<String>) -> Self {
Self { content: Content::text(text), role: Role::Model }
}
pub fn function(name: impl Into<String>, response: serde_json::Value) -> Self {
Self {
content: Content::function_response_json(name, response).with_role(Role::Model),
role: Role::Model,
}
}
pub fn function_str(
name: impl Into<String>,
response: impl Into<String>,
) -> Result<Self, serde_json::Error> {
let response_str = response.into();
let json = serde_json::from_str(&response_str)?;
Ok(Self {
content: Content::function_response_json(name, json).with_role(Role::Model),
role: Role::Model,
})
}
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Modality {
ModalityUnspecified,
Text,
Image,
Audio,
Video,
Document,
Unknown,
}
impl Modality {
fn from_wire_str(value: &str) -> Self {
match value {
"MODALITY_UNSPECIFIED" => Self::ModalityUnspecified,
"TEXT" => Self::Text,
"IMAGE" => Self::Image,
"AUDIO" => Self::Audio,
"VIDEO" => Self::Video,
"DOCUMENT" => Self::Document,
_ => Self::Unknown,
}
}
fn from_wire_number(value: i64) -> Self {
match value {
0 => Self::ModalityUnspecified,
1 => Self::Text,
2 => Self::Image,
3 => Self::Video,
4 => Self::Audio,
5 => Self::Document,
_ => Self::Unknown,
}
}
}
impl<'de> Deserialize<'de> for Modality {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = serde_json::Value::deserialize(deserializer)?;
match value {
serde_json::Value::String(s) => Ok(Self::from_wire_str(&s)),
serde_json::Value::Number(n) => n
.as_i64()
.map(Self::from_wire_number)
.ok_or_else(|| de::Error::custom("modality must be an integer-compatible number")),
_ => Err(de::Error::custom("modality must be a string or integer")),
}
}
}