Skip to content

Commit 9fabb8c

Browse files
committed
✨ add model search API
1 parent a21aedf commit 9fabb8c

11 files changed

Lines changed: 324 additions & 64 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 & 63 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
@@ -145,24 +121,7 @@ public JobResponse reqGetJob(String jobId) {
145121
var noRedirect = RequestConfig.custom().setRedirectsEnabled(false).build();
146122
get.setConfig(noRedirect);
147123

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-
}
124+
return this.executeAPIRequest(get, JobResponse.class);
166125
}
167126

168127
@Override
@@ -179,25 +138,54 @@ public <TResponse extends CommonResponse> TResponse reqGetResult(
179138
inferenceId
180139
);
181140
var get = new HttpGet(url);
141+
return executeAPIRequest(get, responseClass);
142+
}
143+
144+
@Override
145+
public SearchResponse reqGetSearchModels(String modelName, String modelType) {
146+
URIBuilder url;
147+
try {
148+
url = new URIBuilder(this.mindeeSettings.getBaseUrl() + "/models/search");
149+
} catch (URISyntaxException e) {
150+
throw new RuntimeException(e);
151+
}
152+
if (modelName != null) {
153+
url.addParameter("name", modelName);
154+
}
155+
if (modelType != null) {
156+
url.addParameter("type", modelType);
157+
}
158+
var get = new HttpGet(url.toString());
159+
return executeAPIRequest(get, SearchResponse.class);
160+
}
182161

162+
/**
163+
* Executes an enqueue action, common to URL & local inputs.
164+
*
165+
* @param apiRequest HTTP request object.
166+
* @return a valid job response.
167+
*/
168+
private <TResponse extends CommonResponse> TResponse executeAPIRequest(
169+
HttpUriRequestBase apiRequest,
170+
Class<TResponse> responseClass
171+
) {
183172
if (this.mindeeSettings.getApiKey().isPresent()) {
184-
get.setHeader(HttpHeaders.AUTHORIZATION, this.mindeeSettings.getApiKey().get());
173+
apiRequest.setHeader(HttpHeaders.AUTHORIZATION, this.mindeeSettings.getApiKey().get());
185174
}
186-
get.setHeader(HttpHeaders.USER_AGENT, getUserAgent());
175+
apiRequest.setHeader(HttpHeaders.USER_AGENT, getUserAgent());
187176

188177
try (var httpClient = httpClientBuilder.build()) {
189-
190-
return httpClient.execute(get, response -> {
191-
var entity = response.getEntity();
192-
var status = response.getCode();
178+
return httpClient.execute(apiRequest, response -> {
179+
var responseEntity = response.getEntity();
180+
var statusCode = response.getCode();
181+
if (isInvalidStatusCode(statusCode)) {
182+
throw getHttpError(response);
183+
}
193184
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);
185+
var raw = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
186+
return deserializeOrThrow(raw, responseClass, response.getCode());
199187
} finally {
200-
EntityUtils.consumeQuietly(entity);
188+
EntityUtils.consumeQuietly(responseEntity);
201189
}
202190
});
203191
} catch (IOException err) {
@@ -235,11 +223,6 @@ private HttpPost buildHttpPost(String url) {
235223
catch (URISyntaxException err) {
236224
return new HttpPost("invalid URI");
237225
}
238-
239-
if (this.mindeeSettings.getApiKey().isPresent()) {
240-
post.setHeader(HttpHeaders.AUTHORIZATION, this.mindeeSettings.getApiKey().get());
241-
}
242-
post.setHeader(HttpHeaders.USER_AGENT, getUserAgent());
243226
return post;
244227
}
245228

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+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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.List;
6+
import java.util.StringJoiner;
7+
import lombok.AllArgsConstructor;
8+
import lombok.EqualsAndHashCode;
9+
import lombok.Getter;
10+
import lombok.NoArgsConstructor;
11+
12+
/**
13+
* Models search response.
14+
*/
15+
@Getter
16+
@EqualsAndHashCode
17+
@JsonIgnoreProperties(ignoreUnknown = true)
18+
@AllArgsConstructor
19+
@NoArgsConstructor
20+
public class SearchModel {
21+
22+
/**
23+
* ID of the model.
24+
*/
25+
@JsonProperty("id")
26+
private String id;
27+
28+
/**
29+
* Name of the model.
30+
*/
31+
@JsonProperty("name")
32+
private String name;
33+
34+
/**
35+
* Type of the model.
36+
*/
37+
@JsonProperty("model_type")
38+
private String modelType;
39+
40+
/**
41+
* Webhooks associated with the model.
42+
*/
43+
@JsonProperty("webhooks")
44+
private List<ModelWebhook> webhooks;
45+
46+
@Override
47+
public String toString() {
48+
var joiner = new StringJoiner("\n");
49+
joiner.add(":Name: " + name);
50+
joiner.add(":ID: " + id);
51+
joiner.add(":Model Type: " + modelType);
52+
return joiner.toString();
53+
}
54+
}

0 commit comments

Comments
 (0)