in prompt/prompt-executor/prompt-executor-clients/prompt-executor-openai-client/src/commonMain/kotlin/ai/koog/prompt/executor/clients/openai/OpenAILLMClient.kt [445:516]
public override suspend fun moderate(prompt: Prompt, model: LLModel): ModerationResult {
logger.debug { "Moderating text and image content with model: $model" }
model.requireCapability(LLMCapability.Moderation)
require(prompt.messages.isNotEmpty()) { "Can't moderate an empty prompt" }
val input = prompt.messages
.map { message ->
require(message.parts.all { it is ContentPart.Text || it is ContentPart.Image }) {
"Only image attachments are supported for moderation"
}
message.toMessageContent(model)
}
.let { contents ->
/*
If all messages contain only text, merge it all in a single text input,
to support OpenAI-compatible providers that do not support attachments.
Otherwise create a single content instance with all the parts
*/
if (contents.all { it is OpenAIContent.Text }) {
val text = contents.joinToString(separator = "\n\n") { (it as OpenAIContent.Text).value }
OpenAIContent.Text(text)
} else {
val parts = contents.flatMap { content ->
when (content) {
is OpenAIContent.Parts -> content.value
is OpenAIContent.Text -> listOf(OpenAIContentPart.Text(content.value))
}
}
OpenAIContent.Parts(parts)
}
}
val request = OpenAIModerationRequest(
input = input,
model = model.id
)
val openAIResponse = withContext(Dispatchers.SuitableForIO) {
try {
httpClient.post(
path = settings.moderationsPath,
request = request,
requestBodyType = OpenAIModerationRequest::class,
responseType = OpenAIModerationResponse::class
)
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
throw LLMClientException(
clientName = clientName,
message = e.message,
cause = e
)
}
}
if (openAIResponse.results.isEmpty()) {
val exception = LLMClientException(clientName, "Empty results in OpenAI moderation response")
logger.error(exception) { exception.message }
throw exception
}
val result = openAIResponse.results.first()
// Convert OpenAI categories to a map
return convertModerationResult(result)
}