feat: support moderation api (#55)
This commit is contained in:
parent
8f5f986298
commit
e5660e39bf
10 changed files with 543 additions and 0 deletions
|
|
@ -25,6 +25,8 @@ import ai.z.openapi.service.assistant.AssistantService;
|
|||
import ai.z.openapi.service.assistant.AssistantServiceImpl;
|
||||
import ai.z.openapi.service.voiceclone.VoiceCloneService;
|
||||
import ai.z.openapi.service.voiceclone.VoiceCloneServiceImpl;
|
||||
import ai.z.openapi.service.moderations.ModerationService;
|
||||
import ai.z.openapi.service.moderations.ModerationServiceImpl;
|
||||
import ai.z.openapi.core.config.ZaiConfig;
|
||||
import ai.z.openapi.core.model.BiFlowableClientResponse;
|
||||
import ai.z.openapi.core.model.ClientRequest;
|
||||
|
|
@ -104,6 +106,9 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
|
|||
/** Voice clone service for voice cloning operations */
|
||||
private VoiceCloneService voiceCloneService;
|
||||
|
||||
/** Moderation service for content safety detection */
|
||||
private ModerationService moderationService;
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractAiClient with the specified configuration.
|
||||
* @param config the configuration object containing API keys, timeouts, and other
|
||||
|
|
@ -256,6 +261,18 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
|
|||
return voiceCloneService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the moderation service for content safety detection. This service handles
|
||||
* content moderation for text, image, video, and audio inputs.
|
||||
* @return the ModerationService instance (lazily initialized)
|
||||
*/
|
||||
public synchronized ModerationService moderations() {
|
||||
if (moderationService == null) {
|
||||
this.moderationService = new ModerationServiceImpl(this);
|
||||
}
|
||||
return moderationService;
|
||||
}
|
||||
|
||||
// ==================== Utility Methods ====================
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package ai.z.openapi.api.moderations;
|
||||
|
||||
import ai.z.openapi.service.moderations.ModerationCreateParams;
|
||||
import ai.z.openapi.service.moderations.ModerationResult;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.POST;
|
||||
|
||||
/**
|
||||
* Moderation API for content safety detection Provides content moderation capabilities
|
||||
* for text, image, audio, and video formats Accurately identifies risky content including
|
||||
* adult content, violence, illegal content, etc. Returns structured moderation results
|
||||
* including content type, risk type, and specific risk segments
|
||||
*/
|
||||
public interface ModerationApi {
|
||||
|
||||
/**
|
||||
* Create a content moderation request for safety detection Analyzes content for
|
||||
* potential risks including adult content, violence, illegal activities Supports
|
||||
* multiple content types: text strings, images, audio, and video files Returns
|
||||
* detailed risk assessment with structured results and specific risk segments
|
||||
* @param request Moderation parameters including content to analyze (text string or
|
||||
* multimedia object) Text: maximum 2000 characters Images: less than 10M, minimum
|
||||
* resolution 20x20, maximum 6000x6000 Video: recommended duration 30 seconds Audio:
|
||||
* recommended duration 60 seconds
|
||||
* @return Moderation response with risk level (PASS/REVIEW/REJECT), content type,
|
||||
* risk types, and processing time information
|
||||
*/
|
||||
@POST("moderations")
|
||||
Single<ModerationResult> createModeration(@Body ModerationCreateParams request);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package ai.z.openapi.service.moderations;
|
||||
|
||||
import ai.z.openapi.core.model.ClientRequest;
|
||||
import ai.z.openapi.service.CommonRequest;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parameters for creating a moderation request to check content safety.
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class ModerationCreateParams extends CommonRequest implements ClientRequest<ModerationCreateParams> {
|
||||
|
||||
/**
|
||||
* The model to use for moderation. Currently, supports "moderation".
|
||||
*/
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* The input content to moderate. Can be text, image, video, or audio.
|
||||
*/
|
||||
private List<ModerationInput> input;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package ai.z.openapi.service.moderations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Base class for moderation input content
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ModerationInput {
|
||||
|
||||
/**
|
||||
* Type of content being moderated Possible values: "text", "image_url", "video_url",
|
||||
* "audio_url"
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* Text content for text moderation
|
||||
*/
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Image URL configuration for image moderation
|
||||
*/
|
||||
@JsonProperty("image_url")
|
||||
private MediaUrl imageUrl;
|
||||
|
||||
/**
|
||||
* Video URL configuration for video moderation
|
||||
*/
|
||||
@JsonProperty("video_url")
|
||||
private MediaUrl videoUrl;
|
||||
|
||||
/**
|
||||
* Audio URL configuration for audio moderation
|
||||
*/
|
||||
@JsonProperty("audio_url")
|
||||
private MediaUrl audioUrl;
|
||||
|
||||
/**
|
||||
* Helper method to create text input
|
||||
*/
|
||||
public static ModerationInput text(String text) {
|
||||
return ModerationInput.builder().type("text").text(text).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create image input
|
||||
*/
|
||||
public static ModerationInput image(String url) {
|
||||
return ModerationInput.builder().type("image_url").imageUrl(MediaUrl.builder().url(url).build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create video input
|
||||
*/
|
||||
public static ModerationInput video(String url) {
|
||||
return ModerationInput.builder().type("video_url").videoUrl(MediaUrl.builder().url(url).build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create audio input
|
||||
*/
|
||||
public static ModerationInput audio(String url) {
|
||||
return ModerationInput.builder().type("audio_url").audioUrl(MediaUrl.builder().url(url).build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Media URL configuration
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class MediaUrl {
|
||||
|
||||
/**
|
||||
* URL of the media file
|
||||
*/
|
||||
private String url;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package ai.z.openapi.service.moderations;
|
||||
|
||||
import ai.z.openapi.core.model.ClientResponse;
|
||||
import ai.z.openapi.service.model.ChatError;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Response wrapper for moderation API calls. Contains the result of content moderation
|
||||
* operations along with status information.
|
||||
*/
|
||||
@Data
|
||||
public class ModerationResponse implements ClientResponse<ModerationResult> {
|
||||
|
||||
/**
|
||||
* Response status code.
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* Response message.
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* Indicates whether the request was successful.
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* The moderation result data.
|
||||
*/
|
||||
private ModerationResult data;
|
||||
|
||||
/**
|
||||
* Error information if the request failed.
|
||||
*/
|
||||
private ChatError error;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package ai.z.openapi.service.moderations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Result data from the moderation API containing safety analysis results.
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ModerationResult {
|
||||
|
||||
/**
|
||||
* Task id.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Unique request identifier for tracking.
|
||||
*/
|
||||
@JsonProperty("request_id")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* Request time in milliseconds.
|
||||
*/
|
||||
private Long created;
|
||||
|
||||
/**
|
||||
* List of moderation results for each input item.
|
||||
*/
|
||||
@JsonProperty("result_list")
|
||||
private List<ModerationItem> resultList;
|
||||
|
||||
/**
|
||||
* Token usage information for the request.
|
||||
*/
|
||||
private ModerationUsage usage;
|
||||
|
||||
/**
|
||||
* Individual moderation result for a single input item.
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ModerationItem {
|
||||
|
||||
/**
|
||||
* Type of content being moderated (text, image, video, audio).
|
||||
*/
|
||||
@JsonProperty("content_type")
|
||||
private String contentType;
|
||||
|
||||
/**
|
||||
* Risk level assessment: "PASS", "REVIEW", "REJECT".
|
||||
*/
|
||||
@JsonProperty("risk_level")
|
||||
private String riskLevel;
|
||||
|
||||
@JsonProperty("risk_type")
|
||||
private List<String> riskType;
|
||||
|
||||
/**
|
||||
* Check if the content is flagged as unsafe.
|
||||
* @return true if risk level is REVIEW or REJECT
|
||||
*/
|
||||
public boolean isFlagged() {
|
||||
return "REVIEW".equalsIgnoreCase(riskLevel) || "REJECT".equalsIgnoreCase(riskLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the content is safe.
|
||||
* @return true if risk level is low
|
||||
*/
|
||||
public boolean isSafe() {
|
||||
return "PASS".equalsIgnoreCase(riskLevel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package ai.z.openapi.service.moderations;
|
||||
|
||||
/**
|
||||
* Moderation service for content safety detection Provides content moderation
|
||||
* capabilities for text, image, audio, and video formats Accurately identifies risky
|
||||
* content including adult content, violence, illegal content, etc. Returns structured
|
||||
* moderation results including content type, risk type, and specific risk segments
|
||||
*/
|
||||
public interface ModerationService {
|
||||
|
||||
/**
|
||||
* Create a content moderation request for safety detection Analyzes content for
|
||||
* potential risks including adult content, violence, illegal activities Supports
|
||||
* multiple content types: text strings, images, audio, and video files Returns
|
||||
* detailed risk assessment with structured results and specific risk segments
|
||||
* @param request Moderation parameters including content to analyze (text string or
|
||||
* multimedia object) Text: maximum 2000 characters Images: less than 10M, minimum
|
||||
* resolution 20x20, maximum 6000x6000 Video: recommended duration 30 seconds Audio:
|
||||
* recommended duration 60 seconds
|
||||
* @return Moderation response with risk level (PASS/REVIEW/REJECT), content type,
|
||||
* risk types, and processing time information
|
||||
*/
|
||||
ModerationResponse createModeration(ModerationCreateParams request);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package ai.z.openapi.service.moderations;
|
||||
|
||||
import ai.z.openapi.AbstractAiClient;
|
||||
import ai.z.openapi.api.moderations.ModerationApi;
|
||||
import ai.z.openapi.utils.RequestSupplier;
|
||||
|
||||
/**
|
||||
* Implementation of ModerationService
|
||||
*/
|
||||
public class ModerationServiceImpl implements ModerationService {
|
||||
|
||||
private final AbstractAiClient zAiClient;
|
||||
|
||||
private final ModerationApi moderationApi;
|
||||
|
||||
public ModerationServiceImpl(AbstractAiClient zAiClient) {
|
||||
this.zAiClient = zAiClient;
|
||||
this.moderationApi = zAiClient.retrofit().create(ModerationApi.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModerationResponse createModeration(ModerationCreateParams request) {
|
||||
RequestSupplier<ModerationCreateParams, ModerationResult> supplier = moderationApi::createModeration;
|
||||
return this.zAiClient.executeRequest(request, supplier, ModerationResponse.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package ai.z.openapi.service.moderations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Usage statistics for Moderation API calls.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ModerationUsage {
|
||||
|
||||
@JsonProperty("moderation_text")
|
||||
private ModerationText moderationText;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ModerationText {
|
||||
|
||||
@JsonProperty("call_count")
|
||||
private String callCount;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
160
samples/src/main/ai.z.openapi.samples/ModerationExample.java
Normal file
160
samples/src/main/ai.z.openapi.samples/ModerationExample.java
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
package ai.z.openapi.samples;
|
||||
|
||||
import ai.z.openapi.ZaiClient;
|
||||
import ai.z.openapi.service.moderations.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Moderation Example
|
||||
* Demonstrates how to use ZaiClient to moderate content for safety
|
||||
*/
|
||||
public class ModerationExample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create client
|
||||
ZaiClient client = ZaiClient.builder().build();
|
||||
|
||||
// Example 1: Text moderation
|
||||
System.out.println("=== Text Moderation Example ===");
|
||||
moderateText(client);
|
||||
|
||||
// Example 2: Image moderation
|
||||
System.out.println("\n=== Image Moderation Example ===");
|
||||
moderateImage(client);
|
||||
}
|
||||
|
||||
private static void moderateText(ZaiClient client) {
|
||||
// Create text moderation inputs
|
||||
List<ModerationInput> inputs = Arrays.asList(
|
||||
ModerationInput.text("This is a normal message about technology.")
|
||||
);
|
||||
|
||||
// Create moderation request
|
||||
ModerationCreateParams request = ModerationCreateParams.builder()
|
||||
.model("moderation")
|
||||
.input(inputs)
|
||||
.build();
|
||||
|
||||
try {
|
||||
// Execute request
|
||||
ModerationResponse response = client.moderations().createModeration(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
System.out.println("Text moderation completed successfully:");
|
||||
System.out.println("Request ID: " + response.getData().getRequestId());
|
||||
|
||||
response.getData().getResultList().forEach(item -> {
|
||||
System.out.println("\nContent Type: " + item.getContentType());
|
||||
System.out.println("Risk Level: " + item.getRiskLevel());
|
||||
System.out.println("Risk Type: " + item.getRiskType());
|
||||
System.out.println("Is Safe: " + item.isSafe());
|
||||
System.out.println("Is Flagged: " + item.isFlagged());
|
||||
});
|
||||
} else {
|
||||
System.err.println("Error: Text moderation failed: " + response.getMsg());
|
||||
if (response.getError() != null) {
|
||||
System.err.println("Error details: " + response.getError().getMessage());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Text moderation exception: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void moderateImage(ZaiClient client) {
|
||||
// Create image moderation input
|
||||
List<ModerationInput> inputs = Arrays.asList(
|
||||
ModerationInput.image("https://example.com/sample-image.jpg"),
|
||||
ModerationInput.image("https://example.com/another-image.png")
|
||||
);
|
||||
|
||||
// Create moderation request
|
||||
ModerationCreateParams request = ModerationCreateParams.builder()
|
||||
.model("moderation")
|
||||
.input(inputs)
|
||||
.build();
|
||||
|
||||
try {
|
||||
// Execute request
|
||||
ModerationResponse response = client.moderations().createModeration(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
System.out.println("Image moderation completed successfully:");
|
||||
System.out.println("Request ID: " + response.getData().getRequestId());
|
||||
|
||||
response.getData().getResultList().forEach(item -> {
|
||||
System.out.println("\nContent Type: " + item.getContentType());
|
||||
System.out.println("Risk Level: " + item.getRiskLevel());
|
||||
System.out.println("Status: " + (item.isSafe() ? "SAFE" : "FLAGGED"));
|
||||
if (item.getRiskType() != null) {
|
||||
System.out.println("Risk Type: " + item.getRiskType());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
System.err.println("Error: Image moderation failed: " + response.getMsg());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Image moderation exception: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void moderateMixedContent(ZaiClient client) {
|
||||
// Create mixed content moderation inputs
|
||||
List<ModerationInput> inputs = Arrays.asList(
|
||||
ModerationInput.text("This is a text message to be moderated."),
|
||||
ModerationInput.image("https://example.com/image-to-check.jpg"),
|
||||
ModerationInput.video("https://example.com/video-sample.mp4"),
|
||||
ModerationInput.audio("https://example.com/audio-sample.mp3")
|
||||
);
|
||||
|
||||
// Create moderation request
|
||||
ModerationCreateParams request = ModerationCreateParams.builder()
|
||||
.model("moderation")
|
||||
.input(inputs)
|
||||
.build();
|
||||
|
||||
try {
|
||||
// Execute request
|
||||
ModerationResponse response = client.moderations().createModeration(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
System.out.println("Mixed content moderation completed successfully:");
|
||||
System.out.println("Total items processed: " + response.getData().getResultList().size());
|
||||
|
||||
response.getData().getResultList().forEach(item -> {
|
||||
System.out.println("\n--- Moderation Result ---");
|
||||
System.out.println("Content Type: " + item.getContentType());
|
||||
System.out.println("Risk Assessment: " + item.getRiskLevel());
|
||||
System.out.println("Safety Status: " + (item.isSafe() ? "✓ SAFE" : "⚠ FLAGGED"));
|
||||
|
||||
if (item.isFlagged()) {
|
||||
System.out.println("Risk Category: " + item.getRiskType());
|
||||
}
|
||||
});
|
||||
|
||||
// Summary statistics
|
||||
long safeCount = response.getData().getResultList().stream()
|
||||
.mapToLong(item -> item.isSafe() ? 1 : 0)
|
||||
.sum();
|
||||
long flaggedCount = response.getData().getResultList().size() - safeCount;
|
||||
|
||||
System.out.println("\n--- Summary ---");
|
||||
System.out.println("Safe items: " + safeCount);
|
||||
System.out.println("Flagged items: " + flaggedCount);
|
||||
|
||||
if (response.getData().getUsage() != null) {
|
||||
System.out.println("Total tokens used: " + response.getData().getUsage().getModerationText());
|
||||
}
|
||||
} else {
|
||||
System.err.println("Error: Mixed content moderation failed: " + response.getMsg());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Mixed content moderation exception: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue