gproxy_protocol/adapt/generate/image_resources/
stream.rs1use super::*;
2use crate::{capability::ResourceAccess, transform::identity::IdNamespace};
3
4pub struct ImageStreamProgress<H> {
8 namespace: Option<IdNamespace>,
9 pub(crate) revision: u64,
10 source_index: u64,
11 client_index: u64,
12 native_parts: usize,
13 source: BTreeMap<u64, GeminiImageReads>,
14 client: BTreeMap<u64, GeminiImagePublications<H>>,
15 pub(crate) reads: GeminiImageReads,
16}
17
18impl<H> Default for ImageStreamProgress<H> {
19 fn default() -> Self {
20 Self {
21 namespace: None,
22 revision: 0,
23 source_index: 0,
24 client_index: 0,
25 native_parts: 0,
26 source: BTreeMap::new(),
27 client: BTreeMap::new(),
28 reads: Default::default(),
29 }
30 }
31}
32
33impl<H> ImageStreamProgress<H> {
34 pub(crate) fn failed(&self) -> bool {
35 self.source.values().any(|event| event.failed)
36 || self.client.values().any(|event| event.failed)
37 }
38 pub(crate) fn committed<T>(&mut self, value: T) -> Result<T, TransformError> {
39 self.revision = self.revision.checked_add(1).ok_or_else(limit)?;
40 Ok(value)
41 }
42 pub(crate) fn bind(&mut self, namespace: IdNamespace) -> Result<(), TransformError> {
43 if self.namespace.is_some_and(|old| old != namespace) {
44 return Err(conflict(
45 "image stream progress belongs to another invocation",
46 ));
47 }
48 self.namespace = Some(namespace);
49 Ok(())
50 }
51 pub fn publication_budget(&self) -> Result<(usize, u64), TransformError> {
53 let mut count = 0usize;
54 let mut bytes = 0u64;
55 for image in self
56 .client
57 .values()
58 .flat_map(|event| event.publications.values())
59 {
60 let padding = if image.source.data.ends_with("==") {
61 2
62 } else {
63 usize::from(image.source.data.ends_with('='))
64 };
65 let size = (image.source.data.len() / 4 * 3).saturating_sub(padding) as u64;
66 count = count.checked_add(1).ok_or_else(limit)?;
67 bytes = bytes.checked_add(size).ok_or_else(limit)?;
68 }
69 Ok((count, bytes))
70 }
71 pub fn publication_receipts(&self) -> impl Iterator<Item = &PublishedResource<H>> {
72 self.client.values().flat_map(|event| event.receipts())
73 }
74 pub fn publication_ids(&self) -> impl Iterator<Item = &str> {
75 self.client.values().flat_map(|event| event.operation_ids())
76 }
77 pub(crate) async fn source<R: ResourceAccess<PublishedHandle = H>>(
78 &mut self,
79 input: &g::GenerateContentResponseBody,
80 resources: &super::super::GenerationResources<'_, R>,
81 ) -> Result<g::GenerateContentResponseBody, TransformError> {
82 let parts: usize = input
83 .candidates
84 .iter()
85 .flatten()
86 .flat_map(|candidate| candidate.content.iter())
87 .map(|content| content.parts.as_ref().map_or(0, Vec::len))
88 .sum();
89 let has_files = input
90 .candidates
91 .iter()
92 .flatten()
93 .flat_map(|candidate| candidate.content.iter())
94 .flat_map(|content| content.parts.iter().flatten())
95 .any(|part| part.file_data.is_some());
96 let value = if has_files {
97 let used_bytes: u64 = self
98 .source
99 .iter()
100 .filter(|(index, _)| **index != self.source_index)
101 .map(|(_, event)| event.bytes)
102 .sum();
103 let used_reads: usize = self
104 .source
105 .iter()
106 .filter(|(index, _)| **index != self.source_index)
107 .map(|(_, event)| event.attempted)
108 .sum();
109 let mut limits = resources.limits;
110 limits.max_body_bytes = limits.max_body_bytes.saturating_sub(used_bytes);
111 let bound = super::super::GenerationResources {
112 access: resources.access,
113 scope: resources.scope,
114 limits,
115 max_references: resources.max_references.saturating_sub(used_reads),
116 now: resources.now,
117 target: resources.target,
118 };
119 let event = self.source.entry(self.source_index).or_default();
120 let value = event.materialize(input, &bound).await?;
121 for image in event.images() {
122 let mut image = image.clone();
123 image.part = image
124 .part
125 .checked_add(self.native_parts)
126 .ok_or_else(limit)?;
127 self.reads
128 .images
129 .insert((image.candidate, image.part), image);
130 }
131 value
132 } else {
133 input.clone()
134 };
135 self.native_parts = self.native_parts.checked_add(parts).ok_or_else(limit)?;
136 self.source_index = self.source_index.checked_add(1).ok_or_else(limit)?;
137 Ok(value)
138 }
139 pub(crate) async fn client<R: ResourceAccess<PublishedHandle = H>>(
140 &mut self,
141 input: &[g::GenerateContentResponseBody],
142 expires_at: std::time::SystemTime,
143 resources: &super::super::GenerationResources<'_, R>,
144 ) -> Result<Vec<g::GenerateContentResponseBody>, TransformError> {
145 let mut output = Vec::with_capacity(input.len());
146 for (offset, event) in input.iter().enumerate() {
149 let index = self
150 .client_index
151 .checked_add(offset as u64)
152 .ok_or_else(limit)?;
153 let has_images = event
154 .candidates
155 .iter()
156 .flatten()
157 .flat_map(|candidate| candidate.content.iter())
158 .flat_map(|content| content.parts.iter().flatten())
159 .any(|part| part.inline_data.is_some() || part.file_data.is_some());
160 if !has_images {
161 output.push(event.clone());
162 continue;
163 }
164 let used_bytes: u64 = self
165 .client
166 .iter()
167 .filter(|(old, _)| **old != index)
168 .flat_map(|(_, event)| event.publications.values())
169 .map(|image| {
170 let padding = if image.source.data.ends_with("==") {
171 2
172 } else {
173 usize::from(image.source.data.ends_with('='))
174 };
175 (image.source.data.len() / 4 * 3).saturating_sub(padding) as u64
176 })
177 .sum();
178 let used_count: usize = self
179 .client
180 .iter()
181 .filter(|(old, _)| **old != index)
182 .map(|(_, event)| event.publications.len())
183 .sum();
184 let mut limits = resources.limits;
185 limits.max_body_bytes = limits.max_body_bytes.saturating_sub(used_bytes);
186 let bound = super::super::GenerationResources {
187 access: resources.access,
188 scope: resources.scope,
189 limits,
190 max_references: resources.max_references.saturating_sub(used_count),
191 now: resources.now,
192 target: resources.target,
193 };
194 let value = self
195 .client
196 .entry(index)
197 .or_default()
198 .publish_event(
199 event,
200 self.namespace
201 .ok_or_else(|| missing("unbound image stream progress"))?,
202 index,
203 expires_at,
204 &bound,
205 )
206 .await?;
207 output.push(value);
208 }
209 self.client_index = self
210 .client_index
211 .checked_add(input.len() as u64)
212 .ok_or_else(limit)?;
213 Ok(output)
214 }
215}