Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions mindee/input/url_input_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,14 @@ def __fill_filename(self, filename=None) -> str:
return filename

@staticmethod
def __make_request(url, auth, headers, redirects, max_redirects) -> bytes:
def __make_request(
url,
auth,
headers,
redirects,
max_redirects,
http_client: httpx.Client | None = None,
) -> bytes:
"""
Makes an HTTP request to the given URL, while following redirections.

Expand All @@ -185,11 +192,15 @@ def __make_request(url, auth, headers, redirects, max_redirects) -> bytes:
:return: The content of the response.
:raises MindeeSourceError: If max redirects are exceeded or the request fails.
"""
result = httpx.get(url, headers=headers, timeout=120, auth=auth)
http_client = http_client or httpx.Client()
result = http_client.get(
url, headers=headers, timeout=120, auth=auth, follow_redirects=True
)
if 299 < result.status_code < 400:
if redirects == max_redirects:
raise MindeeSourceError(
f"Can't reach URL after {redirects} out of {max_redirects} redirects, "
f"Can't reach URL after {redirects} out of {max_redirects} "
f"redirects, "
f"aborting operation."
)
return URLInputSource.__make_request(
Expand All @@ -201,4 +212,5 @@ def __make_request(url, auth, headers, redirects, max_redirects) -> bytes:
f"Couldn't retrieve file from server, error code {result.status_code}."
)

http_client.close()
return result.content
22 changes: 18 additions & 4 deletions mindee/v1/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from time import sleep

import httpx

from mindee.client_mixin import ClientMixin
from mindee.error.mindee_error import MindeeClientError, MindeeError
from mindee.error.mindee_http_error import handle_error
Expand Down Expand Up @@ -59,14 +61,21 @@ class Client(ClientMixin):
"""

api_key: str
"""API key for all endpoints."""
http_client: httpx.Client
"""HTTP client for making requests."""

def __init__(self, api_key: str = "") -> None:
def __init__(
self, api_key: str = "", http_client: httpx.Client | None = None
) -> None:
"""
Mindee API Client.

:param api_key: Your API key for all endpoints
:param http_client: HTTP client for making requests.
"""
self.api_key = api_key
self.http_client = http_client or httpx.Client()

def parse(
self,
Expand Down Expand Up @@ -522,7 +531,8 @@ def _send_to_workflow(
raise MindeeClientError("No input document provided")

workflow_endpoint = WorkflowEndpoint(
WorkflowSettings(api_key=self.api_key, workflow_id=workflow_id)
WorkflowSettings(api_key=self.api_key, workflow_id=workflow_id),
self.http_client,
)

response = workflow_endpoint.workflow_execution_post(input_source, options)
Expand Down Expand Up @@ -555,8 +565,12 @@ def _build_endpoint(
version=version,
)
if account_name and len(account_name) > 0 and account_name != "mindee":
return CustomEndpoint(endpoint_name, account_name, version, api_settings)
return Endpoint(endpoint_name, account_name, version, api_settings)
return CustomEndpoint(
endpoint_name, account_name, version, api_settings, self.http_client
)
return Endpoint(
endpoint_name, account_name, version, api_settings, self.http_client
)

def create_endpoint(
self,
Expand Down
21 changes: 20 additions & 1 deletion mindee/v1/mindee_http/base_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
import httpx

from mindee.v1.mindee_http.base_settings import BaseSettings


class BaseEndpoint:
"""Base endpoint class for the Mindee API."""

def __init__(self, settings: BaseSettings) -> None:
settings: BaseSettings
http_client: httpx.Client

def __init__(
self, settings: BaseSettings, http_client: httpx.Client | None = None
) -> None:
"""
Base API endpoint class for all endpoints.

:param settings: Settings relating to all endpoints.
:param http_client: HTTP client for making requests.
"""
self.settings = settings
self.http_client = http_client or httpx.Client()

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

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
41 changes: 25 additions & 16 deletions mindee/v1/mindee_http/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,23 @@ class Endpoint(BaseEndpoint):
settings: MindeeAPI

def __init__(
self, url_name: str, owner: str, version: str, settings: MindeeAPI
self,
url_name: str,
owner: str,
version: str,
settings: MindeeAPI,
http_client: httpx.Client | None = None,
) -> None:
"""
Generic API endpoint for a product.

:param owner: owner of the product
:param url_name: name of the product as it appears in the URL
:param version: interface version
:param settings: settings for the API
:param http_client: HTTP client for making requests.
"""
super().__init__(settings)
super().__init__(settings, http_client)
self.owner = owner
self.url_name = url_name
self.version = version
Expand All @@ -42,7 +49,8 @@ def predict_req_post(
:param include_words: Include raw OCR words in the response
:param close_file: Whether to `close()` the file after parsing it.
:param cropper: Including Mindee cropping results.
:param full_text: Whether to include the full OCR text response in compatible APIs.
:param full_text: Whether to include the full OCR text response in compatible
APIs.
:return: httpx response
"""
return self._custom_request(
Expand All @@ -66,7 +74,8 @@ def predict_async_req_post(
:param include_words: Include raw OCR words in the response
:param close_file: Whether to `close()` the file after parsing it.
:param cropper: Including Mindee cropping results.
:param full_text: Whether to include the full OCR text response in compatible APIs.
:param full_text: Whether to include the full OCR text response in compatible
APIs.
:param workflow_id: Workflow ID.
:param rag: If set, will enable Retrieval-Augmented Generation.
:return: httpx response
Expand Down Expand Up @@ -112,7 +121,7 @@ def _custom_request(

if isinstance(input_source, URLInputSource):
data["document"] = input_source.url
response = httpx.post(
response = self.http_client.post(
url=url,
headers=self.settings.base_headers,
data=data,
Expand All @@ -121,7 +130,7 @@ def _custom_request(
)
else:
files = {"document": input_source.read_contents(close_file)}
response = httpx.post(
response = self.http_client.post(
url=url,
files=files,
headers=self.settings.base_headers,
Expand All @@ -138,7 +147,7 @@ def document_queue_req_get(self, queue_id: str) -> httpx.Response:

:param queue_id: queue_id received from the API
"""
return httpx.get(
return self.http_client.get(
f"{self.settings.url_root}/documents/queue/{queue_id}",
headers=self.settings.base_headers,
timeout=self.settings.request_timeout,
Expand All @@ -147,7 +156,7 @@ def document_queue_req_get(self, queue_id: str) -> httpx.Response:

def openapi_get_req(self) -> httpx.Response:
"""Get the OpenAPI specification of the product."""
return httpx.get(
return self.http_client.get(
f"{self.settings.url_root}/openapi.json",
headers=self.settings.base_headers,
timeout=self.settings.request_timeout,
Expand All @@ -163,7 +172,7 @@ def document_feedback_req_put(
:param document_id: ID of the document to send feedback to.
:param feedback: Feedback object to send.
"""
return httpx.put(
return self.http_client.put(
f"{self.settings.base_url}/v1/documents/{document_id}/feedback",
headers=self.settings.base_headers,
data=feedback,
Expand All @@ -187,7 +196,7 @@ def training_req_post(
files = {"document": input_source.read_contents(close_file)}
params = {"training": True, "with_candidates": True}

response = httpx.post(
response = self.http_client.post(
f"{self.settings.url_root}/predict",
files=files,
headers=self.settings.base_headers,
Expand All @@ -209,7 +218,7 @@ def training_async_req_post(
files = {"document": input_source.read_contents(close_file)}
params = {"training": True, "async": True}

response = httpx.post(
response = self.http_client.post(
f"{self.settings.url_root}/predict",
files=files,
headers=self.settings.base_headers,
Expand Down Expand Up @@ -240,7 +249,7 @@ def documents_req_get(self, page_id: int = 1) -> httpx.Response:
params = {
"page": page_id,
}
response = httpx.get(
response = self.http_client.get(
f"{self.settings.url_root}/documents",
headers=self.settings.base_headers,
params=params,
Expand All @@ -260,7 +269,7 @@ def document_req_get(self, document_id: str) -> httpx.Response:
"include_candidates": True,
"global_orientation": True,
}
response = httpx.get(
response = self.http_client.get(
f"{self.settings.url_root}/documents/{document_id}",
headers=self.settings.base_headers,
params=params,
Expand All @@ -279,7 +288,7 @@ def annotations_req_post(
:param annotations: Annotations object
:return: httpx response
"""
response = httpx.post(
response = self.http_client.post(
f"{self.settings.url_root}/documents/{document_id}/annotations",
headers=self.settings.base_headers,
json=annotations,
Expand All @@ -297,7 +306,7 @@ def annotations_req_put(
:param annotations: Annotations object
:return: httpx response
"""
response = httpx.put(
response = self.http_client.put(
f"{self.settings.url_root}/documents/{document_id}/annotations",
headers=self.settings.base_headers,
json=annotations,
Expand All @@ -312,7 +321,7 @@ def annotations_req_del(self, document_id: str) -> httpx.Response:
:param document_id: ID of the document to annotate
:return: httpx response
"""
response = httpx.delete(
response = self.http_client.delete(
f"{self.settings.url_root}/documents/{document_id}/annotations",
headers=self.settings.base_headers,
timeout=self.settings.request_timeout,
Expand Down
11 changes: 7 additions & 4 deletions mindee/v1/mindee_http/workflow_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ class WorkflowEndpoint(BaseEndpoint):
"""Workflow endpoint."""

settings: WorkflowSettings
"""Settings object."""

def __init__(self, settings: WorkflowSettings) -> None:
def __init__(
self, settings: WorkflowSettings, http_client: httpx.Client | None = None
) -> None:
"""
Workflow Endpoint.

:param settings: Settings object.
"""
super().__init__(settings)
super().__init__(settings, http_client)

def workflow_execution_post(
self,
Expand Down Expand Up @@ -50,7 +53,7 @@ def workflow_execution_post(

if isinstance(input_source, URLInputSource):
data["document"] = input_source.url
response = httpx.post(
response = self.http_client.post(
self.settings.url_root,
headers=self.settings.base_headers,
data=data,
Expand All @@ -59,7 +62,7 @@ def workflow_execution_post(
)
else:
files = {"document": input_source.read_contents(True)}
response = httpx.post(
response = self.http_client.post(
self.settings.url_root,
files=files,
headers=self.settings.base_headers,
Expand Down
18 changes: 16 additions & 2 deletions mindee/v2/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from time import sleep
from typing import TypeVar

import httpx

from mindee.client_mixin import ClientMixin
from mindee.client_options.polling_options import PollingOptions
from mindee.error.mindee_error import MindeeError
Expand All @@ -27,14 +29,16 @@ class Client(ClientMixin):
api_key: str | None
mindee_api: MindeeAPIV2

def __init__(self, api_key: str | None = None) -> None:
def __init__(
self, api_key: str | None = None, http_client: httpx.Client | None = None
) -> None:
"""
Mindee API Client.

:param api_key: Your API key for all endpoints
"""
self.api_key = api_key
self.mindee_api = MindeeAPIV2(api_key)
self.mindee_api = MindeeAPIV2(api_key, http_client)

def enqueue(
self,
Expand Down Expand Up @@ -166,3 +170,13 @@ def search_models(
:return: A list of models matching the provided criteria.
"""
return self.mindee_api.get_models(name, model_type)

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

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
Loading
Loading