From d9a6c9ff25e756b363d23f3d6593a6944c244161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Tue, 1 Sep 2026 15:26:41 +0200 Subject: [PATCH 1/5] :sparkles: add RAG search API --- src/main/java/com/mindee/v2/MindeeClient.java | 43 ++++++--- ...meters.java => BaseProductParameters.java} | 30 ++++--- .../clientoptions/BaseSearchParameters.java | 66 ++++++++++++++ .../java/com/mindee/v2/http/MindeeApiV2.java | 40 ++++++--- .../com/mindee/v2/http/MindeeHttpApiV2.java | 86 +++++++++++------- .../com/mindee/v2/parsing/CommonResponse.java | 2 +- .../v2/parsing/error/ErrorResponse.java | 30 ++++++- .../v2/parsing/search/BaseSearchResponse.java | 42 +++++++++ .../mindee/v2/parsing/search/SearchModel.java | 2 +- .../v2/parsing/search/SearchModels.java | 28 ++++++ .../v2/parsing/search/SearchRagDocument.java | 62 +++++++++++++ .../v2/parsing/search/SearchRagDocuments.java | 32 +++++++ .../v2/parsing/search/SearchResponse.java | 32 +------ .../ProductAttributes.java} | 4 +- .../ClassificationResponse.java | 4 +- .../params/ClassificationParameters.java | 10 +-- .../mindee/v2/product/crop/CropResponse.java | 4 +- .../product/crop/params/CropParameters.java | 10 +-- .../extraction/ExtractionResponse.java | 4 +- .../params/ExtractionParameters.java | 34 +++---- .../mindee/v2/product/ocr/OcrResponse.java | 4 +- .../v2/product/ocr/params/OcrParameters.java | 10 +-- .../v2/product/split/SplitResponse.java | 4 +- .../product/split/params/SplitParameters.java | 10 +-- .../search/models/ModelSearchParameters.java | 87 ++++++++++++++++++ .../v2/search/models/ModelSearchResponse.java | 29 ++++++ .../RagDocumentSearchParameters.java | 88 +++++++++++++++++++ .../RagDocumentSearchResponse.java | 22 +++++ .../java/com/mindee/v2/MindeeClientTest.java | 31 +++++-- .../java/com/mindee/v2/product/CropIT.java | 8 +- .../java/com/mindee/v2/product/SplitIT.java | 10 +-- .../com/mindee/v2/search/ModelSearchIT.java | 69 +++++++++++++++ .../com/mindee/v2/search/ModelSearchTest.java | 40 +++++++++ .../mindee/v2/search/RagDocumentSearchIT.java | 41 +++++++++ .../v2/search/RagDocumentSearchTest.java | 57 ++++++++++++ src/test/resources | 2 +- 36 files changed, 915 insertions(+), 162 deletions(-) rename src/main/java/com/mindee/v2/clientoptions/{BaseParameters.java => BaseProductParameters.java} (55%) create mode 100644 src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java create mode 100644 src/main/java/com/mindee/v2/parsing/search/BaseSearchResponse.java create mode 100644 src/main/java/com/mindee/v2/parsing/search/SearchModels.java create mode 100644 src/main/java/com/mindee/v2/parsing/search/SearchRagDocument.java create mode 100644 src/main/java/com/mindee/v2/parsing/search/SearchRagDocuments.java rename src/main/java/com/mindee/v2/{http/ProductInfo.java => product/ProductAttributes.java} (83%) create mode 100644 src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java create mode 100644 src/main/java/com/mindee/v2/search/models/ModelSearchResponse.java create mode 100644 src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java create mode 100644 src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchResponse.java create mode 100644 src/test/java/com/mindee/v2/search/ModelSearchIT.java create mode 100644 src/test/java/com/mindee/v2/search/ModelSearchTest.java create mode 100644 src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java create mode 100644 src/test/java/com/mindee/v2/search/RagDocumentSearchTest.java diff --git a/src/main/java/com/mindee/v2/MindeeClient.java b/src/main/java/com/mindee/v2/MindeeClient.java index 4b2bda9be..1bfebdac8 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,6 +11,7 @@ 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 java.io.IOException; @@ -44,7 +46,7 @@ public MindeeClient(MindeeApiV2 mindeeApi) { */ public JobResponse enqueue( LocalInputSource inputSource, - BaseParameters params + BaseProductParameters params ) throws IOException { return mindeeApi.reqPostEnqueue(inputSource, params); } @@ -55,7 +57,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 +73,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 +87,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 +101,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 +117,7 @@ public TResponse getResultFromUrl( public TResponse enqueueAndGetResult( Class responseClass, LocalInputSource inputSource, - BaseParameters params + BaseProductParameters params ) throws IOException, InterruptedException { return enqueueAndGetResult( responseClass, @@ -136,7 +141,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 +161,7 @@ public TResponse enqueueAndGetResult( public TResponse enqueueAndGetResult( Class responseClass, URLInputSource inputSource, - BaseParameters params + BaseProductParameters params ) throws IOException, InterruptedException { return enqueueAndGetResult( responseClass, @@ -180,7 +185,7 @@ public TResponse enqueueAndGetResult( public TResponse enqueueAndGetResult( Class responseClass, URLInputSource inputSource, - BaseParameters params, + BaseProductParameters params, PollingOptions pollingOptions ) throws IOException, InterruptedException { inputSource.validateSecure(); @@ -188,11 +193,25 @@ public TResponse enqueueAndGetResult( return pollAndFetch(responseClass, job, pollingOptions); } + /** + * Search for resources matching the given criteria. + * + * @param searchParameters Search parameters + */ + public TSearchResponse search( + Class responseClass, + BaseSearchParameters searchParameters + ) { + return mindeeApi.reqGetSearch(responseClass, searchParameters); + } + /** * Return all models. * * @return an instance of {@link SearchResponse} + * @deprecated Use {@link #search} instead. */ + @Deprecated public SearchResponse searchModels() { return searchModels(null, null); } @@ -202,7 +221,9 @@ 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); } @@ -213,7 +234,9 @@ 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); } 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..1b1080718 --- /dev/null +++ b/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java @@ -0,0 +1,66 @@ +package com.mindee.v2.clientoptions; + +import java.util.HashMap; +import java.util.Map; +import lombok.Data; + +/** + * Base parameters for searches. + */ +@Data +public abstract class BaseSearchParameters { + /** + * 1-based page index. + */ + protected final Integer page; + /** + * Number of items per page. + */ + protected final Integer perPage; + + /** + * Gets the request parameters for the search request. + */ + public Map getRequestParameters() { + var parameters = new HashMap(); + + if (this.getPage() != null && this.getPage() > 0) { + parameters.put("page", String.valueOf(getPage())); + } + if (this.getPerPage() != null && this.getPerPage() > 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..0817ab523 100644 --- a/src/main/java/com/mindee/v2/http/MindeeApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeApiV2.java @@ -4,11 +4,14 @@ 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 java.io.IOException; /** @@ -19,22 +22,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 +45,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,17 +61,26 @@ 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 resources with the given criteria. + */ + public abstract TSearchResponse reqGetSearch( + Class responseClass, + BaseSearchParameters parameters + ); + /** * Retrieves a list of models. * * @param modelName search term for model name * @param modelType search term for model type */ + @Deprecated public abstract SearchResponse reqGetSearchModels(String modelName, String modelType); /** @@ -84,8 +96,10 @@ protected ErrorResponse makeUnknownError(int statusCode) { ); } - protected ProductInfo getResponseProductInfo(Class responseClass) { - var productInfo = responseClass.getAnnotation(ProductInfo.class); + protected ProductAttributes getResponseProductInfo( + Class responseClass + ) { + var productInfo = responseClass.getAnnotation(ProductAttributes.class); if (productInfo == null) { throw new MindeeException( "The class " + responseClass.getSimpleName() + " is not annotated with @ProductInfo" @@ -94,8 +108,10 @@ protected ProductInfo getResponseProductInfo(Class res return productInfo; } - protected ProductInfo getParamsProductInfo(Class responseClass) { - var productInfo = responseClass.getAnnotation(ProductInfo.class); + protected ProductAttributes getParamsProductAttributes( + Class responseClass + ) { + var productInfo = responseClass.getAnnotation(ProductAttributes.class); if (productInfo == null) { throw new MindeeException( "The class " + responseClass.getSimpleName() + " is not annotated with @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..58ee79275 100644 --- a/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java @@ -6,10 +6,12 @@ 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 java.io.IOException; import java.net.URISyntaxException; @@ -64,12 +66,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 +88,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 +101,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 +110,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,7 +128,7 @@ public JobResponse reqGetJob(String jobId) { } @Override - public TResponse reqGetResult( + public TResponse reqGetResultById( Class responseClass, String inferenceId ) { @@ -133,12 +140,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 +156,42 @@ public TResponse reqGetResultFromUrl( return executeAPIRequest(get, responseClass); } + @Override + public TSearchResponse reqGetSearch( + Class responseClass, + BaseSearchParameters parameters + ) { + var productInfo = getResponseProductInfo(responseClass); + 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, responseClass); + } + + @Override + @Deprecated + 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); + } + /** * Ensures that a caller-supplied inference URL targets the configured Mindee * base URL so the {@code Authorization} header attached by @@ -222,24 +264,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. * 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..7a1985f62 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,10 @@ 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.search.models.ModelSearchResponse; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.NoArgsConstructor; /** * Models search response. @@ -17,29 +13,7 @@ @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties(ignoreUnknown = true) @AllArgsConstructor -@NoArgsConstructor -public class SearchResponse extends CommonResponse { +@Deprecated +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..2798648d4 --- /dev/null +++ b/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java @@ -0,0 +1,87 @@ +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(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) { + this.name = name; + return this; + } + + /** + * Case-insensitive search term for the model type + */ + public Builder modelType(String modelType) { + 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..79112eb66 --- /dev/null +++ b/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java @@ -0,0 +1,88 @@ +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(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) { + 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..62c65117d 100644 --- a/src/test/java/com/mindee/v2/MindeeClientTest.java +++ b/src/test/java/com/mindee/v2/MindeeClientTest.java @@ -10,14 +10,17 @@ 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.ModelSearchResponse; import java.io.IOException; import java.nio.file.Files; import java.util.concurrent.CancellationException; @@ -41,27 +44,39 @@ 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 TSearchResponse reqGetSearch( + Class responseClass, + BaseSearchParameters parameters + ) { + return (TSearchResponse) new ModelSearchResponse(); + } + + @Override + @Deprecated public SearchResponse reqGetSearchModels(String modelName, String modelType) { 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..d2b11e670 --- /dev/null +++ b/src/test/java/com/mindee/v2/search/ModelSearchIT.java @@ -0,0 +1,69 @@ +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.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(ModelSearchResponse.class, 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( + ModelSearchResponse.class, + 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 { + ModelSearchResponse 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..c6137897d --- /dev/null +++ b/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java @@ -0,0 +1,41 @@ +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( + RagDocumentSearchResponse.class, + 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 From 2220e891c8b0976091ed449872a56101fa488c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Tue, 1 Sep 2026 19:44:18 +0200 Subject: [PATCH 2/5] will it blend? --- .../java/com/mindee/CommandLineInterface.java | 2 + src/main/java/com/mindee/v2/MindeeClient.java | 10 ++- .../java/com/mindee/v2/cli/BaseCommand.java | 69 +++++++++++++++++++ .../mindee/v2/cli/BaseInferenceCommand.java | 63 +---------------- .../mindee/v2/cli/SearchModelsCommand.java | 56 ++++++++------- .../v2/cli/SearchRagDocumentsCommand.java | 50 ++++++++++++++ .../java/com/mindee/v2/http/MindeeApiV2.java | 18 ++--- .../com/mindee/v2/http/MindeeHttpApiV2.java | 20 ------ .../v2/parsing/search/SearchResponse.java | 2 + .../java/com/mindee/v2/MindeeClientTest.java | 7 -- 10 files changed, 167 insertions(+), 130 deletions(-) create mode 100644 src/main/java/com/mindee/v2/cli/BaseCommand.java create mode 100644 src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java 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/v2/MindeeClient.java b/src/main/java/com/mindee/v2/MindeeClient.java index 1bfebdac8..6490809bd 100644 --- a/src/main/java/com/mindee/v2/MindeeClient.java +++ b/src/main/java/com/mindee/v2/MindeeClient.java @@ -14,6 +14,7 @@ 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.concurrent.CancellationException; @@ -213,7 +214,7 @@ public TSearchResponse search( */ @Deprecated public SearchResponse searchModels() { - return searchModels(null, null); + return search(SearchResponse.class, null); } /** @@ -225,7 +226,7 @@ public SearchResponse searchModels() { */ @Deprecated public SearchResponse searchModels(String modelName) { - return searchModels(modelName, null); + return search(SearchResponse.class, ModelSearchParameters.builder().name(modelName).build()); } /** @@ -238,7 +239,10 @@ public SearchResponse searchModels(String modelName) { */ @Deprecated public SearchResponse searchModels(String modelName, String modelType) { - return mindeeApi.reqGetSearchModels(modelName, modelType); + return search( + SearchResponse.class, + ModelSearchParameters.builder().name(modelName).modelType(modelType).build() + ); } /** 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..3d650cd12 --- /dev/null +++ b/src/main/java/com/mindee/v2/cli/BaseCommand.java @@ -0,0 +1,69 @@ +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 V2 inference CLI commands. + * Handles common options (path, model-id, api-key, alias, output) and output formatting. + */ +public abstract class BaseCommand implements Callable { + @CommandLine.Option(names = { "-k", "--api-key" }, description = "Mindee V2 API key.") + 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: a basic summary (default)\n" + + "- full: detail extraction results, including options\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 getSummary(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(getSummary(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..bfcaa77fc 100644 --- a/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java +++ b/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java @@ -1,13 +1,10 @@ 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; @@ -15,7 +12,7 @@ * Abstract base class for V2 inference CLI commands. * Handles common options (path, model-id, api-key, alias, output) and output formatting. */ -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,9 +20,6 @@ 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") protected String alias; @@ -35,23 +29,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 +49,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(String.valueOf(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/SearchModelsCommand.java b/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java index f40979226..0249d35da 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,39 @@ 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(String.valueOf(apiKey)); + var response = client + .search( + ModelSearchResponse.class, + ModelSearchParameters.builder().name(name).modelType(String.valueOf(modelType)).build() + ); + printOutput(response); return 0; } + + @Override + protected String getSummary(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..30261c0fc --- /dev/null +++ b/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java @@ -0,0 +1,50 @@ +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 model name partial match (case insensitive)." + ) + private String filename; + + @Override + protected String getSummary(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(String.valueOf(apiKey)); + var response = client + .search( + RagDocumentSearchResponse.class, + RagDocumentSearchParameters.builder(modelId).filename(filename).build() + ); + printOutput(response); + return 0; + } +} diff --git a/src/main/java/com/mindee/v2/http/MindeeApiV2.java b/src/main/java/com/mindee/v2/http/MindeeApiV2.java index 0817ab523..a69c6c288 100644 --- a/src/main/java/com/mindee/v2/http/MindeeApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeApiV2.java @@ -10,7 +10,6 @@ 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 java.io.IOException; @@ -74,15 +73,6 @@ public abstract TSearchResponse req BaseSearchParameters parameters ); - /** - * Retrieves a list of models. - * - * @param modelName search term for model name - * @param modelType search term for model type - */ - @Deprecated - public abstract SearchResponse reqGetSearchModels(String modelName, String modelType); - /** * Creates an "unknown error" response from an HTTP status code. */ @@ -102,19 +92,19 @@ protected ProductAttributes getResponseProductInfo( 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 ProductAttributes getParamsProductAttributes( - Class responseClass + Class paramsClass ) { - var productInfo = responseClass.getAnnotation(ProductAttributes.class); + 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 58ee79275..6f2a2b9df 100644 --- a/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java @@ -12,7 +12,6 @@ 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 java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -173,25 +172,6 @@ public TSearchResponse reqGetSearch return this.executeAPIRequest(get, responseClass); } - @Override - @Deprecated - 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); - } - /** * Ensures that a caller-supplied inference URL targets the configured Mindee * base URL so the {@code Authorization} header attached by 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 7a1985f62..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,6 +1,7 @@ package com.mindee.v2.parsing.search; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.mindee.v2.product.ProductAttributes; import com.mindee.v2.search.models.ModelSearchResponse; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; @@ -14,6 +15,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @AllArgsConstructor @Deprecated +@ProductAttributes(slug = "models") public class SearchResponse extends ModelSearchResponse { } diff --git a/src/test/java/com/mindee/v2/MindeeClientTest.java b/src/test/java/com/mindee/v2/MindeeClientTest.java index 62c65117d..3c449d4ac 100644 --- a/src/test/java/com/mindee/v2/MindeeClientTest.java +++ b/src/test/java/com/mindee/v2/MindeeClientTest.java @@ -17,7 +17,6 @@ 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.ModelSearchResponse; @@ -69,12 +68,6 @@ public TSearchResponse reqGetSearch return (TSearchResponse) new ModelSearchResponse(); } - @Override - @Deprecated - public SearchResponse reqGetSearchModels(String modelName, String modelType) { - return new SearchResponse(); - } - @Override public TResponse reqGetResultById( Class tResponseClass, From e1f55ae77fc26aa60fbbc4432d1134d76e18e669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Wed, 2 Sep 2026 11:54:05 +0200 Subject: [PATCH 3/5] CLI and error messages --- src/main/java/com/mindee/MindeeException.java | 8 +++--- src/main/java/com/mindee/v2/MindeeClient.java | 6 ++--- .../java/com/mindee/v2/cli/BaseCommand.java | 18 ++++++++----- .../mindee/v2/cli/BaseInferenceCommand.java | 9 ++++--- .../mindee/v2/cli/ClassificationCommand.java | 6 ++--- .../java/com/mindee/v2/cli/CropCommand.java | 6 ++--- .../com/mindee/v2/cli/ExtractionCommand.java | 12 +++------ .../java/com/mindee/v2/cli/OcrCommand.java | 6 ++--- .../mindee/v2/cli/SearchModelsCommand.java | 10 ++++--- .../v2/cli/SearchRagDocumentsCommand.java | 9 ++++--- .../java/com/mindee/v2/cli/SplitCommand.java | 2 +- .../com/mindee/v2/http/MindeeHttpApiV2.java | 24 ++++++++--------- .../mindee/v2/http/MindeeHttpExceptionV2.java | 26 +++++++++++-------- .../search/models/ModelSearchParameters.java | 8 ++++-- .../RagDocumentSearchParameters.java | 4 ++- 15 files changed, 87 insertions(+), 67 deletions(-) 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 6490809bd..991c52ec7 100644 --- a/src/main/java/com/mindee/v2/MindeeClient.java +++ b/src/main/java/com/mindee/v2/MindeeClient.java @@ -280,9 +280,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 index 3d650cd12..4e49626a8 100644 --- a/src/main/java/com/mindee/v2/cli/BaseCommand.java +++ b/src/main/java/com/mindee/v2/cli/BaseCommand.java @@ -7,11 +7,15 @@ import picocli.CommandLine; /** - * Abstract base class for V2 inference CLI commands. - * Handles common options (path, model-id, api-key, alias, output) and output formatting. + * 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.") + @CommandLine.Option( + names = { "-k", "--api-key" }, + description = "Mindee V2 API key.", + defaultValue = "" + ) protected String apiKey; /** Output format for the command. */ @@ -24,8 +28,8 @@ public enum OutputType { @CommandLine.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" + + "- summary: basic formatted response (default)\n" + + "- full: complete formatted response\n" + "- raw: full JSON object", defaultValue = "summary" ) @@ -38,7 +42,7 @@ public enum OutputType { * @param response the response * @return the summary string */ - protected abstract String getSummary(CommonResponse response); + protected abstract String getSummaryOutput(CommonResponse response); /** * Returns the full string for the given response. @@ -62,7 +66,7 @@ protected void printOutput(CommonResponse response) throws Exception { System.out.println(mapper.writeValueAsString(jsonNode)); break; default: - System.out.println(getSummary(response)); + 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 bfcaa77fc..5e7a4ab68 100644 --- a/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java +++ b/src/main/java/com/mindee/v2/cli/BaseInferenceCommand.java @@ -10,7 +10,7 @@ /** * 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 extends BaseCommand { @@ -20,7 +20,10 @@ public abstract class BaseInferenceCommand extends BaseCommand { @Option(names = { "-m", "--model-id" }, description = "ID of the model to use", required = true) protected String modelId; - @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( @@ -51,7 +54,7 @@ protected abstract CommonResponse executeRequest( @Override public Integer call() throws Exception { - var client = new MindeeClient(String.valueOf(apiKey)); + var client = new MindeeClient(apiKey); var inputSource = new LocalInputSource(file); var response = executeRequest(client, inputSource); printOutput(response); 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 0249d35da..8f3e118a5 100644 --- a/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java +++ b/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java @@ -39,18 +39,22 @@ public enum ModelType { @Override public Integer call() throws Exception { - var client = new MindeeClient(String.valueOf(apiKey)); + var client = new MindeeClient(apiKey); var response = client .search( ModelSearchResponse.class, - ModelSearchParameters.builder().name(name).modelType(String.valueOf(modelType)).build() + ModelSearchParameters + .builder() + .name(name) + .modelType(modelType != null ? modelType.name() : null) + .build() ); printOutput(response); return 0; } @Override - protected String getSummary(CommonResponse response) { + protected String getSummaryOutput(CommonResponse response) { return ((ModelSearchResponse) response).getModels().toString(); } diff --git a/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java b/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java index 30261c0fc..752b26855 100644 --- a/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java +++ b/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java @@ -27,7 +27,7 @@ public class SearchRagDocumentsCommand extends BaseCommand { private String filename; @Override - protected String getSummary(CommonResponse response) { + protected String getSummaryOutput(CommonResponse response) { return ((RagDocumentSearchResponse) response).getRagDocuments().toString(); } @@ -38,11 +38,14 @@ protected String getFullOutput(CommonResponse response) { @Override public Integer call() throws Exception { - var client = new MindeeClient(String.valueOf(apiKey)); + var client = new MindeeClient(apiKey); var response = client .search( RagDocumentSearchResponse.class, - RagDocumentSearchParameters.builder(modelId).filename(filename).build() + RagDocumentSearchParameters + .builder(modelId) + .filename(filename != null ? filename : null) + .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/http/MindeeHttpApiV2.java b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java index 6f2a2b9df..87482ddb6 100644 --- a/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java @@ -285,15 +285,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); } } @@ -329,15 +329,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/search/models/ModelSearchParameters.java b/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java index 2798648d4..dc3fbbe09 100644 --- a/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java +++ b/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java @@ -65,7 +65,9 @@ public static final class Builder extends BaseSearchParameters.BaseBuilder Date: Wed, 2 Sep 2026 14:17:59 +0200 Subject: [PATCH 4/5] add checks on base class --- src/main/java/com/mindee/v2/MindeeClient.java | 2 +- .../mindee/v2/clientoptions/BaseSearchParameters.java | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mindee/v2/MindeeClient.java b/src/main/java/com/mindee/v2/MindeeClient.java index 991c52ec7..b3c185fd2 100644 --- a/src/main/java/com/mindee/v2/MindeeClient.java +++ b/src/main/java/com/mindee/v2/MindeeClient.java @@ -214,7 +214,7 @@ public TSearchResponse search( */ @Deprecated public SearchResponse searchModels() { - return search(SearchResponse.class, null); + return search(SearchResponse.class, ModelSearchParameters.builder().build()); } /** diff --git a/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java b/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java index 1b1080718..4a5c1c682 100644 --- a/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java +++ b/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java @@ -24,10 +24,16 @@ public abstract class BaseSearchParameters { public Map getRequestParameters() { var parameters = new HashMap(); - if (this.getPage() != null && this.getPage() > 0) { + 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 && this.getPerPage() > 0) { + if (this.getPerPage() != null) { + if (this.getPerPage() <= 0) { + throw new IllegalArgumentException("perPage must be greater than 0"); + } parameters.put("per_page", String.valueOf(getPerPage())); } From bb16e4545f7456b9413c3cef7b9ee58b2004da5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Wed, 2 Sep 2026 14:38:46 +0200 Subject: [PATCH 5/5] simplify --- src/main/java/com/mindee/v2/MindeeClient.java | 17 ++++++------- .../mindee/v2/cli/SearchModelsCommand.java | 1 - .../v2/cli/SearchRagDocumentsCommand.java | 10 ++------ .../clientoptions/BaseSearchParameters.java | 16 +++++++++++- .../java/com/mindee/v2/http/MindeeApiV2.java | 10 +++++--- .../com/mindee/v2/http/MindeeHttpApiV2.java | 25 +++++++++++++++---- .../search/models/ModelSearchParameters.java | 4 +-- .../RagDocumentSearchParameters.java | 4 +-- .../java/com/mindee/v2/MindeeClientTest.java | 11 ++++++-- .../com/mindee/v2/search/ModelSearchIT.java | 11 +++----- .../mindee/v2/search/RagDocumentSearchIT.java | 5 +--- 11 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/mindee/v2/MindeeClient.java b/src/main/java/com/mindee/v2/MindeeClient.java index b3c185fd2..9f9f2042c 100644 --- a/src/main/java/com/mindee/v2/MindeeClient.java +++ b/src/main/java/com/mindee/v2/MindeeClient.java @@ -16,6 +16,7 @@ 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; /** @@ -200,10 +201,10 @@ public TResponse enqueueAndGetResult( * @param searchParameters Search parameters */ public TSearchResponse search( - Class responseClass, - BaseSearchParameters searchParameters + BaseSearchParameters searchParameters ) { - return mindeeApi.reqGetSearch(responseClass, searchParameters); + Objects.requireNonNull(searchParameters); + return mindeeApi.reqGetSearch(searchParameters); } /** @@ -214,7 +215,7 @@ public TSearchResponse search( */ @Deprecated public SearchResponse searchModels() { - return search(SearchResponse.class, ModelSearchParameters.builder().build()); + return mindeeApi.reqGetSearch(ModelSearchParameters.builder().build()); } /** @@ -226,7 +227,7 @@ public SearchResponse searchModels() { */ @Deprecated public SearchResponse searchModels(String modelName) { - return search(SearchResponse.class, ModelSearchParameters.builder().name(modelName).build()); + return mindeeApi.reqGetSearch(ModelSearchParameters.builder().name(modelName).build()); } /** @@ -239,10 +240,8 @@ public SearchResponse searchModels(String modelName) { */ @Deprecated public SearchResponse searchModels(String modelName, String modelType) { - return search( - SearchResponse.class, - ModelSearchParameters.builder().name(modelName).modelType(modelType).build() - ); + return mindeeApi + .reqGetSearch(ModelSearchParameters.builder().name(modelName).modelType(modelType).build()); } /** diff --git a/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java b/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java index 8f3e118a5..3f5455158 100644 --- a/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java +++ b/src/main/java/com/mindee/v2/cli/SearchModelsCommand.java @@ -42,7 +42,6 @@ public Integer call() throws Exception { var client = new MindeeClient(apiKey); var response = client .search( - ModelSearchResponse.class, ModelSearchParameters .builder() .name(name) diff --git a/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java b/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java index 752b26855..b25bbc6c1 100644 --- a/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java +++ b/src/main/java/com/mindee/v2/cli/SearchRagDocumentsCommand.java @@ -22,7 +22,7 @@ public class SearchRagDocumentsCommand extends BaseCommand { @Option( names = { "-f", "--filename" }, - description = "Filter by model name partial match (case insensitive)." + description = "Filter by file name partial match (case insensitive)." ) private String filename; @@ -40,13 +40,7 @@ protected String getFullOutput(CommonResponse response) { public Integer call() throws Exception { var client = new MindeeClient(apiKey); var response = client - .search( - RagDocumentSearchResponse.class, - RagDocumentSearchParameters - .builder(modelId) - .filename(filename != null ? filename : null) - .build() - ); + .search(RagDocumentSearchParameters.builder(modelId).filename(filename).build()); printOutput(response); return 0; } diff --git a/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java b/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java index 4a5c1c682..8d6d4d39f 100644 --- a/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java +++ b/src/main/java/com/mindee/v2/clientoptions/BaseSearchParameters.java @@ -1,14 +1,18 @@ 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 { +public abstract class BaseSearchParameters { + private final Class responseClass; + /** * 1-based page index. */ @@ -18,6 +22,16 @@ public abstract class BaseSearchParameters { */ 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. */ diff --git a/src/main/java/com/mindee/v2/http/MindeeApiV2.java b/src/main/java/com/mindee/v2/http/MindeeApiV2.java index a69c6c288..c3ebee48f 100644 --- a/src/main/java/com/mindee/v2/http/MindeeApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeApiV2.java @@ -10,7 +10,9 @@ 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; /** @@ -69,10 +71,12 @@ public abstract TResponse reqGetResultByUrl( * Retrieves a list of resources with the given criteria. */ public abstract TSearchResponse reqGetSearch( - Class responseClass, - BaseSearchParameters parameters + BaseSearchParameters parameters ); + @Deprecated + public abstract SearchResponse reqGetSearch(ModelSearchParameters parameters); + /** * Creates an "unknown error" response from an HTTP status code. */ @@ -86,7 +90,7 @@ protected ErrorResponse makeUnknownError(int statusCode) { ); } - protected ProductAttributes getResponseProductInfo( + protected ProductAttributes getResponseProductAttributes( Class responseClass ) { var productInfo = responseClass.getAnnotation(ProductAttributes.class); diff --git a/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java index 87482ddb6..c165a0385 100644 --- a/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java +++ b/src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java @@ -12,6 +12,8 @@ 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; @@ -131,7 +133,7 @@ public TResponse reqGetResultById( Class responseClass, String inferenceId ) { - var productInfo = getResponseProductInfo(responseClass); + var productInfo = getResponseProductAttributes(responseClass); var url = String .format( "%s/products/%s/results/%s", @@ -157,10 +159,9 @@ public TResponse reqGetResultByUrl( @Override public TSearchResponse reqGetSearch( - Class responseClass, - BaseSearchParameters parameters + BaseSearchParameters parameters ) { - var productInfo = getResponseProductInfo(responseClass); + var productInfo = getResponseProductAttributes(parameters.getResponseClass()); URIBuilder url; try { url = new URIBuilder(this.mindeeSettings.getBaseUrl() + "/search/" + productInfo.slug()); @@ -169,7 +170,21 @@ public TSearchResponse reqGetSearch } parameters.getRequestParameters().forEach(url::addParameter); var get = new HttpGet(url.toString()); - return this.executeAPIRequest(get, responseClass); + 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); } /** diff --git a/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java b/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java index dc3fbbe09..0e4fc0595 100644 --- a/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java +++ b/src/main/java/com/mindee/v2/search/models/ModelSearchParameters.java @@ -11,7 +11,7 @@ */ @Getter @EqualsAndHashCode(callSuper = true) -public class ModelSearchParameters extends BaseSearchParameters { +public class ModelSearchParameters extends BaseSearchParameters { /** * Case-insensitive search term for the model name */ @@ -23,7 +23,7 @@ public class ModelSearchParameters extends BaseSearchParameters { private final String modelType; private ModelSearchParameters(String name, String modelType, Integer page, Integer perPage) { - super(page, perPage); + super(ModelSearchResponse.class, page, perPage); this.name = name; this.modelType = modelType; } diff --git a/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java b/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java index 275ad1ea8..c0e0ddf07 100644 --- a/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java +++ b/src/main/java/com/mindee/v2/search/ragdocuments/RagDocumentSearchParameters.java @@ -11,7 +11,7 @@ */ @Getter @EqualsAndHashCode(callSuper = true) -public class RagDocumentSearchParameters extends BaseSearchParameters { +public class RagDocumentSearchParameters extends BaseSearchParameters { /** * Model identifier to search in. */ @@ -28,7 +28,7 @@ private RagDocumentSearchParameters( Integer page, Integer perPage ) { - super(page, perPage); + super(RagDocumentSearchResponse.class, page, perPage); if (modelId == null || modelId.trim().isEmpty()) { throw new IllegalArgumentException("ModelId is required in RagDocumentSearchParameters"); } diff --git a/src/test/java/com/mindee/v2/MindeeClientTest.java b/src/test/java/com/mindee/v2/MindeeClientTest.java index 3c449d4ac..0ccc98b49 100644 --- a/src/test/java/com/mindee/v2/MindeeClientTest.java +++ b/src/test/java/com/mindee/v2/MindeeClientTest.java @@ -17,8 +17,10 @@ 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; @@ -62,12 +64,17 @@ public JobResponse reqGetJobById(String jobId) { @Override public TSearchResponse reqGetSearch( - Class responseClass, - BaseSearchParameters parameters + BaseSearchParameters parameters ) { return (TSearchResponse) new ModelSearchResponse(); } + @Override + @Deprecated + public SearchResponse reqGetSearch(ModelSearchParameters parameters) { + return new SearchResponse(); + } + @Override public TResponse reqGetResultById( Class tResponseClass, diff --git a/src/test/java/com/mindee/v2/search/ModelSearchIT.java b/src/test/java/com/mindee/v2/search/ModelSearchIT.java index d2b11e670..2d8a0f4e5 100644 --- a/src/test/java/com/mindee/v2/search/ModelSearchIT.java +++ b/src/test/java/com/mindee/v2/search/ModelSearchIT.java @@ -6,6 +6,7 @@ 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; @@ -27,8 +28,7 @@ void setUp() { @Test public void ModelSearch_mustHaveResults() throws Exception { - ModelSearchResponse response = client - .search(ModelSearchResponse.class, ModelSearchParameters.builder().build()); + ModelSearchResponse response = client.search(ModelSearchParameters.builder().build()); assertNotNull(response); assertNotNull(response.getModels()); @@ -41,10 +41,7 @@ public void ModelSearch_mustHaveResults() throws Exception { @Test public void ModelSearch_mustReturnEmpty() throws Exception { ModelSearchResponse response = client - .search( - ModelSearchResponse.class, - ModelSearchParameters.builder().name("je n'existe pas tralala").build() - ); + .search(ModelSearchParameters.builder().name("je n'existe pas tralala").build()); assertNotNull(response); assertNotNull(response.getModels()); @@ -57,7 +54,7 @@ public void ModelSearch_mustReturnEmpty() throws Exception { @Test @SuppressWarnings("deprecation") public void ModelSearch_mustReturnEmptyObsolete() throws Exception { - ModelSearchResponse response = client.searchModels("je n'existe pas tralala"); + SearchResponse response = client.searchModels("je n'existe pas tralala"); assertNotNull(response); assertNotNull(response.getModels()); diff --git a/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java b/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java index c6137897d..22f163b14 100644 --- a/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java +++ b/src/test/java/com/mindee/v2/search/RagDocumentSearchIT.java @@ -28,10 +28,7 @@ void setUp() { @Test public void RagDocumentSearch_mustHaveResults() throws Exception { RagDocumentSearchResponse response = client - .search( - RagDocumentSearchResponse.class, - RagDocumentSearchParameters.builder(findocModelId).build() - ); + .search(RagDocumentSearchParameters.builder(findocModelId).build()); assertNotNull(response); assertNotNull(response.getRagDocuments());