Skip to main content

gproxy_protocol/transform/guardian/request/
binding.rs

1//! Source-owned response controls for the Guardian client's SSE envelope.
2
3use super::{GuardianDialectRequest, GuardianPreparedRequest};
4use crate::{
5    transform::TransformError,
6    wire::openai::{guardian as s, responses as r},
7};
8
9impl GuardianPreparedRequest {
10    /// A bounded, typed envelope template for the existing response-pair
11    /// contexts. Private Guardian history is deliberately not fabricated into
12    /// public Responses input. Use this request in GuardianStreamContext.
13    pub fn response_request(&self) -> Result<r::GenerateContentRequestBody, TransformError> {
14        let source = self.source();
15        let mut body = r::GenerateContentRequestBody::builder()
16            .model(source.model.clone())
17            .instructions(source.instructions.clone())
18            .store(Some(source.store))
19            .stream(Some(source.stream))
20            .parallel_tool_calls(Some(source.parallel_tool_calls))
21            .tool_choice(r::ToolChoice::Mode(if source.tool_choice == "none" {
22                r::ToolChoiceMode::None
23            } else {
24                r::ToolChoiceMode::Auto
25            }))
26            .build();
27        body.max_output_tokens = Some(Some(match self.request() {
28            GuardianDialectRequest::Claude(v) => v.body.max_tokens,
29            GuardianDialectRequest::Gemini(v) => v
30                .body
31                .generation_config
32                .as_ref()
33                .and_then(|v| v.max_output_tokens)
34                .expect("prepared output budget"),
35            GuardianDialectRequest::OpenAiChat(v) => v
36                .body
37                .max_completion_tokens
38                .flatten()
39                .expect("prepared output budget"),
40            GuardianDialectRequest::OpenAiResponses(v) => v
41                .body
42                .max_output_tokens
43                .flatten()
44                .expect("prepared output budget"),
45        }));
46        body.prompt_cache_key = source.prompt_cache_key.clone().map(Some);
47        if let Some(reasoning) = &source.reasoning {
48            let mut config = r::ReasoningConfig::builder().build();
49            config.effort = reasoning
50                .effort
51                .as_deref()
52                .filter(|v| *v != "model_defined")
53                .and_then(super::responses_effort)
54                .map(Some);
55            config.context = reasoning.context.as_ref().map(|v| {
56                Some(match v {
57                    s::ReasoningContext::Auto => r::ReasoningContext::Auto,
58                    s::ReasoningContext::CurrentTurn => r::ReasoningContext::CurrentTurn,
59                    s::ReasoningContext::AllTurns => r::ReasoningContext::AllTurns,
60                })
61            });
62            config.summary = reasoning.summary.as_ref().map(|v| match v {
63                s::ReasoningSummary::None => None,
64                s::ReasoningSummary::Auto => Some(r::ReasoningSummary::Auto),
65                s::ReasoningSummary::Concise => Some(r::ReasoningSummary::Concise),
66                s::ReasoningSummary::Detailed => Some(r::ReasoningSummary::Detailed),
67            });
68            body.reasoning = Some(Some(config));
69        }
70        if let Some(text) = &source.text {
71            let mut config = r::TextConfig::builder().build();
72            config.verbosity = text.verbosity.as_ref().map(|v| {
73                Some(match v {
74                    s::ClientVerbosity::Low => r::TextVerbosity::Low,
75                    s::ClientVerbosity::Medium => r::TextVerbosity::Medium,
76                    s::ClientVerbosity::High => r::TextVerbosity::High,
77                })
78            });
79            if text.format.is_some() {
80                let (name, schema, strict) = super::review_schema(source)?;
81                config.format = Some(r::TextFormat::JsonSchema(
82                    r::TextFormatJsonSchema::builder(
83                        name,
84                        schema.as_object().expect("validated object schema").clone(),
85                    )
86                    .strict(Some(strict))
87                    .build(),
88                ));
89            }
90            body.text = Some(config);
91        }
92        body.service_tier = source
93            .service_tier
94            .as_deref()
95            .map(|v| {
96                Ok(Some(match v {
97                    "auto" => r::ServiceTier::Auto,
98                    "default" => r::ServiceTier::Default,
99                    "flex" => r::ServiceTier::Flex,
100                    "scale" => r::ServiceTier::Scale,
101                    "priority" => r::ServiceTier::Priority,
102                    "fast" => r::ServiceTier::Fast,
103                    _ => {
104                        return Err(TransformError::unsupported(
105                            "guardian.service_tier",
106                            "unknown source tier",
107                        ));
108                    }
109                }))
110            })
111            .map(crate::transform::optional)
112            .transpose()?
113            .flatten();
114        Ok(body)
115    }
116}