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 };
118 let event = self.source.entry(self.source_index).or_default();
119 let value = event.materialize(input, &bound).await?;
120 for image in event.images() {
121 let mut image = image.clone();
122 image.part = image
123 .part
124 .checked_add(self.native_parts)
125 .ok_or_else(limit)?;
126 self.reads
127 .images
128 .insert((image.candidate, image.part), image);
129 }
130 value
131 } else {
132 input.clone()
133 };
134 self.native_parts = self.native_parts.checked_add(parts).ok_or_else(limit)?;
135 self.source_index = self.source_index.checked_add(1).ok_or_else(limit)?;
136 Ok(value)
137 }
138 pub(crate) async fn client<R: ResourceAccess<PublishedHandle = H>>(
139 &mut self,
140 input: &[g::GenerateContentResponseBody],
141 expires_at: std::time::SystemTime,
142 resources: &super::super::GenerationResources<'_, R>,
143 ) -> Result<Vec<g::GenerateContentResponseBody>, TransformError> {
144 let mut output = Vec::with_capacity(input.len());
145 for (offset, event) in input.iter().enumerate() {
148 let index = self
149 .client_index
150 .checked_add(offset as u64)
151 .ok_or_else(limit)?;
152 let has_images = event
153 .candidates
154 .iter()
155 .flatten()
156 .flat_map(|candidate| candidate.content.iter())
157 .flat_map(|content| content.parts.iter().flatten())
158 .any(|part| part.inline_data.is_some() || part.file_data.is_some());
159 if !has_images {
160 output.push(event.clone());
161 continue;
162 }
163 let used_bytes: u64 = self
164 .client
165 .iter()
166 .filter(|(old, _)| **old != index)
167 .flat_map(|(_, event)| event.publications.values())
168 .map(|image| {
169 let padding = if image.source.data.ends_with("==") {
170 2
171 } else {
172 usize::from(image.source.data.ends_with('='))
173 };
174 (image.source.data.len() / 4 * 3).saturating_sub(padding) as u64
175 })
176 .sum();
177 let used_count: usize = self
178 .client
179 .iter()
180 .filter(|(old, _)| **old != index)
181 .map(|(_, event)| event.publications.len())
182 .sum();
183 let mut limits = resources.limits;
184 limits.max_body_bytes = limits.max_body_bytes.saturating_sub(used_bytes);
185 let bound = super::super::GenerationResources {
186 access: resources.access,
187 scope: resources.scope,
188 limits,
189 max_references: resources.max_references.saturating_sub(used_count),
190 now: resources.now,
191 };
192 let value = self
193 .client
194 .entry(index)
195 .or_default()
196 .publish_event(
197 event,
198 self.namespace
199 .ok_or_else(|| missing("unbound image stream progress"))?,
200 index,
201 expires_at,
202 &bound,
203 )
204 .await?;
205 output.push(value);
206 }
207 self.client_index = self
208 .client_index
209 .checked_add(input.len() as u64)
210 .ok_or_else(limit)?;
211 Ok(output)
212 }
213}