mod audio;
mod content;
mod image;
mod media;
mod role;
mod video;
pub use audio::AudioData;
pub use content::{ContentPart, MessageContent, VideoUrl};
pub use image::ImageData;
pub use media::{Media, MediaData};
pub use role::Role;
pub use video::VideoData;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
pub content: MessageContent,
#[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<serde_json::Value>>,
}
impl Message {
pub fn user(content: impl Into<MessageContent>) -> Self {
Self {
role: Role::User,
content: content.into(),
name: None,
tool_call_id: None,
tool_calls: None,
}
}
pub fn assistant(content: impl Into<MessageContent>) -> Self {
Self {
role: Role::Assistant,
content: content.into(),
name: None,
tool_call_id: None,
tool_calls: None,
}
}
pub fn system(content: impl Into<MessageContent>) -> Self {
Self {
role: Role::System,
content: content.into(),
name: None,
tool_call_id: None,
tool_calls: None,
}
}
pub fn tool(tool_call_id: impl Into<String>, content: impl Into<MessageContent>) -> Self {
Self {
role: Role::Tool,
content: content.into(),
name: None,
tool_call_id: Some(tool_call_id.into()),
tool_calls: None,
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn has_multimodal_content(&self) -> bool {
self.content.has_multimodal()
}
pub fn text(&self) -> Option<&str> {
self.content.get_text()
}
}
pub fn messages_to_payload(messages: &[Message]) -> Vec<serde_json::Value> {
messages
.iter()
.map(|msg| {
let mut obj = serde_json::json!({
"role": msg.role,
"content": msg.content.to_api_format(),
});
if let Some(name) = &msg.name {
obj["name"] = serde_json::json!(name);
}
if let Some(tool_call_id) = &msg.tool_call_id {
obj["tool_call_id"] = serde_json::json!(tool_call_id);
}
if let Some(tool_calls) = &msg.tool_calls {
obj["tool_calls"] = serde_json::json!(tool_calls);
}
obj
})
.collect()
}
pub fn merge_contiguous_messages(messages: Vec<Message>) -> Vec<Message> {
if messages.is_empty() {
return messages;
}
let mut result: Vec<Message> = Vec::new();
for msg in messages {
if let Some(last) = result.last_mut() {
if last.role == msg.role && last.tool_call_id.is_none() && msg.tool_call_id.is_none() {
last.content = last.content.merge(&msg.content);
continue;
}
}
result.push(msg);
}
result
}