1use std::marker::PhantomData;
13use std::sync::Arc;
14
15use crate::kinds::{OperationFamily, ProtocolKind};
16use http::StatusCode;
17use serde::{Serialize, de::DeserializeOwned};
18
19use crate::transform::utils::TransformError;
20
21trait BodyEnvelope: Sized {
36 fn from_body_bytes(body: &[u8]) -> Result<Self, TransformError>;
39
40 fn into_body_bytes(self) -> Result<Vec<u8>, TransformError>;
43}
44
45macro_rules! impl_body_envelope {
52 (
53 $wrapper:ty,
54 success_body = $success_body:ty,
55 error_body = $error_body:ty,
56 headers = $headers:ty,
57 ) => {
58 impl BodyEnvelope for $wrapper {
59 fn from_body_bytes(bytes: &[u8]) -> Result<Self, TransformError> {
60 let success_err = match serde_json::from_slice::<$success_body>(bytes) {
61 Ok(body) => {
62 return Ok(Self::Success {
63 stats_code: StatusCode::OK,
64 headers: <$headers>::default(),
65 body,
66 });
67 }
68 Err(e) => e,
69 };
70 let error_err = match serde_json::from_slice::<$error_body>(bytes) {
71 Ok(body) => {
72 return Ok(Self::Error {
73 stats_code: StatusCode::BAD_REQUEST,
74 headers: <$headers>::default(),
75 body,
76 });
77 }
78 Err(e) => e,
79 };
80 let preview: String = String::from_utf8_lossy(
81 &bytes[..std::cmp::min(bytes.len(), 600)],
82 )
83 .into_owned();
84 tracing::warn!(
85 wrapper = stringify!($wrapper),
86 success_error = %success_err,
87 error_variant_error = %error_err,
88 body_len = bytes.len(),
89 body_preview = %preview,
90 "response body did not match either variant of wrapper enum"
91 );
92 Err(TransformError::new(format!(
93 "deserialize: body does not match success or error variant of {} \
94 (success_err: {}; error_err: {})",
95 stringify!($wrapper),
96 success_err,
97 error_err
98 )))
99 }
100
101 fn into_body_bytes(self) -> Result<Vec<u8>, TransformError> {
102 match self {
103 Self::Success { body, .. } => serde_json::to_vec(&body)
104 .map_err(|e| TransformError::new(format!("serialize: {e}"))),
105 Self::Error { body, .. } => serde_json::to_vec(&body)
106 .map_err(|e| TransformError::new(format!("serialize: {e}"))),
107 }
108 }
109 }
110 };
111}
112
113impl_body_envelope!(
114 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
115 success_body = crate::gemini::generate_content::response::ResponseBody,
116 error_body = crate::gemini::types::GeminiApiErrorResponse,
117 headers = crate::gemini::types::GeminiResponseHeaders,
118);
119
120impl_body_envelope!(
121 crate::claude::create_message::response::ClaudeCreateMessageResponse,
122 success_body = crate::claude::create_message::response::ResponseBody,
123 error_body = crate::claude::types::BetaErrorResponse,
124 headers = crate::claude::types::ClaudeResponseHeaders,
125);
126
127impl_body_envelope!(
128 crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse,
129 success_body = crate::openai::create_chat_completions::response::ResponseBody,
130 error_body = crate::openai::types::OpenAiApiErrorResponse,
131 headers = crate::openai::types::OpenAiResponseHeaders,
132);
133
134impl_body_envelope!(
135 crate::openai::create_response::response::OpenAiCreateResponseResponse,
136 success_body = crate::openai::create_response::response::ResponseBody,
137 error_body = crate::openai::types::OpenAiApiErrorResponse,
138 headers = crate::openai::types::OpenAiResponseHeaders,
139);
140
141impl_body_envelope!(
142 crate::gemini::count_tokens::response::GeminiCountTokensResponse,
143 success_body = crate::gemini::count_tokens::response::ResponseBody,
144 error_body = crate::gemini::types::GeminiApiErrorResponse,
145 headers = crate::gemini::types::GeminiResponseHeaders,
146);
147
148impl_body_envelope!(
149 crate::openai::count_tokens::response::OpenAiCountTokensResponse,
150 success_body = crate::openai::count_tokens::response::ResponseBody,
151 error_body = crate::openai::types::OpenAiApiErrorResponse,
152 headers = crate::openai::types::OpenAiResponseHeaders,
153);
154
155impl_body_envelope!(
156 crate::claude::count_tokens::response::ClaudeCountTokensResponse,
157 success_body = crate::claude::count_tokens::response::ResponseBody,
158 error_body = crate::claude::types::BetaErrorResponse,
159 headers = crate::claude::types::ClaudeResponseHeaders,
160);
161
162impl_body_envelope!(
163 crate::gemini::model_get::response::GeminiModelGetResponse,
164 success_body = crate::gemini::model_get::response::ResponseBody,
165 error_body = crate::gemini::types::GeminiApiErrorResponse,
166 headers = crate::gemini::types::GeminiResponseHeaders,
167);
168
169impl_body_envelope!(
170 crate::openai::model_get::response::OpenAiModelGetResponse,
171 success_body = crate::openai::model_get::response::ResponseBody,
172 error_body = crate::openai::types::OpenAiApiErrorResponse,
173 headers = crate::openai::types::OpenAiResponseHeaders,
174);
175
176impl_body_envelope!(
177 crate::claude::model_get::response::ClaudeModelGetResponse,
178 success_body = crate::claude::model_get::response::ResponseBody,
179 error_body = crate::claude::types::BetaErrorResponse,
180 headers = crate::claude::types::ClaudeResponseHeaders,
181);
182
183impl_body_envelope!(
184 crate::gemini::model_list::response::GeminiModelListResponse,
185 success_body = crate::gemini::model_list::response::ResponseBody,
186 error_body = crate::gemini::types::GeminiApiErrorResponse,
187 headers = crate::gemini::types::GeminiResponseHeaders,
188);
189
190impl_body_envelope!(
191 crate::openai::model_list::response::OpenAiModelListResponse,
192 success_body = crate::openai::model_list::response::ResponseBody,
193 error_body = crate::openai::types::OpenAiApiErrorResponse,
194 headers = crate::openai::types::OpenAiResponseHeaders,
195);
196
197impl_body_envelope!(
198 crate::claude::model_list::response::ClaudeModelListResponse,
199 success_body = crate::claude::model_list::response::ResponseBody,
200 error_body = crate::claude::types::BetaErrorResponse,
201 headers = crate::claude::types::ClaudeResponseHeaders,
202);
203
204impl_body_envelope!(
205 crate::gemini::embeddings::response::GeminiEmbedContentResponse,
206 success_body = crate::gemini::embeddings::response::ResponseBody,
207 error_body = crate::gemini::types::GeminiApiErrorResponse,
208 headers = crate::gemini::types::GeminiResponseHeaders,
209);
210
211impl_body_envelope!(
212 crate::openai::embeddings::response::OpenAiEmbeddingsResponse,
213 success_body = crate::openai::embeddings::response::ResponseBody,
214 error_body = crate::openai::types::OpenAiApiErrorResponse,
215 headers = crate::openai::types::OpenAiResponseHeaders,
216);
217
218impl_body_envelope!(
219 crate::openai::create_image::response::OpenAiCreateImageResponse,
220 success_body = crate::openai::create_image::response::ResponseBody,
221 error_body = crate::openai::types::OpenAiApiErrorResponse,
222 headers = crate::openai::types::OpenAiResponseHeaders,
223);
224
225impl_body_envelope!(
226 crate::openai::create_image_edit::response::OpenAiCreateImageEditResponse,
227 success_body = crate::openai::create_image_edit::response::ResponseBody,
228 error_body = crate::openai::types::OpenAiApiErrorResponse,
229 headers = crate::openai::types::OpenAiResponseHeaders,
230);
231
232impl_body_envelope!(
233 crate::openai::compact_response::response::OpenAiCompactResponse,
234 success_body = crate::openai::compact_response::response::ResponseBody,
235 error_body = crate::openai::types::OpenAiApiErrorResponse,
236 headers = crate::openai::types::OpenAiResponseHeaders,
237);
238
239trait RequestDescriptor: Sized {
240 type Body: DeserializeOwned + Serialize;
241
242 fn from_body(body: Self::Body) -> Self;
243 fn into_body(self) -> Self::Body;
244
245 fn with_model(self, _model: Option<&str>) -> Self {
255 self
256 }
257}
258
259impl RequestDescriptor
260 for crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest
261{
262 type Body = crate::openai::create_chat_completions::request::RequestBody;
263
264 fn from_body(body: Self::Body) -> Self {
265 Self {
266 body,
267 ..Self::default()
268 }
269 }
270
271 fn into_body(self) -> Self::Body {
272 self.body
273 }
274}
275
276impl RequestDescriptor for crate::openai::create_response::request::OpenAiCreateResponseRequest {
277 type Body = crate::openai::create_response::request::RequestBody;
278
279 fn from_body(body: Self::Body) -> Self {
280 Self {
281 body,
282 ..Self::default()
283 }
284 }
285
286 fn into_body(self) -> Self::Body {
287 self.body
288 }
289}
290
291impl RequestDescriptor for crate::claude::create_message::request::ClaudeCreateMessageRequest {
292 type Body = crate::claude::create_message::request::RequestBody;
293
294 fn from_body(body: Self::Body) -> Self {
295 Self {
296 body,
297 ..Self::default()
298 }
299 }
300
301 fn into_body(self) -> Self::Body {
302 self.body
303 }
304}
305
306impl RequestDescriptor for crate::gemini::generate_content::request::GeminiGenerateContentRequest {
307 type Body = crate::gemini::generate_content::request::RequestBody;
308
309 fn from_body(body: Self::Body) -> Self {
310 Self {
311 body,
312 ..Self::default()
313 }
314 }
315
316 fn into_body(self) -> Self::Body {
317 self.body
318 }
319
320 fn with_model(mut self, model: Option<&str>) -> Self {
321 if let Some(m) = model {
322 self.path.model = m.to_string();
323 }
324 self
325 }
326}
327
328impl RequestDescriptor
329 for crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest
330{
331 type Body = crate::gemini::stream_generate_content::request::RequestBody;
332
333 fn from_body(body: Self::Body) -> Self {
334 Self {
335 body,
336 ..Self::default()
337 }
338 }
339
340 fn into_body(self) -> Self::Body {
341 self.body
342 }
343
344 fn with_model(mut self, model: Option<&str>) -> Self {
345 if let Some(m) = model {
346 self.path.model = m.to_string();
347 }
348 self
349 }
350}
351
352macro_rules! impl_request_descriptor_default_envelope {
365 ($wrapper:ty, body = $body:ty) => {
366 impl RequestDescriptor for $wrapper {
367 type Body = $body;
368
369 fn from_body(body: Self::Body) -> Self {
370 Self {
371 body,
372 ..Self::default()
373 }
374 }
375
376 fn into_body(self) -> Self::Body {
377 self.body
378 }
379 }
380 };
381 ($wrapper:ty, body = $body:ty, path_model = $field:ident) => {
382 impl RequestDescriptor for $wrapper {
383 type Body = $body;
384
385 fn from_body(body: Self::Body) -> Self {
386 Self {
387 body,
388 ..Self::default()
389 }
390 }
391
392 fn into_body(self) -> Self::Body {
393 self.body
394 }
395
396 fn with_model(mut self, model: Option<&str>) -> Self {
397 if let Some(m) = model {
398 self.path.$field = m.to_string();
399 }
400 self
401 }
402 }
403 };
404}
405
406impl_request_descriptor_default_envelope!(
407 crate::claude::count_tokens::request::ClaudeCountTokensRequest,
408 body = crate::claude::count_tokens::request::RequestBody
409);
410impl_request_descriptor_default_envelope!(
411 crate::openai::count_tokens::request::OpenAiCountTokensRequest,
412 body = crate::openai::count_tokens::request::RequestBody
413);
414impl_request_descriptor_default_envelope!(
415 crate::gemini::count_tokens::request::GeminiCountTokensRequest,
416 body = crate::gemini::count_tokens::request::RequestBody,
417 path_model = model
418);
419impl_request_descriptor_default_envelope!(
420 crate::claude::model_get::request::ClaudeModelGetRequest,
421 body = crate::claude::model_get::request::RequestBody,
422 path_model = model_id
423);
424impl_request_descriptor_default_envelope!(
425 crate::openai::model_get::request::OpenAiModelGetRequest,
426 body = crate::openai::model_get::request::RequestBody,
427 path_model = model
428);
429impl_request_descriptor_default_envelope!(
430 crate::gemini::model_get::request::GeminiModelGetRequest,
431 body = crate::gemini::model_get::request::RequestBody,
432 path_model = name
433);
434impl_request_descriptor_default_envelope!(
435 crate::claude::model_list::request::ClaudeModelListRequest,
436 body = crate::claude::model_list::request::RequestBody
437);
438impl_request_descriptor_default_envelope!(
439 crate::openai::model_list::request::OpenAiModelListRequest,
440 body = crate::openai::model_list::request::RequestBody
441);
442impl_request_descriptor_default_envelope!(
443 crate::gemini::model_list::request::GeminiModelListRequest,
444 body = crate::gemini::model_list::request::RequestBody
445);
446impl_request_descriptor_default_envelope!(
447 crate::openai::embeddings::request::OpenAiEmbeddingsRequest,
448 body = crate::openai::embeddings::request::RequestBody
449);
450impl_request_descriptor_default_envelope!(
451 crate::gemini::embeddings::request::GeminiEmbedContentRequest,
452 body = crate::gemini::embeddings::request::RequestBody,
453 path_model = model
454);
455impl_request_descriptor_default_envelope!(
456 crate::openai::create_image::request::OpenAiCreateImageRequest,
457 body = crate::openai::create_image::request::RequestBody
458);
459impl_request_descriptor_default_envelope!(
460 crate::openai::create_image_edit::request::OpenAiCreateImageEditRequest,
461 body = crate::openai::create_image_edit::request::RequestBody
462);
463impl_request_descriptor_default_envelope!(
464 crate::openai::compact_response::request::OpenAiCompactRequest,
465 body = crate::openai::compact_response::request::RequestBody
466);
467
468fn translate_request_query(
473 src_operation: OperationFamily,
474 src_protocol: ProtocolKind,
475 dst_operation: OperationFamily,
476 dst_protocol: ProtocolKind,
477 query: Option<&str>,
478) -> Option<String> {
479 let raw = query?;
480 if raw.is_empty() {
481 return None;
482 }
483
484 if !(src_operation == OperationFamily::ModelList
488 && dst_operation == OperationFamily::ModelList
489 && src_protocol != dst_protocol)
490 {
491 return Some(raw.to_owned());
492 }
493
494 let mut page_size: Option<String> = None;
495 let mut page_token: Option<String> = None;
496 for (key, value) in url::form_urlencoded::parse(raw.as_bytes()) {
497 match (src_protocol, key.as_ref()) {
498 (ProtocolKind::Gemini | ProtocolKind::GeminiNDJson, "pageSize") => {
499 page_size = Some(value.into_owned())
500 }
501 (ProtocolKind::Gemini | ProtocolKind::GeminiNDJson, "pageToken") => {
502 page_token = Some(value.into_owned())
503 }
504 (ProtocolKind::Claude, "limit") => page_size = Some(value.into_owned()),
505 (ProtocolKind::Claude, "after_id") => page_token = Some(value.into_owned()),
506 (
507 ProtocolKind::OpenAi
508 | ProtocolKind::OpenAiChatCompletion
509 | ProtocolKind::OpenAiResponse,
510 "limit",
511 ) => page_size = Some(value.into_owned()),
512 (
513 ProtocolKind::OpenAi
514 | ProtocolKind::OpenAiChatCompletion
515 | ProtocolKind::OpenAiResponse,
516 "after",
517 ) => page_token = Some(value.into_owned()),
518 _ => {}
519 }
520 }
521
522 let mut out = url::form_urlencoded::Serializer::new(String::new());
523 match dst_protocol {
524 ProtocolKind::Gemini | ProtocolKind::GeminiNDJson => {
525 if let Some(v) = page_size {
526 out.append_pair("pageSize", &v);
527 }
528 if let Some(v) = page_token {
529 out.append_pair("pageToken", &v);
530 }
531 }
532 ProtocolKind::Claude => {
533 if let Some(v) = page_size {
534 out.append_pair("limit", &v);
535 }
536 if let Some(v) = page_token {
537 out.append_pair("after_id", &v);
538 }
539 }
540 ProtocolKind::OpenAi
541 | ProtocolKind::OpenAiChatCompletion
542 | ProtocolKind::OpenAiResponse => {
543 if let Some(v) = page_size {
544 out.append_pair("limit", &v);
545 }
546 if let Some(v) = page_token {
547 out.append_pair("after", &v);
548 }
549 }
550 }
551 let s = out.finish();
552 if s.is_empty() { None } else { Some(s) }
553}
554
555pub fn transform_request(
559 src_operation: OperationFamily,
560 src_protocol: ProtocolKind,
561 dst_operation: OperationFamily,
562 dst_protocol: ProtocolKind,
563 model: Option<&str>,
564 query: Option<&str>,
565 body: Vec<u8>,
566) -> Result<(Option<String>, Vec<u8>), TransformError> {
567 if src_operation == dst_operation && src_protocol == dst_protocol {
568 return Ok((query.map(str::to_owned), body));
569 }
570
571 let translated_query = translate_request_query(
574 src_operation,
575 src_protocol,
576 dst_operation,
577 dst_protocol,
578 query,
579 );
580
581 tracing::debug!(
582 src_operation = %src_operation,
583 src_protocol = %src_protocol,
584 dst_operation = %dst_operation,
585 dst_protocol = %dst_protocol,
586 "transforming request"
587 );
588 let key = (src_operation, src_protocol, dst_operation, dst_protocol);
589
590 match key {
591 (
597 OperationFamily::GenerateContent,
598 ProtocolKind::Claude,
599 OperationFamily::GenerateContent,
600 ProtocolKind::OpenAiChatCompletion,
601 ) => transform_request_descriptor::<
602 crate::claude::create_message::request::ClaudeCreateMessageRequest,
603 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
604 >(&body, model),
605 (
606 OperationFamily::GenerateContent,
607 ProtocolKind::Claude,
608 OperationFamily::GenerateContent,
609 ProtocolKind::OpenAiResponse,
610 ) => transform_request_descriptor::<
611 crate::claude::create_message::request::ClaudeCreateMessageRequest,
612 crate::openai::create_response::request::OpenAiCreateResponseRequest,
613 >(&body, model),
614
615 (
617 OperationFamily::GenerateContent,
618 ProtocolKind::Claude,
619 OperationFamily::GenerateContent,
620 ProtocolKind::Gemini,
621 ) => transform_request_descriptor::<
622 crate::claude::create_message::request::ClaudeCreateMessageRequest,
623 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
624 >(&body, model),
625
626 (
628 OperationFamily::GenerateContent,
629 ProtocolKind::OpenAiChatCompletion,
630 OperationFamily::GenerateContent,
631 ProtocolKind::Claude,
632 ) => transform_request_descriptor::<
633 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
634 crate::claude::create_message::request::ClaudeCreateMessageRequest,
635 >(&body, model),
636
637 (
639 OperationFamily::GenerateContent,
640 ProtocolKind::OpenAiChatCompletion,
641 OperationFamily::GenerateContent,
642 ProtocolKind::Gemini,
643 ) => transform_request_descriptor::<
644 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
645 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
646 >(&body, model),
647
648 (
650 OperationFamily::GenerateContent,
651 ProtocolKind::OpenAiChatCompletion,
652 OperationFamily::GenerateContent,
653 ProtocolKind::OpenAiResponse,
654 ) => transform_request_descriptor::<
655 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
656 crate::openai::create_response::request::OpenAiCreateResponseRequest,
657 >(&body, model),
658
659 (
665 OperationFamily::GenerateContent,
666 ProtocolKind::OpenAiResponse,
667 OperationFamily::GenerateContent,
668 ProtocolKind::OpenAiChatCompletion,
669 ) => transform_request_descriptor::<
670 crate::openai::create_response::request::OpenAiCreateResponseRequest,
671 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
672 >(&body, model),
673
674 (
676 OperationFamily::GenerateContent,
677 ProtocolKind::OpenAiResponse,
678 OperationFamily::GenerateContent,
679 ProtocolKind::Claude,
680 ) => transform_request_descriptor::<
681 crate::openai::create_response::request::OpenAiCreateResponseRequest,
682 crate::claude::create_message::request::ClaudeCreateMessageRequest,
683 >(&body, model),
684
685 (
687 OperationFamily::GenerateContent,
688 ProtocolKind::OpenAiResponse,
689 OperationFamily::GenerateContent,
690 ProtocolKind::Gemini,
691 ) => transform_request_descriptor::<
692 crate::openai::create_response::request::OpenAiCreateResponseRequest,
693 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
694 >(&body, model),
695
696 (
698 OperationFamily::GenerateContent,
699 ProtocolKind::Gemini,
700 OperationFamily::GenerateContent,
701 ProtocolKind::Claude,
702 ) => transform_request_descriptor::<
703 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
704 crate::claude::create_message::request::ClaudeCreateMessageRequest,
705 >(&body, model),
706
707 (
709 OperationFamily::GenerateContent,
710 ProtocolKind::Gemini,
711 OperationFamily::GenerateContent,
712 ProtocolKind::OpenAiChatCompletion,
713 ) => transform_request_descriptor::<
714 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
715 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
716 >(&body, model),
717
718 (
720 OperationFamily::GenerateContent,
721 ProtocolKind::Gemini,
722 OperationFamily::GenerateContent,
723 ProtocolKind::OpenAiResponse,
724 ) => transform_request_descriptor::<
725 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
726 crate::openai::create_response::request::OpenAiCreateResponseRequest,
727 >(&body, model),
728
729 (
735 OperationFamily::StreamGenerateContent,
736 ProtocolKind::Claude,
737 OperationFamily::StreamGenerateContent,
738 ProtocolKind::Gemini,
739 ) => transform_request_descriptor_ref::<
740 crate::claude::create_message::request::ClaudeCreateMessageRequest,
741 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
742 >(&body, model),
743 (
744 OperationFamily::StreamGenerateContent,
745 ProtocolKind::Claude,
746 OperationFamily::StreamGenerateContent,
747 ProtocolKind::GeminiNDJson,
748 ) => transform_request_descriptor_ref::<
749 crate::claude::create_message::request::ClaudeCreateMessageRequest,
750 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
751 >(&body, model),
752 (
753 OperationFamily::StreamGenerateContent,
754 ProtocolKind::Claude,
755 OperationFamily::StreamGenerateContent,
756 ProtocolKind::OpenAiChatCompletion,
757 ) => transform_request_descriptor_ref::<
758 crate::claude::create_message::request::ClaudeCreateMessageRequest,
759 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
760 >(&body, model),
761 (
762 OperationFamily::StreamGenerateContent,
763 ProtocolKind::Claude,
764 OperationFamily::StreamGenerateContent,
765 ProtocolKind::OpenAiResponse,
766 ) => transform_request_descriptor_ref::<
767 crate::claude::create_message::request::ClaudeCreateMessageRequest,
768 crate::openai::create_response::request::OpenAiCreateResponseRequest,
769 >(&body, model),
770
771 (
773 OperationFamily::StreamGenerateContent,
774 ProtocolKind::Gemini,
775 OperationFamily::StreamGenerateContent,
776 ProtocolKind::Claude,
777 ) => transform_request_descriptor::<
778 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
779 crate::claude::create_message::request::ClaudeCreateMessageRequest,
780 >(&body, model),
781 (
782 OperationFamily::StreamGenerateContent,
783 ProtocolKind::GeminiNDJson,
784 OperationFamily::StreamGenerateContent,
785 ProtocolKind::Claude,
786 ) => transform_request_descriptor::<
787 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
788 crate::claude::create_message::request::ClaudeCreateMessageRequest,
789 >(&body, model),
790 (
791 OperationFamily::StreamGenerateContent,
792 ProtocolKind::Gemini,
793 OperationFamily::StreamGenerateContent,
794 ProtocolKind::OpenAiChatCompletion,
795 ) => transform_request_descriptor::<
796 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
797 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
798 >(&body, model),
799 (
800 OperationFamily::StreamGenerateContent,
801 ProtocolKind::GeminiNDJson,
802 OperationFamily::StreamGenerateContent,
803 ProtocolKind::OpenAiChatCompletion,
804 ) => transform_request_descriptor::<
805 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
806 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
807 >(&body, model),
808 (
809 OperationFamily::StreamGenerateContent,
810 ProtocolKind::Gemini,
811 OperationFamily::StreamGenerateContent,
812 ProtocolKind::OpenAiResponse,
813 ) => transform_request_descriptor::<
814 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
815 crate::openai::create_response::request::OpenAiCreateResponseRequest,
816 >(&body, model),
817 (
818 OperationFamily::StreamGenerateContent,
819 ProtocolKind::GeminiNDJson,
820 OperationFamily::StreamGenerateContent,
821 ProtocolKind::OpenAiResponse,
822 ) => transform_request_descriptor::<
823 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
824 crate::openai::create_response::request::OpenAiCreateResponseRequest,
825 >(&body, model),
826
827 (
829 OperationFamily::StreamGenerateContent,
830 ProtocolKind::OpenAiChatCompletion,
831 OperationFamily::StreamGenerateContent,
832 ProtocolKind::Claude,
833 ) => transform_request_descriptor_ref::<
834 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
835 crate::claude::create_message::request::ClaudeCreateMessageRequest,
836 >(&body, model),
837 (
838 OperationFamily::StreamGenerateContent,
839 ProtocolKind::OpenAiChatCompletion,
840 OperationFamily::StreamGenerateContent,
841 ProtocolKind::Gemini,
842 ) => transform_request_descriptor_ref::<
843 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
844 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
845 >(&body, model),
846 (
847 OperationFamily::StreamGenerateContent,
848 ProtocolKind::OpenAiChatCompletion,
849 OperationFamily::StreamGenerateContent,
850 ProtocolKind::GeminiNDJson,
851 ) => transform_request_descriptor_ref::<
852 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
853 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
854 >(&body, model),
855 (
856 OperationFamily::StreamGenerateContent,
857 ProtocolKind::OpenAiChatCompletion,
858 OperationFamily::StreamGenerateContent,
859 ProtocolKind::OpenAiResponse,
860 ) => transform_request_descriptor_ref::<
861 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
862 crate::openai::create_response::request::OpenAiCreateResponseRequest,
863 >(&body, model),
864 (
868 OperationFamily::StreamGenerateContent,
869 ProtocolKind::OpenAiResponse,
870 OperationFamily::StreamGenerateContent,
871 ProtocolKind::OpenAiChatCompletion,
872 ) => transform_request_descriptor_ref::<
873 crate::openai::create_response::request::OpenAiCreateResponseRequest,
874 crate::openai::create_chat_completions::request::OpenAiChatCompletionsRequest,
875 >(&body, model),
876
877 (
879 OperationFamily::StreamGenerateContent,
880 ProtocolKind::OpenAiResponse,
881 OperationFamily::StreamGenerateContent,
882 ProtocolKind::Claude,
883 ) => transform_request_descriptor_ref::<
884 crate::openai::create_response::request::OpenAiCreateResponseRequest,
885 crate::claude::create_message::request::ClaudeCreateMessageRequest,
886 >(&body, model),
887 (
888 OperationFamily::StreamGenerateContent,
889 ProtocolKind::OpenAiResponse,
890 OperationFamily::StreamGenerateContent,
891 ProtocolKind::Gemini,
892 ) => transform_request_descriptor_ref::<
893 crate::openai::create_response::request::OpenAiCreateResponseRequest,
894 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
895 >(&body, model),
896 (
897 OperationFamily::StreamGenerateContent,
898 ProtocolKind::OpenAiResponse,
899 OperationFamily::StreamGenerateContent,
900 ProtocolKind::GeminiNDJson,
901 ) => transform_request_descriptor_ref::<
902 crate::openai::create_response::request::OpenAiCreateResponseRequest,
903 crate::gemini::stream_generate_content::request::GeminiStreamGenerateContentRequest,
904 >(&body, model),
905
906 (
912 OperationFamily::CountToken,
913 ProtocolKind::Claude,
914 OperationFamily::CountToken,
915 ProtocolKind::Gemini,
916 ) => transform_request_descriptor::<
917 crate::claude::count_tokens::request::ClaudeCountTokensRequest,
918 crate::gemini::count_tokens::request::GeminiCountTokensRequest,
919 >(&body, model),
920 (
921 OperationFamily::CountToken,
922 ProtocolKind::Claude,
923 OperationFamily::CountToken,
924 ProtocolKind::OpenAi,
925 ) => transform_request_descriptor::<
926 crate::claude::count_tokens::request::ClaudeCountTokensRequest,
927 crate::openai::count_tokens::request::OpenAiCountTokensRequest,
928 >(&body, model),
929
930 (
932 OperationFamily::CountToken,
933 ProtocolKind::OpenAi,
934 OperationFamily::CountToken,
935 ProtocolKind::Claude,
936 ) => transform_request_descriptor::<
937 crate::openai::count_tokens::request::OpenAiCountTokensRequest,
938 crate::claude::count_tokens::request::ClaudeCountTokensRequest,
939 >(&body, model),
940 (
941 OperationFamily::CountToken,
942 ProtocolKind::OpenAi,
943 OperationFamily::CountToken,
944 ProtocolKind::Gemini,
945 ) => transform_request_descriptor::<
946 crate::openai::count_tokens::request::OpenAiCountTokensRequest,
947 crate::gemini::count_tokens::request::GeminiCountTokensRequest,
948 >(&body, model),
949
950 (
952 OperationFamily::CountToken,
953 ProtocolKind::Gemini,
954 OperationFamily::CountToken,
955 ProtocolKind::Claude,
956 ) => transform_request_descriptor::<
957 crate::gemini::count_tokens::request::GeminiCountTokensRequest,
958 crate::claude::count_tokens::request::ClaudeCountTokensRequest,
959 >(&body, model),
960 (
961 OperationFamily::CountToken,
962 ProtocolKind::Gemini,
963 OperationFamily::CountToken,
964 ProtocolKind::OpenAi,
965 ) => transform_request_descriptor::<
966 crate::gemini::count_tokens::request::GeminiCountTokensRequest,
967 crate::openai::count_tokens::request::OpenAiCountTokensRequest,
968 >(&body, model),
969
970 (
976 OperationFamily::ModelGet,
977 ProtocolKind::Claude,
978 OperationFamily::ModelGet,
979 ProtocolKind::Gemini,
980 ) => transform_request_descriptor::<
981 crate::claude::model_get::request::ClaudeModelGetRequest,
982 crate::gemini::model_get::request::GeminiModelGetRequest,
983 >(&body, model),
984 (
985 OperationFamily::ModelGet,
986 ProtocolKind::Claude,
987 OperationFamily::ModelGet,
988 ProtocolKind::OpenAi,
989 ) => transform_request_descriptor::<
990 crate::claude::model_get::request::ClaudeModelGetRequest,
991 crate::openai::model_get::request::OpenAiModelGetRequest,
992 >(&body, model),
993
994 (
996 OperationFamily::ModelGet,
997 ProtocolKind::OpenAi,
998 OperationFamily::ModelGet,
999 ProtocolKind::Claude,
1000 ) => transform_request_descriptor::<
1001 crate::openai::model_get::request::OpenAiModelGetRequest,
1002 crate::claude::model_get::request::ClaudeModelGetRequest,
1003 >(&body, model),
1004 (
1005 OperationFamily::ModelGet,
1006 ProtocolKind::OpenAi,
1007 OperationFamily::ModelGet,
1008 ProtocolKind::Gemini,
1009 ) => transform_request_descriptor::<
1010 crate::openai::model_get::request::OpenAiModelGetRequest,
1011 crate::gemini::model_get::request::GeminiModelGetRequest,
1012 >(&body, model),
1013
1014 (
1016 OperationFamily::ModelGet,
1017 ProtocolKind::Gemini,
1018 OperationFamily::ModelGet,
1019 ProtocolKind::Claude,
1020 ) => transform_request_descriptor::<
1021 crate::gemini::model_get::request::GeminiModelGetRequest,
1022 crate::claude::model_get::request::ClaudeModelGetRequest,
1023 >(&body, model),
1024 (
1025 OperationFamily::ModelGet,
1026 ProtocolKind::Gemini,
1027 OperationFamily::ModelGet,
1028 ProtocolKind::OpenAi,
1029 ) => transform_request_descriptor::<
1030 crate::gemini::model_get::request::GeminiModelGetRequest,
1031 crate::openai::model_get::request::OpenAiModelGetRequest,
1032 >(&body, model),
1033
1034 (
1040 OperationFamily::ModelList,
1041 ProtocolKind::Claude,
1042 OperationFamily::ModelList,
1043 ProtocolKind::Gemini,
1044 ) => transform_request_descriptor::<
1045 crate::claude::model_list::request::ClaudeModelListRequest,
1046 crate::gemini::model_list::request::GeminiModelListRequest,
1047 >(&body, model),
1048 (
1049 OperationFamily::ModelList,
1050 ProtocolKind::Claude,
1051 OperationFamily::ModelList,
1052 ProtocolKind::OpenAi,
1053 ) => transform_request_descriptor::<
1054 crate::claude::model_list::request::ClaudeModelListRequest,
1055 crate::openai::model_list::request::OpenAiModelListRequest,
1056 >(&body, model),
1057
1058 (
1060 OperationFamily::ModelList,
1061 ProtocolKind::OpenAi,
1062 OperationFamily::ModelList,
1063 ProtocolKind::Claude,
1064 ) => transform_request_descriptor::<
1065 crate::openai::model_list::request::OpenAiModelListRequest,
1066 crate::claude::model_list::request::ClaudeModelListRequest,
1067 >(&body, model),
1068 (
1069 OperationFamily::ModelList,
1070 ProtocolKind::OpenAi,
1071 OperationFamily::ModelList,
1072 ProtocolKind::Gemini,
1073 ) => transform_request_descriptor::<
1074 crate::openai::model_list::request::OpenAiModelListRequest,
1075 crate::gemini::model_list::request::GeminiModelListRequest,
1076 >(&body, model),
1077
1078 (
1080 OperationFamily::ModelList,
1081 ProtocolKind::Gemini,
1082 OperationFamily::ModelList,
1083 ProtocolKind::Claude,
1084 ) => transform_request_descriptor::<
1085 crate::gemini::model_list::request::GeminiModelListRequest,
1086 crate::claude::model_list::request::ClaudeModelListRequest,
1087 >(&body, model),
1088 (
1089 OperationFamily::ModelList,
1090 ProtocolKind::Gemini,
1091 OperationFamily::ModelList,
1092 ProtocolKind::OpenAi,
1093 ) => transform_request_descriptor::<
1094 crate::gemini::model_list::request::GeminiModelListRequest,
1095 crate::openai::model_list::request::OpenAiModelListRequest,
1096 >(&body, model),
1097
1098 (
1102 OperationFamily::Embedding,
1103 ProtocolKind::OpenAi,
1104 OperationFamily::Embedding,
1105 ProtocolKind::Gemini,
1106 ) => transform_request_descriptor::<
1107 crate::openai::embeddings::request::OpenAiEmbeddingsRequest,
1108 crate::gemini::embeddings::request::GeminiEmbedContentRequest,
1109 >(&body, model),
1110 (
1111 OperationFamily::Embedding,
1112 ProtocolKind::Gemini,
1113 OperationFamily::Embedding,
1114 ProtocolKind::OpenAi,
1115 ) => transform_request_descriptor::<
1116 crate::gemini::embeddings::request::GeminiEmbedContentRequest,
1117 crate::openai::embeddings::request::OpenAiEmbeddingsRequest,
1118 >(&body, model),
1119
1120 (
1124 OperationFamily::CreateImage,
1125 ProtocolKind::OpenAi,
1126 OperationFamily::GenerateContent,
1127 ProtocolKind::Gemini,
1128 )
1129 | (
1130 OperationFamily::CreateImage,
1131 ProtocolKind::OpenAi,
1132 OperationFamily::StreamGenerateContent,
1133 ProtocolKind::Gemini,
1134 ) => transform_json::<
1135 crate::openai::create_image::request::OpenAiCreateImageRequest,
1136 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
1137 >(&body),
1138
1139 (
1140 OperationFamily::CreateImage,
1141 ProtocolKind::OpenAi,
1142 OperationFamily::StreamGenerateContent,
1143 ProtocolKind::OpenAiResponse,
1144 )
1145 | (
1146 OperationFamily::CreateImage,
1147 ProtocolKind::OpenAi,
1148 OperationFamily::GenerateContent,
1149 ProtocolKind::OpenAiResponse,
1150 ) => transform_json::<
1151 crate::openai::create_image::request::OpenAiCreateImageRequest,
1152 crate::openai::create_response::request::OpenAiCreateResponseRequest,
1153 >(&body),
1154
1155 (
1159 OperationFamily::CreateImageEdit,
1160 ProtocolKind::OpenAi,
1161 OperationFamily::GenerateContent,
1162 ProtocolKind::Gemini,
1163 )
1164 | (
1165 OperationFamily::CreateImageEdit,
1166 ProtocolKind::OpenAi,
1167 OperationFamily::StreamGenerateContent,
1168 ProtocolKind::Gemini,
1169 ) => transform_json::<
1170 crate::openai::create_image_edit::request::OpenAiCreateImageEditRequest,
1171 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
1172 >(&body),
1173
1174 (
1175 OperationFamily::CreateImageEdit,
1176 ProtocolKind::OpenAi,
1177 OperationFamily::StreamGenerateContent,
1178 ProtocolKind::OpenAiResponse,
1179 )
1180 | (
1181 OperationFamily::CreateImageEdit,
1182 ProtocolKind::OpenAi,
1183 OperationFamily::GenerateContent,
1184 ProtocolKind::OpenAiResponse,
1185 ) => transform_json::<
1186 crate::openai::create_image_edit::request::OpenAiCreateImageEditRequest,
1187 crate::openai::create_response::request::OpenAiCreateResponseRequest,
1188 >(&body),
1189
1190 (
1194 OperationFamily::Compact,
1195 ProtocolKind::OpenAi,
1196 OperationFamily::GenerateContent,
1197 ProtocolKind::Claude,
1198 ) => transform_json::<
1199 crate::openai::compact_response::request::OpenAiCompactRequest,
1200 crate::claude::create_message::request::ClaudeCreateMessageRequest,
1201 >(&body),
1202 (
1203 OperationFamily::Compact,
1204 ProtocolKind::OpenAi,
1205 OperationFamily::GenerateContent,
1206 ProtocolKind::Gemini,
1207 ) => transform_json::<
1208 crate::openai::compact_response::request::OpenAiCompactRequest,
1209 crate::gemini::generate_content::request::GeminiGenerateContentRequest,
1210 >(&body),
1211
1212 _ => Err(TransformError::new(format!(
1213 "no request transform for ({}, {}) -> ({}, {})",
1214 src_operation, src_protocol, dst_operation, dst_protocol
1215 ))),
1216 }
1217 .map(|body| (translated_query, body))
1218}
1219
1220pub fn transform_response(
1227 src_operation: OperationFamily,
1228 src_protocol: ProtocolKind,
1229 dst_operation: OperationFamily,
1230 dst_protocol: ProtocolKind,
1231 body: Vec<u8>,
1232) -> Result<Vec<u8>, TransformError> {
1233 tracing::debug!(
1234 src_operation = %src_operation,
1235 src_protocol = %src_protocol,
1236 dst_operation = %dst_operation,
1237 dst_protocol = %dst_protocol,
1238 "transforming response"
1239 );
1240 let key = (dst_operation, dst_protocol, src_operation, src_protocol);
1243
1244 match key {
1245 (
1251 OperationFamily::GenerateContent,
1252 ProtocolKind::Gemini,
1253 OperationFamily::GenerateContent,
1254 ProtocolKind::Claude,
1255 ) => transform_response_json::<
1256 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1257 crate::claude::create_message::response::ClaudeCreateMessageResponse,
1258 >(&body),
1259 (
1261 OperationFamily::GenerateContent,
1262 ProtocolKind::OpenAiChatCompletion,
1263 OperationFamily::GenerateContent,
1264 ProtocolKind::Claude,
1265 ) => transform_response_json::<
1266 crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse,
1267 crate::claude::create_message::response::ClaudeCreateMessageResponse,
1268 >(&body),
1269 (
1271 OperationFamily::GenerateContent,
1272 ProtocolKind::OpenAiResponse,
1273 OperationFamily::GenerateContent,
1274 ProtocolKind::Claude,
1275 ) => transform_response_json::<
1276 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1277 crate::claude::create_message::response::ClaudeCreateMessageResponse,
1278 >(&body),
1279
1280 (
1282 OperationFamily::GenerateContent,
1283 ProtocolKind::Claude,
1284 OperationFamily::GenerateContent,
1285 ProtocolKind::Gemini,
1286 ) => transform_response_json::<
1287 crate::claude::create_message::response::ClaudeCreateMessageResponse,
1288 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1289 >(&body),
1290 (
1292 OperationFamily::GenerateContent,
1293 ProtocolKind::OpenAiChatCompletion,
1294 OperationFamily::GenerateContent,
1295 ProtocolKind::Gemini,
1296 ) => transform_response_json::<
1297 crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse,
1298 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1299 >(&body),
1300 (
1302 OperationFamily::GenerateContent,
1303 ProtocolKind::OpenAiResponse,
1304 OperationFamily::GenerateContent,
1305 ProtocolKind::Gemini,
1306 ) => transform_response_json::<
1307 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1308 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1309 >(&body),
1310
1311 (
1313 OperationFamily::GenerateContent,
1314 ProtocolKind::Claude,
1315 OperationFamily::GenerateContent,
1316 ProtocolKind::OpenAiChatCompletion,
1317 ) => transform_response_json::<
1318 crate::claude::create_message::response::ClaudeCreateMessageResponse,
1319 crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse,
1320 >(&body),
1321 (
1323 OperationFamily::GenerateContent,
1324 ProtocolKind::Gemini,
1325 OperationFamily::GenerateContent,
1326 ProtocolKind::OpenAiChatCompletion,
1327 ) => transform_response_json::<
1328 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1329 crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse,
1330 >(&body),
1331 (
1333 OperationFamily::GenerateContent,
1334 ProtocolKind::OpenAiResponse,
1335 OperationFamily::GenerateContent,
1336 ProtocolKind::OpenAiChatCompletion,
1337 ) => transform_response_json::<
1338 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1339 crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse,
1340 >(&body),
1341 (
1347 OperationFamily::GenerateContent,
1348 ProtocolKind::OpenAiChatCompletion,
1349 OperationFamily::GenerateContent,
1350 ProtocolKind::OpenAiResponse,
1351 ) => transform_response_json::<
1352 crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse,
1353 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1354 >(&body),
1355
1356 (
1358 OperationFamily::GenerateContent,
1359 ProtocolKind::Claude,
1360 OperationFamily::GenerateContent,
1361 ProtocolKind::OpenAiResponse,
1362 ) => transform_response_json::<
1363 crate::claude::create_message::response::ClaudeCreateMessageResponse,
1364 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1365 >(&body),
1366 (
1368 OperationFamily::GenerateContent,
1369 ProtocolKind::Gemini,
1370 OperationFamily::GenerateContent,
1371 ProtocolKind::OpenAiResponse,
1372 ) => transform_response_json::<
1373 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1374 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1375 >(&body),
1376
1377 (
1383 OperationFamily::CountToken,
1384 ProtocolKind::Gemini,
1385 OperationFamily::CountToken,
1386 ProtocolKind::Claude,
1387 ) => transform_response_json::<
1388 crate::gemini::count_tokens::response::GeminiCountTokensResponse,
1389 crate::claude::count_tokens::response::ClaudeCountTokensResponse,
1390 >(&body),
1391 (
1393 OperationFamily::CountToken,
1394 ProtocolKind::OpenAi,
1395 OperationFamily::CountToken,
1396 ProtocolKind::Claude,
1397 ) => transform_response_json::<
1398 crate::openai::count_tokens::response::OpenAiCountTokensResponse,
1399 crate::claude::count_tokens::response::ClaudeCountTokensResponse,
1400 >(&body),
1401
1402 (
1404 OperationFamily::CountToken,
1405 ProtocolKind::Claude,
1406 OperationFamily::CountToken,
1407 ProtocolKind::OpenAi,
1408 ) => transform_response_json::<
1409 crate::claude::count_tokens::response::ClaudeCountTokensResponse,
1410 crate::openai::count_tokens::response::OpenAiCountTokensResponse,
1411 >(&body),
1412 (
1414 OperationFamily::CountToken,
1415 ProtocolKind::Gemini,
1416 OperationFamily::CountToken,
1417 ProtocolKind::OpenAi,
1418 ) => transform_response_json::<
1419 crate::gemini::count_tokens::response::GeminiCountTokensResponse,
1420 crate::openai::count_tokens::response::OpenAiCountTokensResponse,
1421 >(&body),
1422
1423 (
1425 OperationFamily::CountToken,
1426 ProtocolKind::Claude,
1427 OperationFamily::CountToken,
1428 ProtocolKind::Gemini,
1429 ) => transform_response_json::<
1430 crate::claude::count_tokens::response::ClaudeCountTokensResponse,
1431 crate::gemini::count_tokens::response::GeminiCountTokensResponse,
1432 >(&body),
1433 (
1435 OperationFamily::CountToken,
1436 ProtocolKind::OpenAi,
1437 OperationFamily::CountToken,
1438 ProtocolKind::Gemini,
1439 ) => transform_response_json::<
1440 crate::openai::count_tokens::response::OpenAiCountTokensResponse,
1441 crate::gemini::count_tokens::response::GeminiCountTokensResponse,
1442 >(&body),
1443
1444 (
1450 OperationFamily::ModelGet,
1451 ProtocolKind::Gemini,
1452 OperationFamily::ModelGet,
1453 ProtocolKind::Claude,
1454 ) => transform_response_json::<
1455 crate::gemini::model_get::response::GeminiModelGetResponse,
1456 crate::claude::model_get::response::ClaudeModelGetResponse,
1457 >(&body),
1458 (
1460 OperationFamily::ModelGet,
1461 ProtocolKind::OpenAi,
1462 OperationFamily::ModelGet,
1463 ProtocolKind::Claude,
1464 ) => transform_response_json::<
1465 crate::openai::model_get::response::OpenAiModelGetResponse,
1466 crate::claude::model_get::response::ClaudeModelGetResponse,
1467 >(&body),
1468
1469 (
1471 OperationFamily::ModelGet,
1472 ProtocolKind::Claude,
1473 OperationFamily::ModelGet,
1474 ProtocolKind::OpenAi,
1475 ) => transform_response_json::<
1476 crate::claude::model_get::response::ClaudeModelGetResponse,
1477 crate::openai::model_get::response::OpenAiModelGetResponse,
1478 >(&body),
1479 (
1481 OperationFamily::ModelGet,
1482 ProtocolKind::Gemini,
1483 OperationFamily::ModelGet,
1484 ProtocolKind::OpenAi,
1485 ) => transform_response_json::<
1486 crate::gemini::model_get::response::GeminiModelGetResponse,
1487 crate::openai::model_get::response::OpenAiModelGetResponse,
1488 >(&body),
1489
1490 (
1492 OperationFamily::ModelGet,
1493 ProtocolKind::Claude,
1494 OperationFamily::ModelGet,
1495 ProtocolKind::Gemini,
1496 ) => transform_response_json::<
1497 crate::claude::model_get::response::ClaudeModelGetResponse,
1498 crate::gemini::model_get::response::GeminiModelGetResponse,
1499 >(&body),
1500 (
1502 OperationFamily::ModelGet,
1503 ProtocolKind::OpenAi,
1504 OperationFamily::ModelGet,
1505 ProtocolKind::Gemini,
1506 ) => transform_response_json::<
1507 crate::openai::model_get::response::OpenAiModelGetResponse,
1508 crate::gemini::model_get::response::GeminiModelGetResponse,
1509 >(&body),
1510
1511 (
1517 OperationFamily::ModelList,
1518 ProtocolKind::Gemini,
1519 OperationFamily::ModelList,
1520 ProtocolKind::Claude,
1521 ) => transform_response_json::<
1522 crate::gemini::model_list::response::GeminiModelListResponse,
1523 crate::claude::model_list::response::ClaudeModelListResponse,
1524 >(&body),
1525 (
1527 OperationFamily::ModelList,
1528 ProtocolKind::OpenAi,
1529 OperationFamily::ModelList,
1530 ProtocolKind::Claude,
1531 ) => transform_response_json::<
1532 crate::openai::model_list::response::OpenAiModelListResponse,
1533 crate::claude::model_list::response::ClaudeModelListResponse,
1534 >(&body),
1535
1536 (
1538 OperationFamily::ModelList,
1539 ProtocolKind::Claude,
1540 OperationFamily::ModelList,
1541 ProtocolKind::OpenAi,
1542 ) => transform_response_json::<
1543 crate::claude::model_list::response::ClaudeModelListResponse,
1544 crate::openai::model_list::response::OpenAiModelListResponse,
1545 >(&body),
1546 (
1548 OperationFamily::ModelList,
1549 ProtocolKind::Gemini,
1550 OperationFamily::ModelList,
1551 ProtocolKind::OpenAi,
1552 ) => transform_response_json::<
1553 crate::gemini::model_list::response::GeminiModelListResponse,
1554 crate::openai::model_list::response::OpenAiModelListResponse,
1555 >(&body),
1556
1557 (
1559 OperationFamily::ModelList,
1560 ProtocolKind::Claude,
1561 OperationFamily::ModelList,
1562 ProtocolKind::Gemini,
1563 ) => transform_response_json::<
1564 crate::claude::model_list::response::ClaudeModelListResponse,
1565 crate::gemini::model_list::response::GeminiModelListResponse,
1566 >(&body),
1567 (
1569 OperationFamily::ModelList,
1570 ProtocolKind::OpenAi,
1571 OperationFamily::ModelList,
1572 ProtocolKind::Gemini,
1573 ) => transform_response_json::<
1574 crate::openai::model_list::response::OpenAiModelListResponse,
1575 crate::gemini::model_list::response::GeminiModelListResponse,
1576 >(&body),
1577
1578 (
1582 OperationFamily::Embedding,
1583 ProtocolKind::Gemini,
1584 OperationFamily::Embedding,
1585 ProtocolKind::OpenAi,
1586 ) => transform_response_json::<
1587 crate::gemini::embeddings::response::GeminiEmbedContentResponse,
1588 crate::openai::embeddings::response::OpenAiEmbeddingsResponse,
1589 >(&body),
1590 (
1591 OperationFamily::Embedding,
1592 ProtocolKind::OpenAi,
1593 OperationFamily::Embedding,
1594 ProtocolKind::Gemini,
1595 ) => transform_response_json::<
1596 crate::openai::embeddings::response::OpenAiEmbeddingsResponse,
1597 crate::gemini::embeddings::response::GeminiEmbedContentResponse,
1598 >(&body),
1599
1600 (
1604 OperationFamily::GenerateContent,
1605 ProtocolKind::Gemini,
1606 OperationFamily::CreateImage,
1607 ProtocolKind::OpenAi,
1608 )
1609 | (
1610 OperationFamily::StreamGenerateContent,
1611 ProtocolKind::Gemini,
1612 OperationFamily::CreateImage,
1613 ProtocolKind::OpenAi,
1614 ) => transform_response_json::<
1615 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1616 crate::openai::create_image::response::OpenAiCreateImageResponse,
1617 >(&body),
1618
1619 (
1620 OperationFamily::StreamGenerateContent,
1621 ProtocolKind::OpenAiResponse,
1622 OperationFamily::CreateImage,
1623 ProtocolKind::OpenAi,
1624 )
1625 | (
1626 OperationFamily::GenerateContent,
1627 ProtocolKind::OpenAiResponse,
1628 OperationFamily::CreateImage,
1629 ProtocolKind::OpenAi,
1630 ) => transform_response_json::<
1631 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1632 crate::openai::create_image::response::OpenAiCreateImageResponse,
1633 >(&body),
1634
1635 (
1639 OperationFamily::GenerateContent,
1640 ProtocolKind::Gemini,
1641 OperationFamily::CreateImageEdit,
1642 ProtocolKind::OpenAi,
1643 )
1644 | (
1645 OperationFamily::StreamGenerateContent,
1646 ProtocolKind::Gemini,
1647 OperationFamily::CreateImageEdit,
1648 ProtocolKind::OpenAi,
1649 ) => transform_response_json::<
1650 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1651 crate::openai::create_image_edit::response::OpenAiCreateImageEditResponse,
1652 >(&body),
1653
1654 (
1655 OperationFamily::StreamGenerateContent,
1656 ProtocolKind::OpenAiResponse,
1657 OperationFamily::CreateImageEdit,
1658 ProtocolKind::OpenAi,
1659 )
1660 | (
1661 OperationFamily::GenerateContent,
1662 ProtocolKind::OpenAiResponse,
1663 OperationFamily::CreateImageEdit,
1664 ProtocolKind::OpenAi,
1665 ) => transform_response_json::<
1666 crate::openai::create_response::response::OpenAiCreateResponseResponse,
1667 crate::openai::create_image_edit::response::OpenAiCreateImageEditResponse,
1668 >(&body),
1669
1670 (
1674 OperationFamily::GenerateContent,
1675 ProtocolKind::Claude,
1676 OperationFamily::Compact,
1677 ProtocolKind::OpenAi,
1678 ) => transform_response_json::<
1679 crate::claude::create_message::response::ClaudeCreateMessageResponse,
1680 crate::openai::compact_response::response::OpenAiCompactResponse,
1681 >(&body),
1682 (
1683 OperationFamily::GenerateContent,
1684 ProtocolKind::Gemini,
1685 OperationFamily::Compact,
1686 ProtocolKind::OpenAi,
1687 ) => transform_response_json::<
1688 crate::gemini::generate_content::response::GeminiGenerateContentResponse,
1689 crate::openai::compact_response::response::OpenAiCompactResponse,
1690 >(&body),
1691
1692 _ => Err(TransformError::new(format!(
1693 "no response transform from upstream ({}, {}) to client ({}, {})",
1694 dst_operation, dst_protocol, src_operation, src_protocol
1695 ))),
1696 }
1697}
1698
1699fn transform_json<Src, Dst>(body: &[u8]) -> Result<Vec<u8>, TransformError>
1706where
1707 Src: serde::de::DeserializeOwned,
1708 Dst: TryFrom<Src> + serde::Serialize,
1709 Dst::Error: std::fmt::Display,
1710{
1711 let src: Src = serde_json::from_slice(body)
1712 .map_err(|e| TransformError::new(format!("request deserialize: {e}")))?;
1713 let dst = Dst::try_from(src).map_err(|e| TransformError::new(format!("transform: {e}")))?;
1714 serde_json::to_vec(&dst).map_err(|e| TransformError::new(format!("response serialize: {e}")))
1715}
1716
1717fn transform_response_json<Src, Dst>(body: &[u8]) -> Result<Vec<u8>, TransformError>
1730where
1731 Src: BodyEnvelope,
1732 Dst: BodyEnvelope + TryFrom<Src>,
1733 Dst::Error: std::fmt::Display,
1734{
1735 let src = Src::from_body_bytes(body)?;
1736 let dst = Dst::try_from(src).map_err(|e| TransformError::new(format!("transform: {e}")))?;
1737 dst.into_body_bytes()
1738}
1739
1740fn transform_request_descriptor<Src, Dst>(
1741 body: &[u8],
1742 model: Option<&str>,
1743) -> Result<Vec<u8>, TransformError>
1744where
1745 Src: RequestDescriptor,
1746 Dst: RequestDescriptor + TryFrom<Src>,
1747 Dst::Error: std::fmt::Display,
1748{
1749 let src_body: Src::Body = serde_json::from_slice(body)
1750 .map_err(|e| TransformError::new(format!("request deserialize: {}", e)))?;
1751 let src = Src::from_body(src_body).with_model(model);
1752 let dst = Dst::try_from(src).map_err(|e| TransformError::new(format!("transform: {}", e)))?;
1753
1754 serde_json::to_vec(&dst.into_body())
1755 .map_err(|e| TransformError::new(format!("response serialize: {}", e)))
1756}
1757
1758fn transform_request_descriptor_ref<Src, Dst>(
1759 body: &[u8],
1760 model: Option<&str>,
1761) -> Result<Vec<u8>, TransformError>
1762where
1763 Src: RequestDescriptor,
1764 for<'a> Dst: RequestDescriptor + TryFrom<&'a Src>,
1765 for<'a> <Dst as TryFrom<&'a Src>>::Error: std::fmt::Display,
1766{
1767 let src_body: Src::Body = serde_json::from_slice(body)
1768 .map_err(|e| TransformError::new(format!("request deserialize: {}", e)))?;
1769 let src = Src::from_body(src_body).with_model(model);
1770 let dst = Dst::try_from(&src).map_err(|e| TransformError::new(format!("transform: {}", e)))?;
1771
1772 serde_json::to_vec(&dst.into_body())
1773 .map_err(|e| TransformError::new(format!("response serialize: {}", e)))
1774}
1775
1776pub type StreamChunkNormalizer = Arc<dyn Fn(Vec<u8>) -> Vec<u8> + Send + Sync>;
1777
1778pub fn convert_error_body_or_raw(
1806 src_operation: OperationFamily,
1807 src_protocol: ProtocolKind,
1808 dst_operation: OperationFamily,
1809 dst_protocol: ProtocolKind,
1810 body: Vec<u8>,
1811) -> Vec<u8> {
1812 let op_for_error = |op: OperationFamily| match op {
1813 OperationFamily::StreamGenerateContent => OperationFamily::GenerateContent,
1814 other => other,
1815 };
1816 let src_op = op_for_error(src_operation);
1817 let dst_op = op_for_error(dst_operation);
1818
1819 if src_op == dst_op && src_protocol == dst_protocol {
1822 return body;
1823 }
1824
1825 match transform_response(src_op, src_protocol, dst_op, dst_protocol, body.clone()) {
1826 Ok(converted) => converted,
1827 Err(err) => {
1828 tracing::debug!(
1829 error = %err,
1830 src_op = %src_operation,
1831 src_proto = %src_protocol,
1832 dst_op = %dst_operation,
1833 dst_proto = %dst_protocol,
1834 body_len = body.len(),
1835 "error body did not match declared schema; forwarding raw upstream bytes"
1836 );
1837 body
1838 }
1839 }
1840}
1841
1842pub struct StreamResponseTransformer {
1843 decoder: StreamChunkDecoder,
1844 inner: Box<dyn StreamChunkTransform>,
1845 normalizer: Option<StreamChunkNormalizer>,
1846}
1847
1848impl StreamResponseTransformer {
1849 pub fn push_chunk(&mut self, chunk: &[u8]) -> Result<Vec<u8>, TransformError> {
1850 let mut json_chunks = Vec::new();
1851 self.decoder.push_chunk(chunk, &mut json_chunks);
1852 self.process_json_chunks(json_chunks)
1853 }
1854
1855 pub fn finish(&mut self) -> Result<Vec<u8>, TransformError> {
1856 let mut json_chunks = Vec::new();
1857 self.decoder.finish(&mut json_chunks);
1858 let mut out = self.process_json_chunks(json_chunks)?;
1859 self.inner.finish(&mut out)?;
1860 Ok(out)
1861 }
1862
1863 fn process_json_chunks(
1864 &mut self,
1865 json_chunks: Vec<Vec<u8>>,
1866 ) -> Result<Vec<u8>, TransformError> {
1867 let mut out = Vec::new();
1868 for chunk in json_chunks {
1869 let chunk = if let Some(normalizer) = &self.normalizer {
1870 normalizer(chunk)
1871 } else {
1872 chunk
1873 };
1874 if chunk.is_empty() {
1875 continue;
1876 }
1877 self.inner.on_json_chunk(&chunk, &mut out)?;
1878 }
1879 Ok(out)
1880 }
1881}
1882
1883trait StreamChunkTransform: Send {
1884 fn on_json_chunk(&mut self, chunk: &[u8], out: &mut Vec<u8>) -> Result<(), TransformError>;
1885 fn finish(&mut self, out: &mut Vec<u8>) -> Result<(), TransformError>;
1886}
1887
1888trait EventConverter<Input, Output>: Send {
1889 fn on_input(&mut self, input: Input, out: &mut Vec<Output>) -> Result<(), TransformError>;
1890 fn finish(&mut self, out: &mut Vec<Output>) -> Result<(), TransformError>;
1891}
1892
1893struct TypedStreamTransform<Input, Output, Converter> {
1894 converter: Converter,
1895 encoder: StreamChunkEncoder,
1896 _marker: PhantomData<(Input, Output)>,
1897}
1898
1899impl<Input, Output, Converter> StreamChunkTransform
1900 for TypedStreamTransform<Input, Output, Converter>
1901where
1902 Input: DeserializeOwned + Send + 'static,
1903 Output: Serialize + Send + 'static,
1904 Converter: EventConverter<Input, Output> + Send + 'static,
1905{
1906 fn on_json_chunk(&mut self, chunk: &[u8], out: &mut Vec<u8>) -> Result<(), TransformError> {
1907 let input: Input = serde_json::from_slice(chunk)
1908 .map_err(|e| TransformError::new(format!("stream chunk deserialize failed: {e}")))?;
1909 let mut events = Vec::new();
1910 self.converter.on_input(input, &mut events)?;
1911 self.encoder.encode_events(&events, out)
1912 }
1913
1914 fn finish(&mut self, out: &mut Vec<u8>) -> Result<(), TransformError> {
1915 let mut events = Vec::new();
1916 self.converter.finish(&mut events)?;
1917 self.encoder.encode_events(&events, out)?;
1918 self.encoder.finish(out);
1919 Ok(())
1920 }
1921}
1922
1923enum StreamChunkDecoder {
1924 Sse(crate::stream::SseToNdjsonRewriter),
1925 Ndjson(Vec<u8>),
1926}
1927
1928impl StreamChunkDecoder {
1929 fn from_protocol(protocol: ProtocolKind) -> Result<Self, TransformError> {
1930 match protocol {
1931 ProtocolKind::Claude
1932 | ProtocolKind::OpenAiChatCompletion
1933 | ProtocolKind::OpenAiResponse
1934 | ProtocolKind::Gemini => Ok(Self::Sse(crate::stream::SseToNdjsonRewriter::default())),
1935 ProtocolKind::GeminiNDJson => Ok(Self::Ndjson(Vec::new())),
1936 _ => Err(TransformError::new(format!(
1937 "unsupported stream input protocol: {protocol}"
1938 ))),
1939 }
1940 }
1941
1942 fn push_chunk(&mut self, chunk: &[u8], out: &mut Vec<Vec<u8>>) {
1943 match self {
1944 Self::Sse(rewriter) => {
1945 let converted = rewriter.push_chunk(chunk);
1946 split_json_lines(&converted, out);
1947 }
1948 Self::Ndjson(pending) => {
1949 pending.extend_from_slice(chunk);
1950 drain_json_lines(pending, out);
1951 }
1952 }
1953 }
1954
1955 fn finish(&mut self, out: &mut Vec<Vec<u8>>) {
1956 match self {
1957 Self::Sse(rewriter) => {
1958 let converted = rewriter.finish();
1959 split_json_lines(&converted, out);
1960 }
1961 Self::Ndjson(pending) => {
1962 if pending.is_empty() {
1963 return;
1964 }
1965 let mut line = std::mem::take(pending);
1966 if line.last().copied() == Some(b'\r') {
1967 line.pop();
1968 }
1969 if !line.is_empty() {
1970 out.push(line);
1971 }
1972 }
1973 }
1974 }
1975}
1976
1977enum StreamChunkEncoder {
1978 Sse { append_done_marker: bool },
1979 Ndjson,
1980}
1981
1982impl StreamChunkEncoder {
1983 fn from_protocol(protocol: ProtocolKind) -> Result<Self, TransformError> {
1984 match protocol {
1985 ProtocolKind::Claude | ProtocolKind::OpenAiResponse | ProtocolKind::Gemini => {
1986 Ok(Self::Sse {
1987 append_done_marker: false,
1988 })
1989 }
1990 ProtocolKind::OpenAiChatCompletion => Ok(Self::Sse {
1991 append_done_marker: true,
1992 }),
1993 ProtocolKind::GeminiNDJson => Ok(Self::Ndjson),
1994 _ => Err(TransformError::new(format!(
1995 "unsupported stream output protocol: {protocol}"
1996 ))),
1997 }
1998 }
1999
2000 fn encode_events<T: Serialize>(
2001 &self,
2002 events: &[T],
2003 out: &mut Vec<u8>,
2004 ) -> Result<(), TransformError> {
2005 for event in events {
2006 let json = serde_json::to_vec(event)
2007 .map_err(|e| TransformError::new(format!("stream chunk serialize failed: {e}")))?;
2008 match self {
2009 Self::Sse { .. } => {
2010 out.extend_from_slice(b"data: ");
2011 out.extend_from_slice(&json);
2012 out.extend_from_slice(b"\n\n");
2013 }
2014 Self::Ndjson => {
2015 out.extend_from_slice(&json);
2016 out.push(b'\n');
2017 }
2018 }
2019 }
2020 Ok(())
2021 }
2022
2023 fn finish(&self, out: &mut Vec<u8>) {
2024 if let Self::Sse {
2025 append_done_marker: true,
2026 } = self
2027 {
2028 out.extend_from_slice(b"data: [DONE]\n\n");
2029 }
2030 }
2031}
2032
2033use crate::stream::{drain_lines as drain_json_lines, split_lines as split_json_lines};
2034
2035struct IdentityConverter<T>(PhantomData<T>);
2036
2037impl<T> Default for IdentityConverter<T> {
2038 fn default() -> Self {
2039 Self(PhantomData)
2040 }
2041}
2042
2043impl<T: Send> EventConverter<T, T> for IdentityConverter<T> {
2044 fn on_input(&mut self, input: T, out: &mut Vec<T>) -> Result<(), TransformError> {
2045 out.push(input);
2046 Ok(())
2047 }
2048
2049 fn finish(&mut self, _out: &mut Vec<T>) -> Result<(), TransformError> {
2050 Ok(())
2051 }
2052}
2053
2054#[derive(Default)]
2055struct OpenAiChatToClaudeConverter(
2056 crate::transform::claude::stream_generate_content::openai_chat_completions::response::OpenAiChatCompletionsToClaudeStream,
2057);
2058
2059impl
2060 EventConverter<
2061 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2062 crate::claude::create_message::stream::ClaudeStreamEvent,
2063 > for OpenAiChatToClaudeConverter
2064{
2065 fn on_input(
2066 &mut self,
2067 input: crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2068 out: &mut Vec<crate::claude::create_message::stream::ClaudeStreamEvent>,
2069 ) -> Result<(), TransformError> {
2070 self.0.on_chunk(input, out);
2071 Ok(())
2072 }
2073
2074 fn finish(
2075 &mut self,
2076 out: &mut Vec<crate::claude::create_message::stream::ClaudeStreamEvent>,
2077 ) -> Result<(), TransformError> {
2078 self.0.finish(out);
2079 Ok(())
2080 }
2081}
2082
2083#[derive(Default)]
2084struct GeminiToClaudeConverter(
2085 crate::transform::claude::stream_generate_content::gemini::response::GeminiToClaudeStream,
2086);
2087
2088impl
2089 EventConverter<
2090 crate::gemini::generate_content::response::ResponseBody,
2091 crate::claude::create_message::stream::ClaudeStreamEvent,
2092 > for GeminiToClaudeConverter
2093{
2094 fn on_input(
2095 &mut self,
2096 input: crate::gemini::generate_content::response::ResponseBody,
2097 out: &mut Vec<crate::claude::create_message::stream::ClaudeStreamEvent>,
2098 ) -> Result<(), TransformError> {
2099 self.0.on_chunk(input, out);
2100 Ok(())
2101 }
2102
2103 fn finish(
2104 &mut self,
2105 out: &mut Vec<crate::claude::create_message::stream::ClaudeStreamEvent>,
2106 ) -> Result<(), TransformError> {
2107 self.0.finish(out);
2108 Ok(())
2109 }
2110}
2111
2112#[derive(Default)]
2113struct OpenAiResponseToClaudeConverter(
2114 crate::transform::claude::stream_generate_content::openai_response::response::OpenAiResponseToClaudeStream,
2115);
2116
2117impl
2118 EventConverter<
2119 crate::openai::create_response::stream::ResponseStreamEvent,
2120 crate::claude::create_message::stream::ClaudeStreamEvent,
2121 > for OpenAiResponseToClaudeConverter
2122{
2123 fn on_input(
2124 &mut self,
2125 input: crate::openai::create_response::stream::ResponseStreamEvent,
2126 out: &mut Vec<crate::claude::create_message::stream::ClaudeStreamEvent>,
2127 ) -> Result<(), TransformError> {
2128 self.0.on_stream_event(input, out);
2129 Ok(())
2130 }
2131
2132 fn finish(
2133 &mut self,
2134 out: &mut Vec<crate::claude::create_message::stream::ClaudeStreamEvent>,
2135 ) -> Result<(), TransformError> {
2136 self.0.finish(out);
2137 Ok(())
2138 }
2139}
2140
2141#[derive(Default)]
2142struct ClaudeToOpenAiChatConverter(
2143 crate::transform::openai::stream_generate_content::openai_chat_completions::claude::response::ClaudeToOpenAiChatCompletionsStream,
2144);
2145
2146impl
2147 EventConverter<
2148 crate::claude::create_message::stream::ClaudeStreamEvent,
2149 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2150 > for ClaudeToOpenAiChatConverter
2151{
2152 fn on_input(
2153 &mut self,
2154 input: crate::claude::create_message::stream::ClaudeStreamEvent,
2155 out: &mut Vec<crate::openai::create_chat_completions::stream::ChatCompletionChunk>,
2156 ) -> Result<(), TransformError> {
2157 self.0
2158 .on_event(input, out)
2159 .map_err(|e| TransformError::new(format!("stream transform failed: {e}")))
2160 }
2161
2162 fn finish(
2163 &mut self,
2164 out: &mut Vec<crate::openai::create_chat_completions::stream::ChatCompletionChunk>,
2165 ) -> Result<(), TransformError> {
2166 self.0.finish(out);
2167 Ok(())
2168 }
2169}
2170
2171#[derive(Default)]
2172struct GeminiToOpenAiChatConverter(
2173 crate::transform::openai::stream_generate_content::openai_chat_completions::gemini::response::GeminiToOpenAiChatCompletionsStream,
2174);
2175
2176impl
2177 EventConverter<
2178 crate::gemini::generate_content::response::ResponseBody,
2179 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2180 > for GeminiToOpenAiChatConverter
2181{
2182 fn on_input(
2183 &mut self,
2184 input: crate::gemini::generate_content::response::ResponseBody,
2185 out: &mut Vec<crate::openai::create_chat_completions::stream::ChatCompletionChunk>,
2186 ) -> Result<(), TransformError> {
2187 self.0.on_chunk(input, out);
2188 Ok(())
2189 }
2190
2191 fn finish(
2192 &mut self,
2193 out: &mut Vec<crate::openai::create_chat_completions::stream::ChatCompletionChunk>,
2194 ) -> Result<(), TransformError> {
2195 self.0.finish(out);
2196 Ok(())
2197 }
2198}
2199
2200#[derive(Default)]
2201struct ClaudeToOpenAiResponseConverter(
2202 crate::transform::openai::stream_generate_content::openai_response::claude::response::ClaudeToOpenAiResponseStream,
2203);
2204
2205impl
2206 EventConverter<
2207 crate::claude::create_message::stream::ClaudeStreamEvent,
2208 crate::openai::create_response::stream::ResponseStreamEvent,
2209 > for ClaudeToOpenAiResponseConverter
2210{
2211 fn on_input(
2212 &mut self,
2213 input: crate::claude::create_message::stream::ClaudeStreamEvent,
2214 out: &mut Vec<crate::openai::create_response::stream::ResponseStreamEvent>,
2215 ) -> Result<(), TransformError> {
2216 self.0
2217 .on_event(input, out)
2218 .map_err(|e| TransformError::new(format!("stream transform failed: {e}")))
2219 }
2220
2221 fn finish(
2222 &mut self,
2223 out: &mut Vec<crate::openai::create_response::stream::ResponseStreamEvent>,
2224 ) -> Result<(), TransformError> {
2225 self.0.finish(out);
2226 Ok(())
2227 }
2228}
2229
2230#[derive(Default)]
2231struct GeminiToOpenAiResponseConverter(
2232 crate::transform::openai::stream_generate_content::openai_response::gemini::response::GeminiToOpenAiResponseStream,
2233);
2234
2235impl
2236 EventConverter<
2237 crate::gemini::generate_content::response::ResponseBody,
2238 crate::openai::create_response::stream::ResponseStreamEvent,
2239 > for GeminiToOpenAiResponseConverter
2240{
2241 fn on_input(
2242 &mut self,
2243 input: crate::gemini::generate_content::response::ResponseBody,
2244 out: &mut Vec<crate::openai::create_response::stream::ResponseStreamEvent>,
2245 ) -> Result<(), TransformError> {
2246 self.0.on_chunk(input, out);
2247 Ok(())
2248 }
2249
2250 fn finish(
2251 &mut self,
2252 out: &mut Vec<crate::openai::create_response::stream::ResponseStreamEvent>,
2253 ) -> Result<(), TransformError> {
2254 self.0.finish(out);
2255 Ok(())
2256 }
2257}
2258
2259#[derive(Default)]
2260struct ClaudeToGeminiConverter(
2261 crate::transform::gemini::stream_generate_content::claude::response::ClaudeToGeminiStream,
2262);
2263
2264impl
2265 EventConverter<
2266 crate::claude::create_message::stream::ClaudeStreamEvent,
2267 crate::gemini::generate_content::response::ResponseBody,
2268 > for ClaudeToGeminiConverter
2269{
2270 fn on_input(
2271 &mut self,
2272 input: crate::claude::create_message::stream::ClaudeStreamEvent,
2273 out: &mut Vec<crate::gemini::generate_content::response::ResponseBody>,
2274 ) -> Result<(), TransformError> {
2275 self.0
2276 .on_event(input, out)
2277 .map_err(|e| TransformError::new(format!("stream transform failed: {e}")))
2278 }
2279
2280 fn finish(
2281 &mut self,
2282 _out: &mut Vec<crate::gemini::generate_content::response::ResponseBody>,
2283 ) -> Result<(), TransformError> {
2284 Ok(())
2285 }
2286}
2287
2288#[derive(Default)]
2289struct OpenAiChatToGeminiConverter(
2290 crate::transform::gemini::stream_generate_content::openai_chat_completions::response::OpenAiChatCompletionsToGeminiStream,
2291);
2292
2293impl
2294 EventConverter<
2295 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2296 crate::gemini::generate_content::response::ResponseBody,
2297 > for OpenAiChatToGeminiConverter
2298{
2299 fn on_input(
2300 &mut self,
2301 input: crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2302 out: &mut Vec<crate::gemini::generate_content::response::ResponseBody>,
2303 ) -> Result<(), TransformError> {
2304 self.0.on_chunk(input, out);
2305 Ok(())
2306 }
2307
2308 fn finish(
2309 &mut self,
2310 out: &mut Vec<crate::gemini::generate_content::response::ResponseBody>,
2311 ) -> Result<(), TransformError> {
2312 self.0.finish(out);
2313 Ok(())
2314 }
2315}
2316
2317#[derive(Default)]
2318struct OpenAiResponseToGeminiConverter(
2319 crate::transform::gemini::stream_generate_content::openai_response::response::OpenAiResponseToGeminiStream,
2320);
2321
2322impl
2323 EventConverter<
2324 crate::openai::create_response::stream::ResponseStreamEvent,
2325 crate::gemini::generate_content::response::ResponseBody,
2326 > for OpenAiResponseToGeminiConverter
2327{
2328 fn on_input(
2329 &mut self,
2330 input: crate::openai::create_response::stream::ResponseStreamEvent,
2331 out: &mut Vec<crate::gemini::generate_content::response::ResponseBody>,
2332 ) -> Result<(), TransformError> {
2333 self.0.on_stream_event(input, out);
2334 Ok(())
2335 }
2336
2337 fn finish(
2338 &mut self,
2339 out: &mut Vec<crate::gemini::generate_content::response::ResponseBody>,
2340 ) -> Result<(), TransformError> {
2341 self.0.finish(out);
2342 Ok(())
2343 }
2344}
2345
2346#[derive(Default)]
2353struct OpenAiResponseToOpenAiChatCompletionsConverter(
2354 crate::transform::openai::stream_generate_content::openai_chat_completions::openai_response::response::OpenAiResponseToOpenAiChatCompletionsStream,
2355);
2356
2357impl
2358 EventConverter<
2359 crate::openai::create_response::stream::ResponseStreamEvent,
2360 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2361 > for OpenAiResponseToOpenAiChatCompletionsConverter
2362{
2363 fn on_input(
2364 &mut self,
2365 input: crate::openai::create_response::stream::ResponseStreamEvent,
2366 out: &mut Vec<crate::openai::create_chat_completions::stream::ChatCompletionChunk>,
2367 ) -> Result<(), TransformError> {
2368 self.0
2369 .on_stream_event(input, out)
2370 .map_err(|e| TransformError::new(format!("stream convert: {e}")))
2371 }
2372
2373 fn finish(
2374 &mut self,
2375 out: &mut Vec<crate::openai::create_chat_completions::stream::ChatCompletionChunk>,
2376 ) -> Result<(), TransformError> {
2377 self.0
2378 .finish(out)
2379 .map_err(|e| TransformError::new(format!("stream finish: {e}")))
2380 }
2381}
2382
2383#[derive(Default)]
2389struct OpenAiChatCompletionsToOpenAiResponseConverter(
2390 crate::transform::openai::stream_generate_content::openai_response::openai_chat_completions::response::OpenAiChatCompletionsToOpenAiResponseStream,
2391);
2392
2393impl
2394 EventConverter<
2395 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2396 crate::openai::create_response::stream::ResponseStreamEvent,
2397 > for OpenAiChatCompletionsToOpenAiResponseConverter
2398{
2399 fn on_input(
2400 &mut self,
2401 input: crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2402 out: &mut Vec<crate::openai::create_response::stream::ResponseStreamEvent>,
2403 ) -> Result<(), TransformError> {
2404 self.0
2405 .on_stream_event(input, out)
2406 .map_err(|e| TransformError::new(format!("stream convert: {e}")))
2407 }
2408
2409 fn finish(
2410 &mut self,
2411 out: &mut Vec<crate::openai::create_response::stream::ResponseStreamEvent>,
2412 ) -> Result<(), TransformError> {
2413 self.0
2414 .finish(out)
2415 .map_err(|e| TransformError::new(format!("stream finish: {e}")))
2416 }
2417}
2418
2419#[derive(Default)]
2420struct ResponseStreamToImageStreamConverter(
2421 crate::transform::openai::create_image::openai_response::stream::ResponseStreamToImageStream,
2422);
2423
2424impl
2425 EventConverter<
2426 crate::openai::create_response::stream::ResponseStreamEvent,
2427 crate::openai::create_image::stream::ImageGenerationStreamEvent,
2428 > for ResponseStreamToImageStreamConverter
2429{
2430 fn on_input(
2431 &mut self,
2432 input: crate::openai::create_response::stream::ResponseStreamEvent,
2433 out: &mut Vec<crate::openai::create_image::stream::ImageGenerationStreamEvent>,
2434 ) -> Result<(), TransformError> {
2435 self.0.on_event(input, out);
2436 Ok(())
2437 }
2438
2439 fn finish(
2440 &mut self,
2441 out: &mut Vec<crate::openai::create_image::stream::ImageGenerationStreamEvent>,
2442 ) -> Result<(), TransformError> {
2443 self.0.finish(out);
2444 Ok(())
2445 }
2446}
2447
2448#[derive(Default)]
2449struct GeminiToImageStreamConverter {
2450 partial_count: u32,
2451}
2452
2453impl
2454 EventConverter<
2455 crate::gemini::generate_content::response::ResponseBody,
2456 crate::openai::create_image::stream::ImageGenerationStreamEvent,
2457 > for GeminiToImageStreamConverter
2458{
2459 fn on_input(
2460 &mut self,
2461 input: crate::gemini::generate_content::response::ResponseBody,
2462 out: &mut Vec<crate::openai::create_image::stream::ImageGenerationStreamEvent>,
2463 ) -> Result<(), TransformError> {
2464 use crate::openai::create_image::stream::ImageGenerationStreamEvent;
2465 use crate::transform::openai::create_image::gemini::utils::{
2466 best_effort_openai_image_usage_from_gemini, gemini_inline_image_outputs_from_response,
2467 };
2468
2469 let is_finished = input
2470 .candidates
2471 .as_ref()
2472 .and_then(|cs| cs.first())
2473 .and_then(|c| c.finish_reason.as_ref())
2474 .is_some();
2475
2476 let images = gemini_inline_image_outputs_from_response(&input);
2477 let usage_metadata = input.usage_metadata.as_ref();
2478
2479 for img in &images {
2480 if is_finished {
2481 out.push(ImageGenerationStreamEvent::Completed {
2482 b64_json: img.b64_json.clone(),
2483 background: crate::openai::create_image::types::OpenAiImageBackground::Auto,
2484 created_at: 0,
2485 output_format: img.output_format.clone(),
2486 quality: crate::openai::create_image::types::OpenAiImageQuality::Auto,
2487 size: crate::openai::create_image::types::OpenAiImageSize::Auto,
2488 usage: best_effort_openai_image_usage_from_gemini(usage_metadata),
2489 });
2490 } else {
2491 let index = self.partial_count;
2492 self.partial_count += 1;
2493 out.push(ImageGenerationStreamEvent::PartialImage {
2494 b64_json: img.b64_json.clone(),
2495 background: crate::openai::create_image::types::OpenAiImageBackground::Auto,
2496 created_at: 0,
2497 output_format: img.output_format.clone(),
2498 partial_image_index: index,
2499 quality: crate::openai::create_image::types::OpenAiImageQuality::Auto,
2500 size: crate::openai::create_image::types::OpenAiImageSize::Auto,
2501 });
2502 }
2503 }
2504 Ok(())
2505 }
2506
2507 fn finish(
2508 &mut self,
2509 _out: &mut Vec<crate::openai::create_image::stream::ImageGenerationStreamEvent>,
2510 ) -> Result<(), TransformError> {
2511 Ok(())
2512 }
2513}
2514
2515fn build_stream_transform<Input, Output, Converter>(
2516 src_protocol: ProtocolKind,
2517 dst_protocol: ProtocolKind,
2518 converter: Converter,
2519 normalizer: Option<StreamChunkNormalizer>,
2520) -> Result<StreamResponseTransformer, TransformError>
2521where
2522 Input: DeserializeOwned + Send + 'static,
2523 Output: Serialize + Send + 'static,
2524 Converter: EventConverter<Input, Output> + Send + 'static,
2525{
2526 Ok(StreamResponseTransformer {
2527 decoder: StreamChunkDecoder::from_protocol(dst_protocol)?,
2528 inner: Box::new(TypedStreamTransform::<Input, Output, Converter> {
2529 converter,
2530 encoder: StreamChunkEncoder::from_protocol(src_protocol)?,
2531 _marker: PhantomData,
2532 }),
2533 normalizer,
2534 })
2535}
2536
2537pub fn create_stream_response_transformer(
2538 src_operation: OperationFamily,
2539 src_protocol: ProtocolKind,
2540 dst_operation: OperationFamily,
2541 dst_protocol: ProtocolKind,
2542 normalizer: Option<StreamChunkNormalizer>,
2543) -> Result<StreamResponseTransformer, TransformError> {
2544 let key = (src_operation, src_protocol, dst_operation, dst_protocol);
2545
2546 match key {
2547 (
2548 OperationFamily::StreamGenerateContent,
2549 ProtocolKind::Claude,
2550 OperationFamily::StreamGenerateContent,
2551 ProtocolKind::Claude,
2552 ) => build_stream_transform::<
2553 crate::claude::create_message::stream::ClaudeStreamEvent,
2554 crate::claude::create_message::stream::ClaudeStreamEvent,
2555 IdentityConverter<crate::claude::create_message::stream::ClaudeStreamEvent>,
2556 >(
2557 src_protocol,
2558 dst_protocol,
2559 IdentityConverter::default(),
2560 normalizer,
2561 ),
2562 (
2563 OperationFamily::StreamGenerateContent,
2564 ProtocolKind::OpenAiChatCompletion,
2565 OperationFamily::StreamGenerateContent,
2566 ProtocolKind::OpenAiChatCompletion,
2567 ) => build_stream_transform::<
2568 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2569 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2570 IdentityConverter<crate::openai::create_chat_completions::stream::ChatCompletionChunk>,
2571 >(
2572 src_protocol,
2573 dst_protocol,
2574 IdentityConverter::default(),
2575 normalizer,
2576 ),
2577 (
2578 OperationFamily::StreamGenerateContent,
2579 ProtocolKind::OpenAiResponse,
2580 OperationFamily::StreamGenerateContent,
2581 ProtocolKind::OpenAiResponse,
2582 ) => build_stream_transform::<
2583 crate::openai::create_response::stream::ResponseStreamEvent,
2584 crate::openai::create_response::stream::ResponseStreamEvent,
2585 IdentityConverter<crate::openai::create_response::stream::ResponseStreamEvent>,
2586 >(
2587 src_protocol,
2588 dst_protocol,
2589 IdentityConverter::default(),
2590 normalizer,
2591 ),
2592 (
2593 OperationFamily::StreamGenerateContent,
2594 ProtocolKind::Gemini,
2595 OperationFamily::StreamGenerateContent,
2596 ProtocolKind::Gemini,
2597 )
2598 | (
2599 OperationFamily::StreamGenerateContent,
2600 ProtocolKind::Gemini,
2601 OperationFamily::StreamGenerateContent,
2602 ProtocolKind::GeminiNDJson,
2603 )
2604 | (
2605 OperationFamily::StreamGenerateContent,
2606 ProtocolKind::GeminiNDJson,
2607 OperationFamily::StreamGenerateContent,
2608 ProtocolKind::Gemini,
2609 )
2610 | (
2611 OperationFamily::StreamGenerateContent,
2612 ProtocolKind::GeminiNDJson,
2613 OperationFamily::StreamGenerateContent,
2614 ProtocolKind::GeminiNDJson,
2615 ) => build_stream_transform::<
2616 crate::gemini::generate_content::response::ResponseBody,
2617 crate::gemini::generate_content::response::ResponseBody,
2618 IdentityConverter<crate::gemini::generate_content::response::ResponseBody>,
2619 >(
2620 src_protocol,
2621 dst_protocol,
2622 IdentityConverter::default(),
2623 normalizer,
2624 ),
2625
2626 (
2627 OperationFamily::StreamGenerateContent,
2628 ProtocolKind::Claude,
2629 OperationFamily::StreamGenerateContent,
2630 ProtocolKind::OpenAiChatCompletion,
2631 ) => build_stream_transform::<
2632 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2633 crate::claude::create_message::stream::ClaudeStreamEvent,
2634 OpenAiChatToClaudeConverter,
2635 >(
2636 src_protocol,
2637 dst_protocol,
2638 OpenAiChatToClaudeConverter::default(),
2639 normalizer,
2640 ),
2641 (
2642 OperationFamily::StreamGenerateContent,
2643 ProtocolKind::Claude,
2644 OperationFamily::StreamGenerateContent,
2645 ProtocolKind::OpenAiResponse,
2646 ) => build_stream_transform::<
2647 crate::openai::create_response::stream::ResponseStreamEvent,
2648 crate::claude::create_message::stream::ClaudeStreamEvent,
2649 OpenAiResponseToClaudeConverter,
2650 >(
2651 src_protocol,
2652 dst_protocol,
2653 OpenAiResponseToClaudeConverter::default(),
2654 normalizer,
2655 ),
2656 (
2657 OperationFamily::StreamGenerateContent,
2658 ProtocolKind::Claude,
2659 OperationFamily::StreamGenerateContent,
2660 ProtocolKind::Gemini,
2661 )
2662 | (
2663 OperationFamily::StreamGenerateContent,
2664 ProtocolKind::Claude,
2665 OperationFamily::StreamGenerateContent,
2666 ProtocolKind::GeminiNDJson,
2667 ) => build_stream_transform::<
2668 crate::gemini::generate_content::response::ResponseBody,
2669 crate::claude::create_message::stream::ClaudeStreamEvent,
2670 GeminiToClaudeConverter,
2671 >(
2672 src_protocol,
2673 dst_protocol,
2674 GeminiToClaudeConverter::default(),
2675 normalizer,
2676 ),
2677
2678 (
2679 OperationFamily::StreamGenerateContent,
2680 ProtocolKind::OpenAiChatCompletion,
2681 OperationFamily::StreamGenerateContent,
2682 ProtocolKind::Claude,
2683 ) => build_stream_transform::<
2684 crate::claude::create_message::stream::ClaudeStreamEvent,
2685 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2686 ClaudeToOpenAiChatConverter,
2687 >(
2688 src_protocol,
2689 dst_protocol,
2690 ClaudeToOpenAiChatConverter::default(),
2691 normalizer,
2692 ),
2693 (
2694 OperationFamily::StreamGenerateContent,
2695 ProtocolKind::OpenAiChatCompletion,
2696 OperationFamily::StreamGenerateContent,
2697 ProtocolKind::Gemini,
2698 )
2699 | (
2700 OperationFamily::StreamGenerateContent,
2701 ProtocolKind::OpenAiChatCompletion,
2702 OperationFamily::StreamGenerateContent,
2703 ProtocolKind::GeminiNDJson,
2704 ) => build_stream_transform::<
2705 crate::gemini::generate_content::response::ResponseBody,
2706 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2707 GeminiToOpenAiChatConverter,
2708 >(
2709 src_protocol,
2710 dst_protocol,
2711 GeminiToOpenAiChatConverter::default(),
2712 normalizer,
2713 ),
2714
2715 (
2721 OperationFamily::StreamGenerateContent,
2722 ProtocolKind::OpenAiChatCompletion,
2723 OperationFamily::StreamGenerateContent,
2724 ProtocolKind::OpenAiResponse,
2725 ) => build_stream_transform::<
2726 crate::openai::create_response::stream::ResponseStreamEvent,
2727 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2728 OpenAiResponseToOpenAiChatCompletionsConverter,
2729 >(
2730 src_protocol,
2731 dst_protocol,
2732 OpenAiResponseToOpenAiChatCompletionsConverter::default(),
2733 normalizer,
2734 ),
2735
2736 (
2744 OperationFamily::StreamGenerateContent,
2745 ProtocolKind::OpenAiResponse,
2746 OperationFamily::StreamGenerateContent,
2747 ProtocolKind::OpenAiChatCompletion,
2748 ) => build_stream_transform::<
2749 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2750 crate::openai::create_response::stream::ResponseStreamEvent,
2751 OpenAiChatCompletionsToOpenAiResponseConverter,
2752 >(
2753 src_protocol,
2754 dst_protocol,
2755 OpenAiChatCompletionsToOpenAiResponseConverter::default(),
2756 normalizer,
2757 ),
2758
2759 (
2760 OperationFamily::StreamGenerateContent,
2761 ProtocolKind::OpenAiResponse,
2762 OperationFamily::StreamGenerateContent,
2763 ProtocolKind::Claude,
2764 ) => build_stream_transform::<
2765 crate::claude::create_message::stream::ClaudeStreamEvent,
2766 crate::openai::create_response::stream::ResponseStreamEvent,
2767 ClaudeToOpenAiResponseConverter,
2768 >(
2769 src_protocol,
2770 dst_protocol,
2771 ClaudeToOpenAiResponseConverter::default(),
2772 normalizer,
2773 ),
2774 (
2775 OperationFamily::StreamGenerateContent,
2776 ProtocolKind::OpenAiResponse,
2777 OperationFamily::StreamGenerateContent,
2778 ProtocolKind::Gemini,
2779 )
2780 | (
2781 OperationFamily::StreamGenerateContent,
2782 ProtocolKind::OpenAiResponse,
2783 OperationFamily::StreamGenerateContent,
2784 ProtocolKind::GeminiNDJson,
2785 ) => build_stream_transform::<
2786 crate::gemini::generate_content::response::ResponseBody,
2787 crate::openai::create_response::stream::ResponseStreamEvent,
2788 GeminiToOpenAiResponseConverter,
2789 >(
2790 src_protocol,
2791 dst_protocol,
2792 GeminiToOpenAiResponseConverter::default(),
2793 normalizer,
2794 ),
2795
2796 (
2797 OperationFamily::StreamGenerateContent,
2798 ProtocolKind::Gemini,
2799 OperationFamily::StreamGenerateContent,
2800 ProtocolKind::Claude,
2801 )
2802 | (
2803 OperationFamily::StreamGenerateContent,
2804 ProtocolKind::GeminiNDJson,
2805 OperationFamily::StreamGenerateContent,
2806 ProtocolKind::Claude,
2807 ) => build_stream_transform::<
2808 crate::claude::create_message::stream::ClaudeStreamEvent,
2809 crate::gemini::generate_content::response::ResponseBody,
2810 ClaudeToGeminiConverter,
2811 >(
2812 src_protocol,
2813 dst_protocol,
2814 ClaudeToGeminiConverter::default(),
2815 normalizer,
2816 ),
2817 (
2818 OperationFamily::StreamGenerateContent,
2819 ProtocolKind::Gemini,
2820 OperationFamily::StreamGenerateContent,
2821 ProtocolKind::OpenAiChatCompletion,
2822 )
2823 | (
2824 OperationFamily::StreamGenerateContent,
2825 ProtocolKind::GeminiNDJson,
2826 OperationFamily::StreamGenerateContent,
2827 ProtocolKind::OpenAiChatCompletion,
2828 ) => build_stream_transform::<
2829 crate::openai::create_chat_completions::stream::ChatCompletionChunk,
2830 crate::gemini::generate_content::response::ResponseBody,
2831 OpenAiChatToGeminiConverter,
2832 >(
2833 src_protocol,
2834 dst_protocol,
2835 OpenAiChatToGeminiConverter::default(),
2836 normalizer,
2837 ),
2838 (
2839 OperationFamily::StreamGenerateContent,
2840 ProtocolKind::Gemini,
2841 OperationFamily::StreamGenerateContent,
2842 ProtocolKind::OpenAiResponse,
2843 )
2844 | (
2845 OperationFamily::StreamGenerateContent,
2846 ProtocolKind::GeminiNDJson,
2847 OperationFamily::StreamGenerateContent,
2848 ProtocolKind::OpenAiResponse,
2849 ) => build_stream_transform::<
2850 crate::openai::create_response::stream::ResponseStreamEvent,
2851 crate::gemini::generate_content::response::ResponseBody,
2852 OpenAiResponseToGeminiConverter,
2853 >(
2854 src_protocol,
2855 dst_protocol,
2856 OpenAiResponseToGeminiConverter::default(),
2857 normalizer,
2858 ),
2859
2860 (
2864 OperationFamily::StreamCreateImage,
2865 ProtocolKind::OpenAi,
2866 OperationFamily::StreamGenerateContent,
2867 ProtocolKind::OpenAiResponse,
2868 )
2869 | (
2870 OperationFamily::StreamCreateImageEdit,
2871 ProtocolKind::OpenAi,
2872 OperationFamily::StreamGenerateContent,
2873 ProtocolKind::OpenAiResponse,
2874 ) => build_stream_transform::<
2875 crate::openai::create_response::stream::ResponseStreamEvent,
2876 crate::openai::create_image::stream::ImageGenerationStreamEvent,
2877 ResponseStreamToImageStreamConverter,
2878 >(
2879 src_protocol,
2880 dst_protocol,
2881 ResponseStreamToImageStreamConverter::default(),
2882 normalizer,
2883 ),
2884
2885 (
2886 OperationFamily::StreamCreateImage,
2887 ProtocolKind::OpenAi,
2888 OperationFamily::StreamGenerateContent,
2889 ProtocolKind::Gemini,
2890 )
2891 | (
2892 OperationFamily::StreamCreateImage,
2893 ProtocolKind::OpenAi,
2894 OperationFamily::StreamGenerateContent,
2895 ProtocolKind::GeminiNDJson,
2896 )
2897 | (
2898 OperationFamily::StreamCreateImageEdit,
2899 ProtocolKind::OpenAi,
2900 OperationFamily::StreamGenerateContent,
2901 ProtocolKind::Gemini,
2902 )
2903 | (
2904 OperationFamily::StreamCreateImageEdit,
2905 ProtocolKind::OpenAi,
2906 OperationFamily::StreamGenerateContent,
2907 ProtocolKind::GeminiNDJson,
2908 ) => build_stream_transform::<
2909 crate::gemini::generate_content::response::ResponseBody,
2910 crate::openai::create_image::stream::ImageGenerationStreamEvent,
2911 GeminiToImageStreamConverter,
2912 >(
2913 src_protocol,
2914 dst_protocol,
2915 GeminiToImageStreamConverter::default(),
2916 normalizer,
2917 ),
2918
2919 _ => Err(TransformError::new(format!(
2920 "no stream response transform from upstream ({}, {}) to client ({}, {})",
2921 dst_operation, dst_protocol, src_operation, src_protocol
2922 ))),
2923 }
2924}
2925
2926pub fn nonstream_to_stream(
2933 protocol: ProtocolKind,
2934 body: &[u8],
2935 out: &mut Vec<u8>,
2936) -> Result<(), TransformError> {
2937 match protocol {
2938 ProtocolKind::Claude => {
2939 use crate::claude::create_message::response::ClaudeCreateMessageResponse;
2940 use crate::claude::create_message::stream::ClaudeStreamEvent;
2941 use crate::transform::claude::nonstream_to_stream::nonstream_to_stream;
2942
2943 let response: ClaudeCreateMessageResponse = serde_json::from_slice(body)
2944 .map_err(|e| TransformError::new(format!("deserialize: {e}")))?;
2945
2946 let mut events: Vec<ClaudeStreamEvent> = Vec::new();
2947 nonstream_to_stream(response, &mut events)
2948 .map_err(|e| TransformError::new(format!("nonstream_to_stream: {e}")))?;
2949
2950 for event in &events {
2951 let json = serde_json::to_vec(event)
2952 .map_err(|e| TransformError::new(format!("serialize event: {e}")))?;
2953 out.extend_from_slice(&json);
2954 out.push(b'\n');
2955 }
2956 Ok(())
2957 }
2958 ProtocolKind::OpenAiChatCompletion => {
2959 use crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse;
2960 use crate::openai::create_chat_completions::stream::ChatCompletionChunk;
2961
2962 let response: OpenAiChatCompletionsResponse = serde_json::from_slice(body)
2963 .map_err(|e| TransformError::new(format!("deserialize: {e}")))?;
2964
2965 let chunks = Vec::<ChatCompletionChunk>::try_from(response)
2966 .map_err(|e| TransformError::new(format!("nonstream_to_stream: {e}")))?;
2967
2968 for chunk in &chunks {
2969 let json = serde_json::to_vec(chunk)
2970 .map_err(|e| TransformError::new(format!("serialize chunk: {e}")))?;
2971 out.extend_from_slice(&json);
2972 out.push(b'\n');
2973 }
2974 Ok(())
2975 }
2976 ProtocolKind::OpenAiResponse => {
2977 use crate::openai::create_response::response::OpenAiCreateResponseResponse;
2978 use crate::openai::create_response::stream::ResponseStreamEvent;
2979
2980 let response: OpenAiCreateResponseResponse = serde_json::from_slice(body)
2981 .map_err(|e| TransformError::new(format!("deserialize: {e}")))?;
2982
2983 let events = Vec::<ResponseStreamEvent>::try_from(response)
2984 .map_err(|e| TransformError::new(format!("nonstream_to_stream: {e}")))?;
2985
2986 for event in &events {
2987 let json = serde_json::to_vec(event)
2988 .map_err(|e| TransformError::new(format!("serialize event: {e}")))?;
2989 out.extend_from_slice(&json);
2990 out.push(b'\n');
2991 }
2992 Ok(())
2993 }
2994 ProtocolKind::Gemini => {
2995 use crate::gemini::generate_content::response::GeminiGenerateContentResponse;
2996
2997 let response: GeminiGenerateContentResponse = serde_json::from_slice(body)
2998 .map_err(|e| TransformError::new(format!("deserialize: {e}")))?;
2999
3000 if let GeminiGenerateContentResponse::Success { body: resp, .. } = response {
3002 let json = serde_json::to_vec(&resp)
3003 .map_err(|e| TransformError::new(format!("serialize chunk: {e}")))?;
3004 out.extend_from_slice(&json);
3005 out.push(b'\n');
3006 }
3007 Ok(())
3008 }
3009 _ => Err(TransformError::new(format!(
3010 "no nonstream_to_stream for protocol: {protocol}"
3011 ))),
3012 }
3013}
3014
3015pub fn stream_to_nonstream(
3017 protocol: ProtocolKind,
3018 chunks: &[&[u8]],
3019) -> Result<Vec<u8>, TransformError> {
3020 match protocol {
3021 ProtocolKind::Claude => {
3022 use crate::claude::create_message::response::ClaudeCreateMessageResponse;
3023 use crate::claude::create_message::stream::ClaudeStreamEvent;
3024
3025 let events: Vec<ClaudeStreamEvent> = chunks
3026 .iter()
3027 .map(|c| serde_json::from_slice(c))
3028 .collect::<Result<_, _>>()
3029 .map_err(|e| TransformError::new(format!("deserialize events: {e}")))?;
3030
3031 let response = ClaudeCreateMessageResponse::try_from(events)
3032 .map_err(|e| TransformError::new(format!("stream_to_nonstream: {e}")))?;
3033
3034 response.into_body_bytes()
3038 }
3039 ProtocolKind::OpenAiChatCompletion => {
3040 use crate::openai::create_chat_completions::response::OpenAiChatCompletionsResponse;
3041 use crate::openai::create_chat_completions::stream::ChatCompletionChunk;
3042
3043 let chunks_parsed: Vec<ChatCompletionChunk> = chunks
3044 .iter()
3045 .map(|c| serde_json::from_slice(c))
3046 .collect::<Result<_, _>>()
3047 .map_err(|e| TransformError::new(format!("deserialize chunks: {e}")))?;
3048
3049 let response = OpenAiChatCompletionsResponse::try_from(chunks_parsed)
3050 .map_err(|e| TransformError::new(format!("stream_to_nonstream: {e}")))?;
3051
3052 response.into_body_bytes()
3053 }
3054 ProtocolKind::OpenAiResponse => {
3055 use crate::openai::create_response::response::OpenAiCreateResponseResponse;
3056 use crate::openai::create_response::stream::ResponseStreamEvent;
3057
3058 let events: Vec<ResponseStreamEvent> = chunks
3059 .iter()
3060 .map(|c| serde_json::from_slice(c))
3061 .collect::<Result<_, _>>()
3062 .map_err(|e| TransformError::new(format!("deserialize events: {e}")))?;
3063
3064 let response = OpenAiCreateResponseResponse::try_from(events)
3065 .map_err(|e| TransformError::new(format!("stream_to_nonstream: {e}")))?;
3066
3067 response.into_body_bytes()
3068 }
3069 ProtocolKind::Gemini | ProtocolKind::GeminiNDJson => {
3070 use crate::gemini::generate_content::response::ResponseBody;
3071 use crate::gemini::generate_content::types::GeminiCandidate;
3072 use std::collections::BTreeMap;
3073
3074 let mut merged = ResponseBody::default();
3075 let mut candidate_map: BTreeMap<u32, GeminiCandidate> = BTreeMap::new();
3076
3077 for chunk in chunks {
3078 let body: ResponseBody = serde_json::from_slice(chunk)
3079 .map_err(|e| TransformError::new(format!("deserialize chunk: {e}")))?;
3080 crate::transform::gemini::stream_to_nonstream::merge_chunk(
3081 &mut merged,
3082 &mut candidate_map,
3083 body,
3084 );
3085 }
3086
3087 let body =
3088 crate::transform::gemini::stream_to_nonstream::finalize_body(merged, candidate_map);
3089
3090 serde_json::to_vec(&body).map_err(|e| TransformError::new(format!("serialize: {e}")))
3091 }
3092 _ => Err(TransformError::new(format!(
3093 "no stream_to_nonstream for protocol: {protocol}"
3094 ))),
3095 }
3096}
3097
3098#[cfg(test)]
3099mod tests {
3100 use crate::kinds::{OperationFamily, ProtocolKind};
3101 use serde_json::{Value, json};
3102
3103 use super::{
3104 convert_error_body_or_raw, transform_request, transform_response, translate_request_query,
3105 };
3106
3107 #[test]
3108 fn transform_request_supports_openai_chat_to_openai_response() {
3109 let body = br#"{
3110 "model": "gpt-5.4",
3111 "messages": [
3112 { "role": "user", "content": "reply ok" }
3113 ],
3114 "stream": false
3115 }"#
3116 .to_vec();
3117
3118 let (_qout, transformed) = transform_request(
3119 OperationFamily::GenerateContent,
3120 ProtocolKind::OpenAiChatCompletion,
3121 OperationFamily::GenerateContent,
3122 ProtocolKind::OpenAiResponse,
3123 None,
3124 None,
3125 body,
3126 )
3127 .expect("chat -> response request transform should succeed");
3128
3129 let json: Value = serde_json::from_slice(&transformed).expect("transformed json");
3130 assert_eq!(json.get("model").and_then(Value::as_str), Some("gpt-5.4"));
3131 assert!(json.get("input").is_some());
3132 }
3133
3134 #[test]
3145 fn transform_request_count_tokens_claude_to_gemini_accepts_bare_body() {
3146 let body = br#"{
3147 "model": "gemini-3-flash-preview",
3148 "messages": [{"role": "user", "content": "hi"}]
3149 }"#
3150 .to_vec();
3151
3152 let (_qout, transformed) = transform_request(
3153 OperationFamily::CountToken,
3154 ProtocolKind::Claude,
3155 OperationFamily::CountToken,
3156 ProtocolKind::Gemini,
3157 None,
3158 None,
3159 body,
3160 )
3161 .expect("count_tokens Claude -> Gemini request transform should succeed");
3162
3163 let json: Value = serde_json::from_slice(&transformed).expect("transformed json");
3165 assert!(
3166 json.get("contents").is_some()
3167 || json.pointer("/generateContentRequest/contents").is_some(),
3168 "expected gemini countTokens body shape, got: {json}"
3169 );
3170 assert!(
3172 json.get("method").is_none(),
3173 "transformed body leaked the request envelope method field"
3174 );
3175 }
3176
3177 #[test]
3190 fn transform_response_accepts_bare_openai_response_body_for_openai_chat() {
3191 let body = serde_json::to_vec(&json!({
3192 "id": "resp_123",
3193 "created_at": 1,
3194 "metadata": {},
3195 "model": "gpt-5.4",
3196 "object": "response",
3197 "output": [
3198 {
3199 "id": "msg_0",
3200 "content": [
3201 {
3202 "annotations": [],
3203 "text": "OK",
3204 "type": "output_text"
3205 }
3206 ],
3207 "role": "assistant",
3208 "status": "completed",
3209 "type": "message"
3210 }
3211 ],
3212 "parallel_tool_calls": false,
3213 "temperature": 1.0,
3214 "tool_choice": "auto",
3215 "tools": [],
3216 "top_p": 1.0
3217 }))
3218 .expect("serialize response body");
3219
3220 let transformed = transform_response(
3221 OperationFamily::GenerateContent,
3222 ProtocolKind::OpenAiChatCompletion,
3223 OperationFamily::GenerateContent,
3224 ProtocolKind::OpenAiResponse,
3225 body,
3226 )
3227 .expect("bare responses body should now be accepted");
3228
3229 let json: Value = serde_json::from_slice(&transformed).expect("transformed json");
3230 assert_eq!(json.get("model").and_then(Value::as_str), Some("gpt-5.4"));
3231 assert_eq!(
3232 json.pointer("/choices/0/message/content")
3233 .and_then(Value::as_str),
3234 Some("OK")
3235 );
3236 assert!(
3239 json.get("stats_code").is_none(),
3240 "serialized response leaked the internal wrapper envelope"
3241 );
3242 }
3243
3244 #[test]
3254 fn transform_response_gemini_to_claude_accepts_bare_gemini_body() {
3255 let body = serde_json::to_vec(&json!({
3256 "candidates": [{
3257 "content": {
3258 "parts": [{"text": "Hello"}],
3259 "role": "model"
3260 },
3261 "finishReason": "STOP",
3262 "index": 0
3263 }],
3264 "usageMetadata": {
3265 "promptTokenCount": 7,
3266 "candidatesTokenCount": 1,
3267 "totalTokenCount": 8
3268 },
3269 "modelVersion": "gemini-3-flash-preview",
3270 "responseId": "test-response-id"
3271 }))
3272 .expect("serialize gemini body");
3273
3274 let transformed = transform_response(
3275 OperationFamily::GenerateContent,
3276 ProtocolKind::Claude,
3277 OperationFamily::GenerateContent,
3278 ProtocolKind::Gemini,
3279 body,
3280 )
3281 .expect("gemini -> claude transform must accept raw gemini body");
3282
3283 let json: Value = serde_json::from_slice(&transformed).expect("transformed json");
3284 assert_eq!(json.get("type").and_then(Value::as_str), Some("message"));
3286 assert_eq!(json.get("role").and_then(Value::as_str), Some("assistant"));
3287 assert!(
3288 json.get("stats_code").is_none(),
3289 "serialized response leaked the internal wrapper envelope"
3290 );
3291 assert!(json.get("content").is_some(), "missing content field");
3292 }
3293
3294 #[test]
3299 fn convert_error_body_claude_to_openai_chat_rewrites_schema() {
3300 let claude_error = br#"{
3301 "type": "error",
3302 "error": {
3303 "type": "invalid_request_error",
3304 "message": "prompt is too long: 1057153 tokens > 1000000 maximum"
3305 },
3306 "request_id": "req_011Ca1mHSW1W47w6LbmKQNWf"
3307 }"#
3308 .to_vec();
3309
3310 let converted = convert_error_body_or_raw(
3311 OperationFamily::StreamGenerateContent,
3312 ProtocolKind::OpenAiChatCompletion,
3313 OperationFamily::StreamGenerateContent,
3314 ProtocolKind::Claude,
3315 claude_error.clone(),
3316 );
3317
3318 let json: Value =
3319 serde_json::from_slice(&converted).expect("converted body should be valid JSON");
3320 let error_obj = json
3322 .get("error")
3323 .expect("OpenAI error body must have top-level `error` field");
3324 assert_eq!(
3325 error_obj.get("message").and_then(Value::as_str),
3326 Some("prompt is too long: 1057153 tokens > 1000000 maximum"),
3327 "error message must survive the schema conversion"
3328 );
3329 assert!(
3330 json.get("type").and_then(Value::as_str) != Some("error"),
3331 "converted body must not still be in Claude's top-level `type:error` shape"
3332 );
3333 }
3334
3335 #[test]
3341 fn convert_error_body_falls_back_to_raw_on_schema_mismatch() {
3342 let codex_error = br#"{"detail":{"code":"deactivated_workspace"}}"#.to_vec();
3343
3344 let result = convert_error_body_or_raw(
3345 OperationFamily::StreamGenerateContent,
3346 ProtocolKind::OpenAiChatCompletion,
3347 OperationFamily::StreamGenerateContent,
3348 ProtocolKind::OpenAiResponse,
3349 codex_error.clone(),
3350 );
3351
3352 assert_eq!(
3353 result, codex_error,
3354 "fallback must return the raw bytes verbatim"
3355 );
3356 }
3357
3358 #[test]
3362 fn convert_error_body_passthrough_returns_unchanged() {
3363 let claude_error =
3364 br#"{"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}"#
3365 .to_vec();
3366
3367 let result = convert_error_body_or_raw(
3368 OperationFamily::StreamGenerateContent,
3369 ProtocolKind::Claude,
3370 OperationFamily::StreamGenerateContent,
3371 ProtocolKind::Claude,
3372 claude_error.clone(),
3373 );
3374
3375 assert_eq!(result, claude_error);
3376 }
3377
3378 #[test]
3379 fn translate_request_query_gemini_to_claude_model_list() {
3380 let translated = translate_request_query(
3381 OperationFamily::ModelList,
3382 ProtocolKind::Gemini,
3383 OperationFamily::ModelList,
3384 ProtocolKind::Claude,
3385 Some("pageSize=25&pageToken=abc"),
3386 )
3387 .expect("translated query should be present");
3388 assert!(translated.contains("limit=25"), "got: {translated}");
3389 assert!(translated.contains("after_id=abc"), "got: {translated}");
3390 }
3391
3392 #[test]
3393 fn translate_request_query_claude_to_gemini_model_list() {
3394 let translated = translate_request_query(
3395 OperationFamily::ModelList,
3396 ProtocolKind::Claude,
3397 OperationFamily::ModelList,
3398 ProtocolKind::Gemini,
3399 Some("limit=50&after_id=cursor1"),
3400 )
3401 .expect("translated query should be present");
3402 assert!(translated.contains("pageSize=50"), "got: {translated}");
3403 assert!(
3404 translated.contains("pageToken=cursor1"),
3405 "got: {translated}"
3406 );
3407 }
3408
3409 #[test]
3410 fn translate_request_query_passes_through_for_same_protocol() {
3411 let translated = translate_request_query(
3412 OperationFamily::ModelList,
3413 ProtocolKind::Gemini,
3414 OperationFamily::ModelList,
3415 ProtocolKind::Gemini,
3416 Some("pageSize=10"),
3417 );
3418 assert_eq!(translated.as_deref(), Some("pageSize=10"));
3419 }
3420
3421 #[test]
3422 fn translate_request_query_passes_through_for_non_model_list() {
3423 let translated = translate_request_query(
3424 OperationFamily::GenerateContent,
3425 ProtocolKind::Claude,
3426 OperationFamily::GenerateContent,
3427 ProtocolKind::Gemini,
3428 Some("foo=bar"),
3429 );
3430 assert_eq!(translated.as_deref(), Some("foo=bar"));
3431 }
3432
3433 #[test]
3434 fn translate_request_query_drops_unknown_keys_on_cross_protocol_model_list() {
3435 let translated = translate_request_query(
3436 OperationFamily::ModelList,
3437 ProtocolKind::Gemini,
3438 OperationFamily::ModelList,
3439 ProtocolKind::Claude,
3440 Some("pageSize=5&unknown=x"),
3441 )
3442 .expect("translated query");
3443 assert!(translated.contains("limit=5"));
3444 assert!(!translated.contains("unknown"));
3445 }
3446}