Spring Boot Starter for OpenAI integration providing auto-configuration for chat completion, embeddings, image generation, audio speech synthesis, audio transcription, and content moderation models. Includes high-level ChatClient API and conversation memory support.
The OpenAiModerationModel checks content for policy violations and detects harmful content across multiple categories.
import org.springframework.ai.openai.OpenAiModerationModel;
import org.springframework.ai.openai.OpenAiModerationOptions;
import org.springframework.ai.model.moderation.ModerationPrompt;
import org.springframework.ai.model.moderation.ModerationResponse;
import org.springframework.ai.model.moderation.Moderation;
import org.springframework.ai.model.moderation.ModerationResult;
import org.springframework.ai.model.moderation.ModerationMessage;
import org.springframework.ai.model.moderation.Categories;
import org.springframework.ai.model.moderation.CategoryScores;package org.springframework.ai.openai;
public class OpenAiModerationModel implements ModerationModel {
// Check content for moderation issues
public ModerationResponse call(ModerationPrompt moderationPrompt);
// Get default options
public OpenAiModerationOptions getDefaultOptions();
// Create with default options
public static OpenAiModerationModel withDefaultOptions(OpenAiModerationOptions options);
}package org.springframework.ai.openai;
public class OpenAiModerationOptions implements ModerationOptions {
// Builder methods
public static OpenAiModerationOptions.Builder builder();
// Getters
public String getModel();
// Setters
public void setModel(String model);
}package org.springframework.ai.model.moderation;
public class Moderation {
public String getId();
public String getModel();
public List<ModerationResult> getResults();
}package org.springframework.ai.model.moderation;
public class ModerationResult {
public boolean isFlagged();
public Categories getCategories();
public CategoryScores getCategoryScores();
}package org.springframework.ai.model.moderation;
public class Categories {
public boolean isSexual();
public boolean isHate();
public boolean isHarassment();
public boolean isSelfHarm();
public boolean isViolence();
public boolean isViolenceGraphic();
public boolean isSexualMinors();
public boolean isHateThreatening();
public boolean isHarassmentThreatening();
public boolean isSelfHarmIntent();
public boolean isSelfHarmInstructions();
public boolean isDangerousAndCriminalContent();
public boolean isHealth();
public boolean isFinancial();
public boolean isLaw();
public boolean isPii();
}package org.springframework.ai.model.moderation;
public class CategoryScores {
public double getSexual();
public double getHate();
public double getHarassment();
public double getSelfHarm();
public double getViolence();
public double getViolenceGraphic();
public double getSexualMinors();
public double getHateThreatening();
public double getHarassmentThreatening();
public double getSelfHarmIntent();
public double getSelfHarmInstructions();
public double getDangerousAndCriminalContent();
public double getHealth();
public double getFinancial();
public double getLaw();
public double getPii();
}import org.springframework.ai.openai.OpenAiModerationModel;
import org.springframework.ai.model.moderation.ModerationPrompt;
import org.springframework.ai.model.moderation.ModerationResponse;
import org.springframework.ai.model.moderation.Moderation;
import org.springframework.ai.model.moderation.ModerationResult;
import org.springframework.stereotype.Service;
@Service
public class ContentModerationService {
private final OpenAiModerationModel moderationModel;
public ContentModerationService(OpenAiModerationModel moderationModel) {
this.moderationModel = moderationModel;
}
public boolean isContentSafe(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
if (result.isFlagged()) {
return false;
}
}
return true;
}
}import org.springframework.ai.model.moderation.Categories;
import org.springframework.ai.model.moderation.CategoryScores;
import java.util.Map;
import java.util.HashMap;
public Map<String, Object> analyzeContent(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
Map<String, Object> analysis = new HashMap<>();
for (ModerationResult result : moderation.getResults()) {
analysis.put("flagged", result.isFlagged());
Categories categories = result.getCategories();
Map<String, Boolean> categoryMap = new HashMap<>();
categoryMap.put("sexual", categories.isSexual());
categoryMap.put("hate", categories.isHate());
categoryMap.put("harassment", categories.isHarassment());
categoryMap.put("self-harm", categories.isSelfHarm());
categoryMap.put("violence", categories.isViolence());
categoryMap.put("violence/graphic", categories.isViolenceGraphic());
analysis.put("categories", categoryMap);
CategoryScores scores = result.getCategoryScores();
Map<String, Double> scoresMap = new HashMap<>();
scoresMap.put("sexual", scores.getSexual());
scoresMap.put("hate", scores.getHate());
scoresMap.put("harassment", scores.getHarassment());
scoresMap.put("self-harm", scores.getSelfHarm());
scoresMap.put("violence", scores.getViolence());
scoresMap.put("violence/graphic", scores.getViolenceGraphic());
analysis.put("categoryScores", scoresMap);
}
return analysis;
}import java.util.List;
import java.util.stream.Collectors;
public List<Boolean> moderateMultiple(List<String> texts) {
return texts.stream()
.map(text -> {
ModerationPrompt prompt = new ModerationPrompt(text);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
if (result.isFlagged()) {
return false;
}
}
return true;
})
.collect(Collectors.toList());
}public boolean hasViolence(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
Categories categories = result.getCategories();
if (categories.isViolence()) {
return true;
}
}
return false;
}
public boolean hasHate(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
Categories categories = result.getCategories();
if (categories.isHate()) {
return true;
}
}
return false;
}
public boolean hasSexual(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
Categories categories = result.getCategories();
if (categories.isSexual()) {
return true;
}
}
return false;
}public boolean isContentSafeWithThreshold(String content, double threshold) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
CategoryScores scores = result.getCategoryScores();
if (scores.getSexual() >= threshold ||
scores.getHate() >= threshold ||
scores.getHarassment() >= threshold ||
scores.getSelfHarm() >= threshold ||
scores.getViolence() >= threshold ||
scores.getViolenceGraphic() >= threshold ||
scores.getSexualMinors() >= threshold ||
scores.getHateThreatening() >= threshold ||
scores.getHarassmentThreatening() >= threshold ||
scores.getSelfHarmIntent() >= threshold ||
scores.getSelfHarmInstructions() >= threshold) {
return false;
}
}
return true;
}
// Example: Flag content if any category exceeds 0.7
public boolean isSafe(String content) {
return isContentSafeWithThreshold(content, 0.7);
}public void printModerationDetails(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
System.out.println("Model: " + moderation.getModel());
System.out.println("ID: " + moderation.getId());
for (ModerationResult result : moderation.getResults()) {
System.out.println("\nContent flagged: " + result.isFlagged());
Categories categories = result.getCategories();
System.out.println("\nCategory Flags:");
System.out.println(" sexual: " + categories.isSexual());
System.out.println(" hate: " + categories.isHate());
System.out.println(" harassment: " + categories.isHarassment());
System.out.println(" self-harm: " + categories.isSelfHarm());
System.out.println(" violence: " + categories.isViolence());
System.out.println(" violence/graphic: " + categories.isViolenceGraphic());
System.out.println(" sexual/minors: " + categories.isSexualMinors());
System.out.println(" hate/threatening: " + categories.isHateThreatening());
System.out.println(" harassment/threatening: " + categories.isHarassmentThreatening());
System.out.println(" self-harm/intent: " + categories.isSelfHarmIntent());
System.out.println(" self-harm/instructions: " + categories.isSelfHarmInstructions());
CategoryScores scores = result.getCategoryScores();
System.out.println("\nCategory Scores:");
System.out.println(" sexual: " + String.format("%.4f", scores.getSexual()));
System.out.println(" hate: " + String.format("%.4f", scores.getHate()));
System.out.println(" harassment: " + String.format("%.4f", scores.getHarassment()));
System.out.println(" self-harm: " + String.format("%.4f", scores.getSelfHarm()));
System.out.println(" violence: " + String.format("%.4f", scores.getViolence()));
System.out.println(" violence/graphic: " + String.format("%.4f", scores.getViolenceGraphic()));
System.out.println(" sexual/minors: " + String.format("%.4f", scores.getSexualMinors()));
System.out.println(" hate/threatening: " + String.format("%.4f", scores.getHateThreatening()));
System.out.println(" harassment/threatening: " + String.format("%.4f", scores.getHarassmentThreatening()));
System.out.println(" self-harm/intent: " + String.format("%.4f", scores.getSelfHarmIntent()));
System.out.println(" self-harm/instructions: " + String.format("%.4f", scores.getSelfHarmInstructions()));
}
}import org.springframework.ai.openai.OpenAiModerationOptions;
public boolean moderateWithOptions(String content) {
OpenAiModerationOptions options = OpenAiModerationOptions.builder()
.model("text-moderation-latest")
.build();
ModerationPrompt prompt = new ModerationPrompt(content, options);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
if (result.isFlagged()) {
return false;
}
}
return true;
}import java.util.List;
import java.util.ArrayList;
public String filterContent(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
if (result.isFlagged()) {
List<String> flaggedCategories = new ArrayList<>();
Categories categories = result.getCategories();
if (categories.isSexual()) flaggedCategories.add("sexual");
if (categories.isHate()) flaggedCategories.add("hate");
if (categories.isHarassment()) flaggedCategories.add("harassment");
if (categories.isSelfHarm()) flaggedCategories.add("self-harm");
if (categories.isViolence()) flaggedCategories.add("violence");
if (categories.isViolenceGraphic()) flaggedCategories.add("violence/graphic");
if (categories.isSexualMinors()) flaggedCategories.add("sexual/minors");
if (categories.isHateThreatening()) flaggedCategories.add("hate/threatening");
if (categories.isHarassmentThreatening()) flaggedCategories.add("harassment/threatening");
if (categories.isSelfHarmIntent()) flaggedCategories.add("self-harm/intent");
if (categories.isSelfHarmInstructions()) flaggedCategories.add("self-harm/instructions");
return "Content blocked due to: " + String.join(", ", flaggedCategories);
}
}
return content;
}import java.util.Map;
import java.util.HashMap;
public Map<String, Boolean> moderateBatch(List<String> contents) {
Map<String, Boolean> results = new HashMap<>();
for (String content : contents) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
boolean safe = true;
for (ModerationResult result : moderation.getResults()) {
if (result.isFlagged()) {
safe = false;
break;
}
}
results.put(content, safe);
}
return results;
}Configure via application.properties:
# API Connection
spring.ai.openai.moderation.api-key=sk-...
spring.ai.openai.moderation.base-url=https://api.openai.com
spring.ai.openai.moderation.project-id=proj_...
spring.ai.openai.moderation.organization-id=org-...
# Model Configuration (optional)
spring.ai.openai.moderation.options.model=omni-moderation-latestomni-moderation-latest - Latest omni-modal moderation model (default, automatically updated)text-moderation-latest - Latest text-only moderation modeltext-moderation-stable - Stable text-only version, updated less frequentlyThe moderation model checks for the following categories:
Primary Categories:
hate - Content promoting hate based on identityhate/threatening - Hateful content with violence or threatsharassment - Content intended to harass, threaten, or bullyharassment/threatening - Harassment with threatsself-harm - Content promoting self-harmself-harm/intent - Content expressing intent for self-harmself-harm/instructions - Instructions for self-harmsexual - Sexual content intended to arousesexual/minors - Sexual content involving minorsviolence - Content depicting violenceviolence/graphic - Graphic violent contentAdditional Categories (omni-moderation models):
dangerous-and-criminal-content - Content related to dangerous or criminal activitieshealth - Health-related content that may require professional advicefinancial - Financial advice or content requiring professional guidancelaw - Legal content or advicepii - Personally Identifiable InformationEach category has:
package org.springframework.ai.model.moderation;
public class ModerationResponse {
public Generation getResult();
public List<Generation> getResults();
public ModerationResponseMetadata getMetadata();
}The Generation object contains the Moderation output via getOutput().
import org.springframework.ai.openai.api.OpenAiApiException;
public boolean safeModerate(String content) {
try {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
if (result.isFlagged()) {
return false;
}
}
return true;
} catch (OpenAiApiException e) {
// Handle API errors (rate limits, invalid API key, etc.)
// In case of error, default to flagging content as unsafe
return false;
}
}package org.springframework.ai.openai;
public static class Builder {
public Builder model(String model);
public OpenAiModerationOptions build();
}package org.springframework.ai.model.moderation;
public class ModerationPrompt {
public ModerationPrompt(String text);
public ModerationPrompt(String text, ModerationOptions options);
public ModerationPrompt(ModerationMessage message);
public ModerationPrompt(ModerationMessage message, ModerationOptions options);
public ModerationPrompt(List<ModerationMessage> messages);
public ModerationPrompt(List<ModerationMessage> messages, ModerationOptions options);
public ModerationMessage getInstructions();
public ModerationOptions getOptions();
}package org.springframework.ai.model.moderation;
public class ModerationMessage {
public ModerationMessage(String content);
public String getContent();
}ModerationResult objects in the results listtessl i tessl/maven-org-springframework-ai--spring-ai-starter-model-openai@1.1.1