feat: support moderation api (#55)

This commit is contained in:
Tomsun28 2025-10-15 11:12:27 +08:00 committed by GitHub
parent 8f5f986298
commit e5660e39bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 543 additions and 0 deletions

View file

@ -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 ====================
/**

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View 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();
}
}
}