gproxy_protocol/transform/generate/gemini_responses/response/
facts.rs1use super::GeminiUsageFacts;
2use crate::{
3 transform::TransformError,
4 wire::{
5 DeclaredFields,
6 openai::responses::{generate as g, input as i, response as r},
7 },
8};
9
10#[derive(Debug)]
15pub struct GeminiResponseContext {
16 pub request: g::GenerateContentRequestBody,
17 pub effective_parallel_tool_calls: bool,
18 pub effective_tool_choice: i::ToolChoice,
19 pub usage: GeminiUsageFacts,
20 pub created_at: i64,
22 pub effective_prompt_cache_options: Option<r::ResponsePromptCacheOptions>,
23}
24
25impl GeminiResponseContext {
26 pub(crate) fn into_response(
27 self,
28 id: String,
29 created_at: i64,
30 model: String,
31 ) -> Result<r::GenerateContentResponseBody, TransformError> {
32 let request = self.request.into_declared();
33 let choice = self.effective_tool_choice.into_declared();
34 if request
35 .parallel_tool_calls
36 .flatten()
37 .is_some_and(|v| v != self.effective_parallel_tool_calls)
38 || request.tool_choice.as_ref().is_some_and(|v| v != &choice)
39 {
40 return Err(TransformError::shape(
41 "response_context",
42 "effective settings contradict original request",
43 ));
44 }
45 let conversation = request.conversation.map(|value| {
46 value.map(|value| match value {
47 i::ConversationParam::Id(id) => i::ResponseConversationParam {
48 id,
49 rest: Default::default(),
50 },
51 i::ConversationParam::Object(value) => value,
52 })
53 });
54 let cache = self.effective_prompt_cache_options.into_declared();
55 if let Some(requested) = request.prompt_cache_options {
56 let actual = cache
57 .as_ref()
58 .ok_or_else(|| TransformError::missing_metadata("response.prompt_cache_options"))?;
59 if requested.mode.is_some_and(|v| v != actual.mode)
60 || requested.ttl.is_some_and(|v| v != actual.ttl)
61 {
62 return Err(TransformError::shape(
63 "response.prompt_cache_options",
64 "effective cache settings contradict request",
65 ));
66 }
67 }
68 Ok(r::GenerateContentResponseBody {
69 access_programs: None,
70 prompt_cache_diagnostics: None,
71 id,
72 created_at,
73 model,
74 object: r::ResponseObject::Response,
75 error: None,
76 incomplete_details: None,
77 instructions: request.instructions.flatten().map(i::Input::Text),
78 metadata: request.metadata.flatten(),
79 output: Vec::new(),
80 parallel_tool_calls: self.effective_parallel_tool_calls,
81 temperature: request.temperature.flatten(),
82 tool_choice: choice,
83 tools: request.tools.unwrap_or_default(),
84 top_p: request.top_p.flatten(),
85 background: request.background,
86 completed_at: None,
87 conversation,
88 max_output_tokens: request.max_output_tokens,
89 max_tool_calls: request.max_tool_calls,
90 moderation: None,
91 output_text: None,
92 previous_response_id: request.previous_response_id,
93 prompt: request.prompt,
94 prompt_cache_key: request.prompt_cache_key,
95 prompt_cache_options: cache,
96 prompt_cache_retention: request.prompt_cache_retention,
97 reasoning: request.reasoning,
98 safety_identifier: request.safety_identifier,
99 service_tier: None,
100 status: None,
101 text: request.text,
102 top_logprobs: request.top_logprobs,
103 truncation: request.truncation,
104 usage: None,
105 user: request.user,
106 rest: Default::default(),
107 })
108 }
109}