diff --git a/src/main/java/com/mindee/CommandLineInterface.java b/src/main/java/com/mindee/CommandLineInterface.java index 85594c3a5..60bdd1d4c 100644 --- a/src/main/java/com/mindee/CommandLineInterface.java +++ b/src/main/java/com/mindee/CommandLineInterface.java @@ -7,6 +7,7 @@ import com.mindee.v2.cli.ExtractionCommand; import com.mindee.v2.cli.OcrCommand; import com.mindee.v2.cli.SearchModelsCommand; +import com.mindee.v2.cli.SearchRagDocumentsCommand; import com.mindee.v2.cli.SplitCommand; import java.lang.reflect.Method; import picocli.CommandLine; @@ -42,6 +43,7 @@ public static void main(String[] args) { // V2 commands at root rootCmd.addSubcommand("search-models", new CommandLine(new SearchModelsCommand())); + rootCmd.addSubcommand("search-rag-docs", new CommandLine(new SearchRagDocumentsCommand())); rootCmd.addSubcommand("classification", new CommandLine(new ClassificationCommand())); rootCmd.addSubcommand("crop", new CommandLine(new CropCommand())); rootCmd.addSubcommand("extraction", new CommandLine(new ExtractionCommand())); diff --git a/src/main/java/com/mindee/MindeeException.java b/src/main/java/com/mindee/MindeeException.java index c6c231892..307cefda6 100644 --- a/src/main/java/com/mindee/MindeeException.java +++ b/src/main/java/com/mindee/MindeeException.java @@ -5,11 +5,11 @@ */ public class MindeeException extends RuntimeException { - public MindeeException(String errorMessage, Throwable err) { - super(errorMessage, err); + public MindeeException(String message, Throwable cause) { + super(message, cause); } - public MindeeException(String errorMessage) { - super(errorMessage); + public MindeeException(String message) { + super(message); } } diff --git a/src/main/java/com/mindee/v2/MindeeClient.java b/src/main/java/com/mindee/v2/MindeeClient.java index 4b2bda9be..9f9f2042c 100644 --- a/src/main/java/com/mindee/v2/MindeeClient.java +++ b/src/main/java/com/mindee/v2/MindeeClient.java @@ -2,7 +2,8 @@ import com.mindee.input.LocalInputSource; import com.mindee.input.URLInputSource; -import com.mindee.v2.clientoptions.BaseParameters; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.clientoptions.BaseSearchParameters; import com.mindee.v2.clientoptions.PollingOptions; import com.mindee.v2.http.MindeeApiV2; import com.mindee.v2.http.MindeeHttpApiV2; @@ -10,9 +11,12 @@ import com.mindee.v2.parsing.CommonResponse; import com.mindee.v2.parsing.JobResponse; import com.mindee.v2.parsing.error.ErrorResponse; +import com.mindee.v2.parsing.search.BaseSearchResponse; import com.mindee.v2.parsing.search.SearchResponse; import com.mindee.v2.product.extraction.ExtractionResponse; +import com.mindee.v2.search.models.ModelSearchParameters; import java.io.IOException; +import java.util.Objects; import java.util.concurrent.CancellationException; /** @@ -44,7 +48,7 @@ public MindeeClient(MindeeApiV2 mindeeApi) { */ public JobResponse enqueue( LocalInputSource inputSource, - BaseParameters params + BaseProductParameters params ) throws IOException { return mindeeApi.reqPostEnqueue(inputSource, params); } @@ -55,7 +59,10 @@ public JobResponse enqueue( * @param inputSource The URL input source to send. * @param params The parameters to send along with the file. */ - public JobResponse enqueue(URLInputSource inputSource, BaseParameters params) throws IOException { + public JobResponse enqueue( + URLInputSource inputSource, + BaseProductParameters params + ) throws IOException { inputSource.validateSecure(); return mindeeApi.reqPostEnqueue(inputSource, params); } @@ -68,7 +75,7 @@ public JobResponse getJob(String jobId) { if (jobId == null || jobId.trim().isEmpty()) { throw new IllegalArgumentException("jobId must not be null or blank."); } - return mindeeApi.reqGetJob(jobId); + return mindeeApi.reqGetJobById(jobId); } /** @@ -82,7 +89,7 @@ public TResponse getResult( if (inferenceId == null || inferenceId.trim().isEmpty()) { throw new IllegalArgumentException("inferenceId must not be null or blank."); } - return mindeeApi.reqGetResult(responseClass, inferenceId); + return mindeeApi.reqGetResultById(responseClass, inferenceId); } /** @@ -96,7 +103,7 @@ public TResponse getResultFromUrl( if (inferenceUrl == null || inferenceUrl.trim().isEmpty()) { throw new IllegalArgumentException("inferenceUrl must not be null or blank."); } - return mindeeApi.reqGetResultFromUrl(responseClass, inferenceUrl); + return mindeeApi.reqGetResultByUrl(responseClass, inferenceUrl); } /** @@ -112,7 +119,7 @@ public TResponse getResultFromUrl( public TResponse enqueueAndGetResult( Class responseClass, LocalInputSource inputSource, - BaseParameters params + BaseProductParameters params ) throws IOException, InterruptedException { return enqueueAndGetResult( responseClass, @@ -136,7 +143,7 @@ public TResponse enqueueAndGetResult( public TResponse enqueueAndGetResult( Class responseClass, LocalInputSource inputSource, - BaseParameters params, + BaseProductParameters params, PollingOptions pollingOptions ) throws IOException, InterruptedException { JobResponse job = enqueue(inputSource, params); @@ -156,7 +163,7 @@ public TResponse enqueueAndGetResult( public TResponse enqueueAndGetResult( Class responseClass, URLInputSource inputSource, - BaseParameters params + BaseProductParameters params ) throws IOException, InterruptedException { return enqueueAndGetResult( responseClass, @@ -180,7 +187,7 @@ public TResponse enqueueAndGetResult( public TResponse enqueueAndGetResult( Class responseClass, URLInputSource inputSource, - BaseParameters params, + BaseProductParameters params, PollingOptions pollingOptions ) throws IOException, InterruptedException { inputSource.validateSecure(); @@ -188,13 +195,27 @@ public TResponse enqueueAndGetResult( return pollAndFetch(responseClass, job, pollingOptions); } + /** + * Search for resources matching the given criteria. + * + * @param searchParameters Search parameters + */ + public TSearchResponse search( + BaseSearchParameters searchParameters + ) { + Objects.requireNonNull(searchParameters); + return mindeeApi.reqGetSearch(searchParameters); + } + /** * Return all models. * * @return an instance of {@link SearchResponse} + * @deprecated Use {@link #search} instead. */ + @Deprecated public SearchResponse searchModels() { - return searchModels(null, null); + return mindeeApi.reqGetSearch(ModelSearchParameters.builder().build()); } /** @@ -202,9 +223,11 @@ public SearchResponse searchModels() { * * @param modelName name of the model to search for * @return an instance of {@link SearchResponse} + * @deprecated Use {@link #search} instead. */ + @Deprecated public SearchResponse searchModels(String modelName) { - return searchModels(modelName, null); + return mindeeApi.reqGetSearch(ModelSearchParameters.builder().name(modelName).build()); } /** @@ -213,9 +236,12 @@ public SearchResponse searchModels(String modelName) { * @param modelName name of the model to search for * @param modelType type of the model to search for * @return an instance of {@link SearchResponse} + * @deprecated Use {@link #search} instead. */ + @Deprecated public SearchResponse searchModels(String modelName, String modelType) { - return mindeeApi.reqGetSearchModels(modelName, modelType); + return mindeeApi + .reqGetSearch(ModelSearchParameters.builder().name(modelName).modelType(modelType).build()); } /** @@ -253,9 +279,9 @@ private TResponse pollAndFetch( attempts++; } - ErrorResponse error = resp.getJob().getError(); - if (error != null) { - throw new MindeeHttpExceptionV2(error.getStatus(), error.getDetail()); + ErrorResponse errorResponse = resp.getJob().getError(); + if (errorResponse != null) { + throw new MindeeHttpExceptionV2(errorResponse); } throw new RuntimeException("Max retries exceeded (" + max + ")."); } diff --git a/src/main/java/com/mindee/v2/cli/BaseCommand.java b/src/main/java/com/mindee/v2/cli/BaseCommand.java new file mode 100644 index 000000000..4e49626a8 --- /dev/null +++ b/src/main/java/com/mindee/v2/cli/BaseCommand.java @@ -0,0 +1,73 @@ +package com.mindee.v2.cli; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.mindee.v2.parsing.CommonResponse; +import java.util.concurrent.Callable; +import picocli.CommandLine; + +/** + * Abstract base class for all V2 CLI commands. + * Handles common options and output formatting. + */ +public abstract class BaseCommand implements Callable { + @CommandLine.Option( + names = { "-k", "--api-key" }, + description = "Mindee V2 API key.", + defaultValue = "" + ) + protected String apiKey; + + /** Output format for the command. */ + public enum OutputType { + summary, + full, + raw + } + + @CommandLine.Option( + names = { "-o", "--output" }, + description = "Specify how to output the data.\n" + + "- summary: basic formatted response (default)\n" + + "- full: complete formatted response\n" + + "- raw: full JSON object", + defaultValue = "summary" + ) + protected OutputType output; + + /** + * Returns the summary string for the given response. + * Override in each command. + * + * @param response the response + * @return the summary string + */ + protected abstract String getSummaryOutput(CommonResponse response); + + /** + * Returns the full string for the given response. + * + * @param response the product response + * @return the full output string + */ + protected abstract String getFullOutput(CommonResponse response); + + /** + * Prints the output to the console, taking into account the output type. + */ + protected void printOutput(CommonResponse response) throws Exception { + switch (output) { + case full: + System.out.println(getFullOutput(response)); + break; + case raw: + var mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + var jsonNode = mapper.readTree(response.getRawResponse()); + System.out.println(mapper.writeValueAsString(jsonNode)); + break; + default: + System.out.println(getSummaryOutput(response)); + break; + } + } +} diff --git a/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java b/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java index fbf08d084..5e7a4ab68 100644 --- a/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java +++ b/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java @@ -1,21 +1,18 @@ package com.mindee.v2.cli; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import com.mindee.input.LocalInputSource; import com.mindee.v2.MindeeClient; import com.mindee.v2.parsing.CommonResponse; import java.io.File; import java.util.List; -import java.util.concurrent.Callable; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; /** * Abstract base class for V2 inference CLI commands. - * Handles common options (path, model-id, api-key, alias, output) and output formatting. + * Handles common enqueue options. */ -public abstract class BaseInferenceCommand implements Callable { +public abstract class BaseInferenceCommand extends BaseCommand { @Parameters(index = "0", paramLabel = "", description = "The path of the file to parse") protected File file; @@ -23,10 +20,10 @@ public abstract class BaseInferenceCommand implements Callable { @Option(names = { "-m", "--model-id" }, description = "ID of the model to use", required = true) protected String modelId; - @Option(names = { "-k", "--api-key" }, description = "Mindee V2 API key.") - protected String apiKey; - - @Option(names = { "-a", "--alias" }, description = "Alias for the file") + @Option( + names = { "-a", "--alias" }, + description = "A free-form string to tag the request with your own identifier." + ) protected String alias; @Option( @@ -35,23 +32,6 @@ public abstract class BaseInferenceCommand implements Callable { ) private List webhookIds; - /** Output format for the command. */ - public enum OutputType { - summary, - full, - raw - } - - @Option( - names = { "-o", "--output" }, - description = "Specify how to output the data.\n" - + "- summary: a basic summary (default)\n" - + "- full: detail extraction results, including options\n" - + "- raw: full JSON object", - defaultValue = "summary" - ) - protected OutputType output; - /** * @return The properly formatted webhook IDs. */ @@ -72,48 +52,12 @@ protected abstract CommonResponse executeRequest( LocalInputSource inputSource ) throws Exception; - /** - * Returns the summary (result-only) string for the given response. - * Override in each product command. - * - * @param response the product response - * @return the summary string - */ - protected abstract String getSummary(CommonResponse response); - - /** - * Returns the full (inference + options + result) string for the given response. - * Defaults to the same as {@link #getSummary}; override for richer output. - * - * @param response the product response - * @return the full output string - */ - protected String getFullOutput(CommonResponse response) { - return getSummary(response); - } - @Override public Integer call() throws Exception { - var client = new MindeeClient(apiKey != null ? apiKey : ""); + var client = new MindeeClient(apiKey); var inputSource = new LocalInputSource(file); var response = executeRequest(client, inputSource); printOutput(response); return 0; } - - private void printOutput(CommonResponse response) throws Exception { - switch (output) { - case full: - System.out.println(getFullOutput(response)); - break; - case raw: - var mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); - var jsonNode = mapper.readTree(response.getRawResponse()); - System.out.println(mapper.writeValueAsString(jsonNode)); - break; - default: - System.out.println(getSummary(response)); - break; - } - } } diff --git a/src/main/java/com/mindee/v2/cli/ClassificationCommand.java b/src/main/java/com/mindee/v2/cli/ClassificationCommand.java index 12b9ea55e..f28bd7759 100644 --- a/src/main/java/com/mindee/v2/cli/ClassificationCommand.java +++ b/src/main/java/com/mindee/v2/cli/ClassificationCommand.java @@ -8,11 +8,11 @@ import picocli.CommandLine.Command; /** - * CLI command for the V2 classification utility. + * CLI command for the V2 classification product. */ @Command( name = "classification", - description = "Classification utility.", + description = "Classification product.", mixinStandardHelpOptions = true ) public class ClassificationCommand extends BaseInferenceCommand { @@ -31,7 +31,7 @@ protected CommonResponse executeRequest( } @Override - protected String getSummary(CommonResponse response) { + protected String getSummaryOutput(CommonResponse response) { return ((ClassificationResponse) response).getInference().getResult().toString(); } diff --git a/src/main/java/com/mindee/v2/cli/CropCommand.java b/src/main/java/com/mindee/v2/cli/CropCommand.java index 4ccabf5d5..6ad4aee77 100644 --- a/src/main/java/com/mindee/v2/cli/CropCommand.java +++ b/src/main/java/com/mindee/v2/cli/CropCommand.java @@ -8,9 +8,9 @@ import picocli.CommandLine.Command; /** - * CLI command for the V2 crop utility. + * CLI command for the V2 crop product. */ -@Command(name = "crop", description = "Crop utility.", mixinStandardHelpOptions = true) +@Command(name = "crop", description = "Crop product.", mixinStandardHelpOptions = true) public class CropCommand extends BaseInferenceCommand { @Override @@ -27,7 +27,7 @@ protected CommonResponse executeRequest( } @Override - protected String getSummary(CommonResponse response) { + protected String getSummaryOutput(CommonResponse response) { return ((CropResponse) response).getInference().getResult().toString(); } diff --git a/src/main/java/com/mindee/v2/cli/ExtractionCommand.java b/src/main/java/com/mindee/v2/cli/ExtractionCommand.java index 464504372..af92c6ad8 100644 --- a/src/main/java/com/mindee/v2/cli/ExtractionCommand.java +++ b/src/main/java/com/mindee/v2/cli/ExtractionCommand.java @@ -11,18 +11,14 @@ import picocli.CommandLine.Option; /** - * CLI command for the V2 generic all-purpose extraction utility. + * CLI command for the V2 extraction product. */ -@Command( - name = "extraction", - description = "Generic all-purpose extraction.", - mixinStandardHelpOptions = true -) +@Command(name = "extraction", description = "Extraction product.", mixinStandardHelpOptions = true) public class ExtractionCommand extends BaseInferenceCommand { @Option( names = { "-g", "--rag" }, - description = "Enable RAG context. Only valid for 'extraction' product.", + description = "Enable RAG context. False by default.", defaultValue = "false" ) private boolean rag; @@ -77,7 +73,7 @@ protected CommonResponse executeRequest( } @Override - protected String getSummary(CommonResponse response) { + protected String getSummaryOutput(CommonResponse response) { return ((ExtractionResponse) response).getInference().getResult().toString(); } diff --git a/src/main/java/com/mindee/v2/cli/OcrCommand.java b/src/main/java/com/mindee/v2/cli/OcrCommand.java index 771b887f1..8f27d2cc9 100644 --- a/src/main/java/com/mindee/v2/cli/OcrCommand.java +++ b/src/main/java/com/mindee/v2/cli/OcrCommand.java @@ -8,9 +8,9 @@ import picocli.CommandLine.Command; /** - * CLI command for the V2 OCR utility. + * CLI command for the V2 OCR product. */ -@Command(name = "ocr", description = "OCR utility.", mixinStandardHelpOptions = true) +@Command(name = "ocr", description = "OCR product.", mixinStandardHelpOptions = true) public class OcrCommand extends BaseInferenceCommand { @Override @@ -27,7 +27,7 @@ protected CommonResponse executeRequest( } @Override - protected String getSummary(CommonResponse response) { + protected String getSummaryOutput(CommonResponse response) { return ((OcrResponse) response).getInference().getResult().toString(); } diff --git a/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java b/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java index f40979226..3f5455158 100644 --- a/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java +++ b/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java @@ -1,9 +1,9 @@ package com.mindee.v2.cli; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import com.mindee.v2.MindeeClient; -import java.util.concurrent.Callable; +import com.mindee.v2.parsing.CommonResponse; +import com.mindee.v2.search.models.ModelSearchParameters; +import com.mindee.v2.search.models.ModelSearchResponse; import picocli.CommandLine.Command; import picocli.CommandLine.Option; @@ -15,10 +15,7 @@ description = "Search available models.", mixinStandardHelpOptions = true ) -public class SearchModelsCommand implements Callable { - - @Option(names = { "-k", "--api-key" }, description = "Mindee V2 API key.") - private String apiKey; +public class SearchModelsCommand extends BaseCommand { @Option( names = { "-n", "--name" }, @@ -26,30 +23,42 @@ public class SearchModelsCommand implements Callable { ) private String name; - @Option( - names = { "-m", "--model-type" }, - description = "Filter by exact model type (case sensitive)." - ) - private String modelType; + public enum ModelType { + extraction, + crop, + classification, + ocr, + split + } @Option( - names = { "-r", "--raw-json" }, - description = "Whether to output the raw JSON response.", - defaultValue = "false" + names = { "-m", "--model-type" }, + description = "Filter by exact model type.%nAvailable options: ${COMPLETION-CANDIDATES}" ) - private boolean rawJson; + private ModelType modelType; @Override public Integer call() throws Exception { - var client = new MindeeClient(apiKey != null ? apiKey : ""); - var response = client.searchModels(name, modelType); - if (rawJson) { - var mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); - var jsonNode = mapper.readTree(response.getRawResponse()); - System.out.println(mapper.writeValueAsString(jsonNode)); - } else { - System.out.println(response); - } + var client = new MindeeClient(apiKey); + var response = client + .search( + ModelSearchParameters + .builder() + .name(name) + .modelType(modelType != null ? modelType.name() : null) + .build() + ); + printOutput(response); return 0; } + + @Override + protected String getSummaryOutput(CommonResponse response) { + return ((ModelSearchResponse) response).getModels().toString(); + } + + @Override + protected String getFullOutput(CommonResponse response) { + return response.toString(); + } } diff --git a/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java b/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java new file mode 100644 index 000000000..b25bbc6c1 --- /dev/null +++ b/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java @@ -0,0 +1,47 @@ +package com.mindee.v2.cli; + +import com.mindee.v2.MindeeClient; +import com.mindee.v2.parsing.CommonResponse; +import com.mindee.v2.search.ragdocuments.RagDocumentSearchParameters; +import com.mindee.v2.search.ragdocuments.RagDocumentSearchResponse; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +/** + * CLI command for searching available V2 RAG documents. + */ +@Command( + name = "search-rag-docs", + description = "Search available RAG documents for a given model.", + mixinStandardHelpOptions = true +) +public class SearchRagDocumentsCommand extends BaseCommand { + + @Option(names = { "-m", "--model-id" }, description = "Filter by model ID.", required = true) + private String modelId; + + @Option( + names = { "-f", "--filename" }, + description = "Filter by file name partial match (case insensitive)." + ) + private String filename; + + @Override + protected String getSummaryOutput(CommonResponse response) { + return ((RagDocumentSearchResponse) response).getRagDocuments().toString(); + } + + @Override + protected String getFullOutput(CommonResponse response) { + return response.toString(); + } + + @Override + public Integer call() throws Exception { + var client = new MindeeClient(apiKey); + var response = client + .search(RagDocumentSearchParameters.builder(modelId).filename(filename).build()); + printOutput(response); + return 0; + } +} diff --git a/src/main/java/com/mindee/v2/cli/SplitCommand.java b/src/main/java/com/mindee/v2/cli/SplitCommand.java index aca7106b0..9cd072e80 100644 --- a/src/main/java/com/mindee/v2/cli/SplitCommand.java +++ b/src/main/java/com/mindee/v2/cli/SplitCommand.java @@ -27,7 +27,7 @@ protected CommonResponse executeRequest( } @Override - protected String getSummary(CommonResponse response) { + protected String getSummaryOutput(CommonResponse response) { return ((SplitResponse) response).getInference().getResult().toString(); } diff --git a/src/main/java/com/mindee/v2/clientoptions/BaseParameters.java b/src/main/java/com/mindee/v2/clientoptions/BaseProductParameters.java similarity index 55% rename from src/main/java/com/mindee/v2/clientoptions/BaseParameters.java rename to src/main/java/com/mindee/v2/clientoptions/BaseProductParameters.java index 3ccc4ee9f..4adc0e0ce 100644 --- a/src/main/java/com/mindee/v2/clientoptions/BaseParameters.java +++ b/src/main/java/com/mindee/v2/clientoptions/BaseProductParameters.java @@ -1,17 +1,20 @@ package com.mindee.v2.clientoptions; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import lombok.Data; -import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; @Data -public abstract class BaseParameters { +public abstract class BaseProductParameters { /** - * Model ID to use for the inference (required). + * Model ID to use for the inference. Required. */ protected final String modelId; /** - * Optional alias for the file. + * Optional: a free-form string to tag the request with your own identifier. + * For example, an internal document ID, reference number, or database key. + * If set, it will be included in the job and result responses. */ protected final String alias; /** @@ -20,15 +23,19 @@ public abstract class BaseParameters { */ protected final String[] webhookIds; - public MultipartEntityBuilder buildHttpBody(MultipartEntityBuilder builder) { - builder.addTextBody("model_id", this.getModelId()); - if (this.getAlias() != null) { - builder.addTextBody("alias", this.getAlias()); + public Map getRequestParameters() { + var parameters = new HashMap(); + + parameters.put("model_id", this.getModelId()); + + if (this.getAlias() != null && !this.getAlias().isBlank()) { + parameters.put("alias", getAlias()); } if (this.getWebhookIds().length > 0) { - builder.addTextBody("webhook_ids", String.join(",", this.getWebhookIds())); + parameters.put("webhook_ids", String.join(",", this.getWebhookIds())); } - return builder; + + return parameters; } protected static abstract class BaseBuilder> { @@ -42,7 +49,8 @@ protected T self() { } protected BaseBuilder(String modelId) { - this.modelId = Objects.requireNonNull(modelId, "modelId must not be null"); + this.modelId = Objects + .requireNonNull(modelId, "The model ID is required in product parameters"); } /** Set an alias for the uploaded document. */ diff --git a/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java b/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java new file mode 100644 index 000000000..8d6d4d39f --- /dev/null +++ b/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java @@ -0,0 +1,86 @@ +package com.mindee.v2.clientoptions; + +import com.mindee.v2.parsing.search.BaseSearchResponse; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import lombok.Data; + +/** + * Base parameters for searches. + */ +@Data +public abstract class BaseSearchParameters { + private final Class responseClass; + + /** + * 1-based page index. + */ + protected final Integer page; + /** + * Number of items per page. + */ + protected final Integer perPage; + + protected BaseSearchParameters( + Class responseClass, + Integer page, + Integer perPage + ) { + this.responseClass = Objects.requireNonNull(responseClass, "responseClass cannot be null"); + this.page = page; + this.perPage = perPage; + } + + /** + * Gets the request parameters for the search request. + */ + public Map getRequestParameters() { + var parameters = new HashMap(); + + if (this.getPage() != null) { + if (this.getPage() <= 0) { + throw new IllegalArgumentException("page must be greater than 0"); + } + parameters.put("page", String.valueOf(getPage())); + } + if (this.getPerPage() != null) { + if (this.getPerPage() <= 0) { + throw new IllegalArgumentException("perPage must be greater than 0"); + } + parameters.put("per_page", String.valueOf(getPerPage())); + } + + return parameters; + } + + protected static abstract class BaseBuilder> { + protected Integer page; + protected Integer perPage; + + @SuppressWarnings("unchecked") + protected T self() { + return (T) this; + } + + protected BaseBuilder() { + } + + /** + * 1-based page index. + */ + public T page(Integer page) { + this.page = page; + return self(); + } + + /** + * Number of items per page. + */ + public T perPage(Integer perPage) { + this.perPage = perPage; + return self(); + } + } + +} diff --git a/src/main/java/com/mindee/v2/http/MindeeApiV2.java b/src/main/java/com/mindee/v2/http/MindeeApiV2.java index ab5db757f..c3ebee48f 100644 --- a/src/main/java/com/mindee/v2/http/MindeeApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeApiV2.java @@ -4,11 +4,15 @@ import com.mindee.http.MindeeApiCommon; import com.mindee.input.LocalInputSource; import com.mindee.input.URLInputSource; -import com.mindee.v2.clientoptions.BaseParameters; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.clientoptions.BaseSearchParameters; import com.mindee.v2.parsing.CommonResponse; import com.mindee.v2.parsing.JobResponse; import com.mindee.v2.parsing.error.ErrorResponse; +import com.mindee.v2.parsing.search.BaseSearchResponse; import com.mindee.v2.parsing.search.SearchResponse; +import com.mindee.v2.product.ProductAttributes; +import com.mindee.v2.search.models.ModelSearchParameters; import java.io.IOException; /** @@ -19,22 +23,22 @@ public abstract class MindeeApiV2 extends MindeeApiCommon { * Send a file to the prediction queue with a local file. * * @param inputSource Local input source from URL. - * @param options parameters. + * @param parameters parameters. */ public abstract JobResponse reqPostEnqueue( LocalInputSource inputSource, - BaseParameters options + BaseProductParameters parameters ) throws IOException; /** * Send a file to the prediction queue with a remote file. * * @param inputSource Remote input source from URL. - * @param options parameters. + * @param parameters parameters. */ public abstract JobResponse reqPostEnqueue( URLInputSource inputSource, - BaseParameters options + BaseProductParameters parameters ) throws IOException; /** @@ -42,14 +46,14 @@ public abstract JobResponse reqPostEnqueue( * * @param jobId id of the job to get. */ - public abstract JobResponse reqGetJob(String jobId); + public abstract JobResponse reqGetJobById(String jobId); /** * Retrieves the inference from a 302 redirect. * * @param inferenceId ID of the inference to poll. */ - public abstract TResponse reqGetResult( + public abstract TResponse reqGetResultById( Class responseClass, String inferenceId ); @@ -58,18 +62,20 @@ public abstract TResponse reqGetResult( * Retrieves the inference from a given URL. * The inference will only be available after it has finished processing. */ - public abstract TResponse reqGetResultFromUrl( + public abstract TResponse reqGetResultByUrl( Class responseClass, String inferenceUrl ); /** - * Retrieves a list of models. - * - * @param modelName search term for model name - * @param modelType search term for model type + * Retrieves a list of resources with the given criteria. */ - public abstract SearchResponse reqGetSearchModels(String modelName, String modelType); + public abstract TSearchResponse reqGetSearch( + BaseSearchParameters parameters + ); + + @Deprecated + public abstract SearchResponse reqGetSearch(ModelSearchParameters parameters); /** * Creates an "unknown error" response from an HTTP status code. @@ -84,21 +90,25 @@ protected ErrorResponse makeUnknownError(int statusCode) { ); } - protected ProductInfo getResponseProductInfo(Class responseClass) { - var productInfo = responseClass.getAnnotation(ProductInfo.class); + protected ProductAttributes getResponseProductAttributes( + Class responseClass + ) { + var productInfo = responseClass.getAnnotation(ProductAttributes.class); if (productInfo == null) { throw new MindeeException( - "The class " + responseClass.getSimpleName() + " is not annotated with @ProductInfo" + "The class " + responseClass.getSimpleName() + " is not annotated with @ProductAttributes" ); } return productInfo; } - protected ProductInfo getParamsProductInfo(Class responseClass) { - var productInfo = responseClass.getAnnotation(ProductInfo.class); + protected ProductAttributes getParamsProductAttributes( + Class paramsClass + ) { + var productInfo = paramsClass.getAnnotation(ProductAttributes.class); if (productInfo == null) { throw new MindeeException( - "The class " + responseClass.getSimpleName() + " is not annotated with @ProductInfo" + "The class " + paramsClass.getSimpleName() + " is not annotated with @ProductAttributes" ); } return productInfo; diff --git a/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java index 0db80251b..c165a0385 100644 --- a/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java @@ -6,11 +6,14 @@ import com.mindee.input.LocalInputSource; import com.mindee.input.URLInputSource; import com.mindee.v2.MindeeSettings; -import com.mindee.v2.clientoptions.BaseParameters; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.clientoptions.BaseSearchParameters; import com.mindee.v2.parsing.CommonResponse; import com.mindee.v2.parsing.JobResponse; import com.mindee.v2.parsing.error.ErrorResponse; +import com.mindee.v2.parsing.search.BaseSearchResponse; import com.mindee.v2.parsing.search.SearchResponse; +import com.mindee.v2.search.models.ModelSearchParameters; import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -64,12 +67,15 @@ private MindeeHttpApiV2(MindeeSettings mindeeSettings, HttpClientBuilder httpCli * Enqueues a doc with the POST method. * * @param inputSource Input source to send. - * @param options Options to send the file along with. + * @param parameters Options to send the file along with. * @return A job response. */ @Override - public JobResponse reqPostEnqueue(LocalInputSource inputSource, BaseParameters options) { - var productInfo = getParamsProductInfo(options.getClass()); + public JobResponse reqPostEnqueue( + LocalInputSource inputSource, + BaseProductParameters parameters + ) { + var productInfo = getParamsProductAttributes(parameters.getClass()); var url = String .format("%s/products/%s/enqueue", this.mindeeSettings.getBaseUrl(), productInfo.slug()); var post = buildHttpPost(url); @@ -83,7 +89,8 @@ public JobResponse reqPostEnqueue(LocalInputSource inputSource, BaseParameters o ContentType.DEFAULT_BINARY, inputSource.getFilename() ); - post.setEntity(options.buildHttpBody(builder).build()); + parameters.getRequestParameters().forEach(builder::addTextBody); + post.setEntity(builder.build()); return executeAPIRequest(post, JobResponse.class); } @@ -95,8 +102,8 @@ public JobResponse reqPostEnqueue(LocalInputSource inputSource, BaseParameters o * @return A job response. */ @Override - public JobResponse reqPostEnqueue(URLInputSource inputSource, BaseParameters options) { - var productInfo = getParamsProductInfo(options.getClass()); + public JobResponse reqPostEnqueue(URLInputSource inputSource, BaseProductParameters options) { + var productInfo = getParamsProductAttributes(options.getClass()); var url = String .format("%s/products/%s/enqueue", this.mindeeSettings.getBaseUrl(), productInfo.slug()); var post = buildHttpPost(url); @@ -104,12 +111,13 @@ public JobResponse reqPostEnqueue(URLInputSource inputSource, BaseParameters opt var builder = MultipartEntityBuilder.create(); builder.setMode(HttpMultipartMode.EXTENDED); builder.addTextBody("url", inputSource.getUrl().toString()); - post.setEntity(options.buildHttpBody(builder).build()); + options.getRequestParameters().forEach(builder::addTextBody); + post.setEntity(builder.build()); return executeAPIRequest(post, JobResponse.class); } @Override - public JobResponse reqGetJob(String jobId) { + public JobResponse reqGetJobById(String jobId) { var url = this.mindeeSettings.getBaseUrl() + "/jobs/" + jobId; var get = new HttpGet(url); @@ -121,11 +129,11 @@ public JobResponse reqGetJob(String jobId) { } @Override - public TResponse reqGetResult( + public TResponse reqGetResultById( Class responseClass, String inferenceId ) { - var productInfo = getResponseProductInfo(responseClass); + var productInfo = getResponseProductAttributes(responseClass); var url = String .format( "%s/products/%s/results/%s", @@ -133,12 +141,11 @@ public TResponse reqGetResult( productInfo.slug(), inferenceId ); - var get = new HttpGet(url); - return executeAPIRequest(get, responseClass); + return reqGetResultByUrl(responseClass, url); } @Override - public TResponse reqGetResultFromUrl( + public TResponse reqGetResultByUrl( Class responseClass, String inferenceUrl ) { @@ -150,6 +157,36 @@ public TResponse reqGetResultFromUrl( return executeAPIRequest(get, responseClass); } + @Override + public TSearchResponse reqGetSearch( + BaseSearchParameters parameters + ) { + var productInfo = getResponseProductAttributes(parameters.getResponseClass()); + URIBuilder url; + try { + url = new URIBuilder(this.mindeeSettings.getBaseUrl() + "/search/" + productInfo.slug()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + parameters.getRequestParameters().forEach(url::addParameter); + var get = new HttpGet(url.toString()); + return this.executeAPIRequest(get, parameters.getResponseClass()); + } + + @Override + @Deprecated + public SearchResponse reqGetSearch(ModelSearchParameters parameters) { + URIBuilder url; + try { + url = new URIBuilder(this.mindeeSettings.getBaseUrl() + "/search/models"); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + parameters.getRequestParameters().forEach(url::addParameter); + var get = new HttpGet(url.toString()); + return this.executeAPIRequest(get, SearchResponse.class); + } + /** * Ensures that a caller-supplied inference URL targets the configured Mindee * base URL so the {@code Authorization} header attached by @@ -222,24 +259,6 @@ private static int defaultPort(String scheme) { return -1; } - @Override - public SearchResponse reqGetSearchModels(String modelName, String modelType) { - URIBuilder url; - try { - url = new URIBuilder(this.mindeeSettings.getBaseUrl() + "/search/models"); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - if (modelName != null) { - url.addParameter("name", modelName); - } - if (modelType != null) { - url.addParameter("model_type", modelType); - } - var get = new HttpGet(url.toString()); - return executeAPIRequest(get, SearchResponse.class); - } - /** * Executes an enqueue action, common to URL & local inputs. * @@ -281,15 +300,15 @@ private MindeeHttpExceptionV2 getHttpError(ClassicHttpResponse response) { ? "" : EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - var err = mapper.readValue(rawBody, ErrorResponse.class); + var errorResponse = mapper.readValue(rawBody, ErrorResponse.class); - if (err.getDetail() == null) { - err = makeUnknownError(response.getCode()); + if (errorResponse.getDetail() == null) { + errorResponse = makeUnknownError(response.getCode()); } - return new MindeeHttpExceptionV2(err.getStatus(), err.getDetail()); + return new MindeeHttpExceptionV2(errorResponse); - } catch (Exception e) { - return new MindeeHttpExceptionV2(response.getCode(), "Unknown error"); + } catch (Exception exception) { + return new MindeeHttpExceptionV2(makeUnknownError(response.getCode()), exception); } } @@ -325,15 +344,15 @@ private R deserializeOrThrow( } } - ErrorResponse err; + ErrorResponse errorResponse; try { - err = mapper.readValue(body, ErrorResponse.class); - if (err.getDetail() == null) { - err = makeUnknownError(httpStatus); + errorResponse = mapper.readValue(body, ErrorResponse.class); + if (errorResponse.getDetail() == null) { + errorResponse = makeUnknownError(httpStatus); } } catch (Exception ignored) { - err = makeUnknownError(httpStatus); + errorResponse = makeUnknownError(httpStatus); } - throw new MindeeHttpExceptionV2(err.getStatus(), err.getDetail()); + throw new MindeeHttpExceptionV2(errorResponse); } } diff --git a/src/main/java/com/mindee/v2/http/MindeeHttpExceptionV2.java b/src/main/java/com/mindee/v2/http/MindeeHttpExceptionV2.java index 409ca231e..b73bb4702 100644 --- a/src/main/java/com/mindee/v2/http/MindeeHttpExceptionV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeHttpExceptionV2.java @@ -1,6 +1,7 @@ package com.mindee.v2.http; import com.mindee.MindeeException; +import com.mindee.v2.parsing.error.ErrorResponse; import lombok.Getter; /** @@ -10,20 +11,23 @@ public class MindeeHttpExceptionV2 extends MindeeException { /** Standard HTTP status code. */ private final int status; - /** Error details. */ - private final String detail; - public MindeeHttpExceptionV2(int status, String detail) { - super(detail); - this.status = status; - this.detail = detail; + /** Error response. */ + private final ErrorResponse response; + + public MindeeHttpExceptionV2(ErrorResponse response) { + super(response.toString()); + this.response = response; + this.status = response.getStatus(); + } + + public MindeeHttpExceptionV2(ErrorResponse response, Throwable cause) { + super(response.toString(), cause); + this.response = response; + this.status = response.getStatus(); } public String toString() { - String outStr = super.toString() + " - HTTP " + getStatus(); - if (!getDetail().isEmpty()) { - outStr += " - " + getDetail(); - } - return outStr; + return response.toString(); } } diff --git a/src/main/java/com/mindee/v2/parsing/CommonResponse.java b/src/main/java/com/mindee/v2/parsing/CommonResponse.java index 807a3b2ca..3df83bc06 100644 --- a/src/main/java/com/mindee/v2/parsing/CommonResponse.java +++ b/src/main/java/com/mindee/v2/parsing/CommonResponse.java @@ -5,7 +5,7 @@ import lombok.EqualsAndHashCode; /** - * Common response information from Mindee API V2. + * Base class for all responses from the V2 API. */ @Data @EqualsAndHashCode diff --git a/src/main/java/com/mindee/v2/parsing/error/ErrorResponse.java b/src/main/java/com/mindee/v2/parsing/error/ErrorResponse.java index 64f4fac28..94f839c7a 100644 --- a/src/main/java/com/mindee/v2/parsing/error/ErrorResponse.java +++ b/src/main/java/com/mindee/v2/parsing/error/ErrorResponse.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; +import java.util.StringJoiner; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -50,6 +51,33 @@ public final class ErrorResponse { /** For prettier display. */ @Override public String toString() { - return "HTTP " + status + " - " + title + " :: " + code + " - " + detail; + var joiner = new StringJoiner("\n"); + + joiner.add("Error Details"); + joiner.add("============="); + + joiner.add(":HTTP Status: " + status); + joiner.add(":Title: " + title); + joiner.add(":Code: " + code); + joiner.add(":Detail: " + detail); + + if (errors != null && !errors.isEmpty()) { + joiner.add(""); + joiner.add("Error Items"); + joiner.add("-----------"); + + for (int i = 0; i < errors.size(); i++) { + var error = errors.get(i); + joiner.add("**Error " + (i + 1) + ":**"); + joiner.add(" :Pointer: " + error.getPointer()); + joiner.add(" :Detail: " + error.getDetail()); + + if (i < errors.size() - 1) { + joiner.add(""); + } + } + } + + return joiner.toString(); } } diff --git a/src/main/java/com/mindee/v2/parsing/search/BaseSearchResponse.java b/src/main/java/com/mindee/v2/parsing/search/BaseSearchResponse.java new file mode 100644 index 000000000..fcdf40579 --- /dev/null +++ b/src/main/java/com/mindee/v2/parsing/search/BaseSearchResponse.java @@ -0,0 +1,42 @@ +package com.mindee.v2.parsing.search; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.v2.parsing.CommonResponse; +import java.util.List; +import java.util.StringJoiner; +import lombok.Getter; + +/** + * Base class for search responses. + */ +@Getter +public abstract class BaseSearchResponse extends CommonResponse { + + /** + * Pagination metadata. + */ + @JsonProperty("pagination") + protected PaginationMetadata pagination; + + /** + * String representation of the search response. + * + * @return cleaned string summary + */ + public String toString() { + var joiner = new StringJoiner("\n"); + bodyLines().forEach(joiner::add); + joiner.add("Pagination Metadata"); + joiner.add("###################"); + joiner.add(String.valueOf(pagination)); + joiner.add(""); + return joiner.toString(); + } + + /** + * Lines composing the response-specific body (header + items). + * + * @return A list of body lines. + */ + protected abstract List bodyLines(); +} diff --git a/src/main/java/com/mindee/v2/parsing/search/SearchModel.java b/src/main/java/com/mindee/v2/parsing/search/SearchModel.java index bc7d1c906..367f96154 100644 --- a/src/main/java/com/mindee/v2/parsing/search/SearchModel.java +++ b/src/main/java/com/mindee/v2/parsing/search/SearchModel.java @@ -38,7 +38,7 @@ public class SearchModel { private String modelType; /** - * Webhooks associated with the model. + * List of webhooks associated with the model. */ @JsonProperty("webhooks") private List webhooks; diff --git a/src/main/java/com/mindee/v2/parsing/search/SearchModels.java b/src/main/java/com/mindee/v2/parsing/search/SearchModels.java new file mode 100644 index 000000000..c2673a7e9 --- /dev/null +++ b/src/main/java/com/mindee/v2/parsing/search/SearchModels.java @@ -0,0 +1,28 @@ +package com.mindee.v2.parsing.search; + +import java.util.ArrayList; +import java.util.StringJoiner; + +/** + * List of search models. + */ +public class SearchModels extends ArrayList { + + /** + * Default string representation. + */ + @Override + public String toString() { + if (this.isEmpty()) { + return "\n"; + } + var joiner = new StringJoiner("\n"); + for (SearchModel item : this) { + joiner.add("* :Name: " + item.getName()); + joiner.add(" :ID: " + item.getId()); + joiner.add(" :Model Type: " + item.getModelType()); + } + joiner.add(""); + return joiner.toString(); + } +} diff --git a/src/main/java/com/mindee/v2/parsing/search/SearchRagDocument.java b/src/main/java/com/mindee/v2/parsing/search/SearchRagDocument.java new file mode 100644 index 000000000..8dd2a9042 --- /dev/null +++ b/src/main/java/com/mindee/v2/parsing/search/SearchRagDocument.java @@ -0,0 +1,62 @@ +package com.mindee.v2.parsing.search; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Individual RAG document information. + */ +@Getter +@EqualsAndHashCode +@JsonIgnoreProperties(ignoreUnknown = true) +@AllArgsConstructor +@NoArgsConstructor +public class SearchRagDocument { + + /** + * Unique identifier of the RAG document. + */ + @JsonProperty("id") + private String id; + + /** + * Model identifier linked to the RAG document. + */ + @JsonProperty("model_id") + private String modelId; + + /** + * Original filename of the uploaded document. + */ + @JsonProperty("filename") + private String filename; + + /** + * Date and time of the document creation. + */ + @JsonProperty("created_at") + private OffsetDateTime createdAt; + + /** + * Number of times this document was used in an inference. + */ + @JsonProperty("total_matches") + private int totalMatches; + + /** + * Date and time of the latest matching inference, if any. + */ + @JsonProperty("last_match_at") + private OffsetDateTime lastMatchAt; + + /** + * Current status of the RAG document. + */ + @JsonProperty("status") + private String status; +} diff --git a/src/main/java/com/mindee/v2/parsing/search/SearchRagDocuments.java b/src/main/java/com/mindee/v2/parsing/search/SearchRagDocuments.java new file mode 100644 index 000000000..5a95a39e7 --- /dev/null +++ b/src/main/java/com/mindee/v2/parsing/search/SearchRagDocuments.java @@ -0,0 +1,32 @@ +package com.mindee.v2.parsing.search; + +import java.util.ArrayList; +import java.util.StringJoiner; + +/** + * List of RAG documents. + */ +public class SearchRagDocuments extends ArrayList { + + /** + * Default string representation. + */ + @Override + public String toString() { + if (this.isEmpty()) { + return "\n"; + } + var joiner = new StringJoiner("\n"); + for (SearchRagDocument item : this) { + joiner.add("* :ID: " + item.getId()); + joiner.add(" :Model ID: " + item.getModelId()); + joiner.add(" :Filename: " + item.getFilename()); + joiner.add(" :Created At: " + item.getCreatedAt()); + joiner.add(" :Total Matches: " + item.getTotalMatches()); + joiner.add(" :Last Match At: " + item.getLastMatchAt()); + joiner.add(" :Status: " + item.getStatus()); + } + joiner.add(""); + return joiner.toString(); + } +} diff --git a/src/main/java/com/mindee/v2/parsing/search/SearchResponse.java b/src/main/java/com/mindee/v2/parsing/search/SearchResponse.java index 07b0a2f04..9834e69ae 100644 --- a/src/main/java/com/mindee/v2/parsing/search/SearchResponse.java +++ b/src/main/java/com/mindee/v2/parsing/search/SearchResponse.java @@ -1,14 +1,11 @@ package com.mindee.v2.parsing.search; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.mindee.v2.parsing.CommonResponse; -import java.util.List; -import java.util.StringJoiner; +import com.mindee.v2.product.ProductAttributes; +import com.mindee.v2.search.models.ModelSearchResponse; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.NoArgsConstructor; /** * Models search response. @@ -17,29 +14,8 @@ @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties(ignoreUnknown = true) @AllArgsConstructor -@NoArgsConstructor -public class SearchResponse extends CommonResponse { +@Deprecated +@ProductAttributes(slug = "models") +public class SearchResponse extends ModelSearchResponse { - @JsonProperty("models") - private List models; - - @JsonProperty("pagination") - private PaginationMetadata pagination; - - /** - * String representation of the search response. - */ - @Override - public String toString() { - var joiner = new StringJoiner("\n"); - joiner.add("Models").add("#######"); - for (SearchModel model : models) { - joiner.add("* :Name: " + model.getName()); - joiner.add(" :ID: " + model.getId()); - joiner.add(" :Model Type: " + model.getModelType()); - } - joiner.add("Pagination").add("##########"); - joiner.add(pagination.toString()); - return joiner.toString(); - } } diff --git a/src/main/java/com/mindee/v2/http/ProductInfo.java b/src/main/java/com/mindee/v2/product/ProductAttributes.java similarity index 83% rename from src/main/java/com/mindee/v2/http/ProductInfo.java rename to src/main/java/com/mindee/v2/product/ProductAttributes.java index 05057a265..e65a7df43 100644 --- a/src/main/java/com/mindee/v2/http/ProductInfo.java +++ b/src/main/java/com/mindee/v2/product/ProductAttributes.java @@ -1,4 +1,4 @@ -package com.mindee.v2.http; +package com.mindee.v2.product; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; @@ -12,6 +12,6 @@ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface ProductInfo { +public @interface ProductAttributes { String slug(); } diff --git a/src/main/java/com/mindee/v2/product/classification/ClassificationResponse.java b/src/main/java/com/mindee/v2/product/classification/ClassificationResponse.java index eba19e4fd..63820d138 100644 --- a/src/main/java/com/mindee/v2/product/classification/ClassificationResponse.java +++ b/src/main/java/com/mindee/v2/product/classification/ClassificationResponse.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mindee.v2.http.ProductInfo; import com.mindee.v2.parsing.CommonResponse; +import com.mindee.v2.product.ProductAttributes; import lombok.Getter; /** @@ -11,7 +11,7 @@ */ @Getter @JsonIgnoreProperties(ignoreUnknown = true) -@ProductInfo(slug = "classification") +@ProductAttributes(slug = "classification") public class ClassificationResponse extends CommonResponse { /** diff --git a/src/main/java/com/mindee/v2/product/classification/params/ClassificationParameters.java b/src/main/java/com/mindee/v2/product/classification/params/ClassificationParameters.java index 1a66f3176..545dc13ba 100644 --- a/src/main/java/com/mindee/v2/product/classification/params/ClassificationParameters.java +++ b/src/main/java/com/mindee/v2/product/classification/params/ClassificationParameters.java @@ -1,10 +1,10 @@ package com.mindee.v2.product.classification.params; -import com.mindee.v2.clientoptions.BaseParameters; -import com.mindee.v2.http.ProductInfo; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.product.ProductAttributes; -@ProductInfo(slug = "classification") -public class ClassificationParameters extends BaseParameters { +@ProductAttributes(slug = "classification") +public class ClassificationParameters extends BaseProductParameters { public ClassificationParameters(String modelId, String alias, String[] webhookIds) { super(modelId, alias, webhookIds); } @@ -19,7 +19,7 @@ public static Builder builder(String modelId) { return new Builder(modelId); } - public static final class Builder extends BaseParameters.BaseBuilder { + public static final class Builder extends BaseProductParameters.BaseBuilder { Builder(String modelId) { super(modelId); diff --git a/src/main/java/com/mindee/v2/product/crop/CropResponse.java b/src/main/java/com/mindee/v2/product/crop/CropResponse.java index 0c7a34197..e56f9c067 100644 --- a/src/main/java/com/mindee/v2/product/crop/CropResponse.java +++ b/src/main/java/com/mindee/v2/product/crop/CropResponse.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mindee.v2.http.ProductInfo; import com.mindee.v2.parsing.CommonResponse; +import com.mindee.v2.product.ProductAttributes; import lombok.Getter; /** @@ -11,7 +11,7 @@ */ @Getter @JsonIgnoreProperties(ignoreUnknown = true) -@ProductInfo(slug = "crop") +@ProductAttributes(slug = "crop") public class CropResponse extends CommonResponse { /** diff --git a/src/main/java/com/mindee/v2/product/crop/params/CropParameters.java b/src/main/java/com/mindee/v2/product/crop/params/CropParameters.java index 1ebe332cb..1de0b7654 100644 --- a/src/main/java/com/mindee/v2/product/crop/params/CropParameters.java +++ b/src/main/java/com/mindee/v2/product/crop/params/CropParameters.java @@ -1,10 +1,10 @@ package com.mindee.v2.product.crop.params; -import com.mindee.v2.clientoptions.BaseParameters; -import com.mindee.v2.http.ProductInfo; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.product.ProductAttributes; -@ProductInfo(slug = "crop") -public class CropParameters extends BaseParameters { +@ProductAttributes(slug = "crop") +public class CropParameters extends BaseProductParameters { public CropParameters(String modelId, String alias, String[] webhookIds) { super(modelId, alias, webhookIds); @@ -20,7 +20,7 @@ public static Builder builder(String modelId) { return new Builder(modelId); } - public static final class Builder extends BaseParameters.BaseBuilder { + public static final class Builder extends BaseProductParameters.BaseBuilder { Builder(String modelId) { super(modelId); diff --git a/src/main/java/com/mindee/v2/product/extraction/ExtractionResponse.java b/src/main/java/com/mindee/v2/product/extraction/ExtractionResponse.java index a736035fb..e95093cd4 100644 --- a/src/main/java/com/mindee/v2/product/extraction/ExtractionResponse.java +++ b/src/main/java/com/mindee/v2/product/extraction/ExtractionResponse.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mindee.v2.http.ProductInfo; import com.mindee.v2.parsing.CommonResponse; +import com.mindee.v2.product.ProductAttributes; import lombok.Getter; /** @@ -11,7 +11,7 @@ */ @Getter @JsonIgnoreProperties(ignoreUnknown = true) -@ProductInfo(slug = "extraction") +@ProductAttributes(slug = "extraction") public class ExtractionResponse extends CommonResponse { /** diff --git a/src/main/java/com/mindee/v2/product/extraction/params/ExtractionParameters.java b/src/main/java/com/mindee/v2/product/extraction/params/ExtractionParameters.java index 6c455c8d2..26468dfee 100644 --- a/src/main/java/com/mindee/v2/product/extraction/params/ExtractionParameters.java +++ b/src/main/java/com/mindee/v2/product/extraction/params/ExtractionParameters.java @@ -1,18 +1,19 @@ package com.mindee.v2.product.extraction.params; -import com.mindee.v2.clientoptions.BaseParameters; -import com.mindee.v2.http.ProductInfo; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.product.ProductAttributes; +import java.util.HashMap; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Getter; -import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; /** * Options to pass when calling methods using the API V2. */ @Getter @EqualsAndHashCode(callSuper = true) -@ProductInfo(slug = "extraction") -public final class ExtractionParameters extends BaseParameters { +@ProductAttributes(slug = "extraction") +public final class ExtractionParameters extends BaseProductParameters { /** * Enhance extraction accuracy with Retrieval-Augmented Generation. */ @@ -60,27 +61,30 @@ private ExtractionParameters( this.dataSchema = dataSchema; } - public MultipartEntityBuilder buildHttpBody(MultipartEntityBuilder builder) { - builder = super.buildHttpBody(builder); + @Override + public Map getRequestParameters() { + var parameters = new HashMap<>(super.getRequestParameters()); + if (this.getRag() != null) { - builder.addTextBody("rag", this.getRag().toString().toLowerCase()); + parameters.put("rag", this.getRag().toString().toLowerCase()); } if (this.getRawText() != null) { - builder.addTextBody("raw_text", this.getRawText().toString().toLowerCase()); + parameters.put("raw_text", this.getRawText().toString().toLowerCase()); } if (this.getPolygon() != null) { - builder.addTextBody("polygon", this.getPolygon().toString().toLowerCase()); + parameters.put("polygon", this.getPolygon().toString().toLowerCase()); } if (this.getConfidence() != null) { - builder.addTextBody("confidence", this.getConfidence().toString().toLowerCase()); + parameters.put("confidence", this.getConfidence().toString().toLowerCase()); } if (this.getTextContext() != null) { - builder.addTextBody("text_context", this.getTextContext()); + parameters.put("text_context", this.getTextContext()); } if (this.getDataSchema() != null) { - builder.addTextBody("data_schema", this.getDataSchema()); + parameters.put("data_schema", this.getDataSchema()); } - return builder; + + return parameters; } /** @@ -96,7 +100,7 @@ public static Builder builder(String modelId) { /** * Fluent builder for {@link ExtractionParameters}. */ - public static final class Builder extends BaseParameters.BaseBuilder { + public static final class Builder extends BaseProductParameters.BaseBuilder { private Boolean rag = null; private Boolean rawText = null; private Boolean polygon = null; diff --git a/src/main/java/com/mindee/v2/product/ocr/OcrResponse.java b/src/main/java/com/mindee/v2/product/ocr/OcrResponse.java index d5dfea6fc..2f1885db0 100644 --- a/src/main/java/com/mindee/v2/product/ocr/OcrResponse.java +++ b/src/main/java/com/mindee/v2/product/ocr/OcrResponse.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mindee.v2.http.ProductInfo; import com.mindee.v2.parsing.CommonResponse; +import com.mindee.v2.product.ProductAttributes; import lombok.Getter; /** @@ -11,7 +11,7 @@ */ @Getter @JsonIgnoreProperties(ignoreUnknown = true) -@ProductInfo(slug = "ocr") +@ProductAttributes(slug = "ocr") public class OcrResponse extends CommonResponse { /** diff --git a/src/main/java/com/mindee/v2/product/ocr/params/OcrParameters.java b/src/main/java/com/mindee/v2/product/ocr/params/OcrParameters.java index ce125d35b..b891384b8 100644 --- a/src/main/java/com/mindee/v2/product/ocr/params/OcrParameters.java +++ b/src/main/java/com/mindee/v2/product/ocr/params/OcrParameters.java @@ -1,10 +1,10 @@ package com.mindee.v2.product.ocr.params; -import com.mindee.v2.clientoptions.BaseParameters; -import com.mindee.v2.http.ProductInfo; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.product.ProductAttributes; -@ProductInfo(slug = "ocr") -public class OcrParameters extends BaseParameters { +@ProductAttributes(slug = "ocr") +public class OcrParameters extends BaseProductParameters { public OcrParameters(String modelId, String alias, String[] webhookIds) { super(modelId, alias, webhookIds); @@ -20,7 +20,7 @@ public static Builder builder(String modelId) { return new Builder(modelId); } - public static final class Builder extends BaseParameters.BaseBuilder { + public static final class Builder extends BaseProductParameters.BaseBuilder { Builder(String modelId) { super(modelId); diff --git a/src/main/java/com/mindee/v2/product/split/SplitResponse.java b/src/main/java/com/mindee/v2/product/split/SplitResponse.java index 504ede8c7..4d46f9add 100644 --- a/src/main/java/com/mindee/v2/product/split/SplitResponse.java +++ b/src/main/java/com/mindee/v2/product/split/SplitResponse.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.mindee.v2.http.ProductInfo; import com.mindee.v2.parsing.CommonResponse; +import com.mindee.v2.product.ProductAttributes; import lombok.Getter; /** @@ -11,7 +11,7 @@ */ @Getter @JsonIgnoreProperties(ignoreUnknown = true) -@ProductInfo(slug = "split") +@ProductAttributes(slug = "split") public class SplitResponse extends CommonResponse { /** diff --git a/src/main/java/com/mindee/v2/product/split/params/SplitParameters.java b/src/main/java/com/mindee/v2/product/split/params/SplitParameters.java index 126dc86b8..dd636aaad 100644 --- a/src/main/java/com/mindee/v2/product/split/params/SplitParameters.java +++ b/src/main/java/com/mindee/v2/product/split/params/SplitParameters.java @@ -1,10 +1,10 @@ package com.mindee.v2.product.split.params; -import com.mindee.v2.clientoptions.BaseParameters; -import com.mindee.v2.http.ProductInfo; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.product.ProductAttributes; -@ProductInfo(slug = "split") -public class SplitParameters extends BaseParameters { +@ProductAttributes(slug = "split") +public class SplitParameters extends BaseProductParameters { public SplitParameters(String modelId, String alias, String[] webhookIds) { super(modelId, alias, webhookIds); @@ -20,7 +20,7 @@ public static Builder builder(String modelId) { return new Builder(modelId); } - public static final class Builder extends BaseParameters.BaseBuilder { + public static final class Builder extends BaseProductParameters.BaseBuilder { Builder(String modelId) { super(modelId); diff --git a/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java b/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java new file mode 100644 index 000000000..0e4fc0595 --- /dev/null +++ b/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java @@ -0,0 +1,91 @@ +package com.mindee.v2.search.models; + +import com.mindee.v2.clientoptions.BaseSearchParameters; +import java.util.HashMap; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +/** + * Search parameters for models. + */ +@Getter +@EqualsAndHashCode(callSuper = true) +public class ModelSearchParameters extends BaseSearchParameters { + /** + * Case-insensitive search term for the model name + */ + private final String name; + + /** + * Case-insensitive search term for the model type + */ + private final String modelType; + + private ModelSearchParameters(String name, String modelType, Integer page, Integer perPage) { + super(ModelSearchResponse.class, page, perPage); + this.name = name; + this.modelType = modelType; + } + + @Override + public Map getRequestParameters() { + var parameters = new HashMap<>(super.getRequestParameters()); + + if (this.getName() != null && !this.getName().isEmpty()) { + parameters.put("name", this.getName()); + } + if (this.getModelType() != null && !this.getModelType().isEmpty()) { + parameters.put("model_type", this.getModelType()); + } + + return parameters; + } + + /** + * Create a new builder. + * + * @return a fresh {@link Builder} + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Fluent builder for {@link ModelSearchParameters}. + */ + public static final class Builder extends BaseSearchParameters.BaseBuilder { + private String name; + private String modelType; + + Builder() { + } + + /** + * Case-insensitive search term for the model name + */ + public Builder name(String name) { + if (name != null && !name.isEmpty()) { + this.name = name; + } + return this; + } + + /** + * Case-insensitive search term for the model type + */ + public Builder modelType(String modelType) { + if (modelType != null && !modelType.trim().isEmpty()) { + this.modelType = modelType; + } + return this; + } + + /** + * Build an immutable {@link ModelSearchParameters} instance. + */ + public ModelSearchParameters build() { + return new ModelSearchParameters(this.name, this.modelType, this.page, this.perPage); + } + } +} diff --git a/src/main/java/com/mindee/v2/search/models/ModelSearchResponse.java b/src/main/java/com/mindee/v2/search/models/ModelSearchResponse.java new file mode 100644 index 000000000..6f493ad5f --- /dev/null +++ b/src/main/java/com/mindee/v2/search/models/ModelSearchResponse.java @@ -0,0 +1,29 @@ +package com.mindee.v2.search.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.v2.parsing.search.BaseSearchResponse; +import com.mindee.v2.parsing.search.SearchModels; +import com.mindee.v2.product.ProductAttributes; +import java.util.List; +import lombok.Getter; + +/** + * Models search response. + */ +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@ProductAttributes(slug = "models") +public class ModelSearchResponse extends BaseSearchResponse { + + /** + * Paginated list of matching models. + */ + @JsonProperty("models") + private SearchModels models; + + @Override + protected List bodyLines() { + return List.of("Models\n######\n", String.valueOf(models)); + } +} diff --git a/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java b/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java new file mode 100644 index 000000000..c0e0ddf07 --- /dev/null +++ b/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java @@ -0,0 +1,90 @@ +package com.mindee.v2.search.ragdocuments; + +import com.mindee.v2.clientoptions.BaseSearchParameters; +import java.util.HashMap; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +/** + * Search parameters for RAG Documents. + */ +@Getter +@EqualsAndHashCode(callSuper = true) +public class RagDocumentSearchParameters extends BaseSearchParameters { + /** + * Model identifier to search in. + */ + private final String modelId; + + /** + * Case-insensitive substring search on filename. + */ + private final String filename; + + private RagDocumentSearchParameters( + String modelId, + String filename, + Integer page, + Integer perPage + ) { + super(RagDocumentSearchResponse.class, page, perPage); + if (modelId == null || modelId.trim().isEmpty()) { + throw new IllegalArgumentException("ModelId is required in RagDocumentSearchParameters"); + } + this.modelId = modelId; + this.filename = filename; + } + + @Override + public Map getRequestParameters() { + var parameters = new HashMap<>(super.getRequestParameters()); + + parameters.put("model_id", this.getModelId()); + + if (this.getFilename() != null && !this.getFilename().isEmpty()) { + parameters.put("filename", this.getFilename()); + } + + return parameters; + } + + /** + * Create a new builder. + * + * @param modelId the mandatory model identifier + * @return a fresh {@link Builder} + */ + public static Builder builder(String modelId) { + return new Builder(modelId); + } + + /** + * Fluent builder for {@link RagDocumentSearchParameters}. + */ + public static final class Builder extends BaseSearchParameters.BaseBuilder { + private final String modelId; + private String filename; + + Builder(String modelId) { + this.modelId = modelId; + } + + /** + * Case-insensitive substring search on filename. + */ + public Builder filename(String filename) { + if (filename != null && !filename.isEmpty()) { + this.filename = filename; + } + return this; + } + + /** + * Build an immutable {@link RagDocumentSearchParameters} instance. + */ + public RagDocumentSearchParameters build() { + return new RagDocumentSearchParameters(this.modelId, this.filename, this.page, this.perPage); + } + } +} diff --git a/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchResponse.java b/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchResponse.java new file mode 100644 index 000000000..f506cc3f8 --- /dev/null +++ b/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchResponse.java @@ -0,0 +1,22 @@ +package com.mindee.v2.search.ragdocuments; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.mindee.v2.parsing.search.BaseSearchResponse; +import com.mindee.v2.parsing.search.SearchRagDocuments; +import com.mindee.v2.product.ProductAttributes; +import java.util.List; +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +@ProductAttributes(slug = "rag-documents") +public class RagDocumentSearchResponse extends BaseSearchResponse { + @JsonProperty("rag_documents") + private SearchRagDocuments ragDocuments; + + @Override + protected List bodyLines() { + return List.of("RAG Documents\n#############\n", String.valueOf(ragDocuments)); + } +} diff --git a/src/test/java/com/mindee/v2/MindeeClientTest.java b/src/test/java/com/mindee/v2/MindeeClientTest.java index 139273f00..0ccc98b49 100644 --- a/src/test/java/com/mindee/v2/MindeeClientTest.java +++ b/src/test/java/com/mindee/v2/MindeeClientTest.java @@ -10,14 +10,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.mindee.input.LocalInputSource; import com.mindee.input.URLInputSource; -import com.mindee.v2.clientoptions.BaseParameters; +import com.mindee.v2.clientoptions.BaseProductParameters; +import com.mindee.v2.clientoptions.BaseSearchParameters; import com.mindee.v2.clientoptions.PollingOptions; import com.mindee.v2.http.MindeeApiV2; import com.mindee.v2.parsing.CommonResponse; import com.mindee.v2.parsing.JobResponse; +import com.mindee.v2.parsing.search.BaseSearchResponse; import com.mindee.v2.parsing.search.SearchResponse; import com.mindee.v2.product.extraction.ExtractionResponse; import com.mindee.v2.product.extraction.params.ExtractionParameters; +import com.mindee.v2.search.models.ModelSearchParameters; +import com.mindee.v2.search.models.ModelSearchResponse; import java.io.IOException; import java.nio.file.Files; import java.util.concurrent.CancellationException; @@ -41,27 +45,38 @@ public FakeMindeeApiV2(JobResponse jobResponse, CommonResponse resultResponse) { } @Override - public JobResponse reqPostEnqueue(LocalInputSource inputSource, BaseParameters options) { + public JobResponse reqPostEnqueue( + LocalInputSource inputSource, + BaseProductParameters parameters + ) { return jobResponse; } @Override - public JobResponse reqPostEnqueue(URLInputSource inputSource, BaseParameters options) { + public JobResponse reqPostEnqueue(URLInputSource inputSource, BaseProductParameters options) { return jobResponse; } @Override - public JobResponse reqGetJob(String jobId) { + public JobResponse reqGetJobById(String jobId) { return jobResponse; } @Override - public SearchResponse reqGetSearchModels(String modelName, String modelType) { + public TSearchResponse reqGetSearch( + BaseSearchParameters parameters + ) { + return (TSearchResponse) new ModelSearchResponse(); + } + + @Override + @Deprecated + public SearchResponse reqGetSearch(ModelSearchParameters parameters) { return new SearchResponse(); } @Override - public TResponse reqGetResult( + public TResponse reqGetResultById( Class tResponseClass, String inferenceId ) { @@ -69,7 +84,7 @@ public TResponse reqGetResult( } @Override - public TResponse reqGetResultFromUrl( + public TResponse reqGetResultByUrl( Class tResponseClass, String inferenceUrl ) { @@ -168,7 +183,7 @@ void document_getResultFromUrl_async() throws IOException { AtomicReference capturedUrl = new AtomicReference<>(); var api = new FakeMindeeApiV2(null, processed) { @Override - public TResponse reqGetResultFromUrl( + public TResponse reqGetResultByUrl( Class tResponseClass, String inferenceUrl ) { @@ -236,7 +251,7 @@ void polling_cancelToken_aborts() throws IOException { var api = new FakeMindeeApiV2(processing, null) { @Override - public JobResponse reqGetJob(String jobId) { + public JobResponse reqGetJobById(String jobId) { jobCalls.incrementAndGet(); cancel.set(true); return processing; diff --git a/src/test/java/com/mindee/v2/product/CropIT.java b/src/test/java/com/mindee/v2/product/CropIT.java index 1d685c31d..bb44dda84 100644 --- a/src/test/java/com/mindee/v2/product/CropIT.java +++ b/src/test/java/com/mindee/v2/product/CropIT.java @@ -20,7 +20,7 @@ @DisplayName("MindeeV2 – Integration Tests - Crop") class CropIT { - private MindeeClient mindeeClient; + private MindeeClient client; private String cropModelId; private String cropExtractionModelId; @@ -29,7 +29,7 @@ void setUp() { var apiKey = System.getenv("MINDEE_V2_API_KEY"); cropModelId = System.getenv("MINDEE_V2_SE_TESTS_CROP_MODEL_ID"); cropExtractionModelId = System.getenv("MINDEE_V2_SE_TESTS_CROP_EXTRACTION_MODEL_ID"); - mindeeClient = new MindeeClient(apiKey); + client = new MindeeClient(apiKey); } @Test @@ -47,7 +47,7 @@ void filledMultiPage_cropMustSucceed() throws IOException, InterruptedException .maxRetries(80) .build(); - CropResponse response = mindeeClient + CropResponse response = client .enqueueAndGetResult(CropResponse.class, source, params, pollingOptions); assertNotNull(response); @@ -86,7 +86,7 @@ void filledSinglePage_extractionMustSucceed() throws IOException, InterruptedExc .maxRetries(80) .build(); - CropResponse response = mindeeClient + CropResponse response = client .enqueueAndGetResult(CropResponse.class, source, params, pollingOptions); assertNotNull(response); diff --git a/src/test/java/com/mindee/v2/product/SplitIT.java b/src/test/java/com/mindee/v2/product/SplitIT.java index 1de3df17d..3ed83bc36 100644 --- a/src/test/java/com/mindee/v2/product/SplitIT.java +++ b/src/test/java/com/mindee/v2/product/SplitIT.java @@ -3,7 +3,6 @@ import static com.mindee.TestingUtilities.getResourcePath; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import com.mindee.input.LocalInputSource; import com.mindee.v2.MindeeClient; @@ -22,14 +21,14 @@ @DisplayName("MindeeV2 – Integration Tests - Split") class SplitIT { - private MindeeClient mindeeClient; + private MindeeClient client; private String modelId; @BeforeAll void setUp() { var apiKey = System.getenv("MINDEE_V2_API_KEY"); modelId = System.getenv("MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID"); - mindeeClient = new MindeeClient(apiKey); + client = new MindeeClient(apiKey); } @Test @@ -47,8 +46,7 @@ void parseFile_emptyMultiPage_mustSucceed() throws IOException, InterruptedExcep .maxRetries(80) .build(); - var response = mindeeClient - .enqueueAndGetResult(SplitResponse.class, source, params, pollingOptions); + var response = client.enqueueAndGetResult(SplitResponse.class, source, params, pollingOptions); assertNotNull(response); var inference = response.getInference(); @@ -64,6 +62,6 @@ void parseFile_emptyMultiPage_mustSucceed() throws IOException, InterruptedExcep var result = inference.getResult(); assertNotNull(result); - assertTrue(result.getSplits().isEmpty()); + assertEquals(1, result.getSplits().size()); } } diff --git a/src/test/java/com/mindee/v2/search/ModelSearchIT.java b/src/test/java/com/mindee/v2/search/ModelSearchIT.java new file mode 100644 index 000000000..2d8a0f4e5 --- /dev/null +++ b/src/test/java/com/mindee/v2/search/ModelSearchIT.java @@ -0,0 +1,66 @@ +package com.mindee.v2.search; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.mindee.v2.MindeeClient; +import com.mindee.v2.parsing.search.SearchResponse; +import com.mindee.v2.search.models.ModelSearchParameters; +import com.mindee.v2.search.models.ModelSearchResponse; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Tag("integration") +public class ModelSearchIT { + + private MindeeClient client; + + @BeforeAll + void setUp() { + var apiKey = System.getenv("MINDEE_V2_API_KEY"); + client = new MindeeClient(apiKey); + } + + @Test + public void ModelSearch_mustHaveResults() throws Exception { + ModelSearchResponse response = client.search(ModelSearchParameters.builder().build()); + + assertNotNull(response); + assertNotNull(response.getModels()); + assertFalse(response.getModels().isEmpty()); + assertNotNull(response.getPagination()); + assertTrue(response.getPagination().getTotalItems() > 1); + assertEquals(1, response.getPagination().getPage()); + } + + @Test + public void ModelSearch_mustReturnEmpty() throws Exception { + ModelSearchResponse response = client + .search(ModelSearchParameters.builder().name("je n'existe pas tralala").build()); + + assertNotNull(response); + assertNotNull(response.getModels()); + assertTrue(response.getModels().isEmpty()); + assertNotNull(response.getPagination()); + assertEquals(0, response.getPagination().getTotalItems()); + assertEquals(1, response.getPagination().getPage()); + } + + @Test + @SuppressWarnings("deprecation") + public void ModelSearch_mustReturnEmptyObsolete() throws Exception { + SearchResponse response = client.searchModels("je n'existe pas tralala"); + + assertNotNull(response); + assertNotNull(response.getModels()); + assertTrue(response.getModels().isEmpty()); + assertNotNull(response.getPagination()); + assertEquals(0, response.getPagination().getTotalItems()); + assertEquals(1, response.getPagination().getPage()); + } +} diff --git a/src/test/java/com/mindee/v2/search/ModelSearchTest.java b/src/test/java/com/mindee/v2/search/ModelSearchTest.java new file mode 100644 index 000000000..03deb145e --- /dev/null +++ b/src/test/java/com/mindee/v2/search/ModelSearchTest.java @@ -0,0 +1,40 @@ +package com.mindee.v2.search; + +import static com.mindee.TestingUtilities.getV2ResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.mindee.v2.parsing.LocalResponse; +import com.mindee.v2.search.models.ModelSearchResponse; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +public class ModelSearchTest { + @Test + public void modelSearchResponse_LoadsLocally() throws IOException { + LocalResponse localResponse = new LocalResponse(getV2ResourcePath("search/models.json")); + ModelSearchResponse response = localResponse.deserializeResponse(ModelSearchResponse.class); + + assertNotNull(response); + + assertEquals(5, response.getModels().size()); + assertEquals(5, response.getPagination().getTotalItems()); + assertEquals(1, response.getPagination().getPage()); + assertEquals(50, response.getPagination().getPerPage()); + assertEquals(1, response.getPagination().getTotalPages()); + + var firstItem = response.getModels().get(0); + assertEquals("Extraction With Webhooks", firstItem.getName()); + assertEquals("afde5151-aa11-aa11-9289-fa04e50ca3b9", firstItem.getId()); + assertEquals("extraction", firstItem.getModelType()); + + assertEquals(2, firstItem.getWebhooks().size()); + assertEquals("a2286ed9-aa11-aa11-bdc5-2f8496c5641a", firstItem.getWebhooks().get(0).getId()); + assertEquals("FAILURE", firstItem.getWebhooks().get(0).getName()); + assertEquals("https://failure.mindee.com", firstItem.getWebhooks().get(0).getUrl()); + + var lastItem = response.getModels().get(response.getModels().size() - 1); + assertEquals("Extraction Without Webhooks Key", lastItem.getName()); + assertEquals("e14e0923-ee55-ee55-a335-8d2110917d7b", lastItem.getId()); + } +} diff --git a/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java b/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java new file mode 100644 index 000000000..22f163b14 --- /dev/null +++ b/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java @@ -0,0 +1,38 @@ +package com.mindee.v2.search; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.mindee.v2.MindeeClient; +import com.mindee.v2.search.ragdocuments.RagDocumentSearchParameters; +import com.mindee.v2.search.ragdocuments.RagDocumentSearchResponse; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Tag("integration") +public class RagDocumentSearchIT { + + private MindeeClient client; + private String findocModelId; + + @BeforeAll + void setUp() { + var apiKey = System.getenv("MINDEE_V2_API_KEY"); + client = new MindeeClient(apiKey); + findocModelId = System.getenv("MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID"); + } + + @Test + public void RagDocumentSearch_mustHaveResults() throws Exception { + RagDocumentSearchResponse response = client + .search(RagDocumentSearchParameters.builder(findocModelId).build()); + + assertNotNull(response); + assertNotNull(response.getRagDocuments()); + assertNotNull(response.getPagination()); + assertEquals(1, response.getPagination().getPage()); + } +} diff --git a/src/test/java/com/mindee/v2/search/RagDocumentSearchTest.java b/src/test/java/com/mindee/v2/search/RagDocumentSearchTest.java new file mode 100644 index 000000000..fb0084a07 --- /dev/null +++ b/src/test/java/com/mindee/v2/search/RagDocumentSearchTest.java @@ -0,0 +1,57 @@ +package com.mindee.v2.search; + +import static com.mindee.TestingUtilities.getV2ResourcePath; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.mindee.v2.parsing.LocalResponse; +import com.mindee.v2.search.ragdocuments.RagDocumentSearchResponse; +import java.io.IOException; +import java.time.OffsetDateTime; +import org.junit.jupiter.api.Test; + +public class RagDocumentSearchTest { + + @Test + public void ragDocumentSearchResponse_LoadsLocally() throws IOException { + LocalResponse localResponse = new LocalResponse(getV2ResourcePath("search/rag_documents.json")); + RagDocumentSearchResponse response = localResponse + .deserializeResponse(RagDocumentSearchResponse.class); + + assertNotNull(response); + + assertEquals(3, response.getRagDocuments().size()); + assertEquals(3, response.getPagination().getTotalItems()); + assertEquals(1, response.getPagination().getPage()); + assertEquals(50, response.getPagination().getPerPage()); + assertEquals(1, response.getPagination().getTotalPages()); + + var firstItem = response.getRagDocuments().get(0); + assertEquals("cc831599-c545-48b7-aa27-6d7ccd5b8d32", firstItem.getId()); + assertEquals("12345678-1234-1234-1234-123456789abc", firstItem.getModelId()); + assertEquals("invoice_01.pdf", firstItem.getFilename()); + assertEquals(OffsetDateTime.parse("2026-06-30T13:13:46.168586Z"), firstItem.getCreatedAt()); + assertEquals(0, firstItem.getTotalMatches()); + assertNull(firstItem.getLastMatchAt()); + assertEquals("Processing", firstItem.getStatus()); + + var secondItem = response.getRagDocuments().get(1); + assertEquals("27467e4c-5602-4315-90d9-3d2da69b05ab", secondItem.getId()); + assertEquals("12345678-1234-1234-1234-123456789abc", secondItem.getModelId()); + assertEquals("invoice_02.pdf", secondItem.getFilename()); + assertEquals(OffsetDateTime.parse("2026-06-30T13:13:46.168586Z"), secondItem.getCreatedAt()); + assertEquals(0, secondItem.getTotalMatches()); + assertNull(secondItem.getLastMatchAt()); + assertEquals("Draft", secondItem.getStatus()); + + var thirdItem = response.getRagDocuments().get(2); + assertEquals("a6bcae7d-0439-476b-8a63-5a39ec05dc21", thirdItem.getId()); + assertEquals("12345678-1234-1234-1234-jobid1234567", thirdItem.getModelId()); + assertEquals("invoice_03.pdf", thirdItem.getFilename()); + assertEquals(OffsetDateTime.parse("2026-06-17T14:35:46.228006Z"), thirdItem.getCreatedAt()); + assertEquals(5, thirdItem.getTotalMatches()); + assertEquals(OffsetDateTime.parse("2026-06-18T14:35:46.248006Z"), thirdItem.getLastMatchAt()); + assertEquals("Active", thirdItem.getStatus()); + } +} diff --git a/src/test/resources b/src/test/resources index e41ab97c2..4b7f33766 160000 --- a/src/test/resources +++ b/src/test/resources @@ -1 +1 @@ -Subproject commit e41ab97c2833f15ea5c4edf221c2d197d212f632 +Subproject commit 4b7f33766fab0e67804b84447b73c80a902d886a