@@ -18,65 +18,19 @@ use crate::discovery::RestDescription;
1818use crate :: error:: GwsError ;
1919use anyhow:: Context ;
2020use clap:: { Arg , ArgMatches , Command } ;
21- use serde:: { Deserialize , Serialize } ;
2221use serde_json:: json;
2322use std:: future:: Future ;
2423use std:: pin:: Pin ;
2524
26- /// Result of a Model Armor sanitization check.
27- #[ derive( Debug , Clone , Serialize , Deserialize ) ]
28- #[ serde( rename_all = "camelCase" ) ]
29- pub struct SanitizationResult {
30- /// The overall state of the match (e.g., "MATCH_FOUND", "NO_MATCH_FOUND").
31- pub filter_match_state : String ,
32- /// Detailed results from specific filters (PI, Jailbreak, etc.).
33- #[ serde( default ) ]
34- pub filter_results : serde_json:: Value ,
35- /// The final decision based on the policy (e.g., "BLOCK", "ALLOW").
36- #[ serde( default ) ]
37- pub invocation_result : String ,
38- }
39-
40- /// Controls behavior when sanitization finds a match.
41- #[ derive( Debug , Clone , PartialEq ) ]
42- pub enum SanitizeMode {
43- /// Log warning to stderr, annotate output with _sanitization field
44- Warn ,
45- /// Suppress response output, exit non-zero
46- Block ,
47- }
25+ // Re-export sanitization types from the standalone module so existing
26+ // `helpers::modelarmor::` paths continue to compile.
27+ pub use crate :: sanitize:: {
28+ sanitize_text, SanitizationResult , SanitizeConfig , SanitizeMode , CLOUD_PLATFORM_SCOPE ,
29+ } ;
4830
49- /// Configuration for Model Armor sanitization, threaded through the CLI.
50- #[ derive( Debug , Clone ) ]
51- pub struct SanitizeConfig {
52- pub template : Option < String > ,
53- pub mode : SanitizeMode ,
54- }
55-
56- impl Default for SanitizeConfig {
57- /// Provides default values for `SanitizeConfig`.
58- ///
59- /// By default, no template is set (sanitization disabled) and the mode is `Warn`.
60- fn default ( ) -> Self {
61- Self {
62- template : None ,
63- mode : SanitizeMode :: Warn ,
64- }
65- }
66- }
67-
68- impl SanitizeMode {
69- /// Parses a string into a `SanitizeMode`.
70- ///
71- /// * "block" (case-insensitive) -> `Block`
72- /// * Any other value -> `Warn` (safe default)
73- pub fn from_str ( s : & str ) -> Self {
74- match s. to_lowercase ( ) . as_str ( ) {
75- "block" => SanitizeMode :: Block ,
76- _ => SanitizeMode :: Warn ,
77- }
78- }
79- }
31+ // Re-export for tests in this module
32+ #[ cfg( test) ]
33+ pub ( crate ) use crate :: sanitize:: { build_sanitize_request_data, parse_sanitize_response} ;
8034
8135pub struct ModelArmorHelper ;
8236
@@ -243,42 +197,6 @@ TIPS:
243197 }
244198}
245199
246- pub const CLOUD_PLATFORM_SCOPE : & str = "https://www.googleapis.com/auth/cloud-platform" ;
247-
248- /// Sanitize text through a Model Armor template and return the result.
249- /// Template format: projects/PROJECT/locations/LOCATION/templates/TEMPLATE
250- pub async fn sanitize_text ( template : & str , text : & str ) -> Result < SanitizationResult , GwsError > {
251- let ( body, url) = build_sanitize_request_data ( template, text, "sanitizeUserPrompt" ) ?;
252-
253- let token = auth:: get_token ( & [ CLOUD_PLATFORM_SCOPE ] )
254- . await
255- . context ( "Failed to get auth token for Model Armor" ) ?;
256-
257- let client = crate :: client:: build_client ( ) ?;
258- let resp = client
259- . post ( & url)
260- . header ( "Authorization" , format ! ( "Bearer {token}" ) )
261- . header ( "Content-Type" , "application/json" )
262- . body ( body)
263- . send ( )
264- . await
265- . context ( "Model Armor request failed" ) ?;
266-
267- let status = resp. status ( ) ;
268- let resp_text = resp
269- . text ( )
270- . await
271- . context ( "Failed to read Model Armor response" ) ?;
272-
273- if !status. is_success ( ) {
274- return Err ( GwsError :: Other ( anyhow:: anyhow!(
275- "Model Armor API returned status {status}: {resp_text}"
276- ) ) ) ;
277- }
278-
279- parse_sanitize_response ( & resp_text)
280- }
281-
282200/// Make a POST request to Model Armor's regional API endpoint.
283201async fn model_armor_post ( url : & str , body : & str ) -> Result < ( ) , GwsError > {
284202 let token = auth:: get_token ( & [ CLOUD_PLATFORM_SCOPE ] )
@@ -459,23 +377,23 @@ mod tests {
459377
460378 #[ test]
461379 fn test_sanitize_mode_from_str_warn ( ) {
462- assert_eq ! ( SanitizeMode :: from_str ( "warn" ) , SanitizeMode :: Warn ) ;
463- assert_eq ! ( SanitizeMode :: from_str ( "WARN" ) , SanitizeMode :: Warn ) ;
464- assert_eq ! ( SanitizeMode :: from_str ( "Warn" ) , SanitizeMode :: Warn ) ;
380+ assert_eq ! ( SanitizeMode :: from ( "warn" ) , SanitizeMode :: Warn ) ;
381+ assert_eq ! ( SanitizeMode :: from ( "WARN" ) , SanitizeMode :: Warn ) ;
382+ assert_eq ! ( SanitizeMode :: from ( "Warn" ) , SanitizeMode :: Warn ) ;
465383 }
466384
467385 #[ test]
468386 fn test_sanitize_mode_from_str_block ( ) {
469- assert_eq ! ( SanitizeMode :: from_str ( "block" ) , SanitizeMode :: Block ) ;
470- assert_eq ! ( SanitizeMode :: from_str ( "BLOCK" ) , SanitizeMode :: Block ) ;
471- assert_eq ! ( SanitizeMode :: from_str ( "Block" ) , SanitizeMode :: Block ) ;
387+ assert_eq ! ( SanitizeMode :: from ( "block" ) , SanitizeMode :: Block ) ;
388+ assert_eq ! ( SanitizeMode :: from ( "BLOCK" ) , SanitizeMode :: Block ) ;
389+ assert_eq ! ( SanitizeMode :: from ( "Block" ) , SanitizeMode :: Block ) ;
472390 }
473391
474392 #[ test]
475393 fn test_sanitize_mode_from_str_unknown_defaults_to_warn ( ) {
476- assert_eq ! ( SanitizeMode :: from_str ( "" ) , SanitizeMode :: Warn ) ;
477- assert_eq ! ( SanitizeMode :: from_str ( "invalid" ) , SanitizeMode :: Warn ) ;
478- assert_eq ! ( SanitizeMode :: from_str ( "stop" ) , SanitizeMode :: Warn ) ;
394+ assert_eq ! ( SanitizeMode :: from ( "" ) , SanitizeMode :: Warn ) ;
395+ assert_eq ! ( SanitizeMode :: from ( "invalid" ) , SanitizeMode :: Warn ) ;
396+ assert_eq ! ( SanitizeMode :: from ( "stop" ) , SanitizeMode :: Warn ) ;
479397 }
480398
481399 #[ test]
@@ -565,47 +483,6 @@ mod tests {
565483 }
566484}
567485
568- pub fn build_sanitize_request_data (
569- template : & str ,
570- text : & str ,
571- method : & str ,
572- ) -> Result < ( String , String ) , GwsError > {
573- let location = extract_location ( template) . ok_or_else ( || {
574- GwsError :: Validation (
575- "Cannot extract location from --sanitize template. Expected format: projects/PROJECT/locations/LOCATION/templates/TEMPLATE" . to_string ( ) ,
576- )
577- } ) ?;
578-
579- let base = regional_base_url ( location) ;
580- let url = format ! ( "{base}/{template}:{method}" ) ;
581-
582- // Identify data field based on method
583- let data_field = if method == "sanitizeUserPrompt" {
584- "userPromptData"
585- } else {
586- "modelResponseData"
587- } ;
588-
589- let body = json ! ( { data_field: { "text" : text} } ) . to_string ( ) ;
590- Ok ( ( body, url) )
591- }
592-
593- pub fn parse_sanitize_response ( resp_text : & str ) -> Result < SanitizationResult , GwsError > {
594- // Parse the response to extract sanitizationResult
595- let parsed: serde_json:: Value =
596- serde_json:: from_str ( resp_text) . context ( "Failed to parse Model Armor response" ) ?;
597-
598- let result = parsed. get ( "sanitizationResult" ) . ok_or_else ( || {
599- GwsError :: Other ( anyhow:: anyhow!(
600- "No sanitizationResult in Model Armor response"
601- ) )
602- } ) ?;
603-
604- let res =
605- serde_json:: from_value ( result. clone ( ) ) . context ( "Failed to parse sanitization result" ) ?;
606- Ok ( res)
607- }
608-
609486fn parse_sanitize_args ( matches : & ArgMatches , data_field : & str ) -> Result < String , GwsError > {
610487 if let Some ( json_str) = matches. get_one :: < String > ( "json" ) {
611488 Ok ( json_str. clone ( ) )
0 commit comments