Skip to content

Validate user messages on chat completions endpoint #383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

mdevino
Copy link
Collaborator

@mdevino mdevino commented Apr 24, 2025

Closes #382.

Copy link
Collaborator

@evaline-ju evaline-ju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this particular endpoint currently, where /content detectors are invokable on chat completions input or output, we would expect text content to be provided for the last message, with any of the roles that this endpoint will work for. The role validation is done here

Some(Role::User) | Some(Role::Assistant) | Some(Role::System)
on input, so I’m curious why we want to check at the deserialization separately, if the deserialization is not where the error occurs?

@mdevino
Copy link
Collaborator Author

mdevino commented Apr 24, 2025

For this particular endpoint currently, where /content detectors are invokable on chat completions input or output, we would expect text content to be provided for the last message, with any of the roles that this endpoint will work for. The role validation is done here https://github.com/evaline-ju/fms-guardrails-orchestrator/blob/6bc045f0768bc9f9688c47a2dc5f7605a01b4d96/src/orchestrator/handlers/chat_completions_detection/unary.rs#L121 on input, so I’m curious why we want to check at the deserialization separately, if the deserialization is not where the error occurs?

Thanks for catching that. I'm not super familiar with the codebase for this endpoint yet, so I wasn't sure that was the best place to put the validation.
I've updated the code to validate it where you suggested. The one thing I noticed is that, when made the change requested, it doesn't fix the issue if no input detectors are provided (it does fix it if an input detector is provided, though).

Request

{
		"messages": [
				{
					"role": "user",
					"name": "string"
				}
			],
			"model": "my_model_id",
			"n": 1,
			"temperature": 1,
			"top_p": 1,
			"user": "user-1234",
			"detectors": {}
}

Response

{
	"code": 422,
	"details": "chat completion request failed for `my_model_id`: list index out of range"
}

@mdevino mdevino force-pushed the validate_user_messages branch from 12acc24 to fdc369e Compare April 24, 2025 18:13
mdevino added 2 commits April 25, 2025 11:40
Signed-off-by: Mateus Devino <mdevino@ibm.com>
Signed-off-by: Mateus Devino <mdevino@ibm.com>
@mdevino mdevino force-pushed the validate_user_messages branch from fdc369e to 9aac20d Compare April 25, 2025 15:13
Comment on lines 220 to 235
let messages = if let Some(messages) = value.get("messages") {
Vec::<Message>::deserialize(messages)
.map_err(|_| ValidationError::Invalid("error deserializing `messages`".into()))
.map(|messages| {
for message in &messages {
if message.role == Role::User && message.content.is_none() {
return Err(ValidationError::Invalid(
"`content` is mandatory for user messages".into(),
));
}
}
Ok(messages)
})
.map_err(|_| ValidationError::Invalid("error deserializing `messages`".into()))?
} else {
Err(ValidationError::Required("messages".into()))
}?;
Copy link
Collaborator

@declark1 declark1 Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is finished yet, but a better way to write this would be below. It's good to use any / all iterator methods for stuff like this.

let messages = if let Some(messages) = value.get("messages") {
    Vec::<Message>::deserialize(messages)
        .map_err(|_| ValidationError::Invalid("error deserializing `messages`".into()))
} else {
    Err(ValidationError::Required("messages".into()))
}?;
if messages
    .iter()
    .any(|message| message.role == Role::User && message.content.is_none())
{
    return Err(ValidationError::Invalid(
        "`content` is required for user messages".into(),
    ));
};

Btw, this just validates that Content is provided, but the inner value Content::Text(text) could still be an empty String. Do we want to validate that it isn't empty?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the final code yet. I just pushed what I have before going on vacations, so someone could pick up form there if needed. I like the any() suggestion!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Chat completions endpoint going all the way through when user messages have no content
3 participants