@@ -413,6 +413,9 @@ impl RoutedLlmClient for ClassifierClient {
413413 }
414414}
415415
416+ // Conversation text that a provider may quote in an upstream error body.
417+ const LEAKED_CONTENT : & str = "patient name is Jane Doe" ;
418+
416419enum JudgeOutcome {
417420 CallFailure ,
418421 Reply ( & ' static str ) ,
@@ -440,7 +443,7 @@ impl RoutedLlmClient for JudgeClient {
440443 match & self . outcome {
441444 JudgeOutcome :: CallFailure => Err ( LlmClientError :: UpstreamHttp {
442445 status : http:: StatusCode :: INTERNAL_SERVER_ERROR ,
443- body : " server error" . to_string ( ) ,
446+ body : format ! ( r#"{{"error":{{"message":" server error: {LEAKED_CONTENT}"}}}}"# ) ,
444447 } ) ,
445448 JudgeOutcome :: Reply ( text) => Ok ( Response {
446449 llm_response : LlmResponse :: Agg ( text_response ( None , * text) ) ,
@@ -1213,6 +1216,39 @@ async fn typed_client_failure_records_semantic_error_type() {
12131216 ) ;
12141217}
12151218
1219+ /// Verifies that the client span retains the HTTP status without the upstream body.
1220+ #[ tokio:: test]
1221+ async fn upstream_body_is_redacted_from_the_client_call_span ( ) -> switchyard_libsy:: Result < ( ) > {
1222+ let _guard = serialize_test ( ) . lock ( ) . await ;
1223+ let ( store, _, _, _, _) = telemetry ( ) ;
1224+ const JUDGE : & str = "redaction-judge" ;
1225+ let client = Arc :: new ( JudgeClient {
1226+ judge_model : JUDGE . into ( ) ,
1227+ outcome : JudgeOutcome :: CallFailure ,
1228+ } ) as Arc < dyn RoutedLlmClient > ;
1229+ run (
1230+ classifier_router ( JUDGE , "redaction-weak" , "redaction-strong" ) ?,
1231+ client,
1232+ classifier_request ( ) ,
1233+ )
1234+ . await ?;
1235+
1236+ let spans = store. spans ( ) ;
1237+ let client_span = find_span ( & spans, "libsy.client_call" , "selected_model" , JUDGE ) ;
1238+ assert_eq ! (
1239+ client_span. fields. get( "error.type" ) . map( String :: as_str) ,
1240+ Some ( "500" )
1241+ ) ;
1242+ let error = client_span
1243+ . fields
1244+ . get ( "error" )
1245+ . map ( String :: as_str)
1246+ . unwrap_or ( "" ) ;
1247+ assert ! ( error. contains( "upstream HTTP 500" ) , "{client_span:?}" ) ;
1248+ assert ! ( !error. contains( LEAKED_CONTENT ) , "{client_span:?}" ) ;
1249+ Ok ( ( ) )
1250+ }
1251+
12161252#[ tokio:: test]
12171253async fn failed_call_records_error_outcome_and_warn_logs ( ) -> switchyard_libsy:: Result < ( ) > {
12181254 let _guard = serialize_test ( ) . lock ( ) . await ;
0 commit comments