gproxy_protocol/adapt/generate/image_resources/
mod.rs1mod publish;
6mod read;
7mod stream;
8mod thread;
9use super::GenerationProgress;
10use crate::{
11 capability::PublishedResource,
12 transform::{TransformError, TransformErrorKind},
13 wire::gemini as g,
14};
15use std::collections::BTreeMap;
16pub use stream::ImageStreamProgress;
17pub use thread::{ResourceSend, ResourceSync};
18
19#[derive(Debug, Clone)]
20pub struct MaterializedImage {
21 pub candidate: usize,
22 pub part: usize,
23 pub original: g::Part,
24 pub materialized: g::Blob,
25 pub resource_expiry: Option<std::time::SystemTime>,
27}
28
29#[derive(Default)]
32pub struct GeminiImageReads {
33 original: Option<g::GenerateContentResponseBody>,
34 images: BTreeMap<(usize, usize), MaterializedImage>,
35 attempted: usize,
36 bytes: u64,
37 pending: Option<PendingImageRead>,
38 failed: bool,
39}
40
41impl GeminiImageReads {
42 pub fn bytes_read(&self) -> u64 {
43 self.bytes
44 }
45 pub fn attempted_reads(&self) -> usize {
46 self.attempted
47 }
48 pub fn images(&self) -> impl Iterator<Item = &MaterializedImage> {
49 self.images.values()
50 }
51}
52
53pub(super) struct Publication {
54 source: g::Blob,
55 operation: String,
56 expiry: std::time::SystemTime,
57 receipt: Option<usize>,
58}
59
60pub struct GeminiImagePublications<H> {
64 original: Option<g::GenerateContentResponseBody>,
65 publications: BTreeMap<(usize, usize), Publication>,
66 receipts: Vec<PublishedResource<H>>,
67 failed: bool,
68}
69
70impl<H> Default for GeminiImagePublications<H> {
71 fn default() -> Self {
72 Self {
73 original: None,
74 publications: BTreeMap::new(),
75 receipts: Vec::new(),
76 failed: false,
77 }
78 }
79}
80
81impl<H> GeminiImagePublications<H> {
82 pub fn operation_ids(&self) -> impl Iterator<Item = &str> {
83 self.publications
84 .values()
85 .map(|value| value.operation.as_str())
86 }
87 pub fn receipts(&self) -> &[PublishedResource<H>] {
88 &self.receipts
89 }
90}
91
92pub struct ImageResourceProgress<N, H> {
95 pub generation: GenerationProgress<N>,
96 pub reads: GeminiImageReads,
97 pub publications: GeminiImagePublications<H>,
98}
99
100impl<N, H> Default for ImageResourceProgress<N, H> {
101 fn default() -> Self {
102 Self {
103 generation: Default::default(),
104 reads: Default::default(),
105 publications: Default::default(),
106 }
107 }
108}
109
110fn invalid(message: impl Into<String>) -> TransformError {
111 TransformError::invalid_result("generation.image_resources", message)
112}
113
114fn missing(message: impl Into<String>) -> TransformError {
115 TransformError::new(
116 TransformErrorKind::MissingState,
117 "generation.image_resources",
118 message,
119 )
120}
121
122fn conflict(message: impl Into<String>) -> TransformError {
123 TransformError::new(
124 TransformErrorKind::Conflict,
125 "generation.image_resources",
126 message,
127 )
128}
129
130fn limit() -> TransformError {
131 TransformError::new(
132 TransformErrorKind::Limit,
133 "generation.image_resources",
134 "image resource budget exceeded",
135 )
136}
137
138pub(crate) fn wants_uri(input: &g::GenerateContentRequestBody) -> bool {
139 input
140 .generation_config
141 .as_ref()
142 .and_then(|config| config.response_format.as_ref())
143 .and_then(|format| format.image.as_ref())
144 .and_then(|image| image.delivery.as_ref())
145 == Some(&g::Delivery::Uri)
146}
147
148struct PendingImageRead {
149 key: (usize, usize),
150 reference: crate::capability::ResourceReference,
151 metadata: crate::capability::ResourceMetadata,
152 body: crate::HttpBody,
153 buffer: Vec<u8>,
154 limit: u64,
155}
156
157fn codec_error(error: crate::codec::CodecError) -> TransformError {
158 let kind = if error.kind() == crate::codec::CodecErrorKind::Limit {
159 TransformErrorKind::Limit
160 } else {
161 TransformErrorKind::InvalidResult
162 };
163 TransformError::with_source(kind, "generation.image_resources", error.to_string(), error)
164}