1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "lowercase")]
6pub enum Role {
7 System,
8 User,
9 Assistant,
10}
11
12#[derive(Debug, Clone)]
14pub enum ContentPart {
15 Text(String),
16 Reasoning(String),
17 Image(ImageSource),
18 ToolUse(ToolUse),
19 ToolResult(ToolResult),
20}
21
22#[derive(Debug, Clone)]
24pub enum ImageSource {
25 Url(String),
26 Base64 { media_type: String, data: String },
27}
28
29#[derive(Debug, Clone)]
31pub struct ToolUse {
32 pub id: String,
33 pub name: String,
34 pub input: serde_json::Value,
35}
36
37#[derive(Debug, Clone)]
39pub struct ToolResult {
40 pub tool_use_id: String,
41 pub content: String,
42 pub is_error: bool,
43}
44
45#[derive(Debug, Clone)]
47pub struct Message {
48 pub role: Role,
49 pub content: Vec<ContentPart>,
50}
51
52impl Message {
53 pub fn system(text: impl Into<String>) -> Self {
54 Self {
55 role: Role::System,
56 content: vec![ContentPart::Text(text.into())],
57 }
58 }
59
60 pub fn user(text: impl Into<String>) -> Self {
61 Self {
62 role: Role::User,
63 content: vec![ContentPart::Text(text.into())],
64 }
65 }
66
67 pub fn assistant(text: impl Into<String>) -> Self {
68 Self {
69 role: Role::Assistant,
70 content: vec![ContentPart::Text(text.into())],
71 }
72 }
73
74 pub fn assistant_with_reasoning(reasoning: impl Into<String>, text: impl Into<String>) -> Self {
75 Self {
76 role: Role::Assistant,
77 content: vec![
78 ContentPart::Reasoning(reasoning.into()),
79 ContentPart::Text(text.into()),
80 ],
81 }
82 }
83
84 pub fn user_with_image(text: impl Into<String>, image: ImageSource) -> Self {
85 Self {
86 role: Role::User,
87 content: vec![ContentPart::Text(text.into()), ContentPart::Image(image)],
88 }
89 }
90
91 pub fn tool_results(results: Vec<ToolResult>) -> Self {
92 Self {
93 role: Role::User,
94 content: results.into_iter().map(ContentPart::ToolResult).collect(),
95 }
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
101pub enum ToolChoice {
102 Auto,
104 None,
106 Required,
108 Specific(String),
110}
111
112#[derive(Debug, Clone)]
114pub struct Tool {
115 pub name: String,
116 pub description: String,
117 pub parameters: serde_json::Value,
119}
120
121#[derive(Debug, Clone)]
123pub struct ChatRequest {
124 pub model: String,
125 pub messages: Vec<Message>,
126 pub max_tokens: Option<u32>,
127 pub temperature: Option<f32>,
128 pub top_p: Option<f32>,
129 pub tools: Option<Vec<Tool>>,
130 pub tool_choice: Option<ToolChoice>,
132 pub parallel_tool_calls: Option<bool>,
135 pub stop_sequences: Option<Vec<String>>,
138 pub top_k: Option<u32>,
140 pub user: Option<String>,
143 pub budget_tokens: Option<u32>,
145 pub prompt_cache: Option<bool>,
148}
149
150impl ChatRequest {
151 pub fn new(model: impl Into<String>, messages: Vec<Message>) -> Self {
152 Self {
153 model: model.into(),
154 messages,
155 max_tokens: None,
156 temperature: None,
157 top_p: None,
158 tools: None,
159 tool_choice: None,
160 parallel_tool_calls: None,
161 stop_sequences: None,
162 top_k: None,
163 user: None,
164 budget_tokens: None,
165 prompt_cache: None,
166 }
167 }
168
169 pub fn max_tokens(mut self, n: u32) -> Self {
170 self.max_tokens = Some(n);
171 self
172 }
173
174 pub fn temperature(mut self, t: f32) -> Self {
175 self.temperature = Some(t);
176 self
177 }
178
179 pub fn top_p(mut self, p: f32) -> Self {
180 self.top_p = Some(p);
181 self
182 }
183
184 pub fn tools(mut self, tools: Vec<Tool>) -> Self {
185 self.tools = Some(tools);
186 self
187 }
188
189 pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
190 self.tool_choice = Some(choice);
191 self
192 }
193
194 pub fn parallel_tool_calls(mut self, enabled: bool) -> Self {
195 self.parallel_tool_calls = Some(enabled);
196 self
197 }
198
199 pub fn stop_sequences(mut self, sequences: Vec<String>) -> Self {
200 self.stop_sequences = Some(sequences);
201 self
202 }
203
204 pub fn top_k(mut self, k: u32) -> Self {
205 self.top_k = Some(k);
206 self
207 }
208
209 pub fn user(mut self, user: impl Into<String>) -> Self {
210 self.user = Some(user.into());
211 self
212 }
213
214 pub fn budget_tokens(mut self, n: u32) -> Self {
215 self.budget_tokens = Some(n);
216 self
217 }
218
219 pub fn prompt_cache(mut self, enabled: bool) -> Self {
220 self.prompt_cache = Some(enabled);
221 self
222 }
223}
224
225#[derive(Debug, Clone)]
227pub struct ChatResponse {
228 pub id: String,
229 pub model: String,
230 pub content: Vec<ContentPart>,
231 pub usage: Option<Usage>,
232 pub stop_reason: StopReason,
233}
234
235impl ChatResponse {
236 pub fn text(&self) -> Option<&str> {
238 self.content.iter().find_map(|p| match p {
239 ContentPart::Text(t) => Some(t.as_str()),
240 _ => None,
241 })
242 }
243
244 pub fn reasoning(&self) -> Option<&str> {
246 self.content.iter().find_map(|p| match p {
247 ContentPart::Reasoning(t) => Some(t.as_str()),
248 _ => None,
249 })
250 }
251
252 pub fn tool_uses(&self) -> Vec<&ToolUse> {
254 self.content
255 .iter()
256 .filter_map(|p| match p {
257 ContentPart::ToolUse(t) => Some(t),
258 _ => None,
259 })
260 .collect()
261 }
262}
263
264#[derive(Debug, Clone)]
266pub struct Usage {
267 pub input_tokens: u32,
268 pub output_tokens: u32,
269 pub cache_read_input_tokens: Option<u32>,
271 pub cache_creation_input_tokens: Option<u32>,
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
277pub enum StopReason {
278 EndTurn,
279 MaxTokens,
280 ToolUse,
281 Unknown(String),
282}
283
284#[derive(Debug, Clone)]
286pub enum StreamEvent {
287 ContentDelta(String),
289 ReasoningDelta(String),
291 ToolCallStart {
293 index: usize,
294 id: String,
295 name: String,
296 },
297 ToolCallDelta {
299 index: usize,
300 arguments_delta: String,
301 },
302 Usage(Usage),
304 Done(StopReason),
306}