agent_io/llm/types/
content.rs1use serde::{Deserialize, Serialize};
4
5pub type JsonSchema = serde_json::Map<String, serde_json::Value>;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct ContentPartText {
11 #[serde(rename = "type")]
12 pub content_type: String,
13 pub text: String,
14}
15
16impl ContentPartText {
17 pub fn new(text: impl Into<String>) -> Self {
18 Self {
19 content_type: "text".to_string(),
20 text: text.into(),
21 }
22 }
23}
24
25impl From<String> for ContentPartText {
26 fn from(text: String) -> Self {
27 Self::new(text)
28 }
29}
30
31impl From<&str> for ContentPartText {
32 fn from(text: &str) -> Self {
33 Self::new(text)
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct ContentPartImage {
40 #[serde(rename = "type")]
41 pub content_type: String,
42 pub image_url: ImageUrl,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct ImageUrl {
48 pub url: String,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub detail: Option<String>,
51}
52
53impl ContentPartImage {
54 pub fn from_url(url: impl Into<String>) -> Self {
56 Self {
57 content_type: "image_url".to_string(),
58 image_url: ImageUrl {
59 url: url.into(),
60 detail: None,
61 },
62 }
63 }
64
65 pub fn from_base64(media_type: &str, data: &str) -> Self {
67 Self {
68 content_type: "image_url".to_string(),
69 image_url: ImageUrl {
70 url: format!("data:{};base64,{}", media_type, data),
71 detail: None,
72 },
73 }
74 }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct ContentPartDocument {
80 #[serde(rename = "type")]
81 pub content_type: String,
82 pub source: DocumentSource,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct DocumentSource {
87 #[serde(rename = "type")]
88 pub source_type: String,
89 pub media_type: String,
90 pub data: String,
91}
92
93impl ContentPartDocument {
94 pub fn from_base64(media_type: impl Into<String>, data: impl Into<String>) -> Self {
95 Self {
96 content_type: "document".to_string(),
97 source: DocumentSource {
98 source_type: "base64".to_string(),
99 media_type: media_type.into(),
100 data: data.into(),
101 },
102 }
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct ContentPartThinking {
109 #[serde(rename = "type")]
110 pub content_type: String,
111 pub thinking: String,
112}
113
114impl ContentPartThinking {
115 pub fn new(thinking: impl Into<String>) -> Self {
116 Self {
117 content_type: "thinking".to_string(),
118 thinking: thinking.into(),
119 }
120 }
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct ContentPartRedactedThinking {
126 #[serde(rename = "type")]
127 pub content_type: String,
128 pub data: String,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct ContentPartRefusal {
134 #[serde(rename = "type")]
135 pub content_type: String,
136 pub refusal: String,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141#[serde(untagged)]
142pub enum ContentPart {
143 Text(ContentPartText),
144 Image(ContentPartImage),
145 Document(ContentPartDocument),
146 Thinking(ContentPartThinking),
147 RedactedThinking(ContentPartRedactedThinking),
148 Refusal(ContentPartRefusal),
149}
150
151impl ContentPart {
152 pub fn text(content: impl Into<String>) -> Self {
153 ContentPart::Text(ContentPartText::new(content))
154 }
155
156 pub fn is_text(&self) -> bool {
157 matches!(self, ContentPart::Text(_))
158 }
159
160 pub fn as_text(&self) -> Option<&str> {
161 match self {
162 ContentPart::Text(t) => Some(&t.text),
163 _ => None,
164 }
165 }
166}
167
168impl From<String> for ContentPart {
169 fn from(text: String) -> Self {
170 ContentPart::text(text)
171 }
172}
173
174impl From<&str> for ContentPart {
175 fn from(text: &str) -> Self {
176 ContentPart::text(text)
177 }
178}