gproxy_protocol/transform/guardian/
response.rs1use crate::{
2 codec,
3 transform::{TransformError, TransformErrorKind},
4 wire::{
5 claude::generate_content as c,
6 gemini as g,
7 openai::{chat as o, responses as r},
8 },
9};
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct GuardianReviewResult {
15 #[serde(default)]
16 pub risk_level: Option<GuardianRiskLevel>,
17 #[serde(default)]
18 pub user_authorization: Option<GuardianUserAuthorization>,
19 pub outcome: GuardianOutcome,
20 #[serde(default)]
21 pub rationale: Option<String>,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "lowercase")]
26pub enum GuardianRiskLevel {
27 Low,
28 Medium,
29 High,
30 Critical,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(rename_all = "lowercase")]
35pub enum GuardianUserAuthorization {
36 Unknown,
37 Low,
38 Medium,
39 High,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "lowercase")]
44pub enum GuardianOutcome {
45 Allow,
46 Deny,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "lowercase")]
52pub enum GuardianClassifyResult {
53 High,
54 Low,
55}
56
57fn parse_review(text: String) -> Result<GuardianReviewResult, TransformError> {
58 let result: GuardianReviewResult = codec::decode_json(text.as_bytes(), result_limits())
59 .map_err(|e| {
60 TransformError::new(
61 TransformErrorKind::InvalidResult,
62 "guardian.review",
63 e.to_string(),
64 )
65 })?;
66 Ok(result)
67}
68
69fn result_limits() -> codec::CodecLimits {
70 codec::CodecLimits {
71 max_buffer_bytes: 256 * 1024,
72 max_value_bytes: 256 * 1024,
73 max_body_bytes: 256 * 1024,
74 max_line_bytes: 256 * 1024,
75 max_part_bytes: 256 * 1024,
76 max_parts: 16,
77 }
78}
79
80fn parse_classify(text: String) -> Result<GuardianClassifyResult, TransformError> {
81 match text.trim() {
82 "high" => Ok(GuardianClassifyResult::High),
83 "low" => Ok(GuardianClassifyResult::Low),
84 _ => Err(TransformError::invalid_result(
85 "guardian.classify",
86 "expected exactly the native high or low label",
87 )),
88 }
89}
90
91pub fn extract_claude(
92 input: c::GenerateContentResponseBody,
93 operation: super::GuardianOperation,
94) -> Result<GuardianExtraction, TransformError> {
95 let text = crate::transform::memory::claude_text(input)?;
96 extract_text(text, operation)
97}
98
99pub fn extract_gemini(
100 input: g::GenerateContentResponseBody,
101 operation: super::GuardianOperation,
102) -> Result<GuardianExtraction, TransformError> {
103 let text = crate::transform::memory::gemini_text(input)?;
104 extract_text(text, operation)
105}
106
107pub fn extract_chat(
108 input: o::GenerateContentResponseBody,
109 operation: super::GuardianOperation,
110) -> Result<GuardianExtraction, TransformError> {
111 let text = crate::transform::memory::chat_text(input)?;
112 extract_text(text, operation)
113}
114
115pub fn extract_responses(
116 input: r::GenerateContentResponseBody,
117 operation: super::GuardianOperation,
118) -> Result<GuardianExtraction, TransformError> {
119 let text = crate::transform::memory::responses_text(input)?;
120 extract_text(text, operation)
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
124pub enum GuardianExtraction {
125 Review(GuardianReviewResult),
126 Classify(GuardianClassifyResult),
127}
128
129fn extract_text(
130 text: String,
131 operation: super::GuardianOperation,
132) -> Result<GuardianExtraction, TransformError> {
133 match operation {
134 super::GuardianOperation::Review => parse_review(text).map(GuardianExtraction::Review),
135 super::GuardianOperation::Classify => {
136 parse_classify(text).map(GuardianExtraction::Classify)
137 }
138 }
139}