feat: add ocr api (#80)
This commit is contained in:
parent
633b1d323d
commit
59e9a606eb
10 changed files with 379 additions and 2 deletions
|
|
@ -27,6 +27,8 @@ import ai.z.openapi.service.web_reader.WebReaderService;
|
||||||
import ai.z.openapi.service.web_reader.WebReaderServiceImpl;
|
import ai.z.openapi.service.web_reader.WebReaderServiceImpl;
|
||||||
import ai.z.openapi.service.videos.VideosService;
|
import ai.z.openapi.service.videos.VideosService;
|
||||||
import ai.z.openapi.service.videos.VideosServiceImpl;
|
import ai.z.openapi.service.videos.VideosServiceImpl;
|
||||||
|
import ai.z.openapi.service.layoutparsing.LayoutParsingService;
|
||||||
|
import ai.z.openapi.service.layoutparsing.LayoutParsingServiceImpl;
|
||||||
import ai.z.openapi.service.assistant.AssistantService;
|
import ai.z.openapi.service.assistant.AssistantService;
|
||||||
import ai.z.openapi.service.assistant.AssistantServiceImpl;
|
import ai.z.openapi.service.assistant.AssistantServiceImpl;
|
||||||
import ai.z.openapi.service.voiceclone.VoiceCloneService;
|
import ai.z.openapi.service.voiceclone.VoiceCloneService;
|
||||||
|
|
@ -121,6 +123,9 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
|
||||||
/** HandWriting service for handwritingOcrService operations */
|
/** HandWriting service for handwritingOcrService operations */
|
||||||
private HandwritingOcrService handwritingOcrService;
|
private HandwritingOcrService handwritingOcrService;
|
||||||
|
|
||||||
|
/** Layout parsing service for layout_parsing operations */
|
||||||
|
private LayoutParsingService layoutParsingService;
|
||||||
|
|
||||||
/** Moderation service for content safety detection */
|
/** Moderation service for content safety detection */
|
||||||
private ModerationService moderationService;
|
private ModerationService moderationService;
|
||||||
|
|
||||||
|
|
@ -307,6 +312,13 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
|
||||||
return handwritingOcrService;
|
return handwritingOcrService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized LayoutParsingService layoutParsing() {
|
||||||
|
if (layoutParsingService == null) {
|
||||||
|
this.layoutParsingService = new LayoutParsingServiceImpl(this);
|
||||||
|
}
|
||||||
|
return layoutParsingService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the moderation service for content safety detection. This service handles
|
* Returns the moderation service for content safety detection. This service handles
|
||||||
* content moderation for text, image, video, and audio inputs.
|
* content moderation for text, image, video, and audio inputs.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package ai.z.openapi.api.layoutparsing;
|
||||||
|
|
||||||
|
import ai.z.openapi.service.layoutparsing.LayoutParsingCreateParams;
|
||||||
|
import ai.z.openapi.service.layoutparsing.LayoutParsingResult;
|
||||||
|
import io.reactivex.rxjava3.core.Single;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
|
||||||
|
public interface LayoutParsingApi {
|
||||||
|
|
||||||
|
@POST("layout_parsing")
|
||||||
|
Single<LayoutParsingResult> layoutParsing(@Body LayoutParsingCreateParams request);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ai.z.openapi.service.layoutparsing;
|
||||||
|
|
||||||
|
import ai.z.openapi.core.model.ClientRequest;
|
||||||
|
import ai.z.openapi.service.CommonRequest;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class LayoutParsingCreateParams extends CommonRequest implements ClientRequest<LayoutParsingCreateParams> {
|
||||||
|
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
private String file;
|
||||||
|
|
||||||
|
@JsonProperty("return_crop_images")
|
||||||
|
private Boolean returnCropImages;
|
||||||
|
|
||||||
|
@JsonProperty("need_layout_visualization")
|
||||||
|
private Boolean needLayoutVisualization;
|
||||||
|
|
||||||
|
@JsonProperty("start_page_id")
|
||||||
|
private Integer startPageId;
|
||||||
|
|
||||||
|
@JsonProperty("end_page_id")
|
||||||
|
private Integer endPageId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package ai.z.openapi.service.layoutparsing;
|
||||||
|
|
||||||
|
import ai.z.openapi.core.model.ClientResponse;
|
||||||
|
import ai.z.openapi.service.model.ChatError;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LayoutParsingResponse implements ClientResponse<LayoutParsingResult> {
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
private LayoutParsingResult data;
|
||||||
|
|
||||||
|
private ChatError error;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
package ai.z.openapi.service.layoutparsing;
|
||||||
|
|
||||||
|
import ai.z.openapi.service.model.Usage;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class LayoutParsingResult {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private Long created;
|
||||||
|
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
@JsonProperty("md_results")
|
||||||
|
private String mdResults;
|
||||||
|
|
||||||
|
@JsonProperty("layout_details")
|
||||||
|
private List<List<LayoutDetail>> layoutDetails;
|
||||||
|
|
||||||
|
@JsonProperty("layout_visualization")
|
||||||
|
private List<String> layoutVisualization;
|
||||||
|
|
||||||
|
@JsonProperty("data_info")
|
||||||
|
private DataInfo dataInfo;
|
||||||
|
|
||||||
|
@JsonProperty("request_id")
|
||||||
|
private String requestId;
|
||||||
|
|
||||||
|
private Usage usage;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public static class LayoutDetail {
|
||||||
|
|
||||||
|
private Integer index;
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
@JsonProperty("bbox_2d")
|
||||||
|
private Object bbox2dRaw;
|
||||||
|
|
||||||
|
private List<Integer> bbox2d;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
private Integer height;
|
||||||
|
|
||||||
|
private Integer width;
|
||||||
|
|
||||||
|
@JsonProperty("bbox_2d")
|
||||||
|
public void setBbox2dRaw(Object bbox2dRaw) {
|
||||||
|
this.bbox2dRaw = bbox2dRaw;
|
||||||
|
this.bbox2d = convertBbox2d(bbox2dRaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> convertBbox2d(Object bbox2dRaw) {
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
if (bbox2dRaw == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (bbox2dRaw instanceof List) {
|
||||||
|
List<?> rawList = (List<?>) bbox2dRaw;
|
||||||
|
if (!rawList.isEmpty() && rawList.get(0) instanceof List) {
|
||||||
|
// Handle nested array: [[x1, y1, x2, y2]]
|
||||||
|
List<?> innerList = (List<?>) rawList.get(0);
|
||||||
|
for (Object item : innerList) {
|
||||||
|
if (item instanceof Number) {
|
||||||
|
result.add(((Number) item).intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Handle flat array: [x1, y1, x2, y2]
|
||||||
|
for (Object item : rawList) {
|
||||||
|
if (item instanceof Number) {
|
||||||
|
result.add(((Number) item).intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// Log error if needed, but return empty list to avoid breaking the
|
||||||
|
// parsing
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public static class DataInfo {
|
||||||
|
|
||||||
|
@JsonProperty("num_pages")
|
||||||
|
private Integer numPages;
|
||||||
|
|
||||||
|
private List<PageInfo> pages;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public static class PageInfo {
|
||||||
|
|
||||||
|
private Integer width;
|
||||||
|
|
||||||
|
private Integer height;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ai.z.openapi.service.layoutparsing;
|
||||||
|
|
||||||
|
public interface LayoutParsingService {
|
||||||
|
|
||||||
|
LayoutParsingResponse layoutParsing(LayoutParsingCreateParams request);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package ai.z.openapi.service.layoutparsing;
|
||||||
|
|
||||||
|
import ai.z.openapi.AbstractAiClient;
|
||||||
|
import ai.z.openapi.api.layoutparsing.LayoutParsingApi;
|
||||||
|
import ai.z.openapi.utils.RequestSupplier;
|
||||||
|
|
||||||
|
public class LayoutParsingServiceImpl implements LayoutParsingService {
|
||||||
|
|
||||||
|
private final AbstractAiClient zAiClient;
|
||||||
|
|
||||||
|
private final LayoutParsingApi layoutParsingApi;
|
||||||
|
|
||||||
|
public LayoutParsingServiceImpl(AbstractAiClient zAiClient) {
|
||||||
|
this.zAiClient = zAiClient;
|
||||||
|
this.layoutParsingApi = zAiClient.retrofit().create(LayoutParsingApi.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LayoutParsingResponse layoutParsing(LayoutParsingCreateParams request) {
|
||||||
|
validateParams(request);
|
||||||
|
RequestSupplier<LayoutParsingCreateParams, LayoutParsingResult> supplier = layoutParsingApi::layoutParsing;
|
||||||
|
return this.zAiClient.executeRequest(request, supplier, LayoutParsingResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateParams(LayoutParsingCreateParams request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("request cannot be null");
|
||||||
|
}
|
||||||
|
if (request.getModel() == null) {
|
||||||
|
throw new IllegalArgumentException("model cannot be null");
|
||||||
|
}
|
||||||
|
if (request.getFile() == null) {
|
||||||
|
throw new IllegalArgumentException("file cannot be null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2
pom.xml
2
pom.xml
|
|
@ -45,7 +45,7 @@
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>0.3.2</revision>
|
<revision>0.3.3</revision>
|
||||||
<java.version>8</java.version>
|
<java.version>8</java.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ public class FunctionCallingExample {
|
||||||
.parameters(ChatFunctionParameters.builder()
|
.parameters(ChatFunctionParameters.builder()
|
||||||
.type("object")
|
.type("object")
|
||||||
.properties(properties)
|
.properties(properties)
|
||||||
.required(Collections.singletonList("location"))
|
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
|
||||||
121
samples/src/main/ai.z.openapi.samples/LayoutParsingExample.java
Normal file
121
samples/src/main/ai.z.openapi.samples/LayoutParsingExample.java
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
package ai.z.openapi.samples;
|
||||||
|
|
||||||
|
import ai.z.openapi.ZaiClient;
|
||||||
|
import ai.z.openapi.service.layoutparsing.LayoutParsingCreateParams;
|
||||||
|
import ai.z.openapi.service.layoutparsing.LayoutParsingResponse;
|
||||||
|
import ai.z.openapi.service.layoutparsing.LayoutParsingResult;
|
||||||
|
|
||||||
|
public class LayoutParsingExample {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Create client, recommended to set API Key via environment variable
|
||||||
|
// export ZAI_API_KEY=your.api_key
|
||||||
|
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient.builder().ofZHIPU().build()
|
||||||
|
|
||||||
|
// You can also set the API Key directly in the code for testing
|
||||||
|
ZaiClient client = ZaiClient.builder().ofZAI().build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
System.out.println("=== Layout Parsing Example ===");
|
||||||
|
|
||||||
|
// Example 1: Using URL
|
||||||
|
String imageUrl = "https://cdn.bigmodel.cn/static/platform/images/trialcenter/example/visual_img1.jpeg";
|
||||||
|
LayoutParsingResponse response = layoutParsingExample(client, "glm-ocr", imageUrl);
|
||||||
|
printResult(response);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Exception occurred: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Perform layout parsing on an image or PDF
|
||||||
|
* @param client ZaiClient instance
|
||||||
|
* @param model Model name, e.g., "glm-ocr"
|
||||||
|
* @param file Image URL or base64 string
|
||||||
|
* @param userId Optional user ID for tracking
|
||||||
|
* @param requestId Optional request ID for tracking
|
||||||
|
* @return LayoutParsingResponse object
|
||||||
|
*/
|
||||||
|
private static LayoutParsingResponse layoutParsingExample(ZaiClient client, String model, String file) {
|
||||||
|
if (model == null || model.trim().isEmpty()) {
|
||||||
|
System.err.println("Model cannot be null or empty.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (file == null || file.trim().isEmpty()) {
|
||||||
|
System.err.println("File (URL or base64) cannot be null or empty.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
LayoutParsingCreateParams params = LayoutParsingCreateParams.builder()
|
||||||
|
.model(model)
|
||||||
|
.file(file)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
System.out.println("Request parameters:");
|
||||||
|
System.out.println(" model: " + model);
|
||||||
|
System.out.println(" file: " + (file.length() > 80 ? file.substring(0, 80) + "..." : file));
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
System.out.println("Calling layout parsing API...");
|
||||||
|
return client.layoutParsing().layoutParsing(params);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
System.err.println("Layout parsing task error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
// Return null indicates failure
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printResult(LayoutParsingResponse response) {
|
||||||
|
if (response == null) {
|
||||||
|
System.out.println("No response received.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.out.println("Response status: " + (response.isSuccess() ? "SUCCESS" : "FAILED"));
|
||||||
|
System.out.println("Code: " + response.getCode());
|
||||||
|
System.out.println("Message: " + response.getMsg());
|
||||||
|
if (!response.isSuccess() && response.getError() != null) {
|
||||||
|
System.out.println("Error: " + response.getError());
|
||||||
|
}
|
||||||
|
if (response.getData() != null) {
|
||||||
|
LayoutParsingResult data = response.getData();
|
||||||
|
System.out.println("Task ID: " + data.getId());
|
||||||
|
System.out.println("Created: " + data.getCreated());
|
||||||
|
System.out.println("Model: " + data.getModel());
|
||||||
|
System.out.println("Request ID: " + data.getRequestId());
|
||||||
|
System.out.println("Markdown results length: " + (data.getMdResults() != null ? data.getMdResults().length() : 0));
|
||||||
|
if (data.getLayoutDetails() != null) {
|
||||||
|
System.out.println("Layout pages count: " + data.getLayoutDetails().size());
|
||||||
|
data.getLayoutDetails().stream().limit(1).forEach(page -> {
|
||||||
|
System.out.println("First page blocks count: " + (page != null ? page.size() : 0));
|
||||||
|
if (page != null) {
|
||||||
|
page.stream().limit(3).forEach(detail -> {
|
||||||
|
System.out.println(
|
||||||
|
" - index: " + detail.getIndex() + ", label: " + detail.getLabel() + ", bbox: " + detail.getBbox2d());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.getDataInfo() != null) {
|
||||||
|
System.out.println("Data info:");
|
||||||
|
System.out.println(" num_pages: " + data.getDataInfo().getNumPages());
|
||||||
|
if (data.getDataInfo().getPages() != null && !data.getDataInfo().getPages().isEmpty()) {
|
||||||
|
System.out.println(" first page size: " + data.getDataInfo().getPages().get(0).getWidth() + "x" + data.getDataInfo().getPages().get(0).getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.getUsage() != null) {
|
||||||
|
System.out.println("Usage:");
|
||||||
|
System.out.println(" prompt_tokens: " + data.getUsage().getPromptTokens());
|
||||||
|
System.out.println(" completion_tokens: " + data.getUsage().getCompletionTokens());
|
||||||
|
System.out.println(" total_tokens: " + data.getUsage().getTotalTokens());
|
||||||
|
if (data.getUsage().getPromptTokensDetails() != null) {
|
||||||
|
System.out.println(" prompt_tokens_details.cached_tokens: " + data.getUsage().getPromptTokensDetails().getCachedTokens());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue