Skip to main content

gproxy_protocol/adapt/generate/resources/
responses.rs

1use super::*;
2use crate::wire::{DeclaredFields, openai::responses as r};
3
4impl<R: ResourceAccess> GenerationResources<'_, R> {
5    pub async fn responses(
6        &self,
7        input: r::GenerateContentRequestBody,
8    ) -> Result<r::GenerateContentRequestBody, TransformError> {
9        let mut input = input.into_declared();
10        let mut budget = self.budget(crate::Dialect::OpenAi);
11        if let Some(r::Input::Items(items)) = &mut input.input {
12            for item in items {
13                match item {
14                    r::InputItem::Message(message) => {
15                        for part in &mut message.content {
16                            budget.input_part(part).await?;
17                        }
18                    }
19                    r::InputItem::Easy(message) => {
20                        if let r::MessageContent::Parts(parts) = &mut message.content {
21                            for part in parts {
22                                budget.input_part(part).await?;
23                            }
24                        }
25                    }
26                    r::InputItem::FunctionCallOutput(output) => {
27                        if let r::FunctionOutput::Content(parts) = &mut output.output {
28                            for part in parts {
29                                budget.function_part(part).await?;
30                            }
31                        }
32                    }
33                    r::InputItem::CustomToolCallOutput(output) => {
34                        if let r::CustomOutput::Content(parts) = &mut output.output {
35                            for part in parts {
36                                budget.input_part(part).await?;
37                            }
38                        }
39                    }
40                    _ => {}
41                }
42            }
43        }
44        Ok(input)
45    }
46}
47
48impl<R: ResourceAccess> Budget<'_, '_, R> {
49    async fn input_part(&mut self, part: &mut r::InputContent) -> Result<(), TransformError> {
50        match part {
51            r::InputContent::Image(image) => {
52                let id = image.file_id.as_ref().and_then(|v| v.as_ref());
53                let url = image.image_url.as_ref().and_then(|v| v.as_ref());
54                if id.is_some() && url.is_some() {
55                    return Err(TransformError::shape(
56                        "input_image",
57                        "ambiguous native ID and URL",
58                    ));
59                }
60                let reference = if let Some(id) = id {
61                    ResourceReference::Id(id.clone())
62                } else if let Some(url) = url {
63                    if url.starts_with("data:") {
64                        return Ok(());
65                    }
66                    ResourceReference::Url(url.clone())
67                } else {
68                    return Ok(());
69                };
70                let Some(media) = self.read(reference, true).await? else {
71                    return Ok(());
72                };
73                image.image_url = Some(Some(media.data_uri()));
74                image.file_id = None;
75            }
76            r::InputContent::File(file) => {
77                let id = file.file_id.as_ref().and_then(|v| v.as_ref());
78                let url = file.file_url.as_ref();
79                if usize::from(id.is_some())
80                    + usize::from(url.is_some())
81                    + usize::from(file.file_data.is_some())
82                    > 1
83                {
84                    return Err(TransformError::shape(
85                        "input_file",
86                        "ambiguous file representations",
87                    ));
88                }
89                let reference = if let Some(id) = id {
90                    ResourceReference::Id(id.clone())
91                } else if let Some(url) = url {
92                    ResourceReference::Url(url.clone())
93                } else {
94                    return Ok(());
95                };
96                let Some(media) = self.read(reference, false).await? else {
97                    return Ok(());
98                };
99                file.file_data = Some(media.data_uri());
100                file.file_id = None;
101                file.file_url = None;
102                if file.filename.is_none() {
103                    file.filename = media.filename;
104                }
105            }
106            r::InputContent::Text(_) => {}
107        }
108        Ok(())
109    }
110}
111
112impl<R: ResourceAccess> Budget<'_, '_, R> {
113    async fn function_part(
114        &mut self,
115        part: &mut r::FunctionOutputContent,
116    ) -> Result<(), TransformError> {
117        match part {
118            r::FunctionOutputContent::Image(image) => {
119                let id = image.file_id.as_ref().and_then(|v| v.as_ref());
120                let url = image.image_url.as_ref().and_then(|v| v.as_ref());
121                if id.is_some() && url.is_some() {
122                    return Err(TransformError::shape(
123                        "function_output.image",
124                        "ambiguous native ID and URL",
125                    ));
126                }
127                let reference = if let Some(id) = id {
128                    ResourceReference::Id(id.clone())
129                } else if let Some(url) = url {
130                    if url.starts_with("data:") {
131                        return Ok(());
132                    }
133                    ResourceReference::Url(url.clone())
134                } else {
135                    return Ok(());
136                };
137                let Some(media) = self.read(reference, true).await? else {
138                    return Ok(());
139                };
140                image.image_url = Some(Some(media.data_uri()));
141                image.file_id = None;
142            }
143            r::FunctionOutputContent::File(file) => {
144                let id = file.file_id.as_ref().and_then(|v| v.as_ref());
145                let url = file.file_url.as_ref().and_then(|v| v.as_ref());
146                let data = file.file_data.as_ref().and_then(|v| v.as_ref());
147                if usize::from(id.is_some())
148                    + usize::from(url.is_some())
149                    + usize::from(data.is_some())
150                    > 1
151                {
152                    return Err(TransformError::shape(
153                        "function_output.file",
154                        "ambiguous file representations",
155                    ));
156                }
157                let reference = if let Some(id) = id {
158                    ResourceReference::Id(id.clone())
159                } else if let Some(url) = url {
160                    ResourceReference::Url(url.clone())
161                } else {
162                    return Ok(());
163                };
164                let Some(media) = self.read(reference, false).await? else {
165                    return Ok(());
166                };
167                file.file_data = Some(Some(media.data_uri()));
168                file.file_id = None;
169                file.file_url = None;
170                if file.filename.as_ref().is_none_or(|v| v.is_none()) {
171                    file.filename = media.filename.map(Some);
172                }
173            }
174            r::FunctionOutputContent::Text(_) => {}
175        }
176        Ok(())
177    }
178}