vtcode_llm/open_responses/
content.rs1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(tag = "type", rename_all = "snake_case")]
15pub enum ContentPart {
16 OutputText(OutputTextContent),
18
19 InputText(InputTextContent),
21
22 InputImage(InputImageContent),
24
25 InputFile(InputFileContent),
27
28 UrlCitation(UrlCitationContent),
30
31 Refusal(RefusalContent),
33}
34
35impl ContentPart {
36 pub(crate) fn output_text(text: impl Into<String>) -> Self {
38 Self::OutputText(OutputTextContent { text: text.into() })
39 }
40
41 pub(crate) fn input_text(text: impl Into<String>) -> Self {
43 Self::InputText(InputTextContent { text: text.into() })
44 }
45
46 pub fn refusal(refusal: impl Into<String>) -> Self {
48 Self::Refusal(RefusalContent { refusal: refusal.into() })
49 }
50
51 fn as_text(&self) -> Option<&str> {
53 match self {
54 Self::OutputText(c) => Some(&c.text),
55 Self::InputText(c) => Some(&c.text),
56 Self::Refusal(c) => Some(&c.refusal),
57 _ => None,
58 }
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
64pub struct OutputTextContent {
65 pub text: String,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub struct InputTextContent {
72 pub text: String,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub struct InputImageContent {
79 pub(crate) image_url: String,
81
82 #[serde(default, skip_serializing_if = "Option::is_none")]
84 pub(crate) detail: Option<ImageDetail>,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
89#[serde(rename_all = "lowercase")]
90pub enum ImageDetail {
91 Low,
93 High,
95 #[default]
97 Auto,
98 Original,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub struct InputFileContent {
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub(crate) filename: Option<String>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub(crate) file_id: Option<String>,
112
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub(crate) file_data: Option<String>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub(crate) file_url: Option<String>,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124pub struct UrlCitationContent {
125 pub url: String,
127
128 pub title: String,
130
131 pub start_index: usize,
133
134 pub end_index: usize,
136}
137
138#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
140pub struct RefusalContent {
141 pub refusal: String,
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_content_part_output_text() {
151 let part = ContentPart::output_text("Hello, world!");
152 assert_eq!(part.as_text(), Some("Hello, world!"));
153 }
154
155 #[test]
156 fn test_content_part_serialization() {
157 let part = ContentPart::output_text("Test");
158 let json = serde_json::to_string(&part).unwrap();
159 assert!(json.contains("\"type\":\"output_text\""));
160 assert!(json.contains("\"text\":\"Test\""));
161 }
162}