Skip to main content

gproxy_protocol/transform/generate/gemini_responses/response/
facts.rs

1use 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/// The original Responses request and effective settings retained during request
11/// preparation. Required settings must come from that invocation, not guesses
12/// made after receiving a Gemini result. A response conversion consumes this
13/// value and recursively removes extensions before reusing same-dialect fields.
14#[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    /// Actual creation timestamp of this invocation. Gemini has no creation timestamp field.
21    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}