1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
4pub enum MessageRole {
5 System,
6 User,
7 Assistant,
8 Tool,
9}
10
11impl MessageRole {
12 pub fn as_str(&self) -> &'static str {
13 match self {
14 MessageRole::System => "system",
15 MessageRole::User => "user",
16 MessageRole::Assistant => "assistant",
17 MessageRole::Tool => "tool",
18 }
19 }
20
21 pub fn as_api_role(&self) -> &'static str {
22 self.as_str()
23 }
24}
25
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub struct Message {
28 pub role: MessageRole,
29 pub content: String,
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize)]
33pub enum ChatMessage {
34 System {
35 content: String,
36 #[serde(default, skip_serializing)]
38 ephemeral: bool,
39 },
40 User {
41 content: String,
42 #[serde(default, skip_serializing_if = "Vec::is_empty")]
43 images: Vec<ImageAttachment>,
44 #[serde(default, skip_serializing)]
46 ephemeral: bool,
47 },
48 Assistant {
49 content: Option<String>,
50 reasoning_content: Option<String>,
51 tool_calls: Option<Vec<ToolCallMessage>>,
52 },
53 Tool {
54 tool_call_id: String,
55 content: String,
56 },
57}
58
59#[derive(Clone, Debug, Serialize, Deserialize)]
60pub struct ToolCallMessage {
61 pub id: String,
62 pub name: String,
63 pub arguments: String,
64}
65
66#[derive(Clone, Debug, Serialize, Deserialize)]
67pub enum ImageAttachment {
68 Url {
69 url: String,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 detail: Option<ImageDetail>,
72 },
73 Base64 {
74 data: String,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 media_type: Option<String>,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
78 detail: Option<ImageDetail>,
79 },
80}
81
82#[derive(Clone, Debug, Serialize, Deserialize)]
83pub enum ImageDetail {
84 Low,
85 High,
86 Auto,
87}
88
89impl ChatMessage {
90 pub fn system(content: impl Into<String>) -> Self {
91 Self::System {
92 content: content.into(),
93 ephemeral: false,
94 }
95 }
96
97 pub fn system_ephemeral(content: impl Into<String>) -> Self {
99 Self::System {
100 content: content.into(),
101 ephemeral: true,
102 }
103 }
104
105 pub fn user(content: impl Into<String>) -> Self {
106 Self::User {
107 content: content.into(),
108 images: Vec::new(),
109 ephemeral: false,
110 }
111 }
112
113 pub fn user_ephemeral(content: impl Into<String>) -> Self {
115 Self::User {
116 content: content.into(),
117 images: Vec::new(),
118 ephemeral: true,
119 }
120 }
121
122 pub fn user_with_images(content: impl Into<String>, images: Vec<ImageAttachment>) -> Self {
123 Self::User {
124 content: content.into(),
125 images,
126 ephemeral: false,
127 }
128 }
129
130 pub fn is_ephemeral(&self) -> bool {
132 match self {
133 Self::System { ephemeral, .. } => *ephemeral,
134 Self::User { ephemeral, .. } => *ephemeral,
135 _ => false,
136 }
137 }
138
139 pub fn assistant(content: impl Into<String>) -> Self {
140 Self::Assistant {
141 content: Some(content.into()),
142 reasoning_content: None,
143 tool_calls: None,
144 }
145 }
146
147 pub fn assistant_with_reasoning(
148 content: impl Into<String>,
149 reasoning: impl Into<String>,
150 ) -> Self {
151 Self::Assistant {
152 content: Some(content.into()),
153 reasoning_content: Some(reasoning.into()),
154 tool_calls: None,
155 }
156 }
157
158 pub fn assistant_tool_call(
159 tool_call_id: impl Into<String>,
160 tool_name: impl Into<String>,
161 arguments: impl Into<String>,
162 ) -> Self {
163 Self::Assistant {
164 content: None,
165 reasoning_content: None,
166 tool_calls: Some(vec![ToolCallMessage {
167 id: tool_call_id.into(),
168 name: tool_name.into(),
169 arguments: arguments.into(),
170 }]),
171 }
172 }
173
174 pub fn tool(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
175 Self::Tool {
176 tool_call_id: tool_call_id.into(),
177 content: content.into(),
178 }
179 }
180}
181
182impl From<&ChatMessage> for Message {
183 fn from(cm: &ChatMessage) -> Self {
184 match cm {
185 ChatMessage::System { content, .. } => Message {
186 role: MessageRole::System,
187 content: content.clone(),
188 },
189 ChatMessage::User { content, .. } => Message {
190 role: MessageRole::User,
191 content: content.clone(),
192 },
193 ChatMessage::Assistant { content, .. } => Message {
194 role: MessageRole::Assistant,
195 content: content.clone().unwrap_or_default(),
196 },
197 ChatMessage::Tool { content, .. } => Message {
198 role: MessageRole::Tool,
199 content: content.clone(),
200 },
201 }
202 }
203}