diff --git a/core/src/main/java/ai/z/openapi/AbstractAiClient.java b/core/src/main/java/ai/z/openapi/AbstractAiClient.java index e4ec63e..31b350b 100644 --- a/core/src/main/java/ai/z/openapi/AbstractAiClient.java +++ b/core/src/main/java/ai/z/openapi/AbstractAiClient.java @@ -1,6 +1,8 @@ package ai.z.openapi; import ai.z.openapi.service.AbstractClientBaseService; +import ai.z.openapi.service.fileparsing.FileParsingService; +import ai.z.openapi.service.fileparsing.FileParsingServiceImpl; import ai.z.openapi.service.model.ChatError; import ai.z.openapi.service.model.ZAiHttpException; import ai.z.openapi.service.chat.ChatService; @@ -106,6 +108,9 @@ public abstract class AbstractAiClient extends AbstractClientBaseService { /** Voice clone service for voice cloning operations */ private VoiceCloneService voiceCloneService; + /** FileParsing service for fileParsing operations */ + private FileParsingService fileParsingService; + /** Moderation service for content safety detection */ private ModerationService moderationService; @@ -261,6 +266,18 @@ public abstract class AbstractAiClient extends AbstractClientBaseService { return voiceCloneService; } + /** + * Returns the file service for file operations. This service handles file uploads, + * downloads, and management. + * @return the FileParsingService instance (lazily initialized) + */ + public synchronized FileParsingService fileParsing() { + if (fileParsingService == null) { + this.fileParsingService = new FileParsingServiceImpl(this); + } + return fileParsingService; + } + /** * Returns the moderation service for content safety detection. This service handles * content moderation for text, image, video, and audio inputs. diff --git a/core/src/main/java/ai/z/openapi/api/fileparsing/FileParsingApi.java b/core/src/main/java/ai/z/openapi/api/fileparsing/FileParsingApi.java new file mode 100644 index 0000000..58f6d43 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/api/fileparsing/FileParsingApi.java @@ -0,0 +1,47 @@ +package ai.z.openapi.api.fileparsing; + +import ai.z.openapi.service.fileparsing.FileParsingUploadResp; +import ai.z.openapi.service.fileparsing.FileParsingDownloadResp; +import io.reactivex.rxjava3.core.Single; +import okhttp3.MultipartBody; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.Query; +import retrofit2.http.Streaming; +import retrofit2.http.Part; +import retrofit2.http.Multipart; +import retrofit2.http.Header; + +import java.io.File; + +/** + * File Parsing API Provides functionality for uploading files for parsing, and retrieving + * the parsing results. + */ +public interface FileParsingApi { + + /** + * Create a file parsing task. Uploads a file and creates a document parsing job using + * specific tool type and file type. + * @param multipartBody File data with metadata including tool_type and file_type + * @return Information and status of the parsing job + */ + // @Multipart + @POST("files/parser/create") + Single createParseTask(@Body MultipartBody multipartBody); + + /** + * Get a file parsing result. Retrieves the parsing result by taskId and format type. + * @param taskId The unique task ID for the parsing job + * @param formatType The format type of the parsing result + * @return Parsing result content (JSON or raw format) + */ + @Streaming + @GET("files/parser/result/{taskId}/{formatType}") + Call downloadParseResult(@Path("taskId") String taskId, @Path("formatType") String formatType); + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java b/core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java index 4e09cd0..6aa0942 100644 --- a/core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java +++ b/core/src/main/java/ai/z/openapi/service/file/FileDelRequest.java @@ -5,7 +5,6 @@ import ai.z.openapi.core.model.ClientResponse; import ai.z.openapi.service.batches.BatchRequest; import ai.z.openapi.service.model.ChatError; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -16,7 +15,6 @@ import lombok.experimental.SuperBuilder; @NoArgsConstructor @AllArgsConstructor @Data -@Builder public class FileDelRequest implements ClientRequest { private String fileId; diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadReq.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadReq.java new file mode 100644 index 0000000..556fa15 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadReq.java @@ -0,0 +1,30 @@ +package ai.z.openapi.service.fileparsing; + +import ai.z.openapi.core.model.ClientRequest; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * File parsing result download request parameters + */ +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class FileParsingDownloadReq implements ClientRequest { + + /** + * Parsing task ID (required) + */ + private String taskId; + + /** + * Returned content format type (e.g., "download_link", "txt", required) + */ + private String formatType; + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadResp.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadResp.java new file mode 100644 index 0000000..24d084b --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadResp.java @@ -0,0 +1,50 @@ +package ai.z.openapi.service.fileparsing; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * File parsing result response DTO + */ +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class FileParsingDownloadResp { + + /** + * Parsing task ID + */ + @JsonProperty("task_id") + private String taskId; + + /** + * Result status (e.g., succeeded, failed, etc.) + */ + @JsonProperty("status") + private String status; + + /** + * Response message + */ + @JsonProperty("message") + private String message; + + /** + * Parsed result content + */ + @JsonProperty("content") + private String content; + + /** + * Parsing result download link (if available) + */ + @JsonProperty("parsing_result_url") + private String parsingResultUrl; + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadResponse.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadResponse.java new file mode 100644 index 0000000..11def5f --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingDownloadResponse.java @@ -0,0 +1,35 @@ +package ai.z.openapi.service.fileparsing; + +import ai.z.openapi.core.model.ClientResponse; +import ai.z.openapi.service.model.ChatError; +import lombok.Data; + +@Data +public class FileParsingDownloadResponse implements ClientResponse { + + /** + * Response status code. + */ + private int code; + + /** + * Response message. + */ + private String msg; + + /** + * Indicates whether the request was successful. + */ + private boolean success; + + /** + * The FileParsing result data. + */ + private FileParsingDownloadResp data; + + /** + * Error information if the request failed. + */ + private ChatError error; + +} diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingResponse.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingResponse.java new file mode 100644 index 0000000..24b21da --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingResponse.java @@ -0,0 +1,35 @@ +package ai.z.openapi.service.fileparsing; + +import ai.z.openapi.core.model.ClientResponse; +import ai.z.openapi.service.model.ChatError; +import lombok.Data; + +@Data +public class FileParsingResponse implements ClientResponse { + + /** + * Response status code. + */ + private int code; + + /** + * Response message. + */ + private String msg; + + /** + * Indicates whether the request was successful. + */ + private boolean success; + + /** + * The FileParsing result data. + */ + private FileParsingUploadResp data; + + /** + * Error information if the request failed. + */ + private ChatError error; + +} diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingService.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingService.java new file mode 100644 index 0000000..654b2b3 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingService.java @@ -0,0 +1,23 @@ +package ai.z.openapi.service.fileparsing; + +/** + * File parsing service interface + */ +public interface FileParsingService { + + /** + * Submits a file parsing task to the server. + * @param request The file parsing upload request + * @return FileParsingUploadResp containing the parsing task info + */ + FileParsingResponse createParseTask(FileParsingUploadReq request); + + /** + * Retrieves the result of a parsing task. + * @param request The parsing result query request (can include taskId, formatType + * etc) + * @return FileParsingDownloadResp containing the result content + */ + FileParsingDownloadResponse getParseResult(FileParsingDownloadReq request); + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingServiceImpl.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingServiceImpl.java new file mode 100644 index 0000000..c57cdd5 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingServiceImpl.java @@ -0,0 +1,110 @@ +package ai.z.openapi.service.fileparsing; + +import ai.z.openapi.AbstractAiClient; +import ai.z.openapi.api.fileparsing.FileParsingApi; +import ai.z.openapi.core.response.HttpxBinaryResponseContent; +import ai.z.openapi.utils.RequestSupplier; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.reactivex.rxjava3.core.Single; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; +import retrofit2.Response; + +import java.io.File; +import java.io.IOException; + +/** + * File parsing service implementation + */ +public class FileParsingServiceImpl implements FileParsingService { + + private final AbstractAiClient zAiClient; + + private final FileParsingApi fileParsingApi; + + public FileParsingServiceImpl(AbstractAiClient zAiClient) { + this.zAiClient = zAiClient; + this.fileParsingApi = zAiClient.retrofit().create(FileParsingApi.class); + } + + @Override + public FileParsingResponse createParseTask(FileParsingUploadReq request) { + if (request == null) { + throw new IllegalArgumentException("request cannot be null"); + } + if (request.getFilePath() == null) { + throw new IllegalArgumentException("file path cannot be null"); + } + if (request.getToolType() == null) { + throw new IllegalArgumentException("toolType cannot be null"); + } + // 构建 multipart/form-data + RequestSupplier supplier = params -> { + try { + File file = new File(params.getFilePath()); + if (!file.exists()) { + throw new RuntimeException("file not found"); + } + + String toolType = params.getToolType(); + String fileType = params.getFileType() == null ? "" : params.getFileType(); + + MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), + RequestBody.create(MediaType.parse("application/octet-stream"), file)); + MultipartBody.Builder formBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); + formBodyBuilder.addPart(filePart); + formBodyBuilder.addFormDataPart("tool_type", toolType); + formBodyBuilder.addFormDataPart("file_type", fileType); + + MultipartBody multipartBody = formBodyBuilder.build(); + + return fileParsingApi.createParseTask(multipartBody); + } + catch (Exception e) { + throw new RuntimeException(e); + } + }; + return this.zAiClient.executeRequest(request, supplier, FileParsingResponse.class); + } + + @Override + public FileParsingDownloadResponse getParseResult(FileParsingDownloadReq request) { + if (request == null) { + throw new IllegalArgumentException("request cannot be null"); + } + if (request.getTaskId() == null) { + throw new IllegalArgumentException("taskId cannot be null"); + } + if (request.getFormatType() == null) { + throw new IllegalArgumentException("formatType cannot be null"); + } + + RequestSupplier supplier = params -> { + try { + retrofit2.Call call = fileParsingApi.downloadParseResult(request.getTaskId(), + request.getFormatType()); + Response execute = call.execute(); + if (!execute.isSuccessful() || execute.body() == null) { + throw new IOException("Failed to download parse result"); + } + + HttpxBinaryResponseContent httpxBinaryResponseContent = new HttpxBinaryResponseContent(execute); + String result = httpxBinaryResponseContent.getText(); + + ObjectMapper mapper = new ObjectMapper(); + FileParsingDownloadResp fileParsingDownloadResp = mapper.readValue(result, + FileParsingDownloadResp.class); + + return Single.just(fileParsingDownloadResp); + } + catch (Exception e) { + throw new RuntimeException(e); + } + }; + + return this.zAiClient.executeRequest(request, supplier, FileParsingDownloadResponse.class); + } + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingUploadReq.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingUploadReq.java new file mode 100644 index 0000000..849b959 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingUploadReq.java @@ -0,0 +1,35 @@ +package ai.z.openapi.service.fileparsing; + +import ai.z.openapi.core.model.ClientRequest; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * File parsing task upload request parameters + */ +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class FileParsingUploadReq implements ClientRequest { + + /** + * Local file path + */ + private String filePath; + + /** + * Tool type, e.g. "lite" + */ + private String toolType; + + /** + * File type, e.g. "pdf", "doc", etc. + */ + private String fileType; + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingUploadResp.java b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingUploadResp.java new file mode 100644 index 0000000..b8d95b1 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/fileparsing/FileParsingUploadResp.java @@ -0,0 +1,29 @@ +package ai.z.openapi.service.fileparsing; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * File Parsing Task Upload Response DTO Compatible with multiple response structures + */ +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class FileParsingUploadResp { + + /** + * Task ID (API field: task_id or taskId) + */ + private String taskId; + + /** + * Return message (API field: message) + */ + private String message; + +} \ No newline at end of file diff --git a/core/src/test/java/ai/z/openapi/service/fileparsing/FileParsingServiceTest.java b/core/src/test/java/ai/z/openapi/service/fileparsing/FileParsingServiceTest.java new file mode 100644 index 0000000..db58c68 --- /dev/null +++ b/core/src/test/java/ai/z/openapi/service/fileparsing/FileParsingServiceTest.java @@ -0,0 +1,254 @@ +package ai.z.openapi.service.fileparsing; + +import ai.z.openapi.ZaiClient; +import ai.z.openapi.core.config.ZaiConfig; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Base64; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * FileParsingService test class for testing various functionalities of FileParsingService + * and FileParsingServiceImpl + */ +@DisplayName("FileParsingService Tests") +public class FileParsingServiceTest { + + private static final Logger logger = LoggerFactory.getLogger(FileParsingServiceTest.class); + + private static final ObjectMapper mapper = new ObjectMapper(); + + private FileParsingService fileParsingService; + + // Request ID template + private static final String REQUEST_ID_TEMPLATE = "fileparsing-test-%d"; + + @BeforeEach + void setUp() { + ZaiConfig zaiConfig = new ZaiConfig(); + String apiKey = zaiConfig.getApiKey(); + if (apiKey == null) { + zaiConfig.setApiKey("id.test-api-key"); + } + ZaiClient client = new ZaiClient(zaiConfig); + fileParsingService = client.fileParsing(); // 假设你的 client 有 fileParsing() 方法 + } + + @Test + @DisplayName("Test FileParsingService Instantiation") + void testFileParsingServiceInstantiation() { + assertNotNull(fileParsingService, "FileParsingService should be properly instantiated"); + assertInstanceOf(FileParsingServiceImpl.class, fileParsingService, + "FileParsingService should be an instance of FileParsingServiceImpl"); + } + + @Test + @DisplayName("Test File Parsing Task Creation - Basic Functionality") + @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$") + void testCreateParseTask() throws JsonProcessingException { + // Prepare test data + String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis()); + + FileParsingUploadReq request = FileParsingUploadReq.builder() + .toolType("excel") + .fileType("xlsx") + .filePath("src/test/resources/test.xlsx") // 确保有测试文件 + .build(); + + // Execute test + FileParsingResponse response = fileParsingService.createParseTask(request); + + // Verify results + assertNotNull(response, "Response should not be null"); + assertEquals(200, response.getCode()); + assertTrue(response.isSuccess(), "Response should be successful"); + assertNotNull(response.getData(), "Response data should not be null"); + assertNotNull(response.getData().getTaskId(), "Response data taskId should not be null"); + assertNull(response.getError(), "Response error should be null"); + logger.info("File parsing task create response: {}", mapper.writeValueAsString(response)); + } + + @Test + @DisplayName("Test File Parsing Result Retrieval") + @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$") + void testGetParseResult() throws JsonProcessingException { + // First create a file parsing task + String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis()); + + FileParsingUploadReq uploadRequest = FileParsingUploadReq.builder() + .toolType("excel") + .fileType("xlsx") + .filePath("src/test/resources/test.xlsx") + .build(); + + FileParsingResponse createResp = fileParsingService.createParseTask(uploadRequest); + + assertNotNull(createResp, "Create response should not be null"); + assertTrue(createResp.isSuccess(), "Create response should be successful"); + assertNotNull(createResp.getData(), "Create response data should not be null"); + assertNotNull(createResp.getData().getTaskId(), "Task ID should not be null"); + + // Retrieve the result using task ID + String taskId = createResp.getData().getTaskId(); + FileParsingDownloadReq downloadReq = FileParsingDownloadReq.builder().taskId(taskId).formatType("json").build(); + + FileParsingDownloadResponse downloadResp = fileParsingService.getParseResult(downloadReq); + + assertNotNull(downloadResp, "Download response should not be null"); + assertEquals(200, downloadResp.getCode()); + assertNotNull(downloadResp.getData(), "Download response data should not be null"); + logger.info("File parsing result: taskId={}, response={}", taskId, mapper.writeValueAsString(downloadResp)); + } + + @Test + @DisplayName("Test File Parsing Result Error") + @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$") + void testGetParseResultError() { + // Use a mock task ID that doesn't exist + String mockTaskId = "mock-task-id-" + System.currentTimeMillis(); + + FileParsingDownloadReq downloadReq = FileParsingDownloadReq.builder() + .taskId(mockTaskId) + .formatType("json") + .build(); + + FileParsingDownloadResponse response = fileParsingService.getParseResult(downloadReq); + + assertNotNull(response, "Response should not be null"); + // For non-existent task, we expect either an error or unsuccessful response + if (!response.isSuccess()) { + assertNotNull(response.getError(), "Error should be present for non-existent task"); + } + logger.info("File parsing result error test: taskId={}, response={}", mockTaskId, response); + } + + @ParameterizedTest + @ValueSource(strings = { "excel", "csv", "pdf" }) + @DisplayName("Test Different File Tool Types") + @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$") + void testDifferentToolTypes(String toolType) throws JsonProcessingException { + String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis()); + + FileParsingUploadReq request = FileParsingUploadReq.builder() + .toolType(toolType) + .fileType("xlsx") + .filePath("src/test/resources/test.xlsx") + .build(); + + FileParsingResponse response = fileParsingService.createParseTask(request); + + assertNotNull(response, "Response should not be null"); + assertEquals(200, response.getCode()); + logger.info("ToolType {} response: {}", toolType, mapper.writeValueAsString(response)); + } + + @Test + @DisplayName("Test Parameter Validation - Null Request for Create") + void testValidation_NullRequest_Create() { + assertThrows(IllegalArgumentException.class, () -> { + fileParsingService.createParseTask(null); + }, "Null request should throw IllegalArgumentException"); + } + + @Test + @DisplayName("Test Parameter Validation - Null FilePath") + void testValidation_NullFilePath() { + FileParsingUploadReq request = FileParsingUploadReq.builder().toolType("excel").fileType("xlsx").build(); + assertThrows(IllegalArgumentException.class, () -> { + fileParsingService.createParseTask(request); + }, "Null file path should throw IllegalArgumentException"); + } + + @Test + @DisplayName("Test Parameter Validation - Null ToolType") + void testValidation_NullToolType() { + FileParsingUploadReq request = FileParsingUploadReq.builder() + .fileType("xlsx") + .filePath("src/test/resources/test.xlsx") + .build(); + assertThrows(IllegalArgumentException.class, () -> { + fileParsingService.createParseTask(request); + }, "Null toolType should throw IllegalArgumentException"); + } + + @Test + @DisplayName("Test Parameter Validation - Null Request for Download") + void testValidation_NullRequest_Download() { + assertThrows(IllegalArgumentException.class, () -> { + fileParsingService.getParseResult(null); + }, "Null download request should throw IllegalArgumentException"); + } + + @Test + @DisplayName("Test Parameter Validation - Null Task ID Download") + void testValidation_NullTaskId_Download() { + FileParsingDownloadReq downloadReq = FileParsingDownloadReq.builder().formatType("json").build(); + assertThrows(IllegalArgumentException.class, () -> { + fileParsingService.getParseResult(downloadReq); + }, "Null taskId should throw IllegalArgumentException"); + } + + @Test + @DisplayName("Test Parameter Validation - Null FormatType Download") + void testValidation_NullFormatType_Download() { + FileParsingDownloadReq downloadReq = FileParsingDownloadReq.builder().taskId("test-task-id").build(); + assertThrows(IllegalArgumentException.class, () -> { + fileParsingService.getParseResult(downloadReq); + }, "Null formatType should throw IllegalArgumentException"); + } + + @Test + @DisplayName("Test File Parsing with Image Input") + @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$") + void testCreateParseTaskWithImage() throws IOException { + String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis()); + String file = ClassLoader.getSystemResource("image_file.png").getFile(); + byte[] bytes = Files.readAllBytes(new File(file).toPath()); + Base64.Encoder encoder = Base64.getEncoder(); + String imageBase64 = encoder.encodeToString(bytes); + // 假设解析接口支持 imageBase64 字段 + FileParsingUploadReq request = FileParsingUploadReq.builder() + .toolType("image") + .fileType("png") + .filePath(file) // 或文件路径 + .build(); + + FileParsingResponse response = fileParsingService.createParseTask(request); + + assertNotNull(response, "Response should not be null"); + assertEquals(200, response.getCode()); + logger.info("File parsing with image input response: {}", mapper.writeValueAsString(response)); + } + + @Test + @DisplayName("Test File Parsing Create Task with Custom Settings") + @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$") + void testCreateParseTaskWithCustomSettings() throws JsonProcessingException { + String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis()); + + FileParsingUploadReq request = FileParsingUploadReq.builder() + .toolType("excel") + .fileType("xlsx") + .filePath("src/test/resources/test.xlsx") + .build(); + + FileParsingResponse response = fileParsingService.createParseTask(request); + + assertNotNull(response, "Response should not be null"); + assertEquals(200, response.getCode()); + logger.info("File parsing with custom settings response: {}", mapper.writeValueAsString(response)); + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3e46f03..84dbede 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ - 0.0.6 + 0.0.6.1 8 UTF-8 UTF-8 diff --git a/samples/src/main/ai.z.openapi.samples/FileParsingExample.java b/samples/src/main/ai.z.openapi.samples/FileParsingExample.java new file mode 100644 index 0000000..077fd99 --- /dev/null +++ b/samples/src/main/ai.z.openapi.samples/FileParsingExample.java @@ -0,0 +1,125 @@ +package ai.z.openapi.samples; + +import ai.z.openapi.ZaiClient; +import ai.z.openapi.service.fileparsing.FileParsingDownloadReq; +import ai.z.openapi.service.fileparsing.FileParsingDownloadResponse; +import ai.z.openapi.service.fileparsing.FileParsingResponse; +import ai.z.openapi.service.fileparsing.FileParsingUploadReq; +import ai.z.openapi.utils.StringUtils; + +public class FileParsingExample { + + public static void main(String[] args) { + // 建议通过环境变量设置 API Key + // export ZAI_API_KEY=your.api_key + // ZaiClient client = ZaiClient.builder().build(); + + // 也可在代码中直接指定 API Key + ZaiClient client = ZaiClient.builder() + .apiKey("API Key") + .build(); + + try { + // 示例1: 创建解析任务 + System.out.println("=== 文件解析任务创建示例 ==="); + String filePath = "your file path"; + String taskId = createFileParsingTaskExample(client, filePath, "pdf", "lite"); + + // 示例2: 获取解析结果 + System.out.println("\n=== 获取解析结果示例 ==="); + getFileParsingResultExample(client, taskId); + + } catch (Exception e) { + System.err.println("发生异常: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * 示例:创建解析任务(上传文件并解析) + * + * @param client ZaiClient 实例 + * @return 解析任务的 taskId + */ + private static String createFileParsingTaskExample(ZaiClient client, String filePath, String fileType, String toolType) { + if (StringUtils.isEmpty(filePath)) { + System.err.println("无效的文件路径。"); + return null; + } + try { + FileParsingUploadReq uploadReq = FileParsingUploadReq.builder() + .filePath(filePath) + .fileType(fileType) // 支持: pdf, docx 等 + .toolType(toolType) // 解析工具类型: lite, prime, expert + .build(); + + System.out.println("正在上传并创建解析任务..."); + FileParsingResponse response = client.fileParsing().createParseTask(uploadReq); + if (response.isSuccess()) { + if (null != response.getData().getTaskId()) { + String taskId = response.getData().getTaskId(); + System.out.println("解析任务创建成功,TaskId: " + taskId); + return taskId; + } else { + System.err.println("解析任务创建失败: " + response.getData().getMessage()); + } + } else { + System.err.println("解析任务创建失败: " + response.getMsg()); + } + } catch (Exception e) { + System.err.println("文件解析任务错误: " + e.getMessage()); + } + // 返回 null 表示创建失败 + return null; + } + + /** + * 示例:获取解析结果 + * + * @param client ZaiClient 实例 + * @param taskId 解析任务ID + */ + private static void getFileParsingResultExample(ZaiClient client, String taskId) { + if (taskId == null || taskId.isEmpty()) { + System.err.println("无效的任务ID,无法获取解析结果。"); + return; + } + + try { + int maxRetry = 100; // 最多轮询100次 + int intervalMs = 3000; // 每次间隔3秒 + for (int i = 0; i < maxRetry; i++) { + FileParsingDownloadReq downloadReq = FileParsingDownloadReq.builder() + .taskId(taskId) + .formatType("text") + .build(); + + FileParsingDownloadResponse response = client.fileParsing().getParseResult(downloadReq); + + if (response.isSuccess()) { + String status = response.getData().getStatus(); + System.out.println("当前任务状态: " + status); + + if ("succeeded".equalsIgnoreCase(status)) { + System.out.println("解析结果获取成功!"); + System.out.println("解析内容: " + response.getData().getContent()); + System.out.println("内容下载链接: " + response.getData().getParsingResultUrl()); + return; + } else if ("processing".equalsIgnoreCase(status)) { + System.out.println("解析进行中,请稍候..."); + Thread.sleep(intervalMs); + } else { + System.out.println("解析任务异常,状态: " + status + ",消息: " + response.getData().getMessage()); + return; + } + } else { + System.err.println("解析结果获取失败: " + response.getMsg()); + return; + } + } + System.out.println("等待超时,请稍后自行查询解析结果。"); + } catch (Exception e) { + System.err.println("获取解析结果时异常: " + e.getMessage()); + } + } +}