1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum ConversationRole {
11 User,
12 Assistant,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct ToolUseBlock {
21 pub tool_use_id: String,
22 pub name: String,
23 pub input: Value,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28#[serde(rename_all = "lowercase")]
29pub enum ToolResultContent {
30 Text(String),
31 Json(Value),
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct ToolResultBlock {
38 pub tool_use_id: String,
39 pub content: Vec<ToolResultContent>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub status: Option<String>,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "lowercase")]
47pub enum ImageFormat {
48 Png,
49 Jpeg,
50 Gif,
51 Webp,
52}
53
54impl ImageFormat {
55 pub fn as_str(&self) -> &'static str {
57 match self {
58 ImageFormat::Png => "png",
59 ImageFormat::Jpeg => "jpeg",
60 ImageFormat::Gif => "gif",
61 ImageFormat::Webp => "webp",
62 }
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68#[serde(rename_all = "lowercase")]
69pub enum ImageSource {
70 Bytes(Vec<u8>),
72 Url(String),
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub struct ImageBlock {
79 pub format: ImageFormat,
80 pub source: ImageSource,
81}
82
83#[derive(Debug, Clone)]
85pub enum ContentBlock {
86 Text(String),
87 Image(ImageBlock),
88 ToolUse(ToolUseBlock),
89 ToolResult(ToolResultBlock),
90}
91
92impl ContentBlock {
93 pub fn as_text(&self) -> Result<&str, &Self> {
95 match self {
96 ContentBlock::Text(s) => Ok(s.as_str()),
97 _ => Err(self),
98 }
99 }
100
101 pub fn as_image(&self) -> Result<&ImageBlock, &Self> {
103 match self {
104 ContentBlock::Image(b) => Ok(b),
105 _ => Err(self),
106 }
107 }
108
109 pub fn as_tool_use(&self) -> Result<&ToolUseBlock, &Self> {
111 match self {
112 ContentBlock::ToolUse(b) => Ok(b),
113 _ => Err(self),
114 }
115 }
116
117 pub fn as_tool_result(&self) -> Result<&ToolResultBlock, &Self> {
119 match self {
120 ContentBlock::ToolResult(b) => Ok(b),
121 _ => Err(self),
122 }
123 }
124}
125
126#[derive(Debug, Clone)]
130pub struct Message {
131 pub role: ConversationRole,
132 pub content: Vec<ContentBlock>,
133}
134
135impl Message {
136 pub fn builder() -> MessageBuilder {
137 MessageBuilder::default()
138 }
139
140 pub fn role(&self) -> &ConversationRole {
141 &self.role
142 }
143
144 pub fn content(&self) -> &[ContentBlock] {
145 &self.content
146 }
147}
148
149#[derive(Default)]
150pub struct MessageBuilder {
151 role: Option<ConversationRole>,
152 content: Vec<ContentBlock>,
153}
154
155impl MessageBuilder {
156 pub fn role(mut self, role: ConversationRole) -> Self {
157 self.role = Some(role);
158 self
159 }
160
161 pub fn content(mut self, block: ContentBlock) -> Self {
162 self.content.push(block);
163 self
164 }
165
166 pub fn build(self) -> Result<Message, crate::Error> {
167 let role = self
168 .role
169 .ok_or_else(|| crate::Error::Sdk("Message.role is required".into()))?;
170 if self.content.is_empty() {
171 return Err(crate::Error::Sdk(
172 "Message must have at least one content block".into(),
173 ));
174 }
175 Ok(Message {
176 role,
177 content: self.content,
178 })
179 }
180}
181
182#[derive(Debug, Clone)]
185pub struct SystemContentBlock {
186 pub text: String,
187}
188
189impl SystemContentBlock {
190 pub fn text(text: impl Into<String>) -> Self {
191 Self { text: text.into() }
192 }
193}
194
195#[derive(Debug, Clone, Default)]
198pub struct InferenceConfiguration {
199 pub max_tokens: Option<u32>,
200 pub temperature: Option<f64>,
201 pub top_p: Option<f64>,
202 pub stop_sequences: Option<Vec<String>>,
203}
204
205impl InferenceConfiguration {
206 pub fn builder() -> InferenceConfigurationBuilder {
207 InferenceConfigurationBuilder::default()
208 }
209}
210
211#[derive(Default)]
212pub struct InferenceConfigurationBuilder {
213 inner: InferenceConfiguration,
214}
215
216impl InferenceConfigurationBuilder {
217 pub fn max_tokens(mut self, v: u32) -> Self {
218 self.inner.max_tokens = Some(v);
219 self
220 }
221 pub fn temperature(mut self, v: f64) -> Self {
222 self.inner.temperature = Some(v);
223 self
224 }
225 pub fn top_p(mut self, v: f64) -> Self {
226 self.inner.top_p = Some(v);
227 self
228 }
229 pub fn stop_sequences(mut self, v: Vec<String>) -> Self {
230 self.inner.stop_sequences = Some(v);
231 self
232 }
233 pub fn build(self) -> InferenceConfiguration {
234 self.inner
235 }
236}
237
238#[derive(Debug, Clone)]
241pub struct ToolInputSchema {
242 pub json: Value,
243}
244
245#[derive(Debug, Clone)]
246pub struct ToolSpecification {
247 pub name: String,
248 pub description: Option<String>,
249 pub input_schema: ToolInputSchema,
250}
251
252#[derive(Debug, Clone)]
253pub struct Tool {
254 pub tool_spec: ToolSpecification,
255}
256
257#[derive(Debug, Clone)]
258pub enum ToolChoice {
259 Auto,
260 Any,
261 Tool { name: String },
262}
263
264#[derive(Debug, Clone)]
265pub struct ToolConfiguration {
266 pub tools: Vec<Tool>,
267 pub tool_choice: Option<ToolChoice>,
268}
269
270#[derive(Debug, Clone)]
274pub enum ConverseOutputEnum {
275 Message(Message),
276}
277
278impl ConverseOutputEnum {
279 pub fn as_message(&self) -> Result<&Message, &Self> {
281 match self {
282 ConverseOutputEnum::Message(m) => Ok(m),
283 }
285 }
286}
287
288#[derive(Debug, Clone)]
289pub struct TokenUsage {
290 pub input_tokens: u32,
291 pub output_tokens: u32,
292 pub total_tokens: u32,
293}
294
295#[derive(Debug, Clone)]
296pub struct Metrics {
297 pub latency_ms: u64,
298}
299
300#[derive(Debug, Clone, PartialEq, Eq)]
301pub enum StopReason {
302 EndTurn,
303 MaxTokens,
304 ToolUse,
305 StopSequence,
306 ContentFiltered,
307}
308
309impl StopReason {
310 pub(crate) fn from_finish_reason(s: &str) -> Self {
311 match s {
312 "stop" => StopReason::EndTurn,
313 "length" => StopReason::MaxTokens,
314 "tool_calls" | "function_call" => StopReason::ToolUse,
315 "content_filter" => StopReason::ContentFiltered,
316 _ => StopReason::EndTurn,
317 }
318 }
319}
320
321#[derive(Debug)]
323pub struct ConverseOutput {
324 pub(crate) output: Option<ConverseOutputEnum>,
325 pub(crate) stop_reason: StopReason,
326 pub(crate) usage: TokenUsage,
327 pub(crate) metrics: Metrics,
328}
329
330impl ConverseOutput {
331 pub fn output(&self) -> Option<&ConverseOutputEnum> {
332 self.output.as_ref()
333 }
334 pub fn stop_reason(&self) -> &StopReason {
335 &self.stop_reason
336 }
337 pub fn usage(&self) -> &TokenUsage {
338 &self.usage
339 }
340 pub fn metrics(&self) -> &Metrics {
341 &self.metrics
342 }
343}
344
345#[derive(Debug, Clone)]
348pub struct MessageStartEvent {
349 pub role: ConversationRole,
350}
351
352#[derive(Debug, Clone)]
353pub struct ContentBlockStartToolUse {
354 pub tool_use_id: String,
355 pub name: String,
356}
357
358#[derive(Debug, Clone)]
359pub enum ContentBlockStartPayload {
360 ToolUse(ContentBlockStartToolUse),
361}
362
363#[derive(Debug, Clone)]
364pub struct ContentBlockStartEvent {
365 pub content_block_index: u32,
366 pub start: ContentBlockStartPayload,
367}
368
369#[derive(Debug, Clone)]
370pub enum ContentBlockDeltaPayload {
371 Text(String),
372 ToolUse { input: String },
373}
374
375#[derive(Debug, Clone)]
376pub struct ContentBlockDeltaEvent {
377 pub content_block_index: u32,
378 pub delta: ContentBlockDeltaPayload,
379}
380
381#[derive(Debug, Clone)]
382pub struct ContentBlockStopEvent {
383 pub content_block_index: u32,
384}
385
386#[derive(Debug, Clone)]
387pub struct MessageStopEvent {
388 pub stop_reason: StopReason,
389}
390
391#[derive(Debug, Clone)]
392pub struct MetadataEvent {
393 pub usage: TokenUsage,
394 pub metrics: Metrics,
395}
396
397#[derive(Debug, Clone)]
399pub enum ConverseStreamOutput {
400 MessageStart(MessageStartEvent),
401 ContentBlockStart(ContentBlockStartEvent),
402 ContentBlockDelta(ContentBlockDeltaEvent),
403 ContentBlockStop(ContentBlockStopEvent),
404 MessageStop(MessageStopEvent),
405 Metadata(MetadataEvent),
406}