From 4e0c785cd244fe8528d348ab098e58e665911006 Mon Sep 17 00:00:00 2001 From: Adil Hafeez Date: Tue, 9 Dec 2025 15:12:40 -0800 Subject: [PATCH] handle agent error better if agent filter return 4xx, properly cascade the error text back to the developer this code change will help build agent filters and allow them to early terminate if for example intent deviates or jailbreak attempt is made --- .../src/handlers/agent_chat_completions.rs | 33 +++++++++++++++++- .../src/handlers/pipeline_processor.rs | 34 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/crates/brightstaff/src/handlers/agent_chat_completions.rs b/crates/brightstaff/src/handlers/agent_chat_completions.rs index a1a00f88..e870e514 100644 --- a/crates/brightstaff/src/handlers/agent_chat_completions.rs +++ b/crates/brightstaff/src/handlers/agent_chat_completions.rs @@ -37,7 +37,38 @@ pub async fn agent_chat( match handle_agent_chat(request, router_service, agents_list, listeners).await { Ok(response) => Ok(response), Err(err) => { - // Print detailed error information with full error chain + // Check if this is a client error from the pipeline that should be cascaded + if let AgentFilterChainError::Pipeline(PipelineError::ClientError { + agent, + status, + body, + }) = &err + { + warn!( + "Client error from agent '{}' (HTTP {}): {}", + agent, status, body + ); + + // Create error response with the original status code and body + let error_json = serde_json::json!({ + "error": "ClientError", + "agent": agent, + "status": status, + "agent_response": body + }); + + let json_string = error_json.to_string(); + let mut response = Response::new(ResponseHandler::create_full_body(json_string)); + *response.status_mut() = + hyper::StatusCode::from_u16(*status).unwrap_or(hyper::StatusCode::BAD_REQUEST); + response.headers_mut().insert( + hyper::header::CONTENT_TYPE, + "application/json".parse().unwrap(), + ); + return Ok(response); + } + + // Print detailed error information with full error chain for other errors let mut error_chain = Vec::new(); let mut current_error: &dyn std::error::Error = &err; diff --git a/crates/brightstaff/src/handlers/pipeline_processor.rs b/crates/brightstaff/src/handlers/pipeline_processor.rs index b62ce175..6d70c30a 100644 --- a/crates/brightstaff/src/handlers/pipeline_processor.rs +++ b/crates/brightstaff/src/handlers/pipeline_processor.rs @@ -19,6 +19,18 @@ pub enum PipelineError { NoChoicesInResponse(String), #[error("No content in response from agent '{0}'")] NoContentInResponse(String), + #[error("Client error from agent '{agent}' (HTTP {status}): {body}")] + ClientError { + agent: String, + status: u16, + body: String, + }, + #[error("Server error from agent '{agent}' (HTTP {status}): {body}")] + ServerError { + agent: String, + status: u16, + body: String, + }, } /// Service for processing agent pipelines @@ -122,8 +134,30 @@ impl PipelineProcessor { .send() .await?; + let status = response.status(); let response_bytes = response.bytes().await?; + // Check for HTTP errors and handle them appropriately + if !status.is_success() { + let error_body = String::from_utf8_lossy(&response_bytes).to_string(); + + if status.is_client_error() { + // 4xx errors - cascade back to developer + return Err(PipelineError::ClientError { + agent: agent.id.clone(), + status: status.as_u16(), + body: error_body, + }); + } else if status.is_server_error() { + // 5xx errors - server/agent error + return Err(PipelineError::ServerError { + agent: agent.id.clone(), + status: status.as_u16(), + body: error_body, + }); + } + } + // Parse the response as JSON to extract the content let response_json: serde_json::Value = serde_json::from_slice(&response_bytes)?;