Skip to main content

gproxy_protocol/transform/guardian/request/
claude.rs

1use super::*;
2
3pub fn prepare_claude(
4    input: source::GuardianRequestBody,
5    context: GuardianRequestContext,
6) -> Result<Converted<GuardianPreparedRequest>, TransformError> {
7    prepare_claude_with_limits(input, context, default_history_limits())
8}
9
10pub fn prepare_claude_with_limits(
11    input: source::GuardianRequestBody,
12    context: GuardianRequestContext,
13    limits: CodecLimits,
14) -> Result<Converted<GuardianPreparedRequest>, TransformError> {
15    prepare_claude_plan(input, context, limits, true)
16}
17
18pub(super) fn prepare_claude_plan(
19    input: source::GuardianRequestBody,
20    context: GuardianRequestContext,
21    limits: CodecLimits,
22    attach: bool,
23) -> Result<Converted<GuardianPreparedRequest>, TransformError> {
24    let input = input.into_declared();
25
26    bound_source(&input, limits)?;
27    let payload = task_payload(&input, context.operation, limits)?;
28    let mut body = cg::GenerateContentRequestBody::builder(
29        context.max_tokens,
30        vec![cc::Message::builder(cc::Role::User, cc::MessageContent::Text(payload)).build()],
31        context.target_model.clone(),
32    )
33    .build();
34    body.system = Some(ct::SystemPrompt::Text(instructions(
35        &input,
36        context.operation,
37    )));
38    body.stream = Some(false);
39    body.service_tier = input
40        .service_tier
41        .as_deref()
42        .map(|value| {
43            Ok(match value {
44                "auto" => cg::ServiceTier::Auto,
45                "default" => cg::ServiceTier::StandardOnly,
46                _ => {
47                    return Err(TransformError::unsupported(
48                        "guardian.service_tier",
49                        "Claude lacks requested service tier",
50                    ));
51                }
52            })
53        })
54        .map(crate::transform::optional)
55        .transpose()?
56        .flatten();
57
58    match effort(&input) {
59        None => {}
60        Some("none") => {
61            body.thinking = Some(ct::ThinkingConfig::Disabled(
62                ct::ThinkingDisabled::builder().build(),
63            ))
64        }
65        Some(level @ ("low" | "medium" | "high" | "xhigh" | "max")) => {
66            let level = match level {
67                "low" => ct::Effort::Low,
68                "medium" => ct::Effort::Medium,
69                "high" => ct::Effort::High,
70                "xhigh" => ct::Effort::Xhigh,
71                "max" => ct::Effort::Max,
72                _ => {
73                    return Err(TransformError::unsupported(
74                        "guardian.reasoning.effort",
75                        "Claude lacks requested effort",
76                    ));
77                }
78            };
79            body.thinking = Some(ct::ThinkingConfig::Adaptive(
80                ct::ThinkingAdaptive::builder().build(),
81            ));
82            body.output_config = Some(ct::OutputConfig::builder().effort(level).build());
83        }
84        Some(_) => {}
85    }
86    if context.operation == GuardianOperation::Review {
87        let (_, output_schema, _) = review_schema(&input)?;
88        body.output_format = Some(
89            ct::JsonOutputFormat::builder(ct::JsonOutputFormatType::JsonSchema, output_schema)
90                .build(),
91        );
92    }
93    let mut target = GuardianDialectRequest::Claude(request("/v1/messages", body));
94    let media = media(&input)?;
95
96    if attach {
97        attach_media(&mut target, &media)?;
98    }
99    preflight_request(&target, limits, limits.max_body_bytes)?;
100    let report = report(&input, &target);
101    Ok(Converted {
102        value: GuardianPreparedRequest {
103            operation: context.operation,
104            request: target,
105            source: input,
106        },
107        report,
108    })
109}