Skip to main content

alchemy_llm/types/
content.rs

1use base64::Engine;
2use serde::{Deserialize, Serialize};
3
4use super::tool_call_id::ToolCallId;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(tag = "type", rename_all = "camelCase")]
8pub enum Content {
9    #[serde(rename = "text")]
10    Text {
11        #[serde(flatten)]
12        inner: TextContent,
13    },
14    #[serde(rename = "thinking")]
15    Thinking {
16        #[serde(flatten)]
17        inner: ThinkingContent,
18    },
19    #[serde(rename = "image")]
20    Image {
21        #[serde(flatten)]
22        inner: ImageContent,
23    },
24    #[serde(rename = "toolCall")]
25    ToolCall {
26        #[serde(flatten)]
27        inner: ToolCall,
28    },
29}
30
31impl Content {
32    pub fn text(text: impl Into<String>) -> Self {
33        Self::Text {
34            inner: TextContent {
35                text: text.into(),
36                text_signature: None,
37            },
38        }
39    }
40
41    pub fn thinking(thinking: impl Into<String>) -> Self {
42        Self::Thinking {
43            inner: ThinkingContent {
44                thinking: thinking.into(),
45                thinking_signature: None,
46            },
47        }
48    }
49
50    pub fn tool_call(
51        id: impl Into<ToolCallId>,
52        name: impl Into<String>,
53        arguments: serde_json::Value,
54    ) -> Self {
55        Self::ToolCall {
56            inner: ToolCall {
57                id: id.into(),
58                name: name.into(),
59                arguments,
60                thought_signature: None,
61            },
62        }
63    }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct TextContent {
68    pub text: String,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub text_signature: Option<String>,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ThinkingContent {
75    pub thinking: String,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub thinking_signature: Option<String>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ImageContent {
82    pub data: Vec<u8>,
83    pub mime_type: String,
84}
85
86impl ImageContent {
87    pub fn from_base64(data: &str, mime_type: String) -> Result<Self, base64::DecodeError> {
88        Ok(Self {
89            data: base64::engine::general_purpose::STANDARD.decode(data)?,
90            mime_type,
91        })
92    }
93
94    pub fn to_base64(&self) -> String {
95        base64::engine::general_purpose::STANDARD.encode(&self.data)
96    }
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct ToolCall {
101    pub id: ToolCallId,
102    pub name: String,
103    pub arguments: serde_json::Value,
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub thought_signature: Option<String>,
106}