Skip to content

Commit 99bef9c

Browse files
committed
✨ add RAG search API
1 parent 1a05ec1 commit 99bef9c

26 files changed

Lines changed: 384 additions & 299 deletions

.github/workflows/_test-units.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
4848
- name: Unit testing with pytest
4949
run: |
50-
pytest --cov mindee --cov-fail-under 87
50+
pytest --cov mindee --cov-fail-under 86
5151
5252
pytest-lite:
5353
name: Run Unit Tests (Lite)

mindee/v2/client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def enqueue(
5757
:return: A valid inference response.
5858
"""
5959
logger.debug("Enqueuing inference using model: %s", params.model_id)
60-
return self.mindee_api.enqueue(input_source, params)
60+
return self.mindee_api.req_post_product_enqueue(input_source, params)
6161

6262
def get_job(self, job_id: str) -> JobResponse:
6363
"""
@@ -70,7 +70,7 @@ def get_job(self, job_id: str) -> JobResponse:
7070
"""
7171
logger.debug("Fetching job: %s", job_id)
7272

73-
return self.mindee_api.get_job(job_id)
73+
return self.mindee_api.req_get_job_by_id(job_id)
7474

7575
def get_result(
7676
self,
@@ -88,7 +88,7 @@ def get_result(
8888
"""
8989
logger.debug("Fetching result: %s", inference_id)
9090

91-
return self.mindee_api.get_result(response_type, inference_id)
91+
return self.mindee_api.req_get_product_result_by_id(response_type, inference_id)
9292

9393
def get_result_from_url(
9494
self, response_type: type[TypeBaseInferenceResponse], url: str
@@ -100,7 +100,7 @@ def get_result_from_url(
100100
:param url: URL of the inference to retrieve.
101101
:return: The result of the inference.
102102
"""
103-
return self.mindee_api.get_result_by_url(response_type, url)
103+
return self.mindee_api.req_get_product_result_by_url(response_type, url)
104104

105105
def enqueue_and_get_result(
106106
self,
@@ -179,7 +179,7 @@ def search_models(
179179
:param model_type: Type of the model to filter by.
180180
:return: A list of models matching the provided criteria.
181181
"""
182-
return self.mindee_api.get_models(name, model_type)
182+
return self.mindee_api.req_get_search_models(name, model_type)
183183

184184
def close(self) -> None:
185185
"""Closes the underlying HTTP client."""

mindee/v2/client_options/base_product_parameters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@dataclass
99
class BaseProductParameters(ABC):
10-
"""Base parameters for sending a document to a product."""
10+
"""Base parameters for sending a file to a Mindee V2 product."""
1111

1212
model_id: str
1313
"""Model ID to use for the inference. Required."""
@@ -32,7 +32,7 @@ class BaseProductParameters(ABC):
3232
"""Whether to close the file after product."""
3333

3434
_slug: ClassVar[str]
35-
"""Slug of the endpoint."""
35+
"""Slug of the product."""
3636

3737
def get_request_parameters(self) -> dict[str, str | list[str]]:
3838
"""
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from abc import ABC
2+
from dataclasses import dataclass
3+
from typing import ClassVar
4+
5+
6+
@dataclass
7+
class BaseSearchParameters(ABC):
8+
"""Base parameters for searches."""
9+
10+
page: int | None
11+
"""1-based page index."""
12+
13+
per_page: int | None
14+
"""Number of items per page."""
15+
16+
_slug: ClassVar[str]
17+
"""Slug of the endpoint."""
18+
19+
def get_request_parameters(self) -> dict[str, str | list[str]]:
20+
"""
21+
Gets the request parameters for the search request.
22+
23+
:return: A dict of parameters.
24+
"""
25+
data: dict[str, str | list[str]] = {}
26+
27+
if self.page is not None:
28+
data["page"] = str(self.page)
29+
if self.per_page is not None:
30+
data["per_page"] = str(self.per_page)
31+
32+
return data

mindee/v2/mindee_http/mindee_api_v2.py

Lines changed: 48 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def __init__(self, api_key: str | None, http_client: httpx.Client | None = None)
5555
else os.environ.get(API_KEY_V2_ENV_NAME, API_KEY_V2_DEFAULT)
5656
)
5757
self.set_base_url(BASE_URL_DEFAULT)
58-
self.set_from_env()
58+
self._set_from_env()
5959
if not self.api_key:
6060
raise MindeeAPIV2Error(
6161
f"Missing API key,"
@@ -77,7 +77,7 @@ def base_headers(self) -> dict[str, str]:
7777
"User-Agent": USER_AGENT,
7878
}
7979

80-
def set_from_env(self) -> None:
80+
def _set_from_env(self) -> None:
8181
"""Set various parameters from environment variables, if present."""
8282
env_vars = {
8383
BASE_URL_ENV_NAME: self.set_base_url,
@@ -89,22 +89,21 @@ def set_from_env(self) -> None:
8989
func(env_val)
9090
logger.debug("Value was set from env: %s", name)
9191

92-
def req_post_inference_enqueue(
92+
def req_post_product_enqueue(
9393
self,
9494
input_source: LocalInputSource | URLInputSource,
9595
params: BaseProductParameters,
96-
slug: str,
97-
) -> httpx.Response:
96+
) -> JobResponse:
9897
"""
99-
Make a request to POST a document for enqueue on the V2 API.
98+
Send a file to the asynchronous processing queue for inference processing.
10099
101100
:param input_source: Input object.
102101
:param params: Options for the enqueueing of the document.
103-
:param slug: Slug to use for the enqueueing, defaults to 'inferences'.
104102
:return: httpx response.
105103
"""
106104
data = params.get_request_parameters()
107-
url = f"{self.url_root}/v2/{slug}/enqueue"
105+
slug = params.get_enqueue_slug()
106+
url = f"{self.url_root}/v2/products/{slug}/enqueue"
108107
post_kwargs: StringDict = {}
109108
if isinstance(input_source, LocalInputSource):
110109
post_kwargs["files"] = {
@@ -116,182 +115,115 @@ def req_post_inference_enqueue(
116115
post_caller: Callable
117116
if self.http_client is None or self.http_client.is_closed:
118117
post_caller = httpx.post
119-
post_kwargs["timeout"] = self.request_timeout
120118
else:
121119
post_caller = self.http_client.post
122-
return post_caller(
120+
121+
response = post_caller(
123122
url,
124123
headers=self.base_headers,
125124
data=data,
125+
timeout=self.request_timeout,
126126
**post_kwargs,
127127
)
128+
dict_response = self._response_json(response)
129+
130+
if not is_valid_post_response(response):
131+
handle_error_v2(dict_response)
132+
return JobResponse(dict_response)
128133

129-
def req_get_job(self, job_id: str) -> httpx.Response:
134+
def req_get_job_by_id(self, job_id: str) -> JobResponse:
130135
"""
131-
Sends a request matching a given queue_id. Returns either a Job or a Document.
136+
Get the result of an inference that was previously enqueued.
132137
133138
:param job_id: Job ID, returned by the enqueue request.
134139
"""
135140
get_caller: Callable
136-
get_kwargs: StringDict = {}
137141
if self.http_client is None or self.http_client.is_closed:
138142
get_caller = httpx.get
139-
get_kwargs["timeout"] = self.request_timeout
140143
else:
141144
get_caller = self.http_client.get
142-
return get_caller(
145+
146+
response = get_caller(
143147
url=f"{self.url_root}/v2/jobs/{job_id}",
144148
headers=self.base_headers,
145149
follow_redirects=False,
146-
**get_kwargs,
150+
timeout=self.request_timeout,
147151
)
152+
dict_response = self._response_json(response)
153+
if not is_valid_get_response(response):
154+
handle_error_v2(dict_response)
155+
return JobResponse(dict_response)
148156

149-
def req_get_inference_by_url(self, url: str) -> httpx.Response:
157+
def req_get_product_result_by_url(
158+
self, response_type: type[ResponseT], url: str
159+
) -> ResponseT:
150160
"""
151-
Sends a request matching a given inference_id. Returns either a Job or a
152-
Document.
161+
Get the result of an inference that was previously enqueued.
153162
154163
:param url: URL to use for the request.
164+
:param response_type: Type of the response to return.
155165
:return: Response object from the request.
156166
"""
157167
get_caller: Callable
158-
get_kwargs: StringDict = {}
159168
if self.http_client is None or self.http_client.is_closed:
160169
get_caller = httpx.get
161-
get_kwargs["timeout"] = self.request_timeout
162170
else:
163171
get_caller = self.http_client.get
164-
return get_caller(
172+
173+
response = get_caller(
165174
url=url,
166175
headers=self.base_headers,
167176
follow_redirects=False,
168-
**get_kwargs,
177+
timeout=self.request_timeout,
169178
)
179+
dict_response = self._response_json(response)
180+
if not is_valid_get_response(response):
181+
handle_error_v2(dict_response)
182+
return response_type(dict_response)
170183

171-
def req_get_inference(self, inference_id: str, slug: str) -> httpx.Response:
184+
def req_get_product_result_by_id(
185+
self, response_type: type[ResponseT], inference_id: str
186+
) -> ResponseT:
172187
"""
173188
Sends a request matching a given queue_id. Returns either a Job or a Document.
174189
175190
:param inference_id: Inference ID, returned by the job request.
176-
:param slug: Slug of the inference, defaults to nothing.
191+
:param response_type: Type of the response to return.
177192
"""
178-
get_caller: Callable
179-
get_kwargs: StringDict = {}
180-
if self.http_client is None or self.http_client.is_closed:
181-
get_caller = httpx.get
182-
get_kwargs["timeout"] = self.request_timeout
183-
else:
184-
get_caller = self.http_client.get
185-
return get_caller(
186-
url=f"{self.url_root}/v2/{slug}/{inference_id}",
187-
headers=self.base_headers,
188-
follow_redirects=False,
189-
**get_kwargs,
193+
slug = response_type.get_result_slug()
194+
return self.req_get_product_result_by_url(
195+
response_type=response_type,
196+
url=f"{self.url_root}/v2/products/{slug}/results/{inference_id}",
190197
)
191198

192199
def req_get_search_models(
193200
self, name: str | None, model_type: str | None
194-
) -> httpx.Response:
201+
) -> SearchResponse:
195202
"""
196203
Searches for a list of models matching criteria.
197204
:param name: Name pattern to search for.
198205
:param model_type: Type of model to search for (exact match).
199206
:return: Response object containing search results.
200207
"""
201208
get_caller: Callable
202-
get_kwargs: StringDict = {}
203209
if self.http_client is None or self.http_client.is_closed:
204210
get_caller = httpx.get
205-
get_kwargs["timeout"] = self.request_timeout
206211
else:
207212
get_caller = self.http_client.get
208213
params = {}
209214
if name:
210215
params["name"] = name
211216
if model_type:
212217
params["model_type"] = model_type
213-
return get_caller(
218+
219+
response = get_caller(
214220
url=f"{self.url_root}/v2/search/models",
215221
headers=self.base_headers,
216222
params=params,
217223
follow_redirects=False,
218-
**get_kwargs,
219-
)
220-
221-
def enqueue(
222-
self,
223-
input_source: LocalInputSource | URLInputSource,
224-
params: BaseProductParameters,
225-
) -> JobResponse:
226-
"""
227-
Enqueues a document to a given model.
228-
:param input_source: Input object.
229-
:param params: Parameters
230-
:return: A valid inference Response.
231-
"""
232-
response = self.req_post_inference_enqueue(
233-
input_source=input_source, params=params, slug=params.get_enqueue_slug()
224+
timeout=self.request_timeout,
234225
)
235226
dict_response = self._response_json(response)
236-
237-
if not is_valid_post_response(response):
238-
handle_error_v2(dict_response)
239-
return JobResponse(dict_response)
240-
241-
def get_job(self, job_id: str) -> JobResponse:
242-
"""
243-
Get the status of an inference that was previously enqueued.
244-
245-
Can be used for polling.
246-
247-
:param job_id: UUID of the job to retrieve.
248-
:return: A job response.
249-
"""
250-
response = self.req_get_job(job_id)
251-
dict_response = self._response_json(response)
252-
if not is_valid_get_response(response):
253-
handle_error_v2(dict_response)
254-
return JobResponse(dict_response)
255-
256-
def get_result(self, response_type: type[ResponseT], inference_id: str):
257-
"""
258-
Get the result of an inference that was previously enqueued.
259-
260-
:param response_type: Type of the response to return.
261-
:param inference_id: UUID of the inference to retrieve.
262-
:return: The result of the inference.
263-
"""
264-
response = self.req_get_inference(inference_id, response_type.get_result_slug())
265-
dict_response = self._response_json(response)
266-
if not is_valid_get_response(response):
267-
handle_error_v2(dict_response)
268-
return response_type(dict_response)
269-
270-
def get_result_by_url(self, response_type: type[ResponseT], url: str):
271-
"""
272-
Get the result of an inference that was previously enqueued by its URL.
273-
274-
:param response_type: Type of the response to return.
275-
:param url: URL of the inference to retrieve.
276-
:return: The result of the inference.
277-
"""
278-
response = self.req_get_inference_by_url(url)
279-
dict_response = self._response_json(response)
280-
if not is_valid_get_response(response):
281-
handle_error_v2(dict_response)
282-
return response_type(dict_response)
283-
284-
def get_models(self, name: str | None, model_type: str | None):
285-
"""
286-
Get a list of models matching the provided name and type.
287-
288-
:param name: Name of the model to filter by.
289-
:param model_type: Type of the model to filter by.
290-
:return: A list of models matching the provided criteria.
291-
"""
292-
logger.debug("Fetching models matching: name=%s and type=%s", name, model_type)
293-
response = self.req_get_search_models(name, model_type)
294-
dict_response = self._response_json(response)
295227
if not is_valid_get_response(response):
296228
handle_error_v2(dict_response)
297229
return SearchResponse(dict_response)

mindee/v2/parsing/inference/base_inference_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class BaseInferenceResponse(ABC, CommonResponse):
1212
"""The inference result for a split utility request"""
1313

1414
_slug: ClassVar[str]
15-
"""Slug of the inference."""
15+
"""Slug of the product."""
1616

1717
def __str__(self) -> str:
1818
return str(self.inference)

0 commit comments

Comments
 (0)