diff --git a/jigsawstack/__init__.py b/jigsawstack/__init__.py index e6a2f5f..3702bdb 100644 --- a/jigsawstack/__init__.py +++ b/jigsawstack/__init__.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Union, Dict import os from .audio import Audio, AsyncAudio from .vision import Vision, AsyncVision @@ -27,13 +27,15 @@ class JigsawStack: classification: Classification api_key: str api_url: str - disable_request_logging: bool + headers: Dict[str, str] + # disable_request_logging: bool def __init__( self, api_key: Union[str, None] = None, api_url: Union[str, None] = None, - disable_request_logging: Union[bool, None] = None, + # disable_request_logging: Union[bool, None] = None, + headers: Union[Dict[str, str], None] = None, ) -> None: if api_key is None: api_key = os.environ.get("JIGSAWSTACK_API_KEY") @@ -51,6 +53,10 @@ def __init__( self.api_key = api_key self.api_url = api_url + self.headers = headers or {} + + disable_request_logging = self.headers.get("x-jigsaw-no-request-log") + self.audio = Audio( api_key=api_key, api_url=api_url, diff --git a/jigsawstack/async_request.py b/jigsawstack/async_request.py index fd48f20..033b39b 100644 --- a/jigsawstack/async_request.py +++ b/jigsawstack/async_request.py @@ -36,7 +36,9 @@ def __init__( self.disable_request_logging = config.get("disable_request_logging") self.stream = stream - def __convert_params(self, params: Union[Dict[Any, Any], List[Dict[Any, Any]]]) -> Dict[str, str]: + def __convert_params( + self, params: Union[Dict[Any, Any], List[Dict[Any, Any]]] + ) -> Dict[str, str]: """ Convert parameters to string values for URL encoding. """ @@ -45,10 +47,10 @@ def __convert_params(self, params: Union[Dict[Any, Any], List[Dict[Any, Any]]]) if isinstance(params, str): return params - + if isinstance(params, list): return {} # List params are only used in JSON body - + converted = {} for key, value in params.items(): if isinstance(value, bool): @@ -67,7 +69,15 @@ async def perform(self) -> Union[T, None]: # For binary responses if resp.status == 200: content_type = resp.headers.get("content-type", "") - if not resp.text or any(t in content_type for t in ["audio/", "image/", "application/octet-stream", "image/png"]): + if not resp.text or any( + t in content_type + for t in [ + "audio/", + "image/", + "application/octet-stream", + "image/png", + ] + ): content = await resp.read() return cast(T, content) diff --git a/jigsawstack/audio.py b/jigsawstack/audio.py index e3bcab7..c3d37e7 100644 --- a/jigsawstack/audio.py +++ b/jigsawstack/audio.py @@ -9,7 +9,6 @@ from .helpers import build_path - class SpeechToTextParams(TypedDict): url: NotRequired[str] file_store_key: NotRequired[str] @@ -57,7 +56,7 @@ def __init__( def speech_to_text(self, params: SpeechToTextParams) -> SpeechToTextResponse: ... @overload def speech_to_text( - self, file: bytes, options: Optional[SpeechToTextParams] = None + self, blob: bytes, options: Optional[SpeechToTextParams] = None ) -> SpeechToTextResponse: ... def speech_to_text( @@ -114,7 +113,7 @@ async def speech_to_text( ) -> SpeechToTextResponse: ... @overload async def speech_to_text( - self, file: bytes, options: Optional[SpeechToTextParams] = None + self, blob: bytes, options: Optional[SpeechToTextParams] = None ) -> SpeechToTextResponse: ... async def speech_to_text( @@ -145,4 +144,3 @@ async def speech_to_text( verb="post", ).perform_with_content() return resp - diff --git a/jigsawstack/embedding.py b/jigsawstack/embedding.py index a9c4d05..be09660 100644 --- a/jigsawstack/embedding.py +++ b/jigsawstack/embedding.py @@ -42,7 +42,7 @@ def __init__( @overload def execute(self, params: EmbeddingParams) -> EmbeddingResponse: ... @overload - def execute(self, file: bytes, options: EmbeddingParams = None) -> EmbeddingResponse: ... + def execute(self, blob: bytes, options: EmbeddingParams = None) -> EmbeddingResponse: ... def execute( self, @@ -95,7 +95,7 @@ def __init__( @overload async def execute(self, params: EmbeddingParams) -> EmbeddingResponse: ... @overload - async def execute(self, file: bytes, options: EmbeddingParams = None) -> EmbeddingResponse: ... + async def execute(self, blob: bytes, options: EmbeddingParams = None) -> EmbeddingResponse: ... async def execute( self, diff --git a/jigsawstack/request.py b/jigsawstack/request.py index 624b31a..55e8e7f 100644 --- a/jigsawstack/request.py +++ b/jigsawstack/request.py @@ -50,13 +50,16 @@ def perform(self) -> Union[T, None]: """ resp = self.make_request(url=f"{self.api_url}{self.path}") - #for binary responses + # for binary responses if resp.status_code == 200: content_type = resp.headers.get("content-type", "") - if not resp.text or any(t in content_type for t in ["audio/", "image/", "application/octet-stream", "image/png"]): + if not resp.text or any( + t in content_type + for t in ["audio/", "image/", "application/octet-stream", "image/png"] + ): return cast(T, resp.content) - #for json resposes. + # for json resposes. if resp.status_code != 200: try: error = resp.json() @@ -105,7 +108,7 @@ def perform_file(self) -> Union[T, None]: err=error.get("error"), ) - #for binary responses + # for binary responses if resp.status_code == 200: content_type = resp.headers.get("content-type", "") if "application/json" not in content_type: diff --git a/jigsawstack/translate.py b/jigsawstack/translate.py index 9b0bcfb..885cc0c 100644 --- a/jigsawstack/translate.py +++ b/jigsawstack/translate.py @@ -98,7 +98,7 @@ def text( @overload def image(self, params: TranslateImageParams) -> TranslateImageResponse: ... @overload - def image(self, file: bytes, options: TranslateImageParams = None) -> TranslateImageParams: ... + def image(self, blob: bytes, options: TranslateImageParams = None) -> TranslateImageParams: ... def image( self, @@ -160,7 +160,7 @@ async def text( @overload async def image(self, params: TranslateImageParams) -> TranslateImageResponse: ... @overload - async def image(self, file: bytes, options: TranslateImageParams = None) -> TranslateImageParams: ... + async def image(self, blob: bytes, options: TranslateImageParams = None) -> TranslateImageParams: ... async def image( self, diff --git a/jigsawstack/validate.py b/jigsawstack/validate.py index 2298b00..11f899d 100644 --- a/jigsawstack/validate.py +++ b/jigsawstack/validate.py @@ -50,7 +50,6 @@ class NSFWParams(TypedDict): file_store_key: NotRequired[str] - class NSFWResponse(TypedDict): success: bool nsfw: bool @@ -107,25 +106,39 @@ def email(self, params: EmailValidationParams) -> EmailValidationResponse: verb="get", ).perform_with_content() return resp - - def nsfw(self, params: Union[NSFWParams, bytes]) -> NSFWResponse: - path="/validate/nsfw" - if isinstance(params, dict): + + @overload + def nsfw(self, params: NSFWParams) -> NSFWResponse: ... + @overload + def nsfw(self, blob: bytes, options: NSFWParams = None) -> NSFWResponse: ... + + def nsfw( + self, + blob: Union[NSFWParams, bytes], + options: NSFWParams = None, + ) -> NSFWResponse: + if isinstance( + blob, dict + ): # If params is provided as a dict, we assume it's the first argument resp = Request( config=self.config, - path=path, - params=cast(Dict[Any, Any], params), + path="/validate/nsfw", + params=cast(Dict[Any, Any], blob), verb="post", ).perform_with_content() return resp - _headers = {"Content-Type": "application/octet-stream"} + options = options or {} + path = build_path(base_path="/validate/nsfw", params=options) + content_type = options.get("content_type", "application/octet-stream") + headers = {"Content-Type": content_type} + resp = Request( config=self.config, path=path, - params={}, #since we're already passing data. - data=params, - headers=_headers, + params=options, + data=blob, + headers=headers, verb="post", ).perform_with_content() return resp @@ -138,9 +151,7 @@ def profanity(self, params: ProfanityParams) -> ProfanityResponse: resp = Request( config=self.config, path=path, - params=cast( - Dict[Any, Any], params - ), + params=cast(Dict[Any, Any], params), verb="post", ).perform_with_content() return resp @@ -198,25 +209,39 @@ async def email(self, params: EmailValidationParams) -> EmailValidationResponse: verb="get", ).perform_with_content() return resp - - async def nsfw(self, params: Union[NSFWParams, bytes]) -> NSFWResponse: - path="/validate/nsfw" - if isinstance(params, dict): + + @overload + async def nsfw(self, params: NSFWParams) -> NSFWResponse: ... + @overload + async def nsfw(self, blob: bytes, options: NSFWParams = None) -> NSFWResponse: ... + + async def nsfw( + self, + blob: Union[NSFWParams, bytes], + options: NSFWParams = None, + ) -> NSFWResponse: + if isinstance( + blob, dict + ): # If params is provided as a dict, we assume it's the first argument resp = await AsyncRequest( config=self.config, - path=path, - params=cast(Dict[Any, Any], params), + path="/validate/nsfw", + params=cast(Dict[Any, Any], blob), verb="post", ).perform_with_content() return resp - _headers = {"Content-Type": "application/octet-stream"} + options = options or {} + path = build_path(base_path="/validate/nsfw", params=options) + content_type = options.get("content_type", "application/octet-stream") + headers = {"Content-Type": content_type} + resp = await AsyncRequest( config=self.config, path=path, - params={}, - data=params, - headers=_headers, + params=options, + data=blob, + headers=headers, verb="post", ).perform_with_content() return resp @@ -229,9 +254,7 @@ async def profanity(self, params: ProfanityParams) -> ProfanityResponse: resp = await AsyncRequest( config=self.config, path=path, - params=cast( - Dict[Any, Any], params - ), + params=cast(Dict[Any, Any], params), verb="post", ).perform_with_content() return resp diff --git a/jigsawstack/vision.py b/jigsawstack/vision.py index 06d7c9b..3974e36 100644 --- a/jigsawstack/vision.py +++ b/jigsawstack/vision.py @@ -1,10 +1,9 @@ -from typing import Any, Dict, List, Union, cast, Optional -from typing_extensions import NotRequired, TypedDict, Literal -from typing import Any, Dict, List, cast +from typing import Any, Dict, List, Union, cast, Optional, overload from typing_extensions import NotRequired, TypedDict, Literal from .request import Request, RequestConfig from .async_request import AsyncRequest, AsyncRequestConfig from ._config import ClientConfig +from .helpers import build_path class Point(TypedDict): @@ -162,24 +161,76 @@ def __init__( disable_request_logging=disable_request_logging, ) - def vocr(self, params: VOCRParams) -> OCRResponse: - path = "/vocr" + @overload + def vocr(self, params: VOCRParams) -> OCRResponse: ... + @overload + def vocr(self, blob: bytes, options: VOCRParams = None) -> OCRResponse: ... + + def vocr( + self, + blob: Union[VOCRParams, bytes], + options: VOCRParams = None, + ) -> OCRResponse: + if isinstance( + blob, dict + ): # If params is provided as a dict, we assume it's the first argument + resp = Request( + config=self.config, + path="/vocr", + params=cast(Dict[Any, Any], blob), + verb="post", + ).perform_with_content() + return resp + + options = options or {} + path = build_path(base_path="/vocr", params=options) + content_type = options.get("content_type", "application/octet-stream") + headers = {"Content-Type": content_type} + resp = Request( config=self.config, path=path, - params=cast(Dict[Any, Any], params), + params=options, + data=blob, + headers=headers, verb="post", ).perform_with_content() return resp + @overload def object_detection( self, params: ObjectDetectionParams + ) -> ObjectDetectionResponse: ... + @overload + def object_detection( + self, blob: bytes, options: ObjectDetectionParams = None + ) -> ObjectDetectionResponse: ... + + def object_detection( + self, + blob: Union[ObjectDetectionParams, bytes], + options: ObjectDetectionParams = None, ) -> ObjectDetectionResponse: - path = "/object_detection" + if isinstance(blob, dict): + resp = Request( + config=self.config, + path="/object_detection", + params=cast(Dict[Any, Any], blob), + verb="post", + ).perform_with_content() + return resp + + options = options or {} + path = build_path(base_path="/object_detection", params=options) + content_type = options.get("content_type", "application/octet-stream") + headers = {"Content-Type": content_type} + resp = Request( config=self.config, path=path, - params=cast(Dict[Any, Any], params), + params=options, + data=blob, + headers=headers, verb="post", ).perform_with_content() return resp @@ -201,24 +252,76 @@ def __init__( disable_request_logging=disable_request_logging, ) - async def vocr(self, params: VOCRParams) -> OCRResponse: - path = "/vocr" + @overload + async def vocr(self, params: VOCRParams) -> OCRResponse: ... + @overload + async def vocr(self, blob: bytes, options: VOCRParams = None) -> OCRResponse: ... + + async def vocr( + self, + blob: Union[VOCRParams, bytes], + options: VOCRParams = None, + ) -> OCRResponse: + if isinstance(blob, dict): + resp = await AsyncRequest( + config=self.config, + path="/vocr", + params=cast(Dict[Any, Any], blob), + verb="post", + ).perform_with_content() + return resp + + options = options or {} + path = build_path(base_path="/vocr", params=options) + content_type = options.get("content_type", "application/octet-stream") + headers = {"Content-Type": content_type} + resp = await AsyncRequest( config=self.config, path=path, - params=cast(Dict[Any, Any], params), + params=options, + data=blob, + headers=headers, verb="post", ).perform_with_content() return resp + @overload async def object_detection( self, params: ObjectDetectionParams + ) -> ObjectDetectionResponse: ... + @overload + async def object_detection( + self, blob: bytes, options: ObjectDetectionParams = None + ) -> ObjectDetectionResponse: ... + + async def object_detection( + self, + blob: Union[ObjectDetectionParams, bytes], + options: ObjectDetectionParams = None, ) -> ObjectDetectionResponse: - path = "/object_detection" + if isinstance( + blob, dict + ): # If params is provided as a dict, we assume it's the first argument + resp = await AsyncRequest( + config=self.config, + path="/object_detection", + params=cast(Dict[Any, Any], blob), + verb="post", + ).perform_with_content() + return resp + + options = options or {} + path = build_path(base_path="/object_detection", params=options) + content_type = options.get("content_type", "application/octet-stream") + headers = {"Content-Type": content_type} + resp = await AsyncRequest( config=self.config, path=path, - params=cast(Dict[Any, Any], params), + params=options, + data=blob, + headers=headers, verb="post", ).perform_with_content() return resp