Skip to content

Commit cc064f1

Browse files
authored
Validate message content on chat completions endpoint (#383)
* Add content validation Signed-off-by: Mateus Devino <mdevino@ibm.com> * Add no_detectors tests for chat completions Signed-off-by: Mateus Devino <mdevino@ibm.com> * Add empty content message test for chat completions Signed-off-by: Mateus Devino <mdevino@ibm.com> * Add comments about changing validation Signed-off-by: Mateus Devino <mdevino@ibm.com> * Update chat completions passthrough tests for empty message Signed-off-by: Mateus Devino <mdevino@ibm.com> * Add further validation for empty content Signed-off-by: Mateus Devino <mdevino@ibm.com> * Test case: passthrough with last message as an array Signed-off-by: Mateus Devino <mdevino@ibm.com> * Prevent content array on last message when input detector is present Signed-off-by: Mateus Devino <mdevino@ibm.com> * nit: combine if clauses Signed-off-by: Mateus Devino <mdevino@ibm.com> --------- Signed-off-by: Mateus Devino <mdevino@ibm.com>
1 parent 88dcf85 commit cc064f1

File tree

3 files changed

+413
-1
lines changed

3 files changed

+413
-1
lines changed

src/clients/openai.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,26 @@ impl ChatCompletionsRequest {
259259
"`messages` must not be empty".into(),
260260
));
261261
}
262+
263+
if !self.detectors.input.is_empty() {
264+
// Content of type Array is not supported yet
265+
// Adding this validation separately as we do plan to support arrays of string in the future
266+
if let Some(Content::Array(_)) = self.messages.last().unwrap().content {
267+
return Err(ValidationError::Invalid(
268+
"Detection on array is not supported".into(),
269+
));
270+
}
271+
272+
// As text_content detections only run on last message at the moment, only the last
273+
// message is being validated.
274+
if self.messages.last().unwrap().is_text_content_empty() {
275+
return Err(ValidationError::Invalid(
276+
"if input detectors are provided, `content` must not be empty on last message"
277+
.into(),
278+
));
279+
}
280+
}
281+
262282
Ok(())
263283
}
264284
}
@@ -414,6 +434,32 @@ pub struct Message {
414434
pub tool_call_id: Option<String>,
415435
}
416436

437+
impl Message {
438+
/// Checks if text content of a message is empty.
439+
///
440+
/// The following messages are considered empty:
441+
/// 1. [`Message::content`] is None.
442+
/// 2. [`Message::content`] is an empty string.
443+
/// 3. [`Message::content`] is an empty array.
444+
/// 4. [`Message::content`] is an array of empty strings and ContentType is Text.
445+
pub fn is_text_content_empty(&self) -> bool {
446+
match &self.content {
447+
Some(content) => match content {
448+
Content::Text(string) => string.is_empty(),
449+
Content::Array(content_parts) => {
450+
content_parts.is_empty()
451+
|| content_parts.iter().all(|content_part| {
452+
content_part.text.is_none()
453+
|| (content_part.r#type == ContentType::Text
454+
&& content_part.text.as_ref().unwrap().is_empty())
455+
})
456+
}
457+
},
458+
None => true,
459+
}
460+
}
461+
}
462+
417463
/// Content.
418464
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
419465
#[serde(untagged)]

src/orchestrator/handlers/chat_completions_detection/unary.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ async fn handle_input_detection(
108108
let model_id = task.request.model.clone();
109109

110110
// Input detectors are only applied to the last message
111+
// If this changes, the empty content validation in [`ChatCompletionsRequest::validate`]
112+
// should also change.
111113
// Get the last message
112114
let messages = task.request.messages();
113115
let message = if let Some(message) = messages.last() {

0 commit comments

Comments
 (0)