1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum Role {
8 System,
9 User,
10 Assistant,
11 Tool,
14}
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21#[serde(tag = "type", rename_all = "snake_case")]
22pub enum Part {
23 Text {
24 text: String,
25 },
26 ToolCall {
27 id: String,
28 name: String,
29 arguments: Value,
32 },
33 ToolResult {
34 call_id: String,
35 content: String,
36 is_error: bool,
37 },
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub struct Message {
42 pub role: Role,
43 pub parts: Vec<Part>,
44}
45
46impl Message {
47 pub fn new(role: Role, parts: Vec<Part>) -> Self {
48 Self { role, parts }
49 }
50
51 pub fn user(text: impl Into<String>) -> Self {
52 Self::text(Role::User, text)
53 }
54
55 pub fn assistant(text: impl Into<String>) -> Self {
56 Self::text(Role::Assistant, text)
57 }
58
59 pub fn text(role: Role, text: impl Into<String>) -> Self {
60 Self::new(role, vec![Part::Text { text: text.into() }])
61 }
62
63 pub fn tool_result(
66 call_id: impl Into<String>,
67 content: impl Into<String>,
68 is_error: bool,
69 ) -> Self {
70 Self::new(
71 Role::Tool,
72 vec![Part::ToolResult {
73 call_id: call_id.into(),
74 content: content.into(),
75 is_error,
76 }],
77 )
78 }
79
80 pub fn text_content(&self) -> String {
82 self.parts
83 .iter()
84 .filter_map(|p| match p {
85 Part::Text { text } => Some(text.as_str()),
86 _ => None,
87 })
88 .collect::<Vec<_>>()
89 .join("")
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
95pub struct Tool {
96 pub name: String,
97 pub description: String,
98 pub parameters: Value,
99}
100
101impl Tool {
102 pub fn new(name: impl Into<String>, description: impl Into<String>, parameters: Value) -> Self {
103 Self {
104 name: name.into(),
105 description: description.into(),
106 parameters,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Default)]
113pub struct ChatRequest {
114 pub model: String,
115 pub system: Option<String>,
118 pub messages: Vec<Message>,
119 pub tools: Vec<Tool>,
120 pub max_tokens: Option<u32>,
122 pub temperature: Option<f32>,
125}
126
127pub const DEFAULT_MAX_TOKENS: u32 = 4096;
129
130impl ChatRequest {
131 pub fn new(model: impl Into<String>) -> Self {
132 Self {
133 model: model.into(),
134 ..Default::default()
135 }
136 }
137
138 pub fn system(mut self, system: impl Into<String>) -> Self {
139 self.system = Some(system.into());
140 self
141 }
142
143 pub fn message(mut self, message: Message) -> Self {
144 self.messages.push(message);
145 self
146 }
147
148 pub fn tool(mut self, tool: Tool) -> Self {
149 self.tools.push(tool);
150 self
151 }
152
153 pub fn max_tokens(mut self, max_tokens: u32) -> Self {
154 self.max_tokens = Some(max_tokens);
155 self
156 }
157
158 pub fn temperature(mut self, temperature: f32) -> Self {
159 self.temperature = Some(temperature);
160 self
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166pub enum StopReason {
167 EndTurn,
169 ToolUse,
171 MaxTokens,
173 Paused,
175 Refusal,
177 Other(String),
179}
180
181#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
182pub struct Usage {
183 pub input_tokens: u32,
184 pub output_tokens: u32,
185}
186
187#[derive(Debug, Clone, PartialEq)]
188pub struct ChatResponse {
189 pub model: String,
190 pub message: Message,
191 pub stop_reason: StopReason,
192 pub usage: Usage,
193}
194
195impl ChatResponse {
196 pub fn text(&self) -> String {
198 self.message.text_content()
199 }
200
201 pub fn tool_calls(&self) -> Vec<(String, String, Value)> {
203 self.message
204 .parts
205 .iter()
206 .filter_map(|p| match p {
207 Part::ToolCall {
208 id,
209 name,
210 arguments,
211 } => Some((id.clone(), name.clone(), arguments.clone())),
212 _ => None,
213 })
214 .collect()
215 }
216}