Skip to content

Commit 0a720d5

Browse files
committed
✨ add model search API
1 parent a21aedf commit 0a720d5

11 files changed

Lines changed: 324 additions & 68 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## v5.0.0-beta2 - 2026-05-20
44
### ¡Breaking Changes!
55
* :recycle: put error parsing in its own package
6+
* :sparkles: add model search API
67

78

89
## v5.0.0-beta1 - 2026-05-06

src/main/java/com/mindee/v2/MindeeClient.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.mindee.v2.parsing.CommonResponse;
1111
import com.mindee.v2.parsing.JobResponse;
1212
import com.mindee.v2.parsing.error.ErrorResponse;
13+
import com.mindee.v2.parsing.search.SearchResponse;
1314
import com.mindee.v2.product.extraction.ExtractionResponse;
1415
import java.io.IOException;
1516

@@ -172,6 +173,36 @@ public <TResponse extends CommonResponse> TResponse enqueueAndGetResult(
172173
return pollAndFetch(responseClass, job, pollingOptions);
173174
}
174175

176+
/**
177+
* Return all models.
178+
*
179+
* @return an instance of {@link SearchResponse}
180+
*/
181+
public SearchResponse searchModels() {
182+
return searchModels(null, null);
183+
}
184+
185+
/**
186+
* Search for models by name.
187+
*
188+
* @param modelName name of the model to search for
189+
* @return an instance of {@link SearchResponse}
190+
*/
191+
public SearchResponse searchModels(String modelName) {
192+
return searchModels(modelName, null);
193+
}
194+
195+
/**
196+
* Search for models by name and type.
197+
*
198+
* @param modelName name of the model to search for
199+
* @param modelType type of the model to search for
200+
* @return an instance of {@link SearchResponse}
201+
*/
202+
public SearchResponse searchModels(String modelName, String modelType) {
203+
return mindeeApi.reqGetSearchModels(modelName, modelType);
204+
}
205+
175206
/**
176207
* Common logic for polling an asynchronous job for local & url files.
177208
*

src/main/java/com/mindee/v2/http/MindeeApiV2.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.mindee.v2.parsing.CommonResponse;
99
import com.mindee.v2.parsing.JobResponse;
1010
import com.mindee.v2.parsing.error.ErrorResponse;
11+
import com.mindee.v2.parsing.search.SearchResponse;
1112
import java.io.IOException;
1213

1314
/**
@@ -48,11 +49,19 @@ public abstract JobResponse reqPostEnqueue(
4849
*
4950
* @param inferenceId ID of the inference to poll.
5051
*/
51-
abstract public <TResponse extends CommonResponse> TResponse reqGetResult(
52+
public abstract <TResponse extends CommonResponse> TResponse reqGetResult(
5253
Class<TResponse> responseClass,
5354
String inferenceId
5455
);
5556

57+
/**
58+
* Retrieves a list of models.
59+
*
60+
* @param modelName search term for model name
61+
* @param modelType search term for model type
62+
*/
63+
public abstract SearchResponse reqGetSearchModels(String modelName, String modelType);
64+
5665
/**
5766
* Creates an "unknown error" response from an HTTP status code.
5867
*/

src/main/java/com/mindee/v2/http/MindeeHttpApiV2.java

Lines changed: 46 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
import com.mindee.v2.parsing.CommonResponse;
1111
import com.mindee.v2.parsing.JobResponse;
1212
import com.mindee.v2.parsing.error.ErrorResponse;
13+
import com.mindee.v2.parsing.search.SearchResponse;
1314
import java.io.IOException;
1415
import java.net.URISyntaxException;
1516
import java.nio.charset.StandardCharsets;
1617
import lombok.Builder;
1718
import org.apache.hc.client5.http.classic.methods.HttpGet;
1819
import org.apache.hc.client5.http.classic.methods.HttpPost;
20+
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
1921
import org.apache.hc.client5.http.config.RequestConfig;
2022
import org.apache.hc.client5.http.entity.mime.HttpMultipartMode;
2123
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
@@ -82,7 +84,7 @@ public JobResponse reqPostEnqueue(LocalInputSource inputSource, BaseParameters o
8284
inputSource.getFilename()
8385
);
8486
post.setEntity(options.buildHttpBody(builder).build());
85-
return executeEnqueue(post);
87+
return executeAPIRequest(post, JobResponse.class);
8688
}
8789

8890
/**
@@ -103,33 +105,7 @@ public JobResponse reqPostEnqueue(URLInputSource inputSource, BaseParameters opt
103105
builder.setMode(HttpMultipartMode.EXTENDED);
104106
builder.addTextBody("url", inputSource.getUrl().toString());
105107
post.setEntity(options.buildHttpBody(builder).build());
106-
return executeEnqueue(post);
107-
}
108-
109-
/**
110-
* Executes an enqueue action, common to URL & local inputs.
111-
*
112-
* @param post HTTP Post object.
113-
* @return a valid job response.
114-
*/
115-
private JobResponse executeEnqueue(HttpPost post) {
116-
try (var httpClient = httpClientBuilder.build()) {
117-
return httpClient.execute(post, response -> {
118-
var responseEntity = response.getEntity();
119-
var statusCode = response.getCode();
120-
if (isInvalidStatusCode(statusCode)) {
121-
throw getHttpError(response);
122-
}
123-
try {
124-
var raw = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
125-
return deserializeOrThrow(raw, JobResponse.class, response.getCode());
126-
} finally {
127-
EntityUtils.consumeQuietly(responseEntity);
128-
}
129-
});
130-
} catch (IOException err) {
131-
throw new MindeeException(err.getMessage(), err);
132-
}
108+
return executeAPIRequest(post, JobResponse.class);
133109
}
134110

135111
@Override
@@ -138,31 +114,10 @@ public JobResponse reqGetJob(String jobId) {
138114
var url = this.mindeeSettings.getBaseUrl() + "/jobs/" + jobId;
139115
var get = new HttpGet(url);
140116

141-
if (this.mindeeSettings.getApiKey().isPresent()) {
142-
get.setHeader(HttpHeaders.AUTHORIZATION, this.mindeeSettings.getApiKey().get());
143-
}
144-
get.setHeader(HttpHeaders.USER_AGENT, getUserAgent());
145117
var noRedirect = RequestConfig.custom().setRedirectsEnabled(false).build();
146118
get.setConfig(noRedirect);
147119

148-
try (var httpClient = httpClientBuilder.build()) {
149-
return httpClient.execute(get, response -> {
150-
var responseEntity = response.getEntity();
151-
var statusCode = response.getCode();
152-
if (isInvalidStatusCode(statusCode)) {
153-
throw getHttpError(response);
154-
}
155-
try {
156-
var raw = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
157-
158-
return deserializeOrThrow(raw, JobResponse.class, response.getCode());
159-
} finally {
160-
EntityUtils.consumeQuietly(responseEntity);
161-
}
162-
});
163-
} catch (IOException err) {
164-
throw new MindeeException(err.getMessage(), err);
165-
}
120+
return this.executeAPIRequest(get, JobResponse.class);
166121
}
167122

168123
@Override
@@ -179,25 +134,54 @@ public <TResponse extends CommonResponse> TResponse reqGetResult(
179134
inferenceId
180135
);
181136
var get = new HttpGet(url);
137+
return executeAPIRequest(get, responseClass);
138+
}
182139

140+
@Override
141+
public SearchResponse reqGetSearchModels(String modelName, String modelType) {
142+
URIBuilder url;
143+
try {
144+
url = new URIBuilder(this.mindeeSettings.getBaseUrl() + "/search/models");
145+
} catch (URISyntaxException e) {
146+
throw new RuntimeException(e);
147+
}
148+
if (modelName != null) {
149+
url.addParameter("name", modelName);
150+
}
151+
if (modelType != null) {
152+
url.addParameter("type", modelType);
153+
}
154+
var get = new HttpGet(url.toString());
155+
return executeAPIRequest(get, SearchResponse.class);
156+
}
157+
158+
/**
159+
* Executes an enqueue action, common to URL & local inputs.
160+
*
161+
* @param apiRequest HTTP request object.
162+
* @return a valid job response.
163+
*/
164+
private <TResponse extends CommonResponse> TResponse executeAPIRequest(
165+
HttpUriRequestBase apiRequest,
166+
Class<TResponse> responseClass
167+
) {
183168
if (this.mindeeSettings.getApiKey().isPresent()) {
184-
get.setHeader(HttpHeaders.AUTHORIZATION, this.mindeeSettings.getApiKey().get());
169+
apiRequest.setHeader(HttpHeaders.AUTHORIZATION, this.mindeeSettings.getApiKey().get());
185170
}
186-
get.setHeader(HttpHeaders.USER_AGENT, getUserAgent());
171+
apiRequest.setHeader(HttpHeaders.USER_AGENT, getUserAgent());
187172

188173
try (var httpClient = httpClientBuilder.build()) {
189-
190-
return httpClient.execute(get, response -> {
191-
var entity = response.getEntity();
192-
var status = response.getCode();
174+
return httpClient.execute(apiRequest, response -> {
175+
var responseEntity = response.getEntity();
176+
var statusCode = response.getCode();
177+
if (isInvalidStatusCode(statusCode)) {
178+
throw getHttpError(response);
179+
}
193180
try {
194-
if (isInvalidStatusCode(status)) {
195-
throw getHttpError(response);
196-
}
197-
var raw = EntityUtils.toString(entity, StandardCharsets.UTF_8);
198-
return deserializeOrThrow(raw, responseClass, status);
181+
var raw = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
182+
return deserializeOrThrow(raw, responseClass, response.getCode());
199183
} finally {
200-
EntityUtils.consumeQuietly(entity);
184+
EntityUtils.consumeQuietly(responseEntity);
201185
}
202186
});
203187
} catch (IOException err) {
@@ -235,11 +219,6 @@ private HttpPost buildHttpPost(String url) {
235219
catch (URISyntaxException err) {
236220
return new HttpPost("invalid URI");
237221
}
238-
239-
if (this.mindeeSettings.getApiKey().isPresent()) {
240-
post.setHeader(HttpHeaders.AUTHORIZATION, this.mindeeSettings.getApiKey().get());
241-
}
242-
post.setHeader(HttpHeaders.USER_AGENT, getUserAgent());
243222
return post;
244223
}
245224

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.mindee.v2.parsing.search;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import lombok.AllArgsConstructor;
6+
import lombok.EqualsAndHashCode;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
10+
/**
11+
* Model webhook info.
12+
*/
13+
@Getter
14+
@EqualsAndHashCode
15+
@JsonIgnoreProperties(ignoreUnknown = true)
16+
@AllArgsConstructor
17+
@NoArgsConstructor
18+
public class ModelWebhook {
19+
/**
20+
* ID of the webhook.
21+
*/
22+
@JsonProperty("id")
23+
private String id;
24+
25+
/**
26+
* Name of the webhook.
27+
*/
28+
@JsonProperty("name")
29+
private String name;
30+
31+
/**
32+
* URL of the webhook.
33+
*/
34+
@JsonProperty("url")
35+
private String url;
36+
37+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.mindee.v2.parsing.search;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import java.util.StringJoiner;
6+
import lombok.AllArgsConstructor;
7+
import lombok.EqualsAndHashCode;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
/**
12+
* PaginationMetadata data associated with model search.
13+
*/
14+
@Getter
15+
@EqualsAndHashCode
16+
@JsonIgnoreProperties(ignoreUnknown = true)
17+
@AllArgsConstructor
18+
@NoArgsConstructor
19+
public class PaginationMetadata {
20+
21+
/**
22+
* Number of items per page.
23+
*/
24+
@JsonProperty("per_page")
25+
private int perPage;
26+
27+
/**
28+
* 1-indexed page number.
29+
*/
30+
@JsonProperty("page")
31+
private int page;
32+
33+
/**
34+
* Total items.
35+
*/
36+
@JsonProperty("total_items")
37+
private int totalItems;
38+
39+
/**
40+
* Total number of pages.
41+
*/
42+
@JsonProperty("total_pages")
43+
private int totalPages;
44+
45+
/**
46+
* String representation of the pagination metadata.
47+
*/
48+
@Override
49+
public String toString() {
50+
var joiner = new StringJoiner("\n");
51+
joiner.add(":Per Page: " + perPage);
52+
joiner.add(":Page: " + page);
53+
joiner.add(":Total Items: " + totalItems);
54+
joiner.add(":Total Pages: " + totalPages);
55+
return joiner.toString();
56+
}
57+
}

0 commit comments

Comments
 (0)