修改为东南天坐标系
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from ._version import (
|
||||
__title__,
|
||||
__version__,
|
||||
__openapi_doc_version__,
|
||||
__gen_version__,
|
||||
__user_agent__,
|
||||
)
|
||||
from .sdk import *
|
||||
from .sdkconfiguration import *
|
||||
|
||||
|
||||
VERSION: str = __version__
|
||||
OPENAPI_DOC_VERSION = __openapi_doc_version__
|
||||
SPEAKEASY_GENERATOR_VERSION = __gen_version__
|
||||
USER_AGENT = __user_agent__
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from .sdkhooks import *
|
||||
from .types import *
|
||||
from .registration import *
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
from .clean_server_url_hook import CleanServerUrlSDKInitHook
|
||||
from .logger_hook import LoggerHook
|
||||
from .split_pdf_hook import SplitPdfHook
|
||||
import logging
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,45 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Tuple
|
||||
from urllib.parse import ParseResult, urlparse, urlunparse
|
||||
|
||||
from unstructured_client._hooks.types import SDKInitHook
|
||||
from unstructured_client.httpclient import HttpClient
|
||||
|
||||
|
||||
def clean_server_url(base_url: str | None) -> str:
|
||||
"""Fix url scheme and remove subpath for URLs under Unstructured domains."""
|
||||
|
||||
if not base_url:
|
||||
return ""
|
||||
|
||||
# add a url scheme if not present (urllib.parse does not work reliably without it)
|
||||
if "http" not in base_url:
|
||||
base_url = "http://" + base_url
|
||||
|
||||
parsed_url: ParseResult = urlparse(base_url)
|
||||
|
||||
if "unstructuredapp.io" in parsed_url.netloc:
|
||||
if parsed_url.scheme != "https":
|
||||
parsed_url = parsed_url._replace(scheme="https")
|
||||
# We only want the base url for Unstructured domains
|
||||
clean_url = urlunparse(parsed_url._replace(path="", params="", query="", fragment=""))
|
||||
|
||||
else:
|
||||
# For other domains, we want to keep the path
|
||||
clean_url = urlunparse(parsed_url._replace(params="", query="", fragment=""))
|
||||
|
||||
return clean_url.rstrip("/")
|
||||
|
||||
|
||||
|
||||
class CleanServerUrlSDKInitHook(SDKInitHook):
|
||||
"""Hook fixing common mistakes by users in defining `server_url` in the unstructured-client"""
|
||||
|
||||
def sdk_init(
|
||||
self, base_url: str, client: HttpClient
|
||||
) -> Tuple[str, HttpClient]:
|
||||
"""Concrete implementation for SDKInitHook."""
|
||||
cleaned_url = clean_server_url(base_url)
|
||||
|
||||
return cleaned_url, client
|
||||
@@ -0,0 +1 @@
|
||||
UNSTRUCTURED_CLIENT_LOGGER_NAME = "unstructured-client"
|
||||
@@ -0,0 +1,302 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
from requests_toolbelt.multipart.decoder import MultipartDecoder # type: ignore
|
||||
|
||||
from unstructured_client._hooks.custom.common import UNSTRUCTURED_CLIENT_LOGGER_NAME
|
||||
from unstructured_client.models import shared
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Union
|
||||
|
||||
logger = logging.getLogger(UNSTRUCTURED_CLIENT_LOGGER_NAME)
|
||||
FormData: TypeAlias = "dict[str, Union[str, shared.Files, list[str]]]"
|
||||
|
||||
PARTITION_FORM_FILES_KEY = "files"
|
||||
PARTITION_FORM_SPLIT_PDF_PAGE_KEY = "split_pdf_page"
|
||||
PARTITION_FORM_PAGE_RANGE_KEY = "split_pdf_page_range[]"
|
||||
PARTITION_FORM_SPLIT_PDF_ALLOW_FAILED_KEY = "split_pdf_allow_failed"
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_KEY = "split_pdf_cache_tmp_data"
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_DIR_KEY = "split_pdf_cache_tmp_data_dir"
|
||||
PARTITION_FORM_STARTING_PAGE_NUMBER_KEY = "starting_page_number"
|
||||
PARTITION_FORM_CONCURRENCY_LEVEL_KEY = "split_pdf_concurrency_level"
|
||||
|
||||
|
||||
def get_page_range(form_data: FormData, key: str, max_pages: int) -> tuple[int, int]:
|
||||
"""Retrieves the split page range from the given form data.
|
||||
|
||||
If the range is invalid or outside the bounds of the page count,
|
||||
returns (1, num_pages), i.e. the full range.
|
||||
|
||||
Args:
|
||||
form_data: The form data containing the page range
|
||||
key: The key to look for in the form data.
|
||||
|
||||
Returns:
|
||||
The range of pages to send in the request in the form (start, end)
|
||||
"""
|
||||
_page_range = None
|
||||
try:
|
||||
_page_range = form_data.get(key)
|
||||
|
||||
if isinstance(_page_range, list):
|
||||
page_range = (int(_page_range[0]), int(_page_range[1]))
|
||||
else:
|
||||
page_range = (1, max_pages)
|
||||
|
||||
except (ValueError, IndexError) as exc:
|
||||
msg = f"{_page_range} is not a valid page range."
|
||||
logger.error(msg)
|
||||
raise ValueError(msg) from exc
|
||||
|
||||
start, end = page_range
|
||||
|
||||
if not 0 < start <= max_pages or not 0 < end <= max_pages or not start <= end:
|
||||
msg = f"Page range {page_range} is out of bounds. Start and end values should be between 1 and {max_pages}."
|
||||
logger.error(msg)
|
||||
raise ValueError(msg)
|
||||
|
||||
return page_range
|
||||
|
||||
|
||||
def get_starting_page_number(form_data: FormData, key: str, fallback_value: int) -> int:
|
||||
"""Retrieves the starting page number from the given form data.
|
||||
|
||||
In case given starting page number is not a valid integer or less than 1, it will
|
||||
use the default value.
|
||||
|
||||
Args:
|
||||
form_data: The form data containing the starting page number.
|
||||
key: The key to look for in the form data.
|
||||
fallback_value: The default value to use in case of an error.
|
||||
|
||||
Returns:
|
||||
The starting page number.
|
||||
"""
|
||||
starting_page_number = fallback_value
|
||||
try:
|
||||
_starting_page_number = form_data.get(key) or fallback_value
|
||||
starting_page_number = int(_starting_page_number) # type: ignore
|
||||
except ValueError:
|
||||
logger.warning(
|
||||
"'%s' is not a valid integer. Using default value '%d'.",
|
||||
key,
|
||||
fallback_value,
|
||||
)
|
||||
|
||||
if starting_page_number < 1:
|
||||
logger.warning(
|
||||
"'%s' is less than 1. Using default value '%d'.",
|
||||
key,
|
||||
fallback_value,
|
||||
)
|
||||
starting_page_number = fallback_value
|
||||
|
||||
return starting_page_number
|
||||
|
||||
def get_split_pdf_allow_failed_param(
|
||||
form_data: FormData, key: str, fallback_value: bool,
|
||||
) -> bool:
|
||||
"""Retrieves the value for allow failed that should be used for splitting pdf.
|
||||
|
||||
In case given the number is not a "false" or "true" literal, it will use the
|
||||
default value.
|
||||
|
||||
Args:
|
||||
form_data: The form data containing the desired concurrency level.
|
||||
key: The key to look for in the form data.
|
||||
fallback_value: The default value to use in case of an error.
|
||||
|
||||
Returns:
|
||||
The concurrency level after validation.
|
||||
"""
|
||||
allow_failed = form_data.get(key)
|
||||
|
||||
if not isinstance(allow_failed, str):
|
||||
return fallback_value
|
||||
|
||||
if allow_failed.lower() not in ["true", "false"]:
|
||||
logger.warning(
|
||||
"'%s' is not a valid boolean. Using default value '%s'.",
|
||||
key,
|
||||
fallback_value,
|
||||
)
|
||||
return fallback_value
|
||||
|
||||
return allow_failed.lower() == "true"
|
||||
|
||||
def get_split_pdf_cache_tmp_data(
|
||||
form_data: FormData, key: str, fallback_value: bool,
|
||||
) -> bool:
|
||||
"""Retrieves the value for cache tmp data that should be used for splitting pdf.
|
||||
|
||||
In case given the value is not a correct (existing) dir (Path), it will use the
|
||||
default value.
|
||||
|
||||
Args:
|
||||
form_data: The form data containing the desired flag value.
|
||||
key: The key to look for in the form data.
|
||||
fallback_value: The default value to use in case of an error.
|
||||
|
||||
Returns:
|
||||
The flag value for 'cache tmp data' feature after validation.
|
||||
"""
|
||||
cache_tmp_data = form_data.get(key)
|
||||
|
||||
if not isinstance(cache_tmp_data, str):
|
||||
return fallback_value
|
||||
|
||||
if cache_tmp_data.lower() not in ["true", "false"]:
|
||||
logger.warning(
|
||||
"'%s' is not a valid boolean. Using default value '%s'.",
|
||||
key,
|
||||
fallback_value,
|
||||
)
|
||||
return fallback_value
|
||||
|
||||
return cache_tmp_data.lower() == "true"
|
||||
|
||||
def get_split_pdf_cache_tmp_data_dir(
|
||||
form_data: FormData, key: str, fallback_value: str,
|
||||
) -> str:
|
||||
"""Retrieves the value for cache tmp data dir that should be used for splitting pdf.
|
||||
|
||||
In case given the number is not a "false" or "true" literal, it will use the
|
||||
default value.
|
||||
|
||||
Args:
|
||||
form_data: The form data containing the desired flag value.
|
||||
key: The key to look for in the form data.
|
||||
fallback_value: The default value to use in case of an error.
|
||||
|
||||
Returns:
|
||||
The flag value for 'cache tmp data' feature after validation.
|
||||
"""
|
||||
cache_tmp_data_dir = form_data.get(key)
|
||||
|
||||
if not isinstance(cache_tmp_data_dir, str):
|
||||
return fallback_value
|
||||
cache_tmp_data_path = Path(cache_tmp_data_dir)
|
||||
|
||||
if not cache_tmp_data_path.exists():
|
||||
logger.warning(
|
||||
"'%s' does not exist. Using default value '%s'.",
|
||||
key,
|
||||
fallback_value,
|
||||
)
|
||||
return fallback_value
|
||||
|
||||
return str(cache_tmp_data_path.resolve())
|
||||
|
||||
|
||||
def get_split_pdf_concurrency_level_param(
|
||||
form_data: FormData, key: str, fallback_value: int, max_allowed: int
|
||||
) -> int:
|
||||
"""Retrieves the value for concurreny level that should be used for splitting pdf.
|
||||
|
||||
In case given the number is not a valid integer or less than 1, it will use the
|
||||
default value.
|
||||
|
||||
Args:
|
||||
form_data: The form data containing the desired concurrency level.
|
||||
key: The key to look for in the form data.
|
||||
fallback_value: The default value to use in case of an error.
|
||||
max_allowed: The maximum allowed value for the concurrency level.
|
||||
|
||||
Returns:
|
||||
The concurrency level after validation.
|
||||
"""
|
||||
concurrency_level_str = form_data.get(key)
|
||||
|
||||
if not isinstance(concurrency_level_str, str):
|
||||
return fallback_value
|
||||
|
||||
try:
|
||||
concurrency_level = int(concurrency_level_str)
|
||||
except ValueError:
|
||||
logger.warning(
|
||||
"'%s' is not a valid integer. Using default value '%s'.",
|
||||
key,
|
||||
fallback_value,
|
||||
)
|
||||
return fallback_value
|
||||
|
||||
if concurrency_level < 1:
|
||||
logger.warning(
|
||||
"'%s' is less than 1. Using the default value = %s.",
|
||||
key,
|
||||
fallback_value,
|
||||
)
|
||||
return fallback_value
|
||||
|
||||
if concurrency_level > max_allowed:
|
||||
logger.warning(
|
||||
"'%s' is greater than %s. Using the maximum allowed value = %s.",
|
||||
key,
|
||||
max_allowed,
|
||||
max_allowed,
|
||||
)
|
||||
return max_allowed
|
||||
|
||||
return concurrency_level
|
||||
|
||||
|
||||
def decode_content_disposition(content_disposition: bytes) -> dict[str, str]:
|
||||
"""Decode the `Content-Disposition` header and return the parameters as a dictionary.
|
||||
|
||||
Args:
|
||||
content_disposition: The `Content-Disposition` header as bytes.
|
||||
|
||||
Returns:
|
||||
A dictionary containing the parameters extracted from the
|
||||
`Content-Disposition` header.
|
||||
"""
|
||||
data = content_disposition.decode().split("; ")[1:]
|
||||
parameters = [d.split("=") for d in data]
|
||||
parameters_dict = {p[0]: p[1].strip('"') for p in parameters}
|
||||
return parameters_dict
|
||||
|
||||
|
||||
def parse_form_data(decoded_data: MultipartDecoder) -> FormData:
|
||||
"""Parses the form data from the decoded multipart data.
|
||||
|
||||
Args:
|
||||
decoded_data: The decoded multipart data.
|
||||
|
||||
Returns:
|
||||
The parsed form data.
|
||||
"""
|
||||
form_data: FormData = {}
|
||||
|
||||
for part in decoded_data.parts:
|
||||
content_disposition = part.headers.get(b"Content-Disposition") # type: ignore
|
||||
if content_disposition is None:
|
||||
raise RuntimeError("Content-Disposition header not found. Can't split pdf file.")
|
||||
part_params = decode_content_disposition(content_disposition)
|
||||
name = part_params.get("name")
|
||||
|
||||
if name is None:
|
||||
continue
|
||||
|
||||
if name == PARTITION_FORM_FILES_KEY:
|
||||
filename = part_params.get("filename")
|
||||
if filename is None or not filename.strip():
|
||||
raise ValueError("Filename can't be an empty string.")
|
||||
form_data[PARTITION_FORM_FILES_KEY] = shared.Files(content=part.content, file_name=filename)
|
||||
else:
|
||||
content = part.content.decode()
|
||||
if name in form_data:
|
||||
form_data_value = form_data[name]
|
||||
if isinstance(form_data_value, list):
|
||||
form_data_value.append(content)
|
||||
else:
|
||||
new_list = [form_data_value, content]
|
||||
form_data[name] = new_list
|
||||
else:
|
||||
form_data[name] = content
|
||||
|
||||
return form_data
|
||||
@@ -0,0 +1,85 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional, Tuple, Union, DefaultDict
|
||||
|
||||
import httpx
|
||||
|
||||
from unstructured_client._hooks.custom.common import UNSTRUCTURED_CLIENT_LOGGER_NAME
|
||||
from unstructured_client._hooks.types import (
|
||||
AfterSuccessContext,
|
||||
AfterErrorContext,
|
||||
AfterErrorHook,
|
||||
SDKInitHook,
|
||||
AfterSuccessHook,
|
||||
)
|
||||
from unstructured_client.httpclient import HttpClient
|
||||
from collections import defaultdict
|
||||
|
||||
logger = logging.getLogger(UNSTRUCTURED_CLIENT_LOGGER_NAME)
|
||||
|
||||
|
||||
class LoggerHook(AfterErrorHook, AfterSuccessHook, SDKInitHook):
|
||||
"""Hook providing custom logging"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.retries_counter: DefaultDict[str, int] = defaultdict(int)
|
||||
|
||||
def log_retries(self, response: Optional[httpx.Response], error: Optional[Exception], operation_id: str,):
|
||||
"""Log retries to give users visibility into requests."""
|
||||
|
||||
if response is not None and response.status_code // 100 == 5:
|
||||
logger.info(
|
||||
"Failed to process a request due to API server error with status code %d. "
|
||||
"Attempting retry number %d after sleep.",
|
||||
response.status_code,
|
||||
self.retries_counter[operation_id],
|
||||
)
|
||||
if response.text:
|
||||
logger.info("Server message - %s", response.text)
|
||||
|
||||
elif error is not None and isinstance(error, httpx.ConnectError):
|
||||
logger.info(
|
||||
"Failed to process a request due to connection error - %s. "
|
||||
"Attempting retry number %d after sleep.",
|
||||
error,
|
||||
self.retries_counter[operation_id],
|
||||
)
|
||||
|
||||
|
||||
def sdk_init(
|
||||
self, base_url: str, client: HttpClient
|
||||
) -> Tuple[str, HttpClient]:
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
||||
return base_url, client
|
||||
|
||||
def after_success(
|
||||
self, hook_ctx: AfterSuccessContext, response: httpx.Response
|
||||
) -> Union[httpx.Response, Exception]:
|
||||
self.retries_counter.pop(hook_ctx.operation_id, None)
|
||||
# Note(austin) - pdf splitting returns a mock request
|
||||
# so we always reach the AfterSuccessHook
|
||||
# This doesn't mean the splits succeeded
|
||||
# Need to revisit our logging strategy
|
||||
# logger.info("Successfully partitioned the document.")
|
||||
return response
|
||||
|
||||
def after_error(
|
||||
self,
|
||||
hook_ctx: AfterErrorContext,
|
||||
response: Optional[httpx.Response],
|
||||
error: Optional[Exception],
|
||||
) -> Union[Tuple[Optional[httpx.Response], Optional[Exception]], Exception]:
|
||||
"""Concrete implementation for AfterErrorHook."""
|
||||
self.retries_counter[hook_ctx.operation_id] += 1
|
||||
self.log_retries(response, error, hook_ctx.operation_id)
|
||||
|
||||
if response and response.status_code == 200:
|
||||
# NOTE: Even though this is an after_error method, due to split_pdf_hook logic we may get
|
||||
# a success here when one of the split requests was partitioned successfully
|
||||
return response, error
|
||||
if response:
|
||||
logger.error("Server responded with %d - %s", response.status_code, response.text)
|
||||
if error is not None:
|
||||
logger.error("Following error occurred - %s", error, exc_info=error)
|
||||
return response, error
|
||||
@@ -0,0 +1,75 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import logging
|
||||
from typing import cast, Optional, BinaryIO, Union
|
||||
|
||||
from pypdf import PdfReader
|
||||
from pypdf.errors import FileNotDecryptedError, PdfReadError
|
||||
|
||||
from unstructured_client._hooks.custom.common import UNSTRUCTURED_CLIENT_LOGGER_NAME
|
||||
from unstructured_client._hooks.custom.validation_errors import FileValidationError
|
||||
|
||||
logger = logging.getLogger(UNSTRUCTURED_CLIENT_LOGGER_NAME)
|
||||
|
||||
# Loading pdfs with strict=False can dump a lot of warnings
|
||||
# We don't need to display these
|
||||
pdf_logger = logging.getLogger("pypdf")
|
||||
pdf_logger.setLevel(logging.ERROR)
|
||||
|
||||
|
||||
class PDFValidationError(FileValidationError):
|
||||
"""Exception for PDF validation errors."""
|
||||
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message, file_type="PDF")
|
||||
|
||||
|
||||
def read_pdf(pdf_file: Union[BinaryIO, bytes]) -> Optional[PdfReader]:
|
||||
"""Reads the given PDF file.
|
||||
|
||||
Args:
|
||||
pdf_file: The PDF file to be read.
|
||||
|
||||
Returns:
|
||||
The PdfReader object if the file is a PDF, None otherwise.
|
||||
"""
|
||||
|
||||
try:
|
||||
if isinstance(pdf_file, bytes):
|
||||
content = cast(bytes, pdf_file)
|
||||
pdf_file = io.BytesIO(content)
|
||||
return PdfReader(pdf_file, strict=False)
|
||||
except (PdfReadError, UnicodeDecodeError):
|
||||
return None
|
||||
|
||||
|
||||
def check_pdf(pdf: PdfReader) -> PdfReader:
|
||||
"""
|
||||
Check if PDF is:
|
||||
- Encrypted
|
||||
- Has corrupted pages
|
||||
- Has corrupted root object
|
||||
|
||||
Throws:
|
||||
- PDFValidationError if file is encrypted or corrupted
|
||||
"""
|
||||
try:
|
||||
# This will raise if the file is encrypted
|
||||
pdf.metadata # pylint: disable=pointless-statement
|
||||
|
||||
# This will raise if the file's root object is corrupted
|
||||
pdf.root_object # pylint: disable=pointless-statement
|
||||
|
||||
# This will raise if the file's pages are corrupted
|
||||
list(pdf.pages)
|
||||
|
||||
return pdf
|
||||
except FileNotDecryptedError as e:
|
||||
raise PDFValidationError(
|
||||
"File is encrypted. Please decrypt it with password.",
|
||||
) from e
|
||||
except PdfReadError as e:
|
||||
raise PDFValidationError(
|
||||
f"File does not appear to be a valid PDF. Error: {e}",
|
||||
) from e
|
||||
@@ -0,0 +1,248 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
from typing import Tuple, Any, BinaryIO
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
from httpx import URL
|
||||
from httpx._multipart import DataField, FileField
|
||||
|
||||
from unstructured_client._hooks.custom.common import UNSTRUCTURED_CLIENT_LOGGER_NAME
|
||||
from unstructured_client._hooks.custom.form_utils import (
|
||||
PARTITION_FORM_FILES_KEY,
|
||||
PARTITION_FORM_SPLIT_PDF_PAGE_KEY,
|
||||
PARTITION_FORM_SPLIT_PDF_ALLOW_FAILED_KEY,
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_KEY,
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_DIR_KEY,
|
||||
PARTITION_FORM_PAGE_RANGE_KEY,
|
||||
PARTITION_FORM_STARTING_PAGE_NUMBER_KEY,
|
||||
FormData,
|
||||
)
|
||||
from unstructured_client.models import shared
|
||||
from unstructured_client.utils import (
|
||||
BackoffStrategy,
|
||||
Retries,
|
||||
RetryConfig,
|
||||
retry_async,
|
||||
serialize_request_body,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(UNSTRUCTURED_CLIENT_LOGGER_NAME)
|
||||
|
||||
|
||||
def get_multipart_stream_fields(request: httpx.Request) -> dict[str, Any]:
|
||||
"""Extracts the multipart fields from the request.
|
||||
|
||||
Args:
|
||||
request: The request object.
|
||||
|
||||
Returns:
|
||||
The multipart fields.
|
||||
|
||||
Raises:
|
||||
Exception: If the filename is not set
|
||||
"""
|
||||
content_type = request.headers.get("Content-Type", "")
|
||||
if "multipart" not in content_type:
|
||||
return {}
|
||||
if request.stream is None or not hasattr(request.stream, "fields"):
|
||||
return {}
|
||||
fields = request.stream.fields
|
||||
|
||||
mapped_fields: dict[str, Any] = {}
|
||||
for field in fields:
|
||||
if isinstance(field, DataField):
|
||||
if "[]" in field.name:
|
||||
name = field.name.replace("[]", "")
|
||||
if name not in mapped_fields:
|
||||
mapped_fields[name] = []
|
||||
mapped_fields[name].append(field.value)
|
||||
mapped_fields[field.name] = field.value
|
||||
elif isinstance(field, FileField):
|
||||
if field.filename is None or not field.filename.strip():
|
||||
raise ValueError("Filename can't be an empty string.")
|
||||
mapped_fields[field.name] = {
|
||||
"filename": field.filename,
|
||||
"content_type": field.headers.get("Content-Type", ""),
|
||||
"file": field.file,
|
||||
}
|
||||
return mapped_fields
|
||||
|
||||
|
||||
def create_pdf_chunk_request_params(
|
||||
form_data: FormData, page_number: int
|
||||
) -> dict[str, Any]:
|
||||
"""Creates the request body for the partition API."
|
||||
|
||||
Args:
|
||||
form_data: The form data.
|
||||
page_number: The page number.
|
||||
|
||||
Returns:
|
||||
The updated request payload for the chunk.
|
||||
"""
|
||||
fields_to_drop = [
|
||||
PARTITION_FORM_SPLIT_PDF_PAGE_KEY,
|
||||
PARTITION_FORM_SPLIT_PDF_ALLOW_FAILED_KEY,
|
||||
PARTITION_FORM_FILES_KEY,
|
||||
PARTITION_FORM_PAGE_RANGE_KEY,
|
||||
PARTITION_FORM_PAGE_RANGE_KEY.replace("[]", ""),
|
||||
PARTITION_FORM_STARTING_PAGE_NUMBER_KEY,
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_KEY,
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_DIR_KEY,
|
||||
]
|
||||
chunk_payload = {
|
||||
key: form_data[key] for key in form_data if key not in fields_to_drop
|
||||
}
|
||||
chunk_payload[PARTITION_FORM_SPLIT_PDF_PAGE_KEY] = "false"
|
||||
chunk_payload[PARTITION_FORM_STARTING_PAGE_NUMBER_KEY] = str(page_number)
|
||||
return chunk_payload
|
||||
|
||||
|
||||
def create_pdf_chunk_request(
|
||||
form_data: FormData,
|
||||
pdf_chunk: Tuple[BinaryIO, int],
|
||||
original_request: httpx.Request,
|
||||
filename: str,
|
||||
) -> httpx.Request:
|
||||
"""Creates a new request object with the updated payload for the partition API.
|
||||
|
||||
Args:
|
||||
form_data: The form data.
|
||||
pdf_chunk: Tuple of pdf chunk contents (can be both io.BytesIO or
|
||||
a file object created with e.g. open()) and the page number.
|
||||
original_request: The original request.
|
||||
filename: The filename.
|
||||
|
||||
Returns:
|
||||
The updated request object.
|
||||
"""
|
||||
pdf_chunk_file, page_number = pdf_chunk
|
||||
data = create_pdf_chunk_request_params(form_data, page_number)
|
||||
original_headers = prepare_request_headers(original_request.headers)
|
||||
|
||||
pdf_chunk_content: BinaryIO | bytes = (
|
||||
pdf_chunk_file.getvalue()
|
||||
if isinstance(pdf_chunk_file, io.BytesIO)
|
||||
else pdf_chunk_file
|
||||
)
|
||||
|
||||
pdf_chunk_partition_params = shared.PartitionParameters(
|
||||
files=shared.Files(
|
||||
content=pdf_chunk_content,
|
||||
file_name=filename,
|
||||
content_type="application/pdf",
|
||||
),
|
||||
**data,
|
||||
)
|
||||
serialized_body = serialize_request_body(
|
||||
pdf_chunk_partition_params,
|
||||
False,
|
||||
False,
|
||||
"multipart",
|
||||
shared.PartitionParameters,
|
||||
)
|
||||
if serialized_body is None:
|
||||
raise ValueError("Failed to serialize the request body.")
|
||||
return httpx.Request(
|
||||
method="POST",
|
||||
url=original_request.url or "",
|
||||
headers={**original_headers},
|
||||
content=serialized_body.content,
|
||||
data=serialized_body.data,
|
||||
files=serialized_body.files,
|
||||
)
|
||||
|
||||
|
||||
async def call_api_async(
|
||||
client: httpx.AsyncClient,
|
||||
pdf_chunk_request: httpx.Request,
|
||||
pdf_chunk_file: BinaryIO,
|
||||
limiter: asyncio.Semaphore,
|
||||
) -> httpx.Response:
|
||||
one_second = 1000
|
||||
one_minute = 1000 * 60
|
||||
|
||||
retry_config = RetryConfig(
|
||||
"backoff",
|
||||
BackoffStrategy(
|
||||
initial_interval=one_second * 3,
|
||||
max_interval=one_minute * 12,
|
||||
max_elapsed_time=one_minute * 30,
|
||||
exponent=1.88,
|
||||
),
|
||||
retry_connection_errors=True,
|
||||
)
|
||||
|
||||
retryable_codes = ["5xx"]
|
||||
|
||||
async def do_request():
|
||||
return await client.send(pdf_chunk_request)
|
||||
|
||||
async with limiter:
|
||||
try:
|
||||
response = await retry_async(
|
||||
do_request, Retries(retry_config, retryable_codes)
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
logger.error("Request failed with error: %s", e, exc_info=e)
|
||||
raise e
|
||||
finally:
|
||||
if not isinstance(pdf_chunk_file, io.BytesIO) and not pdf_chunk_file.closed:
|
||||
pdf_chunk_file.close()
|
||||
|
||||
|
||||
def prepare_request_headers(
|
||||
headers: httpx.Headers,
|
||||
) -> httpx.Headers:
|
||||
"""Prepare the request headers by removing the 'Content-Type' and 'Content-Length' headers.
|
||||
|
||||
Args:
|
||||
headers: The original request headers.
|
||||
|
||||
Returns:
|
||||
The modified request headers.
|
||||
"""
|
||||
new_headers = headers.copy()
|
||||
new_headers.pop("Content-Type", None)
|
||||
new_headers.pop("Content-Length", None)
|
||||
return new_headers
|
||||
|
||||
|
||||
def create_response(elements: list) -> httpx.Response:
|
||||
"""
|
||||
Creates a modified response object with updated content.
|
||||
|
||||
Args:
|
||||
elements: The list of elements to be serialized and added to
|
||||
the response.
|
||||
|
||||
Returns:
|
||||
The modified response object with updated content.
|
||||
"""
|
||||
response = httpx.Response(
|
||||
status_code=200, headers={"Content-Type": "application/json"}
|
||||
)
|
||||
content = json.dumps(elements).encode()
|
||||
content_length = str(len(content))
|
||||
response.headers.update({"Content-Length": content_length})
|
||||
setattr(response, "_content", content)
|
||||
return response
|
||||
|
||||
|
||||
def get_base_url(url: str | URL) -> str:
|
||||
"""Extracts the base URL from the given URL.
|
||||
|
||||
Args:
|
||||
url: The URL.
|
||||
|
||||
Returns:
|
||||
The base URL.
|
||||
"""
|
||||
parsed_url = urlparse(str(url))
|
||||
return f"{parsed_url.scheme}://{parsed_url.netloc}"
|
||||
@@ -0,0 +1,721 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import tempfile
|
||||
import uuid
|
||||
from collections.abc import Awaitable
|
||||
from concurrent import futures
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from typing import Any, Coroutine, Optional, Tuple, Union, cast, Generator, BinaryIO
|
||||
|
||||
import aiofiles
|
||||
import httpx
|
||||
from httpx import AsyncClient
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
|
||||
from unstructured_client._hooks.custom import form_utils, pdf_utils, request_utils
|
||||
from unstructured_client._hooks.custom.common import UNSTRUCTURED_CLIENT_LOGGER_NAME
|
||||
from unstructured_client._hooks.custom.form_utils import (
|
||||
PARTITION_FORM_CONCURRENCY_LEVEL_KEY,
|
||||
PARTITION_FORM_FILES_KEY,
|
||||
PARTITION_FORM_PAGE_RANGE_KEY,
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_DIR_KEY,
|
||||
PARTITION_FORM_SPLIT_CACHE_TMP_DATA_KEY,
|
||||
PARTITION_FORM_SPLIT_PDF_ALLOW_FAILED_KEY,
|
||||
PARTITION_FORM_SPLIT_PDF_PAGE_KEY,
|
||||
PARTITION_FORM_STARTING_PAGE_NUMBER_KEY,
|
||||
)
|
||||
from unstructured_client._hooks.custom.request_utils import get_base_url
|
||||
from unstructured_client._hooks.types import (
|
||||
AfterErrorContext,
|
||||
AfterErrorHook,
|
||||
AfterSuccessContext,
|
||||
AfterSuccessHook,
|
||||
BeforeRequestContext,
|
||||
BeforeRequestHook,
|
||||
SDKInitHook,
|
||||
)
|
||||
from unstructured_client.httpclient import HttpClient, AsyncHttpClient
|
||||
|
||||
logger = logging.getLogger(UNSTRUCTURED_CLIENT_LOGGER_NAME)
|
||||
|
||||
DEFAULT_STARTING_PAGE_NUMBER = 1
|
||||
DEFAULT_ALLOW_FAILED = False
|
||||
DEFAULT_CONCURRENCY_LEVEL = 10
|
||||
DEFAULT_CACHE_TMP_DATA = False
|
||||
DEFAULT_CACHE_TMP_DATA_DIR = tempfile.gettempdir()
|
||||
MAX_CONCURRENCY_LEVEL = 50
|
||||
MIN_PAGES_PER_SPLIT = 2
|
||||
MAX_PAGES_PER_SPLIT = 20
|
||||
HI_RES_STRATEGY = 'hi_res'
|
||||
MAX_PAGE_LENGTH = 4000
|
||||
|
||||
def _run_coroutines_in_separate_thread(
|
||||
coroutines_task: Coroutine[Any, Any, list[tuple[int, httpx.Response]]],
|
||||
) -> list[tuple[int, httpx.Response]]:
|
||||
return asyncio.run(coroutines_task)
|
||||
|
||||
|
||||
async def _order_keeper(index: int, coro: Awaitable) -> Tuple[int, httpx.Response]:
|
||||
response = await coro
|
||||
return index, response
|
||||
|
||||
|
||||
async def run_tasks(
|
||||
coroutines: list[partial[Coroutine[Any, Any, httpx.Response]]],
|
||||
allow_failed: bool = False,
|
||||
concurrency_level: int = 10,
|
||||
) -> list[tuple[int, httpx.Response]]:
|
||||
"""Run a list of coroutines in parallel and return the results in order.
|
||||
|
||||
Args:
|
||||
coroutines (list[Callable[[Coroutine], Awaitable]): A list of fuctions
|
||||
parametrized with async_client that return Awaitable objects.
|
||||
allow_failed (bool, optional): If True, failed responses will be included
|
||||
in the results. Otherwise, the first failed request breaks the
|
||||
process. Defaults to False.
|
||||
"""
|
||||
|
||||
|
||||
# Use a variable to adjust the httpx client timeout, or default to 30 minutes
|
||||
# When we're able to reuse the SDK to make these calls, we can remove this var
|
||||
# The SDK timeout will be controlled by parameter
|
||||
limiter = asyncio.Semaphore(concurrency_level)
|
||||
client_timeout_minutes = 60
|
||||
if timeout_var := os.getenv("UNSTRUCTURED_CLIENT_TIMEOUT_MINUTES"):
|
||||
client_timeout_minutes = int(timeout_var)
|
||||
client_timeout = httpx.Timeout(60 * client_timeout_minutes)
|
||||
|
||||
async with httpx.AsyncClient(timeout=client_timeout) as client:
|
||||
armed_coroutines = [coro(async_client=client, limiter=limiter) for coro in coroutines] # type: ignore
|
||||
if allow_failed:
|
||||
responses = await asyncio.gather(*armed_coroutines, return_exceptions=False)
|
||||
return list(enumerate(responses, 1))
|
||||
# TODO: replace with asyncio.TaskGroup for python >3.11 # pylint: disable=fixme
|
||||
tasks = [asyncio.create_task(_order_keeper(index, coro))
|
||||
for index, coro in enumerate(armed_coroutines, 1)]
|
||||
results = []
|
||||
remaining_tasks = dict(enumerate(tasks, 1))
|
||||
for future in asyncio.as_completed(tasks):
|
||||
index, response = await future
|
||||
if response.status_code != 200:
|
||||
# cancel all remaining tasks
|
||||
for remaining_task in remaining_tasks.values():
|
||||
remaining_task.cancel()
|
||||
results.append((index, response))
|
||||
break
|
||||
results.append((index, response))
|
||||
# remove task from remaining_tasks that should be cancelled in case of failure
|
||||
del remaining_tasks[index]
|
||||
# return results in the original order
|
||||
return sorted(results, key=lambda x: x[0])
|
||||
|
||||
|
||||
def get_optimal_split_size(num_pages: int, concurrency_level: int) -> int:
|
||||
"""Distributes pages to workers evenly based on the number of pages and desired concurrency level."""
|
||||
if num_pages < MAX_PAGES_PER_SPLIT * concurrency_level:
|
||||
split_size = math.ceil(num_pages / concurrency_level)
|
||||
else:
|
||||
split_size = MAX_PAGES_PER_SPLIT
|
||||
|
||||
return max(split_size, MIN_PAGES_PER_SPLIT)
|
||||
|
||||
|
||||
def load_elements_from_response(response: httpx.Response) -> list[dict]:
|
||||
"""Loads elements from the response content - the response was modified
|
||||
to keep the path for the json file that should be loaded and returned
|
||||
|
||||
Args:
|
||||
response (httpx.Response): The response object, which contains the path
|
||||
to the json file that should be loaded.
|
||||
|
||||
Returns:
|
||||
list[dict]: The elements loaded from the response content cached in the json file.
|
||||
"""
|
||||
with open(response.text, mode="r", encoding="utf-8") as file:
|
||||
return json.load(file)
|
||||
|
||||
|
||||
class SplitPdfHook(SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorHook):
|
||||
"""
|
||||
A hook class that splits a PDF file into multiple pages and sends each page as
|
||||
a separate request. This hook is designed to be used with an Speakeasy SDK.
|
||||
|
||||
Usage:
|
||||
1. Create an instance of the `SplitPdfHook` class.
|
||||
2. Register SDK Init, Before Request, After Success and After Error hooks.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.client: Optional[HttpClient] = None
|
||||
self.partition_base_url: Optional[str] = None
|
||||
self.is_partition_request: bool = False
|
||||
self.async_client: Optional[AsyncHttpClient] = None
|
||||
self.coroutines_to_execute: dict[
|
||||
str, list[partial[Coroutine[Any, Any, httpx.Response]]]
|
||||
] = {}
|
||||
self.concurrency_level: dict[str, int] = {}
|
||||
self.api_successful_responses: dict[str, list[httpx.Response]] = {}
|
||||
self.api_failed_responses: dict[str, list[httpx.Response]] = {}
|
||||
self.executors: dict[str, futures.ThreadPoolExecutor] = {}
|
||||
self.tempdirs: dict[str, tempfile.TemporaryDirectory] = {}
|
||||
self.allow_failed: bool = DEFAULT_ALLOW_FAILED
|
||||
self.cache_tmp_data_feature: bool = DEFAULT_CACHE_TMP_DATA
|
||||
self.cache_tmp_data_dir: str = DEFAULT_CACHE_TMP_DATA_DIR
|
||||
|
||||
def sdk_init(
|
||||
self, base_url: str, client: HttpClient
|
||||
) -> Tuple[str, HttpClient]:
|
||||
"""Initializes Split PDF Hook.
|
||||
|
||||
Adds a mock transport layer to the httpx client. This will return an
|
||||
empty 200 response whenever the specified "dummy host" is used. The before_request
|
||||
hook returns this request so the SDK always succeeds and jumps straight to
|
||||
after_success, where we can await the split results.
|
||||
|
||||
Args:
|
||||
base_url (str): URL of the API.
|
||||
client (HttpClient): HTTP Client.
|
||||
|
||||
Returns:
|
||||
Tuple[str, HttpClient]: The initialized SDK options.
|
||||
"""
|
||||
class DummyTransport(httpx.BaseTransport):
|
||||
def __init__(self, base_transport: httpx.BaseTransport):
|
||||
self.base_transport = base_transport
|
||||
|
||||
def handle_request(self, request: httpx.Request) -> httpx.Response:
|
||||
# Return an empty 200 response if we send a request to this dummy host
|
||||
if request.method == "GET" and request.url.host == "no-op":
|
||||
return httpx.Response(status_code=200, content=b'')
|
||||
|
||||
# Otherwise, pass the request to the default transport
|
||||
return self.base_transport.handle_request(request)
|
||||
|
||||
# Note(austin) - This hook doesn't have access to the async_client
|
||||
# So, we can't do the same no-op trick for partition_async
|
||||
# class AsyncDummyTransport(httpx.AsyncBaseTransport):
|
||||
# def __init__(self, base_transport: httpx.AsyncBaseTransport):
|
||||
# self.base_transport = base_transport
|
||||
|
||||
# async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
|
||||
# # Return an empty 200 response if we send a request to this dummy host
|
||||
# if request.method == "GET" and request.url.host == "no-op":
|
||||
# return httpx.Response(status_code=200, content=b'')
|
||||
|
||||
# # Otherwise, pass the request to the default transport
|
||||
# return await self.base_transport.handle_async_request(request)
|
||||
|
||||
# Instead, save the base url so we can use it for our dummy request
|
||||
# As this can be overwritten with Platform API URL, we need to get it again in
|
||||
# `before_request` hook from the request object as the real URL is not available here.
|
||||
self.partition_base_url = base_url
|
||||
|
||||
# Explicit cast to httpx.Client to avoid a typing error
|
||||
httpx_client = cast(httpx.Client, client)
|
||||
# async_httpx_client = cast(httpx.AsyncClient, async_client)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
httpx_client._transport = DummyTransport(httpx_client._transport)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
# async_httpx_client._transport = AsyncDummyTransport(async_httpx_client._transport)
|
||||
|
||||
self.client = httpx_client
|
||||
return base_url, self.client
|
||||
|
||||
# pylint: disable=too-many-return-statements
|
||||
def before_request(
|
||||
self, hook_ctx: BeforeRequestContext, request: httpx.Request
|
||||
) -> Union[httpx.Request, Exception]:
|
||||
"""If `splitPdfPage` is set to `true` in the request, the PDF file is split into
|
||||
separate pages. Each page is sent as a separate request in parallel. The last
|
||||
page request is returned by this method. It will return the original request
|
||||
when: `splitPdfPage` is set to `false`, the file is not a PDF, or the HTTP
|
||||
has not been initialized.
|
||||
|
||||
Args:
|
||||
hook_ctx (BeforeRequestContext): The hook context containing information about
|
||||
the operation.
|
||||
request (httpx.PreparedRequest): The request object.
|
||||
|
||||
Returns:
|
||||
Union[httpx.PreparedRequest, Exception]: If `splitPdfPage` is set to `true`,
|
||||
the last page request; otherwise, the original request.
|
||||
"""
|
||||
|
||||
# Actually the general.partition operation overwrites the default client's base url (as
|
||||
# the platform operations do). Here we need to get the base url from the request object.
|
||||
if hook_ctx.operation_id == "partition":
|
||||
self.partition_base_url = get_base_url(request.url)
|
||||
self.is_partition_request = True
|
||||
else:
|
||||
self.is_partition_request = False
|
||||
return request
|
||||
|
||||
if self.client is None:
|
||||
logger.warning("HTTP client not accessible! Continuing without splitting.")
|
||||
return request
|
||||
|
||||
# This is our key into coroutines_to_execute
|
||||
# We need to pass it on to after_success so
|
||||
# we know which results are ours
|
||||
operation_id = str(uuid.uuid4())
|
||||
|
||||
content_type = request.headers.get("Content-Type")
|
||||
if content_type is None:
|
||||
return request
|
||||
|
||||
form_data = request_utils.get_multipart_stream_fields(request)
|
||||
if not form_data:
|
||||
return request
|
||||
|
||||
split_pdf_page = form_data.get(PARTITION_FORM_SPLIT_PDF_PAGE_KEY)
|
||||
if split_pdf_page is None or split_pdf_page == "false":
|
||||
return request
|
||||
|
||||
pdf_file_meta = form_data.get(PARTITION_FORM_FILES_KEY)
|
||||
if (
|
||||
pdf_file_meta is None or not all(metadata in pdf_file_meta for metadata in
|
||||
["filename", "content_type", "file"])
|
||||
):
|
||||
return request
|
||||
pdf_file = pdf_file_meta.get("file")
|
||||
if pdf_file is None:
|
||||
return request
|
||||
|
||||
pdf = pdf_utils.read_pdf(pdf_file)
|
||||
if pdf is None:
|
||||
return request
|
||||
|
||||
pdf = pdf_utils.check_pdf(pdf)
|
||||
|
||||
starting_page_number = form_utils.get_starting_page_number(
|
||||
form_data,
|
||||
key=PARTITION_FORM_STARTING_PAGE_NUMBER_KEY,
|
||||
fallback_value=DEFAULT_STARTING_PAGE_NUMBER,
|
||||
)
|
||||
|
||||
self.allow_failed = form_utils.get_split_pdf_allow_failed_param(
|
||||
form_data,
|
||||
key=PARTITION_FORM_SPLIT_PDF_ALLOW_FAILED_KEY,
|
||||
fallback_value=DEFAULT_ALLOW_FAILED,
|
||||
)
|
||||
|
||||
self.concurrency_level[operation_id] = form_utils.get_split_pdf_concurrency_level_param(
|
||||
form_data,
|
||||
key=PARTITION_FORM_CONCURRENCY_LEVEL_KEY,
|
||||
fallback_value=DEFAULT_CONCURRENCY_LEVEL,
|
||||
max_allowed=MAX_CONCURRENCY_LEVEL,
|
||||
)
|
||||
|
||||
executor = futures.ThreadPoolExecutor(max_workers=1)
|
||||
self.executors[operation_id] = executor
|
||||
|
||||
self.cache_tmp_data_feature = form_utils.get_split_pdf_cache_tmp_data(
|
||||
form_data,
|
||||
key=PARTITION_FORM_SPLIT_CACHE_TMP_DATA_KEY,
|
||||
fallback_value=DEFAULT_CACHE_TMP_DATA,
|
||||
)
|
||||
|
||||
self.cache_tmp_data_dir = form_utils.get_split_pdf_cache_tmp_data_dir(
|
||||
form_data,
|
||||
key=PARTITION_FORM_SPLIT_CACHE_TMP_DATA_DIR_KEY,
|
||||
fallback_value=DEFAULT_CACHE_TMP_DATA_DIR,
|
||||
)
|
||||
|
||||
page_range_start, page_range_end = form_utils.get_page_range(
|
||||
form_data,
|
||||
key=PARTITION_FORM_PAGE_RANGE_KEY.replace("[]", ""),
|
||||
max_pages=pdf.get_num_pages(),
|
||||
)
|
||||
|
||||
page_count = page_range_end - page_range_start + 1
|
||||
|
||||
split_size = get_optimal_split_size(
|
||||
num_pages=page_count, concurrency_level=self.concurrency_level[operation_id]
|
||||
)
|
||||
|
||||
# If the doc is small enough, and we aren't slicing it with a page range:
|
||||
# do not split, just continue with the original request
|
||||
if split_size >= page_count and page_count == len(pdf.pages):
|
||||
return request
|
||||
|
||||
pdf = self._trim_large_pages(pdf, form_data)
|
||||
|
||||
if self.cache_tmp_data_feature:
|
||||
pdf_chunk_paths = self._get_pdf_chunk_paths(
|
||||
pdf,
|
||||
operation_id=operation_id,
|
||||
split_size=split_size,
|
||||
page_start=page_range_start,
|
||||
page_end=page_range_end
|
||||
)
|
||||
# force free PDF object memory
|
||||
del pdf
|
||||
pdf_chunks = self._get_pdf_chunk_files(pdf_chunk_paths)
|
||||
else:
|
||||
pdf_chunks = self._get_pdf_chunks_in_memory(
|
||||
pdf,
|
||||
split_size=split_size,
|
||||
page_start=page_range_start,
|
||||
page_end=page_range_end
|
||||
)
|
||||
|
||||
self.coroutines_to_execute[operation_id] = []
|
||||
set_index = 1
|
||||
for pdf_chunk_file, page_index in pdf_chunks:
|
||||
page_number = page_index + starting_page_number
|
||||
pdf_chunk_request = request_utils.create_pdf_chunk_request(
|
||||
form_data=form_data,
|
||||
pdf_chunk=(pdf_chunk_file, page_number),
|
||||
filename=pdf_file_meta["filename"],
|
||||
original_request=request,
|
||||
)
|
||||
# using partial as the shared client parameter must be passed in `run_tasks` function
|
||||
# in `after_success`.
|
||||
coroutine = partial(
|
||||
self.call_api_partial,
|
||||
operation_id=operation_id,
|
||||
pdf_chunk_request=pdf_chunk_request,
|
||||
pdf_chunk_file=pdf_chunk_file,
|
||||
)
|
||||
self.coroutines_to_execute[operation_id].append(coroutine)
|
||||
set_index += 1
|
||||
|
||||
# Return a dummy request for the SDK to use
|
||||
# This allows us to skip right to the AfterRequestHook and await all the calls
|
||||
# Also, pass the operation_id so after_success can await the right results
|
||||
|
||||
# Note: We need access to the async_client from the sdk_init hook in order to set
|
||||
# up a mock request like this.
|
||||
# For now, just make an extra request against our api, which should return 200.
|
||||
# dummy_request = httpx.Request("GET", "http://no-op")
|
||||
return httpx.Request(
|
||||
"GET",
|
||||
f"{self.partition_base_url}/general/docs",
|
||||
headers={"operation_id": operation_id},
|
||||
)
|
||||
|
||||
async def call_api_partial(
|
||||
self,
|
||||
pdf_chunk_request: httpx.Request,
|
||||
pdf_chunk_file: BinaryIO,
|
||||
limiter: asyncio.Semaphore,
|
||||
operation_id: str,
|
||||
async_client: AsyncClient,
|
||||
) -> httpx.Response:
|
||||
response = await request_utils.call_api_async(
|
||||
client=async_client,
|
||||
limiter=limiter,
|
||||
pdf_chunk_request=pdf_chunk_request,
|
||||
pdf_chunk_file=pdf_chunk_file,
|
||||
)
|
||||
|
||||
# Immediately delete request to save memory
|
||||
del response._request # pylint: disable=protected-access
|
||||
response._request = None # pylint: disable=protected-access
|
||||
|
||||
|
||||
if response.status_code == 200:
|
||||
if self.cache_tmp_data_feature:
|
||||
# If we get 200, dump the contents to a file and return the path
|
||||
temp_dir = self.tempdirs[operation_id]
|
||||
temp_file_name = f"{temp_dir.name}/{uuid.uuid4()}.json"
|
||||
async with aiofiles.open(temp_file_name, mode='wb') as temp_file:
|
||||
# Avoid reading the entire response into memory
|
||||
async for bytes_chunk in response.aiter_bytes():
|
||||
await temp_file.write(bytes_chunk)
|
||||
# we save the path in content attribute to be used in after_success
|
||||
response._content = temp_file_name.encode() # pylint: disable=protected-access
|
||||
|
||||
return response
|
||||
|
||||
def _trim_large_pages(self, pdf: PdfReader, form_data: dict[str, Any]) -> PdfReader:
|
||||
if form_data['strategy'] != HI_RES_STRATEGY:
|
||||
return pdf
|
||||
|
||||
max_page_length = MAX_PAGE_LENGTH
|
||||
any_page_over_maximum_length = False
|
||||
for page in pdf.pages:
|
||||
if page.mediabox.height >= max_page_length:
|
||||
any_page_over_maximum_length = True
|
||||
|
||||
# early exit if all pages are safely under the max page length
|
||||
if not any_page_over_maximum_length:
|
||||
return pdf
|
||||
|
||||
w = PdfWriter()
|
||||
|
||||
# trims large pages that exceed the maximum supported height for processing
|
||||
for page in pdf.pages:
|
||||
if page.mediabox.height >= max_page_length:
|
||||
page.mediabox.top = page.mediabox.height
|
||||
page.mediabox.bottom = page.mediabox.top - max_page_length
|
||||
w.add_page(page)
|
||||
|
||||
chunk_buffer = io.BytesIO()
|
||||
w.write(chunk_buffer)
|
||||
chunk_buffer.seek(0)
|
||||
return PdfReader(chunk_buffer)
|
||||
|
||||
def _get_pdf_chunks_in_memory(
|
||||
self,
|
||||
pdf: PdfReader,
|
||||
split_size: int = 1,
|
||||
page_start: int = 1,
|
||||
page_end: Optional[int] = None
|
||||
) -> Generator[Tuple[BinaryIO, int], None, None]:
|
||||
"""Reads given bytes of a pdf file and split it into n pdf-chunks, each
|
||||
with `split_size` pages. The chunks are written into temporary files in
|
||||
a temporary directory corresponding to the operation_id.
|
||||
|
||||
Args:
|
||||
file_content: Content of the PDF file.
|
||||
split_size: Split size, e.g. if the given file has 10 pages
|
||||
and this value is set to 2 it will yield 5 documents, each containing 2 pages
|
||||
of the original document. By default it will split each page to a separate file.
|
||||
page_start: Begin splitting at this page number
|
||||
page_end: If provided, split up to and including this page number
|
||||
|
||||
Returns:
|
||||
The list of temporary file paths.
|
||||
"""
|
||||
|
||||
offset = page_start - 1
|
||||
offset_end = page_end or len(pdf.pages)
|
||||
|
||||
chunk_no = 0
|
||||
while offset < offset_end:
|
||||
chunk_no += 1
|
||||
new_pdf = PdfWriter()
|
||||
chunk_buffer = io.BytesIO()
|
||||
|
||||
end = min(offset + split_size, offset_end)
|
||||
|
||||
for page in list(pdf.pages[offset:end]):
|
||||
new_pdf.add_page(page)
|
||||
new_pdf.write(chunk_buffer)
|
||||
chunk_buffer.seek(0)
|
||||
yield chunk_buffer, offset
|
||||
offset += split_size
|
||||
|
||||
def _get_pdf_chunk_paths(
|
||||
self,
|
||||
pdf: PdfReader,
|
||||
operation_id: str,
|
||||
split_size: int = 1,
|
||||
page_start: int = 1,
|
||||
page_end: Optional[int] = None
|
||||
) -> list[Tuple[Path, int]]:
|
||||
"""Reads given bytes of a pdf file and split it into n pdf-chunks, each
|
||||
with `split_size` pages. The chunks are written into temporary files in
|
||||
a temporary directory corresponding to the operation_id.
|
||||
|
||||
Args:
|
||||
file_content: Content of the PDF file.
|
||||
split_size: Split size, e.g. if the given file has 10 pages
|
||||
and this value is set to 2 it will yield 5 documents, each containing 2 pages
|
||||
of the original document. By default it will split each page to a separate file.
|
||||
page_start: Begin splitting at this page number
|
||||
page_end: If provided, split up to and including this page number
|
||||
|
||||
Returns:
|
||||
The list of temporary file paths.
|
||||
"""
|
||||
|
||||
offset = page_start - 1
|
||||
offset_end = page_end or len(pdf.pages)
|
||||
|
||||
tempdir = tempfile.TemporaryDirectory( # pylint: disable=consider-using-with
|
||||
dir=self.cache_tmp_data_dir,
|
||||
prefix="unstructured_client_"
|
||||
)
|
||||
self.tempdirs[operation_id] = tempdir
|
||||
tempdir_path = Path(tempdir.name)
|
||||
pdf_chunk_paths: list[Tuple[Path, int]] = []
|
||||
chunk_no = 0
|
||||
while offset < offset_end:
|
||||
chunk_no += 1
|
||||
new_pdf = PdfWriter()
|
||||
|
||||
end = min(offset + split_size, offset_end)
|
||||
|
||||
for page in list(pdf.pages[offset:end]):
|
||||
new_pdf.add_page(page)
|
||||
with open(tempdir_path / f"chunk_{chunk_no}.pdf", "wb") as pdf_chunk:
|
||||
new_pdf.write(pdf_chunk)
|
||||
pdf_chunk_paths.append((Path(pdf_chunk.name), offset))
|
||||
offset += split_size
|
||||
return pdf_chunk_paths
|
||||
|
||||
def _get_pdf_chunk_files(
|
||||
self, pdf_chunks: list[Tuple[Path, int]]
|
||||
) -> Generator[Tuple[BinaryIO, int], None, None]:
|
||||
"""Yields the file objects for the given pdf chunk paths.
|
||||
|
||||
Args:
|
||||
pdf_chunks (list[Tuple[Path, int]]): The list of pdf chunk paths and
|
||||
their page offsets.
|
||||
|
||||
Yields:
|
||||
Tuple[BinaryIO, int]: The file object and the page offset.
|
||||
|
||||
Raises:
|
||||
Exception: If the file cannot be opened.
|
||||
"""
|
||||
for pdf_chunk_filename, offset in pdf_chunks:
|
||||
pdf_chunk_file = None
|
||||
try:
|
||||
pdf_chunk_file = open( # pylint: disable=consider-using-with
|
||||
pdf_chunk_filename,
|
||||
mode="rb"
|
||||
)
|
||||
except (FileNotFoundError, IOError):
|
||||
if pdf_chunk_file and not pdf_chunk_file.closed:
|
||||
pdf_chunk_file.close()
|
||||
raise
|
||||
yield pdf_chunk_file, offset
|
||||
|
||||
def _await_elements(self, operation_id: str) -> Optional[list]:
|
||||
"""
|
||||
Waits for the partition requests to complete and returns the flattened
|
||||
elements.
|
||||
|
||||
Args:
|
||||
operation_id (str): The ID of the operation.
|
||||
|
||||
Returns:
|
||||
Optional[list]: The flattened elements if the partition requests are
|
||||
completed, otherwise None.
|
||||
"""
|
||||
tasks = self.coroutines_to_execute.get(operation_id)
|
||||
if tasks is None:
|
||||
return None
|
||||
|
||||
concurrency_level = self.concurrency_level.get(operation_id, DEFAULT_CONCURRENCY_LEVEL)
|
||||
coroutines = run_tasks(tasks, allow_failed=self.allow_failed, concurrency_level=concurrency_level)
|
||||
|
||||
# sending the coroutines to a separate thread to avoid blocking the current event loop
|
||||
# this operation should be removed when the SDK is updated to support async hooks
|
||||
executor = self.executors.get(operation_id)
|
||||
if executor is None:
|
||||
raise RuntimeError("Executor not found for operation_id")
|
||||
task_responses_future = executor.submit(_run_coroutines_in_separate_thread, coroutines)
|
||||
task_responses = task_responses_future.result()
|
||||
|
||||
if task_responses is None:
|
||||
return None
|
||||
|
||||
successful_responses = []
|
||||
failed_responses = []
|
||||
elements = []
|
||||
for response_number, res in task_responses:
|
||||
if res.status_code == 200:
|
||||
logger.debug(
|
||||
"Successfully partitioned set #%d, elements added to the final result.",
|
||||
response_number,
|
||||
)
|
||||
successful_responses.append(res)
|
||||
if self.cache_tmp_data_feature:
|
||||
elements.append(load_elements_from_response(res))
|
||||
else:
|
||||
elements.append(res.json())
|
||||
else:
|
||||
error_message = f"Failed to partition set {response_number}."
|
||||
|
||||
if self.allow_failed:
|
||||
error_message += " Its elements will be omitted from the result."
|
||||
|
||||
logger.error(error_message)
|
||||
failed_responses.append(res)
|
||||
|
||||
self.api_successful_responses[operation_id] = successful_responses
|
||||
self.api_failed_responses[operation_id] = failed_responses
|
||||
flattened_elements = [element for sublist in elements for element in sublist]
|
||||
return flattened_elements
|
||||
|
||||
def after_success(
|
||||
self, hook_ctx: AfterSuccessContext, response: httpx.Response
|
||||
) -> Union[httpx.Response, Exception]:
|
||||
"""Executes after a successful API request. Awaits all parallel requests and
|
||||
combines the responses into a single response object.
|
||||
|
||||
Args:
|
||||
hook_ctx (AfterSuccessContext): The context object containing information
|
||||
about the hook execution.
|
||||
response (httpx.Response): The response object from the SDK call. This was a dummy
|
||||
request just to get us to the AfterSuccessHook.
|
||||
|
||||
Returns:
|
||||
Union[httpx.Response, Exception]: If requests were run in parallel, a
|
||||
combined response object; otherwise, the original response. Can return
|
||||
exception if it ocurred during the execution.
|
||||
"""
|
||||
if not self.is_partition_request:
|
||||
return response
|
||||
|
||||
# Grab the correct id out of the dummy request
|
||||
operation_id = response.request.headers.get("operation_id")
|
||||
|
||||
elements = self._await_elements(operation_id)
|
||||
|
||||
# if fails are disallowed, return the first failed response
|
||||
if not self.allow_failed and self.api_failed_responses.get(operation_id):
|
||||
failure_response = self.api_failed_responses[operation_id][0]
|
||||
|
||||
self._clear_operation(operation_id)
|
||||
return failure_response
|
||||
|
||||
if elements is None:
|
||||
return response
|
||||
|
||||
new_response = request_utils.create_response(elements)
|
||||
self._clear_operation(operation_id)
|
||||
|
||||
return new_response
|
||||
|
||||
def after_error(
|
||||
self,
|
||||
hook_ctx: AfterErrorContext,
|
||||
response: Optional[httpx.Response],
|
||||
error: Optional[Exception],
|
||||
) -> Union[Tuple[Optional[httpx.Response], Optional[Exception]], Exception]:
|
||||
"""This hook is unused. In the before hook, we return a mock request
|
||||
for the SDK to run. This will take us right to the after_success hook
|
||||
to await the split results.
|
||||
|
||||
Args:
|
||||
hook_ctx (AfterErrorContext): The AfterErrorContext object containing
|
||||
information about the hook context.
|
||||
response (Optional[httpx.Response]): The Response object representing
|
||||
the response received before the exception occurred.
|
||||
error (Optional[Exception]): The exception object that was thrown.
|
||||
|
||||
Returns:
|
||||
Union[Tuple[Optional[httpx.Response], Optional[Exception]], Exception]:
|
||||
"""
|
||||
return (response, error)
|
||||
|
||||
def _clear_operation(self, operation_id: str) -> None:
|
||||
"""
|
||||
Clears the operation data associated with the given operation ID.
|
||||
|
||||
Args:
|
||||
operation_id (str): The ID of the operation to clear.
|
||||
"""
|
||||
self.coroutines_to_execute.pop(operation_id, None)
|
||||
self.api_successful_responses.pop(operation_id, None)
|
||||
self.concurrency_level.pop(operation_id, None)
|
||||
executor = self.executors.pop(operation_id, None)
|
||||
if executor is not None:
|
||||
executor.shutdown(wait=True)
|
||||
tempdir = self.tempdirs.pop(operation_id, None)
|
||||
if tempdir:
|
||||
tempdir.cleanup()
|
||||
@@ -0,0 +1,16 @@
|
||||
"""File validation error classes for the Unstructured client."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class FileValidationError(Exception):
|
||||
"""Base exception for file validation errors.
|
||||
|
||||
This exception should be raised when a file fails validation
|
||||
checks before being processed by the API.
|
||||
"""
|
||||
|
||||
def __init__(self, message: str, file_type: Optional[str] = None):
|
||||
self.message: str = message
|
||||
self.file_type: Optional[str] = file_type
|
||||
super().__init__(self.message)
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Registration of custom, human-written hooks."""
|
||||
|
||||
from .custom import (
|
||||
CleanServerUrlSDKInitHook,
|
||||
LoggerHook,
|
||||
SplitPdfHook,
|
||||
)
|
||||
from .types import Hooks
|
||||
|
||||
|
||||
# This file is only ever generated once on the first generation and then is free to be modified.
|
||||
# Any hooks you wish to add should be registered in the init_hooks function. Feel free to define
|
||||
# them in this file or in separate files in the hooks folder.
|
||||
|
||||
|
||||
def init_hooks(hooks: Hooks):
|
||||
# pylint: disable=unused-argument
|
||||
"""Add hooks by calling `hooks.register_<type_or>_hook` with an instance of that hook.
|
||||
|
||||
Hooks are registered per SDK instance, and are valid for the lifetime of the SDK instance
|
||||
"""
|
||||
|
||||
# Initialize custom hooks
|
||||
clean_server_url_hook = CleanServerUrlSDKInitHook()
|
||||
logger_hook = LoggerHook()
|
||||
split_pdf_hook = SplitPdfHook()
|
||||
|
||||
# NOTE: logger_hook should stay registered last as logs the status of
|
||||
# request and whether it will be retried which can be changed by e.g. split_pdf_hook
|
||||
|
||||
# Register SDK Init hooks
|
||||
hooks.register_sdk_init_hook(clean_server_url_hook)
|
||||
hooks.register_sdk_init_hook(logger_hook)
|
||||
hooks.register_sdk_init_hook(split_pdf_hook)
|
||||
|
||||
# Register Before Request hooks
|
||||
hooks.register_before_request_hook(split_pdf_hook)
|
||||
|
||||
# Register After Error hooks
|
||||
hooks.register_after_success_hook(split_pdf_hook)
|
||||
hooks.register_after_success_hook(logger_hook)
|
||||
|
||||
# Register After Error hooks
|
||||
hooks.register_after_error_hook(split_pdf_hook)
|
||||
hooks.register_after_error_hook(logger_hook)
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
import httpx
|
||||
from .types import (
|
||||
SDKInitHook,
|
||||
BeforeRequestContext,
|
||||
BeforeRequestHook,
|
||||
AfterSuccessContext,
|
||||
AfterSuccessHook,
|
||||
AfterErrorContext,
|
||||
AfterErrorHook,
|
||||
Hooks,
|
||||
)
|
||||
from .registration import init_hooks
|
||||
from typing import List, Optional, Tuple
|
||||
from unstructured_client.httpclient import HttpClient
|
||||
|
||||
|
||||
class SDKHooks(Hooks):
|
||||
def __init__(self) -> None:
|
||||
self.sdk_init_hooks: List[SDKInitHook] = []
|
||||
self.before_request_hooks: List[BeforeRequestHook] = []
|
||||
self.after_success_hooks: List[AfterSuccessHook] = []
|
||||
self.after_error_hooks: List[AfterErrorHook] = []
|
||||
init_hooks(self)
|
||||
|
||||
def register_sdk_init_hook(self, hook: SDKInitHook) -> None:
|
||||
self.sdk_init_hooks.append(hook)
|
||||
|
||||
def register_before_request_hook(self, hook: BeforeRequestHook) -> None:
|
||||
self.before_request_hooks.append(hook)
|
||||
|
||||
def register_after_success_hook(self, hook: AfterSuccessHook) -> None:
|
||||
self.after_success_hooks.append(hook)
|
||||
|
||||
def register_after_error_hook(self, hook: AfterErrorHook) -> None:
|
||||
self.after_error_hooks.append(hook)
|
||||
|
||||
def sdk_init(self, base_url: str, client: HttpClient) -> Tuple[str, HttpClient]:
|
||||
for hook in self.sdk_init_hooks:
|
||||
base_url, client = hook.sdk_init(base_url, client)
|
||||
return base_url, client
|
||||
|
||||
def before_request(
|
||||
self, hook_ctx: BeforeRequestContext, request: httpx.Request
|
||||
) -> httpx.Request:
|
||||
for hook in self.before_request_hooks:
|
||||
out = hook.before_request(hook_ctx, request)
|
||||
if isinstance(out, Exception):
|
||||
raise out
|
||||
request = out
|
||||
|
||||
return request
|
||||
|
||||
def after_success(
|
||||
self, hook_ctx: AfterSuccessContext, response: httpx.Response
|
||||
) -> httpx.Response:
|
||||
for hook in self.after_success_hooks:
|
||||
out = hook.after_success(hook_ctx, response)
|
||||
if isinstance(out, Exception):
|
||||
raise out
|
||||
response = out
|
||||
return response
|
||||
|
||||
def after_error(
|
||||
self,
|
||||
hook_ctx: AfterErrorContext,
|
||||
response: Optional[httpx.Response],
|
||||
error: Optional[Exception],
|
||||
) -> Tuple[Optional[httpx.Response], Optional[Exception]]:
|
||||
for hook in self.after_error_hooks:
|
||||
result = hook.after_error(hook_ctx, response, error)
|
||||
if isinstance(result, Exception):
|
||||
raise result
|
||||
response, error = result
|
||||
return response, error
|
||||
@@ -0,0 +1,113 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import httpx
|
||||
from typing import Any, Callable, List, Optional, Tuple, Union
|
||||
from unstructured_client.httpclient import HttpClient
|
||||
from unstructured_client.sdkconfiguration import SDKConfiguration
|
||||
|
||||
|
||||
class HookContext:
|
||||
config: SDKConfiguration
|
||||
base_url: str
|
||||
operation_id: str
|
||||
oauth2_scopes: Optional[List[str]] = None
|
||||
security_source: Optional[Union[Any, Callable[[], Any]]] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: SDKConfiguration,
|
||||
base_url: str,
|
||||
operation_id: str,
|
||||
oauth2_scopes: Optional[List[str]],
|
||||
security_source: Optional[Union[Any, Callable[[], Any]]],
|
||||
):
|
||||
self.config = config
|
||||
self.base_url = base_url
|
||||
self.operation_id = operation_id
|
||||
self.oauth2_scopes = oauth2_scopes
|
||||
self.security_source = security_source
|
||||
|
||||
|
||||
class BeforeRequestContext(HookContext):
|
||||
def __init__(self, hook_ctx: HookContext):
|
||||
super().__init__(
|
||||
hook_ctx.config,
|
||||
hook_ctx.base_url,
|
||||
hook_ctx.operation_id,
|
||||
hook_ctx.oauth2_scopes,
|
||||
hook_ctx.security_source,
|
||||
)
|
||||
|
||||
|
||||
class AfterSuccessContext(HookContext):
|
||||
def __init__(self, hook_ctx: HookContext):
|
||||
super().__init__(
|
||||
hook_ctx.config,
|
||||
hook_ctx.base_url,
|
||||
hook_ctx.operation_id,
|
||||
hook_ctx.oauth2_scopes,
|
||||
hook_ctx.security_source,
|
||||
)
|
||||
|
||||
|
||||
class AfterErrorContext(HookContext):
|
||||
def __init__(self, hook_ctx: HookContext):
|
||||
super().__init__(
|
||||
hook_ctx.config,
|
||||
hook_ctx.base_url,
|
||||
hook_ctx.operation_id,
|
||||
hook_ctx.oauth2_scopes,
|
||||
hook_ctx.security_source,
|
||||
)
|
||||
|
||||
|
||||
class SDKInitHook(ABC):
|
||||
@abstractmethod
|
||||
def sdk_init(self, base_url: str, client: HttpClient) -> Tuple[str, HttpClient]:
|
||||
pass
|
||||
|
||||
|
||||
class BeforeRequestHook(ABC):
|
||||
@abstractmethod
|
||||
def before_request(
|
||||
self, hook_ctx: BeforeRequestContext, request: httpx.Request
|
||||
) -> Union[httpx.Request, Exception]:
|
||||
pass
|
||||
|
||||
|
||||
class AfterSuccessHook(ABC):
|
||||
@abstractmethod
|
||||
def after_success(
|
||||
self, hook_ctx: AfterSuccessContext, response: httpx.Response
|
||||
) -> Union[httpx.Response, Exception]:
|
||||
pass
|
||||
|
||||
|
||||
class AfterErrorHook(ABC):
|
||||
@abstractmethod
|
||||
def after_error(
|
||||
self,
|
||||
hook_ctx: AfterErrorContext,
|
||||
response: Optional[httpx.Response],
|
||||
error: Optional[Exception],
|
||||
) -> Union[Tuple[Optional[httpx.Response], Optional[Exception]], Exception]:
|
||||
pass
|
||||
|
||||
|
||||
class Hooks(ABC):
|
||||
@abstractmethod
|
||||
def register_sdk_init_hook(self, hook: SDKInitHook):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def register_before_request_hook(self, hook: BeforeRequestHook):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def register_after_success_hook(self, hook: AfterSuccessHook):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def register_after_error_hook(self, hook: AfterErrorHook):
|
||||
pass
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
import importlib.metadata
|
||||
|
||||
__title__: str = "unstructured-client"
|
||||
__version__: str = "0.42.8"
|
||||
__openapi_doc_version__: str = "1.2.23"
|
||||
__gen_version__: str = "2.680.0"
|
||||
__user_agent__: str = "speakeasy-sdk/python 0.42.8 2.680.0 1.2.23 unstructured-client"
|
||||
|
||||
try:
|
||||
if __package__ is not None:
|
||||
__version__ = importlib.metadata.version(__package__)
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
pass
|
||||
@@ -0,0 +1,359 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from .sdkconfiguration import SDKConfiguration
|
||||
import httpx
|
||||
from typing import Callable, List, Mapping, Optional, Tuple
|
||||
from unstructured_client import utils
|
||||
from unstructured_client._hooks import (
|
||||
AfterErrorContext,
|
||||
AfterSuccessContext,
|
||||
BeforeRequestContext,
|
||||
)
|
||||
from unstructured_client.models import errors
|
||||
from unstructured_client.utils import (
|
||||
RetryConfig,
|
||||
SerializedRequestBody,
|
||||
get_body_content,
|
||||
)
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
|
||||
class BaseSDK:
|
||||
sdk_configuration: SDKConfiguration
|
||||
|
||||
def __init__(self, sdk_config: SDKConfiguration) -> None:
|
||||
self.sdk_configuration = sdk_config
|
||||
|
||||
def _get_url(self, base_url, url_variables):
|
||||
sdk_url, sdk_variables = self.sdk_configuration.get_server_details()
|
||||
|
||||
if base_url is None:
|
||||
base_url = sdk_url
|
||||
|
||||
if url_variables is None:
|
||||
url_variables = sdk_variables
|
||||
|
||||
return utils.template_url(base_url, url_variables)
|
||||
|
||||
def _build_request_async(
|
||||
self,
|
||||
method,
|
||||
path,
|
||||
base_url,
|
||||
url_variables,
|
||||
request,
|
||||
request_body_required,
|
||||
request_has_path_params,
|
||||
request_has_query_params,
|
||||
user_agent_header,
|
||||
accept_header_value,
|
||||
_globals=None,
|
||||
security=None,
|
||||
timeout_ms: Optional[int] = None,
|
||||
get_serialized_body: Optional[
|
||||
Callable[[], Optional[SerializedRequestBody]]
|
||||
] = None,
|
||||
url_override: Optional[str] = None,
|
||||
http_headers: Optional[Mapping[str, str]] = None,
|
||||
) -> httpx.Request:
|
||||
client = self.sdk_configuration.async_client
|
||||
return self._build_request_with_client(
|
||||
client,
|
||||
method,
|
||||
path,
|
||||
base_url,
|
||||
url_variables,
|
||||
request,
|
||||
request_body_required,
|
||||
request_has_path_params,
|
||||
request_has_query_params,
|
||||
user_agent_header,
|
||||
accept_header_value,
|
||||
_globals,
|
||||
security,
|
||||
timeout_ms,
|
||||
get_serialized_body,
|
||||
url_override,
|
||||
http_headers,
|
||||
)
|
||||
|
||||
def _build_request(
|
||||
self,
|
||||
method,
|
||||
path,
|
||||
base_url,
|
||||
url_variables,
|
||||
request,
|
||||
request_body_required,
|
||||
request_has_path_params,
|
||||
request_has_query_params,
|
||||
user_agent_header,
|
||||
accept_header_value,
|
||||
_globals=None,
|
||||
security=None,
|
||||
timeout_ms: Optional[int] = None,
|
||||
get_serialized_body: Optional[
|
||||
Callable[[], Optional[SerializedRequestBody]]
|
||||
] = None,
|
||||
url_override: Optional[str] = None,
|
||||
http_headers: Optional[Mapping[str, str]] = None,
|
||||
) -> httpx.Request:
|
||||
client = self.sdk_configuration.client
|
||||
return self._build_request_with_client(
|
||||
client,
|
||||
method,
|
||||
path,
|
||||
base_url,
|
||||
url_variables,
|
||||
request,
|
||||
request_body_required,
|
||||
request_has_path_params,
|
||||
request_has_query_params,
|
||||
user_agent_header,
|
||||
accept_header_value,
|
||||
_globals,
|
||||
security,
|
||||
timeout_ms,
|
||||
get_serialized_body,
|
||||
url_override,
|
||||
http_headers,
|
||||
)
|
||||
|
||||
def _build_request_with_client(
|
||||
self,
|
||||
client,
|
||||
method,
|
||||
path,
|
||||
base_url,
|
||||
url_variables,
|
||||
request,
|
||||
request_body_required,
|
||||
request_has_path_params,
|
||||
request_has_query_params,
|
||||
user_agent_header,
|
||||
accept_header_value,
|
||||
_globals=None,
|
||||
security=None,
|
||||
timeout_ms: Optional[int] = None,
|
||||
get_serialized_body: Optional[
|
||||
Callable[[], Optional[SerializedRequestBody]]
|
||||
] = None,
|
||||
url_override: Optional[str] = None,
|
||||
http_headers: Optional[Mapping[str, str]] = None,
|
||||
) -> httpx.Request:
|
||||
query_params = {}
|
||||
|
||||
url = url_override
|
||||
if url is None:
|
||||
url = utils.generate_url(
|
||||
self._get_url(base_url, url_variables),
|
||||
path,
|
||||
request if request_has_path_params else None,
|
||||
_globals if request_has_path_params else None,
|
||||
)
|
||||
|
||||
query_params = utils.get_query_params(
|
||||
request if request_has_query_params else None,
|
||||
_globals if request_has_query_params else None,
|
||||
)
|
||||
else:
|
||||
# Pick up the query parameter from the override so they can be
|
||||
# preserved when building the request later on (necessary as of
|
||||
# httpx 0.28).
|
||||
parsed_override = urlparse(str(url_override))
|
||||
query_params = parse_qs(parsed_override.query, keep_blank_values=True)
|
||||
|
||||
headers = utils.get_headers(request, _globals)
|
||||
headers["Accept"] = accept_header_value
|
||||
headers[user_agent_header] = self.sdk_configuration.user_agent
|
||||
|
||||
if security is not None:
|
||||
if callable(security):
|
||||
security = security()
|
||||
|
||||
if security is not None:
|
||||
security_headers, security_query_params = utils.get_security(security)
|
||||
headers = {**headers, **security_headers}
|
||||
query_params = {**query_params, **security_query_params}
|
||||
|
||||
serialized_request_body = SerializedRequestBody()
|
||||
if get_serialized_body is not None:
|
||||
rb = get_serialized_body()
|
||||
if request_body_required and rb is None:
|
||||
raise ValueError("request body is required")
|
||||
|
||||
if rb is not None:
|
||||
serialized_request_body = rb
|
||||
|
||||
if (
|
||||
serialized_request_body.media_type is not None
|
||||
and serialized_request_body.media_type
|
||||
not in (
|
||||
"multipart/form-data",
|
||||
"multipart/mixed",
|
||||
)
|
||||
):
|
||||
headers["content-type"] = serialized_request_body.media_type
|
||||
|
||||
if http_headers is not None:
|
||||
for header, value in http_headers.items():
|
||||
headers[header] = value
|
||||
|
||||
timeout = timeout_ms / 1000 if timeout_ms is not None else None
|
||||
|
||||
return client.build_request(
|
||||
method,
|
||||
url,
|
||||
params=query_params,
|
||||
content=serialized_request_body.content,
|
||||
data=serialized_request_body.data,
|
||||
files=serialized_request_body.files,
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
def do_request(
|
||||
self,
|
||||
hook_ctx,
|
||||
request,
|
||||
error_status_codes,
|
||||
stream=False,
|
||||
retry_config: Optional[Tuple[RetryConfig, List[str]]] = None,
|
||||
) -> httpx.Response:
|
||||
client = self.sdk_configuration.client
|
||||
logger = self.sdk_configuration.debug_logger
|
||||
|
||||
hooks = self.sdk_configuration.__dict__["_hooks"]
|
||||
|
||||
def do():
|
||||
http_res = None
|
||||
try:
|
||||
req = hooks.before_request(BeforeRequestContext(hook_ctx), request)
|
||||
logger.debug(
|
||||
"Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s",
|
||||
req.method,
|
||||
req.url,
|
||||
req.headers,
|
||||
get_body_content(req),
|
||||
)
|
||||
|
||||
if client is None:
|
||||
raise ValueError("client is required")
|
||||
|
||||
http_res = client.send(req, stream=stream)
|
||||
except Exception as e:
|
||||
_, e = hooks.after_error(AfterErrorContext(hook_ctx), None, e)
|
||||
if e is not None:
|
||||
logger.debug("Request Exception", exc_info=True)
|
||||
raise e
|
||||
|
||||
if http_res is None:
|
||||
logger.debug("Raising no response SDK error")
|
||||
raise errors.NoResponseError("No response received")
|
||||
|
||||
logger.debug(
|
||||
"Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s",
|
||||
http_res.status_code,
|
||||
http_res.url,
|
||||
http_res.headers,
|
||||
"<streaming response>" if stream else http_res.text,
|
||||
)
|
||||
|
||||
if utils.match_status_codes(error_status_codes, http_res.status_code):
|
||||
result, err = hooks.after_error(
|
||||
AfterErrorContext(hook_ctx), http_res, None
|
||||
)
|
||||
if err is not None:
|
||||
logger.debug("Request Exception", exc_info=True)
|
||||
raise err
|
||||
if result is not None:
|
||||
http_res = result
|
||||
else:
|
||||
logger.debug("Raising unexpected SDK error")
|
||||
raise errors.SDKError("Unexpected error occurred", http_res)
|
||||
|
||||
return http_res
|
||||
|
||||
if retry_config is not None:
|
||||
http_res = utils.retry(do, utils.Retries(retry_config[0], retry_config[1]))
|
||||
else:
|
||||
http_res = do()
|
||||
|
||||
if not utils.match_status_codes(error_status_codes, http_res.status_code):
|
||||
http_res = hooks.after_success(AfterSuccessContext(hook_ctx), http_res)
|
||||
|
||||
return http_res
|
||||
|
||||
async def do_request_async(
|
||||
self,
|
||||
hook_ctx,
|
||||
request,
|
||||
error_status_codes,
|
||||
stream=False,
|
||||
retry_config: Optional[Tuple[RetryConfig, List[str]]] = None,
|
||||
) -> httpx.Response:
|
||||
client = self.sdk_configuration.async_client
|
||||
logger = self.sdk_configuration.debug_logger
|
||||
|
||||
hooks = self.sdk_configuration.__dict__["_hooks"]
|
||||
|
||||
async def do():
|
||||
http_res = None
|
||||
try:
|
||||
req = hooks.before_request(BeforeRequestContext(hook_ctx), request)
|
||||
logger.debug(
|
||||
"Request:\nMethod: %s\nURL: %s\nHeaders: %s\nBody: %s",
|
||||
req.method,
|
||||
req.url,
|
||||
req.headers,
|
||||
get_body_content(req),
|
||||
)
|
||||
|
||||
if client is None:
|
||||
raise ValueError("client is required")
|
||||
|
||||
http_res = await client.send(req, stream=stream)
|
||||
except Exception as e:
|
||||
_, e = hooks.after_error(AfterErrorContext(hook_ctx), None, e)
|
||||
if e is not None:
|
||||
logger.debug("Request Exception", exc_info=True)
|
||||
raise e
|
||||
|
||||
if http_res is None:
|
||||
logger.debug("Raising no response SDK error")
|
||||
raise errors.NoResponseError("No response received")
|
||||
|
||||
logger.debug(
|
||||
"Response:\nStatus Code: %s\nURL: %s\nHeaders: %s\nBody: %s",
|
||||
http_res.status_code,
|
||||
http_res.url,
|
||||
http_res.headers,
|
||||
"<streaming response>" if stream else http_res.text,
|
||||
)
|
||||
|
||||
if utils.match_status_codes(error_status_codes, http_res.status_code):
|
||||
result, err = hooks.after_error(
|
||||
AfterErrorContext(hook_ctx), http_res, None
|
||||
)
|
||||
if err is not None:
|
||||
logger.debug("Request Exception", exc_info=True)
|
||||
raise err
|
||||
if result is not None:
|
||||
http_res = result
|
||||
else:
|
||||
logger.debug("Raising unexpected SDK error")
|
||||
raise errors.SDKError("Unexpected error occurred", http_res)
|
||||
|
||||
return http_res
|
||||
|
||||
if retry_config is not None:
|
||||
http_res = await utils.retry_async(
|
||||
do, utils.Retries(retry_config[0], retry_config[1])
|
||||
)
|
||||
else:
|
||||
http_res = await do()
|
||||
|
||||
if not utils.match_status_codes(error_status_codes, http_res.status_code):
|
||||
http_res = hooks.after_success(AfterSuccessContext(hook_ctx), http_res)
|
||||
|
||||
return http_res
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from .basesdk import BaseSDK
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Mapping, Optional, Union, cast
|
||||
from unstructured_client import utils
|
||||
from unstructured_client._hooks import HookContext
|
||||
from unstructured_client.models import errors, operations, shared
|
||||
from unstructured_client.types import BaseModel, OptionalNullable, UNSET
|
||||
from unstructured_client._hooks.custom.clean_server_url_hook import clean_server_url
|
||||
from unstructured_client.utils.unmarshal_json_response import unmarshal_json_response
|
||||
|
||||
|
||||
class PartitionAcceptEnum(str, Enum):
|
||||
APPLICATION_JSON = "application/json"
|
||||
TEXT_CSV = "text/csv"
|
||||
|
||||
|
||||
class General(BaseSDK):
|
||||
def partition(
|
||||
self,
|
||||
*,
|
||||
request: Union[
|
||||
operations.PartitionRequest, operations.PartitionRequestTypedDict
|
||||
],
|
||||
retries: OptionalNullable[utils.RetryConfig] = UNSET,
|
||||
server_url: Optional[str] = None,
|
||||
timeout_ms: Optional[int] = None,
|
||||
accept_header_override: Optional[PartitionAcceptEnum] = None,
|
||||
http_headers: Optional[Mapping[str, str]] = None,
|
||||
) -> operations.PartitionResponse:
|
||||
r"""Summary
|
||||
|
||||
Description
|
||||
|
||||
:param request: The request object to send.
|
||||
:param retries: Override the default retry configuration for this method
|
||||
:param server_url: Override the default server URL for this method
|
||||
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
|
||||
:param accept_header_override: Override the default accept header for this method
|
||||
:param http_headers: Additional headers to set or replace on requests.
|
||||
"""
|
||||
base_url = None
|
||||
url_variables = None
|
||||
if timeout_ms is None:
|
||||
timeout_ms = self.sdk_configuration.timeout_ms
|
||||
|
||||
if server_url is not None:
|
||||
base_url = server_url
|
||||
else:
|
||||
base_url = self._get_url(base_url, url_variables)
|
||||
|
||||
# Note(austin): Add a custom check to handle the default server URL
|
||||
# The SDK globally defaults to the platform URL.
|
||||
# If that hasn't changed, we need to switch to the partition url here.
|
||||
base_url = clean_server_url(base_url)
|
||||
if base_url == "https://platform.unstructuredapp.io":
|
||||
base_url = "https://api.unstructuredapp.io"
|
||||
|
||||
if not isinstance(request, BaseModel):
|
||||
request = utils.unmarshal(request, operations.PartitionRequest)
|
||||
request = cast(operations.PartitionRequest, request)
|
||||
|
||||
req = self._build_request(
|
||||
method="POST",
|
||||
path="/general/v0/general",
|
||||
base_url=base_url,
|
||||
url_variables=url_variables,
|
||||
request=request,
|
||||
request_body_required=True,
|
||||
request_has_path_params=False,
|
||||
request_has_query_params=True,
|
||||
user_agent_header="user-agent",
|
||||
accept_header_value=accept_header_override.value
|
||||
if accept_header_override is not None
|
||||
else "application/json;q=1, text/csv;q=0",
|
||||
http_headers=http_headers,
|
||||
security=self.sdk_configuration.security,
|
||||
get_serialized_body=lambda: utils.serialize_request_body(
|
||||
request.partition_parameters,
|
||||
False,
|
||||
False,
|
||||
"multipart",
|
||||
shared.PartitionParameters,
|
||||
),
|
||||
timeout_ms=timeout_ms,
|
||||
)
|
||||
|
||||
if retries == UNSET:
|
||||
if self.sdk_configuration.retry_config is not UNSET:
|
||||
retries = self.sdk_configuration.retry_config
|
||||
else:
|
||||
retries = utils.RetryConfig(
|
||||
"backoff", utils.BackoffStrategy(3000, 720000, 1.88, 1800000), True
|
||||
)
|
||||
|
||||
retry_config = None
|
||||
if isinstance(retries, utils.RetryConfig):
|
||||
retry_config = (retries, ["5xx"])
|
||||
|
||||
http_res = self.do_request(
|
||||
hook_ctx=HookContext(
|
||||
config=self.sdk_configuration,
|
||||
base_url=base_url or "",
|
||||
operation_id="partition",
|
||||
oauth2_scopes=[],
|
||||
security_source=self.sdk_configuration.security,
|
||||
),
|
||||
request=req,
|
||||
error_status_codes=["422", "4XX", "5XX"],
|
||||
retry_config=retry_config,
|
||||
)
|
||||
|
||||
response_data: Any = None
|
||||
if utils.match_response(http_res, "200", "application/json"):
|
||||
return operations.PartitionResponse(
|
||||
elements=unmarshal_json_response(
|
||||
Optional[List[Dict[str, Any]]], http_res
|
||||
),
|
||||
status_code=http_res.status_code,
|
||||
content_type=http_res.headers.get("Content-Type") or "",
|
||||
raw_response=http_res,
|
||||
)
|
||||
if utils.match_response(http_res, "200", "text/csv"):
|
||||
return operations.PartitionResponse(
|
||||
csv_elements=http_res.text,
|
||||
status_code=http_res.status_code,
|
||||
content_type=http_res.headers.get("Content-Type") or "",
|
||||
raw_response=http_res,
|
||||
)
|
||||
if utils.match_response(http_res, "422", "application/json"):
|
||||
response_data = unmarshal_json_response(
|
||||
errors.HTTPValidationErrorData, http_res
|
||||
)
|
||||
raise errors.HTTPValidationError(response_data, http_res)
|
||||
if utils.match_response(http_res, "4XX", "*"):
|
||||
http_res_text = utils.stream_to_text(http_res)
|
||||
raise errors.SDKError("API error occurred", http_res, http_res_text)
|
||||
if utils.match_response(http_res, "5XX", "application/json"):
|
||||
response_data = unmarshal_json_response(errors.ServerErrorData, http_res)
|
||||
raise errors.ServerError(response_data, http_res)
|
||||
|
||||
raise errors.SDKError("Unexpected response received", http_res)
|
||||
|
||||
async def partition_async(
|
||||
self,
|
||||
*,
|
||||
request: Union[
|
||||
operations.PartitionRequest, operations.PartitionRequestTypedDict
|
||||
],
|
||||
retries: OptionalNullable[utils.RetryConfig] = UNSET,
|
||||
server_url: Optional[str] = None,
|
||||
timeout_ms: Optional[int] = None,
|
||||
accept_header_override: Optional[PartitionAcceptEnum] = None,
|
||||
http_headers: Optional[Mapping[str, str]] = None,
|
||||
) -> operations.PartitionResponse:
|
||||
r"""Summary
|
||||
|
||||
Description
|
||||
|
||||
:param request: The request object to send.
|
||||
:param retries: Override the default retry configuration for this method
|
||||
:param server_url: Override the default server URL for this method
|
||||
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
|
||||
:param accept_header_override: Override the default accept header for this method
|
||||
:param http_headers: Additional headers to set or replace on requests.
|
||||
"""
|
||||
base_url = None
|
||||
url_variables = None
|
||||
if timeout_ms is None:
|
||||
timeout_ms = self.sdk_configuration.timeout_ms
|
||||
|
||||
if server_url is not None:
|
||||
base_url = server_url
|
||||
else:
|
||||
base_url = self._get_url(base_url, url_variables)
|
||||
|
||||
# Note(austin): Add a custom check to handle the default server URL
|
||||
# The SDK globally defaults to the platform URL.
|
||||
# If that hasn't changed, we need to switch to the partition url here.
|
||||
base_url = clean_server_url(base_url)
|
||||
if base_url == "https://platform.unstructuredapp.io":
|
||||
base_url = "https://api.unstructuredapp.io"
|
||||
|
||||
if not isinstance(request, BaseModel):
|
||||
request = utils.unmarshal(request, operations.PartitionRequest)
|
||||
request = cast(operations.PartitionRequest, request)
|
||||
|
||||
req = self._build_request_async(
|
||||
method="POST",
|
||||
path="/general/v0/general",
|
||||
base_url=base_url,
|
||||
url_variables=url_variables,
|
||||
request=request,
|
||||
request_body_required=True,
|
||||
request_has_path_params=False,
|
||||
request_has_query_params=True,
|
||||
user_agent_header="user-agent",
|
||||
accept_header_value=accept_header_override.value
|
||||
if accept_header_override is not None
|
||||
else "application/json;q=1, text/csv;q=0",
|
||||
http_headers=http_headers,
|
||||
security=self.sdk_configuration.security,
|
||||
get_serialized_body=lambda: utils.serialize_request_body(
|
||||
request.partition_parameters,
|
||||
False,
|
||||
False,
|
||||
"multipart",
|
||||
shared.PartitionParameters,
|
||||
),
|
||||
timeout_ms=timeout_ms,
|
||||
)
|
||||
|
||||
if retries == UNSET:
|
||||
if self.sdk_configuration.retry_config is not UNSET:
|
||||
retries = self.sdk_configuration.retry_config
|
||||
else:
|
||||
retries = utils.RetryConfig(
|
||||
"backoff", utils.BackoffStrategy(3000, 720000, 1.88, 1800000), True
|
||||
)
|
||||
|
||||
retry_config = None
|
||||
if isinstance(retries, utils.RetryConfig):
|
||||
retry_config = (retries, ["5xx"])
|
||||
|
||||
http_res = await self.do_request_async(
|
||||
hook_ctx=HookContext(
|
||||
config=self.sdk_configuration,
|
||||
base_url=base_url or "",
|
||||
operation_id="partition",
|
||||
oauth2_scopes=[],
|
||||
security_source=self.sdk_configuration.security,
|
||||
),
|
||||
request=req,
|
||||
error_status_codes=["422", "4XX", "5XX"],
|
||||
retry_config=retry_config,
|
||||
)
|
||||
|
||||
response_data: Any = None
|
||||
if utils.match_response(http_res, "200", "application/json"):
|
||||
return operations.PartitionResponse(
|
||||
elements=unmarshal_json_response(
|
||||
Optional[List[Dict[str, Any]]], http_res
|
||||
),
|
||||
status_code=http_res.status_code,
|
||||
content_type=http_res.headers.get("Content-Type") or "",
|
||||
raw_response=http_res,
|
||||
)
|
||||
if utils.match_response(http_res, "200", "text/csv"):
|
||||
return operations.PartitionResponse(
|
||||
csv_elements=http_res.text,
|
||||
status_code=http_res.status_code,
|
||||
content_type=http_res.headers.get("Content-Type") or "",
|
||||
raw_response=http_res,
|
||||
)
|
||||
if utils.match_response(http_res, "422", "application/json"):
|
||||
response_data = unmarshal_json_response(
|
||||
errors.HTTPValidationErrorData, http_res
|
||||
)
|
||||
raise errors.HTTPValidationError(response_data, http_res)
|
||||
if utils.match_response(http_res, "4XX", "*"):
|
||||
http_res_text = await utils.stream_to_text_async(http_res)
|
||||
raise errors.SDKError("API error occurred", http_res, http_res_text)
|
||||
if utils.match_response(http_res, "5XX", "application/json"):
|
||||
response_data = unmarshal_json_response(errors.ServerErrorData, http_res)
|
||||
raise errors.ServerError(response_data, http_res)
|
||||
|
||||
raise errors.SDKError("Unexpected response received", http_res)
|
||||
@@ -0,0 +1,126 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
# pyright: reportReturnType = false
|
||||
import asyncio
|
||||
from typing_extensions import Protocol, runtime_checkable
|
||||
import httpx
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class HttpClient(Protocol):
|
||||
def send(
|
||||
self,
|
||||
request: httpx.Request,
|
||||
*,
|
||||
stream: bool = False,
|
||||
auth: Union[
|
||||
httpx._types.AuthTypes, httpx._client.UseClientDefault, None
|
||||
] = httpx.USE_CLIENT_DEFAULT,
|
||||
follow_redirects: Union[
|
||||
bool, httpx._client.UseClientDefault
|
||||
] = httpx.USE_CLIENT_DEFAULT,
|
||||
) -> httpx.Response:
|
||||
pass
|
||||
|
||||
def build_request(
|
||||
self,
|
||||
method: str,
|
||||
url: httpx._types.URLTypes,
|
||||
*,
|
||||
content: Optional[httpx._types.RequestContent] = None,
|
||||
data: Optional[httpx._types.RequestData] = None,
|
||||
files: Optional[httpx._types.RequestFiles] = None,
|
||||
json: Optional[Any] = None,
|
||||
params: Optional[httpx._types.QueryParamTypes] = None,
|
||||
headers: Optional[httpx._types.HeaderTypes] = None,
|
||||
cookies: Optional[httpx._types.CookieTypes] = None,
|
||||
timeout: Union[
|
||||
httpx._types.TimeoutTypes, httpx._client.UseClientDefault
|
||||
] = httpx.USE_CLIENT_DEFAULT,
|
||||
extensions: Optional[httpx._types.RequestExtensions] = None,
|
||||
) -> httpx.Request:
|
||||
pass
|
||||
|
||||
def close(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class AsyncHttpClient(Protocol):
|
||||
async def send(
|
||||
self,
|
||||
request: httpx.Request,
|
||||
*,
|
||||
stream: bool = False,
|
||||
auth: Union[
|
||||
httpx._types.AuthTypes, httpx._client.UseClientDefault, None
|
||||
] = httpx.USE_CLIENT_DEFAULT,
|
||||
follow_redirects: Union[
|
||||
bool, httpx._client.UseClientDefault
|
||||
] = httpx.USE_CLIENT_DEFAULT,
|
||||
) -> httpx.Response:
|
||||
pass
|
||||
|
||||
def build_request(
|
||||
self,
|
||||
method: str,
|
||||
url: httpx._types.URLTypes,
|
||||
*,
|
||||
content: Optional[httpx._types.RequestContent] = None,
|
||||
data: Optional[httpx._types.RequestData] = None,
|
||||
files: Optional[httpx._types.RequestFiles] = None,
|
||||
json: Optional[Any] = None,
|
||||
params: Optional[httpx._types.QueryParamTypes] = None,
|
||||
headers: Optional[httpx._types.HeaderTypes] = None,
|
||||
cookies: Optional[httpx._types.CookieTypes] = None,
|
||||
timeout: Union[
|
||||
httpx._types.TimeoutTypes, httpx._client.UseClientDefault
|
||||
] = httpx.USE_CLIENT_DEFAULT,
|
||||
extensions: Optional[httpx._types.RequestExtensions] = None,
|
||||
) -> httpx.Request:
|
||||
pass
|
||||
|
||||
async def aclose(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class ClientOwner(Protocol):
|
||||
client: Union[HttpClient, None]
|
||||
async_client: Union[AsyncHttpClient, None]
|
||||
|
||||
|
||||
def close_clients(
|
||||
owner: ClientOwner,
|
||||
sync_client: Union[HttpClient, None],
|
||||
sync_client_supplied: bool,
|
||||
async_client: Union[AsyncHttpClient, None],
|
||||
async_client_supplied: bool,
|
||||
) -> None:
|
||||
"""
|
||||
A finalizer function that is meant to be used with weakref.finalize to close
|
||||
httpx clients used by an SDK so that underlying resources can be garbage
|
||||
collected.
|
||||
"""
|
||||
|
||||
# Unset the client/async_client properties so there are no more references
|
||||
# to them from the owning SDK instance and they can be reaped.
|
||||
owner.client = None
|
||||
owner.async_client = None
|
||||
|
||||
if sync_client is not None and not sync_client_supplied:
|
||||
try:
|
||||
sync_client.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if async_client is not None and not async_client_supplied:
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
asyncio.run_coroutine_threadsafe(async_client.aclose(), loop)
|
||||
except RuntimeError:
|
||||
try:
|
||||
asyncio.run(async_client.aclose())
|
||||
except RuntimeError:
|
||||
# best effort
|
||||
pass
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
# package
|
||||
Binary file not shown.
@@ -0,0 +1,67 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from importlib import import_module
|
||||
import builtins
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .httpvalidationerror import (
|
||||
Detail,
|
||||
HTTPValidationError,
|
||||
HTTPValidationErrorData,
|
||||
)
|
||||
from .no_response_error import NoResponseError
|
||||
from .responsevalidationerror import ResponseValidationError
|
||||
from .sdkerror import SDKError
|
||||
from .servererror import ServerError, ServerErrorData
|
||||
from .unstructuredclienterror import UnstructuredClientError
|
||||
|
||||
__all__ = [
|
||||
"Detail",
|
||||
"HTTPValidationError",
|
||||
"HTTPValidationErrorData",
|
||||
"NoResponseError",
|
||||
"ResponseValidationError",
|
||||
"SDKError",
|
||||
"ServerError",
|
||||
"ServerErrorData",
|
||||
"UnstructuredClientError",
|
||||
]
|
||||
|
||||
_dynamic_imports: dict[str, str] = {
|
||||
"Detail": ".httpvalidationerror",
|
||||
"HTTPValidationError": ".httpvalidationerror",
|
||||
"HTTPValidationErrorData": ".httpvalidationerror",
|
||||
"NoResponseError": ".no_response_error",
|
||||
"ResponseValidationError": ".responsevalidationerror",
|
||||
"SDKError": ".sdkerror",
|
||||
"ServerError": ".servererror",
|
||||
"ServerErrorData": ".servererror",
|
||||
"UnstructuredClientError": ".unstructuredclienterror",
|
||||
}
|
||||
|
||||
|
||||
def __getattr__(attr_name: str) -> object:
|
||||
module_name = _dynamic_imports.get(attr_name)
|
||||
if module_name is None:
|
||||
raise AttributeError(
|
||||
f"No {attr_name} found in _dynamic_imports for module name -> {__name__} "
|
||||
)
|
||||
|
||||
try:
|
||||
module = import_module(module_name, __package__)
|
||||
result = getattr(module, attr_name)
|
||||
return result
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
f"Failed to import {attr_name} from {module_name}: {e}"
|
||||
) from e
|
||||
except AttributeError as e:
|
||||
raise AttributeError(
|
||||
f"Failed to get {attr_name} from {module_name}: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
def __dir__():
|
||||
lazy_attrs = builtins.list(_dynamic_imports.keys())
|
||||
return builtins.sorted(lazy_attrs)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
from typing import List, Optional, Union
|
||||
from typing_extensions import TypeAliasType
|
||||
from unstructured_client.models.errors import UnstructuredClientError
|
||||
from unstructured_client.models.shared import validationerror as shared_validationerror
|
||||
from unstructured_client.types import BaseModel
|
||||
|
||||
|
||||
DetailTypedDict = TypeAliasType(
|
||||
"DetailTypedDict", Union[List[shared_validationerror.ValidationErrorTypedDict], str]
|
||||
)
|
||||
|
||||
|
||||
Detail = TypeAliasType(
|
||||
"Detail", Union[List[shared_validationerror.ValidationError], str]
|
||||
)
|
||||
|
||||
|
||||
class HTTPValidationErrorData(BaseModel):
|
||||
detail: Optional[Detail] = None
|
||||
|
||||
|
||||
class HTTPValidationError(UnstructuredClientError):
|
||||
data: HTTPValidationErrorData
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: HTTPValidationErrorData,
|
||||
raw_response: httpx.Response,
|
||||
body: Optional[str] = None,
|
||||
):
|
||||
message = body or raw_response.text
|
||||
super().__init__(message, raw_response, body)
|
||||
self.data = data
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
class NoResponseError(Exception):
|
||||
"""Error raised when no HTTP response is received from the server."""
|
||||
|
||||
message: str
|
||||
|
||||
def __init__(self, message: str = "No response received"):
|
||||
self.message = message
|
||||
super().__init__(message)
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
import httpx
|
||||
from typing import Optional
|
||||
|
||||
from unstructured_client.models.errors import UnstructuredClientError
|
||||
|
||||
|
||||
class ResponseValidationError(UnstructuredClientError):
|
||||
"""Error raised when there is a type mismatch between the response data and the expected Pydantic model."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
raw_response: httpx.Response,
|
||||
cause: Exception,
|
||||
body: Optional[str] = None,
|
||||
):
|
||||
message = f"{message}: {cause}"
|
||||
super().__init__(message, raw_response, body)
|
||||
|
||||
@property
|
||||
def cause(self):
|
||||
"""Normally the Pydantic ValidationError"""
|
||||
return self.__cause__
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
import httpx
|
||||
from typing import Optional
|
||||
|
||||
from unstructured_client.models.errors import UnstructuredClientError
|
||||
|
||||
MAX_MESSAGE_LEN = 10_000
|
||||
|
||||
|
||||
class SDKError(UnstructuredClientError):
|
||||
"""The fallback error class if no more specific error class is matched."""
|
||||
|
||||
def __init__(
|
||||
self, message: str, raw_response: httpx.Response, body: Optional[str] = None
|
||||
):
|
||||
body_display = body or raw_response.text or '""'
|
||||
|
||||
if message:
|
||||
message += ": "
|
||||
message += f"Status {raw_response.status_code}"
|
||||
|
||||
headers = raw_response.headers
|
||||
content_type = headers.get("content-type", '""')
|
||||
if content_type != "application/json":
|
||||
if " " in content_type:
|
||||
content_type = f'"{content_type}"'
|
||||
message += f" Content-Type {content_type}"
|
||||
|
||||
if len(body_display) > MAX_MESSAGE_LEN:
|
||||
truncated = body_display[:MAX_MESSAGE_LEN]
|
||||
remaining = len(body_display) - MAX_MESSAGE_LEN
|
||||
body_display = f"{truncated}...and {remaining} more chars"
|
||||
|
||||
message += f". Body: {body_display}"
|
||||
message = message.strip()
|
||||
|
||||
super().__init__(message, raw_response, body)
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
from typing import Optional
|
||||
from unstructured_client.models.errors import UnstructuredClientError
|
||||
from unstructured_client.types import BaseModel
|
||||
|
||||
|
||||
class ServerErrorData(BaseModel):
|
||||
detail: Optional[str] = None
|
||||
|
||||
|
||||
class ServerError(UnstructuredClientError):
|
||||
data: ServerErrorData
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: ServerErrorData,
|
||||
raw_response: httpx.Response,
|
||||
body: Optional[str] = None,
|
||||
):
|
||||
message = body or raw_response.text
|
||||
super().__init__(message, raw_response, body)
|
||||
self.data = data
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
import httpx
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class UnstructuredClientError(Exception):
|
||||
"""The base class for all HTTP error responses."""
|
||||
|
||||
message: str
|
||||
status_code: int
|
||||
body: str
|
||||
headers: httpx.Headers
|
||||
raw_response: httpx.Response
|
||||
|
||||
def __init__(
|
||||
self, message: str, raw_response: httpx.Response, body: Optional[str] = None
|
||||
):
|
||||
self.message = message
|
||||
self.status_code = raw_response.status_code
|
||||
self.body = body if body is not None else raw_response.text
|
||||
self.headers = raw_response.headers
|
||||
self.raw_response = raw_response
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
@@ -0,0 +1,459 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from importlib import import_module
|
||||
import builtins
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .cancel_job import (
|
||||
CancelJobRequest,
|
||||
CancelJobRequestTypedDict,
|
||||
CancelJobResponse,
|
||||
CancelJobResponseTypedDict,
|
||||
)
|
||||
from .create_connection_check_destinations import (
|
||||
CreateConnectionCheckDestinationsRequest,
|
||||
CreateConnectionCheckDestinationsRequestTypedDict,
|
||||
CreateConnectionCheckDestinationsResponse,
|
||||
CreateConnectionCheckDestinationsResponseTypedDict,
|
||||
)
|
||||
from .create_connection_check_sources import (
|
||||
CreateConnectionCheckSourcesRequest,
|
||||
CreateConnectionCheckSourcesRequestTypedDict,
|
||||
CreateConnectionCheckSourcesResponse,
|
||||
CreateConnectionCheckSourcesResponseTypedDict,
|
||||
)
|
||||
from .create_destination import (
|
||||
CreateDestinationRequest,
|
||||
CreateDestinationRequestTypedDict,
|
||||
CreateDestinationResponse,
|
||||
CreateDestinationResponseTypedDict,
|
||||
)
|
||||
from .create_job import (
|
||||
CreateJobRequest,
|
||||
CreateJobRequestTypedDict,
|
||||
CreateJobResponse,
|
||||
CreateJobResponseTypedDict,
|
||||
)
|
||||
from .create_source import (
|
||||
CreateSourceRequest,
|
||||
CreateSourceRequestTypedDict,
|
||||
CreateSourceResponse,
|
||||
CreateSourceResponseTypedDict,
|
||||
)
|
||||
from .create_workflow import (
|
||||
CreateWorkflowRequest,
|
||||
CreateWorkflowRequestTypedDict,
|
||||
CreateWorkflowResponse,
|
||||
CreateWorkflowResponseTypedDict,
|
||||
)
|
||||
from .delete_destination import (
|
||||
DeleteDestinationRequest,
|
||||
DeleteDestinationRequestTypedDict,
|
||||
DeleteDestinationResponse,
|
||||
DeleteDestinationResponseTypedDict,
|
||||
)
|
||||
from .delete_source import (
|
||||
DeleteSourceRequest,
|
||||
DeleteSourceRequestTypedDict,
|
||||
DeleteSourceResponse,
|
||||
DeleteSourceResponseTypedDict,
|
||||
)
|
||||
from .delete_workflow import (
|
||||
DeleteWorkflowRequest,
|
||||
DeleteWorkflowRequestTypedDict,
|
||||
DeleteWorkflowResponse,
|
||||
DeleteWorkflowResponseTypedDict,
|
||||
)
|
||||
from .download_job_output import (
|
||||
DownloadJobOutputRequest,
|
||||
DownloadJobOutputRequestTypedDict,
|
||||
DownloadJobOutputResponse,
|
||||
DownloadJobOutputResponseTypedDict,
|
||||
)
|
||||
from .get_connection_check_destinations import (
|
||||
GetConnectionCheckDestinationsRequest,
|
||||
GetConnectionCheckDestinationsRequestTypedDict,
|
||||
GetConnectionCheckDestinationsResponse,
|
||||
GetConnectionCheckDestinationsResponseTypedDict,
|
||||
)
|
||||
from .get_connection_check_sources import (
|
||||
GetConnectionCheckSourcesRequest,
|
||||
GetConnectionCheckSourcesRequestTypedDict,
|
||||
GetConnectionCheckSourcesResponse,
|
||||
GetConnectionCheckSourcesResponseTypedDict,
|
||||
)
|
||||
from .get_destination import (
|
||||
GetDestinationRequest,
|
||||
GetDestinationRequestTypedDict,
|
||||
GetDestinationResponse,
|
||||
GetDestinationResponseTypedDict,
|
||||
)
|
||||
from .get_job import (
|
||||
GetJobRequest,
|
||||
GetJobRequestTypedDict,
|
||||
GetJobResponse,
|
||||
GetJobResponseTypedDict,
|
||||
)
|
||||
from .get_job_details import (
|
||||
GetJobDetailsRequest,
|
||||
GetJobDetailsRequestTypedDict,
|
||||
GetJobDetailsResponse,
|
||||
GetJobDetailsResponseTypedDict,
|
||||
)
|
||||
from .get_job_failed_files import (
|
||||
GetJobFailedFilesRequest,
|
||||
GetJobFailedFilesRequestTypedDict,
|
||||
GetJobFailedFilesResponse,
|
||||
GetJobFailedFilesResponseTypedDict,
|
||||
)
|
||||
from .get_source import (
|
||||
GetSourceRequest,
|
||||
GetSourceRequestTypedDict,
|
||||
GetSourceResponse,
|
||||
GetSourceResponseTypedDict,
|
||||
)
|
||||
from .get_template import (
|
||||
GetTemplateRequest,
|
||||
GetTemplateRequestTypedDict,
|
||||
GetTemplateResponse,
|
||||
GetTemplateResponseTypedDict,
|
||||
)
|
||||
from .get_workflow import (
|
||||
GetWorkflowRequest,
|
||||
GetWorkflowRequestTypedDict,
|
||||
GetWorkflowResponse,
|
||||
GetWorkflowResponseTypedDict,
|
||||
)
|
||||
from .list_destinations import (
|
||||
ListDestinationsRequest,
|
||||
ListDestinationsRequestTypedDict,
|
||||
ListDestinationsResponse,
|
||||
ListDestinationsResponseTypedDict,
|
||||
)
|
||||
from .list_jobs import (
|
||||
ListJobsRequest,
|
||||
ListJobsRequestTypedDict,
|
||||
ListJobsResponse,
|
||||
ListJobsResponseTypedDict,
|
||||
)
|
||||
from .list_sources import (
|
||||
ListSourcesRequest,
|
||||
ListSourcesRequestTypedDict,
|
||||
ListSourcesResponse,
|
||||
ListSourcesResponseTypedDict,
|
||||
)
|
||||
from .list_templates import (
|
||||
ListTemplatesRequest,
|
||||
ListTemplatesRequestTypedDict,
|
||||
ListTemplatesResponse,
|
||||
ListTemplatesResponseTypedDict,
|
||||
)
|
||||
from .list_workflows import (
|
||||
ListWorkflowsRequest,
|
||||
ListWorkflowsRequestTypedDict,
|
||||
ListWorkflowsResponse,
|
||||
ListWorkflowsResponseTypedDict,
|
||||
)
|
||||
from .partition import (
|
||||
PartitionRequest,
|
||||
PartitionRequestTypedDict,
|
||||
PartitionResponse,
|
||||
PartitionResponseTypedDict,
|
||||
)
|
||||
from .run_workflow import (
|
||||
RunWorkflowRequest,
|
||||
RunWorkflowRequestTypedDict,
|
||||
RunWorkflowResponse,
|
||||
RunWorkflowResponseTypedDict,
|
||||
)
|
||||
from .update_destination import (
|
||||
UpdateDestinationRequest,
|
||||
UpdateDestinationRequestTypedDict,
|
||||
UpdateDestinationResponse,
|
||||
UpdateDestinationResponseTypedDict,
|
||||
)
|
||||
from .update_source import (
|
||||
UpdateSourceRequest,
|
||||
UpdateSourceRequestTypedDict,
|
||||
UpdateSourceResponse,
|
||||
UpdateSourceResponseTypedDict,
|
||||
)
|
||||
from .update_workflow import (
|
||||
UpdateWorkflowRequest,
|
||||
UpdateWorkflowRequestTypedDict,
|
||||
UpdateWorkflowResponse,
|
||||
UpdateWorkflowResponseTypedDict,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CancelJobRequest",
|
||||
"CancelJobRequestTypedDict",
|
||||
"CancelJobResponse",
|
||||
"CancelJobResponseTypedDict",
|
||||
"CreateConnectionCheckDestinationsRequest",
|
||||
"CreateConnectionCheckDestinationsRequestTypedDict",
|
||||
"CreateConnectionCheckDestinationsResponse",
|
||||
"CreateConnectionCheckDestinationsResponseTypedDict",
|
||||
"CreateConnectionCheckSourcesRequest",
|
||||
"CreateConnectionCheckSourcesRequestTypedDict",
|
||||
"CreateConnectionCheckSourcesResponse",
|
||||
"CreateConnectionCheckSourcesResponseTypedDict",
|
||||
"CreateDestinationRequest",
|
||||
"CreateDestinationRequestTypedDict",
|
||||
"CreateDestinationResponse",
|
||||
"CreateDestinationResponseTypedDict",
|
||||
"CreateJobRequest",
|
||||
"CreateJobRequestTypedDict",
|
||||
"CreateJobResponse",
|
||||
"CreateJobResponseTypedDict",
|
||||
"CreateSourceRequest",
|
||||
"CreateSourceRequestTypedDict",
|
||||
"CreateSourceResponse",
|
||||
"CreateSourceResponseTypedDict",
|
||||
"CreateWorkflowRequest",
|
||||
"CreateWorkflowRequestTypedDict",
|
||||
"CreateWorkflowResponse",
|
||||
"CreateWorkflowResponseTypedDict",
|
||||
"DeleteDestinationRequest",
|
||||
"DeleteDestinationRequestTypedDict",
|
||||
"DeleteDestinationResponse",
|
||||
"DeleteDestinationResponseTypedDict",
|
||||
"DeleteSourceRequest",
|
||||
"DeleteSourceRequestTypedDict",
|
||||
"DeleteSourceResponse",
|
||||
"DeleteSourceResponseTypedDict",
|
||||
"DeleteWorkflowRequest",
|
||||
"DeleteWorkflowRequestTypedDict",
|
||||
"DeleteWorkflowResponse",
|
||||
"DeleteWorkflowResponseTypedDict",
|
||||
"DownloadJobOutputRequest",
|
||||
"DownloadJobOutputRequestTypedDict",
|
||||
"DownloadJobOutputResponse",
|
||||
"DownloadJobOutputResponseTypedDict",
|
||||
"GetConnectionCheckDestinationsRequest",
|
||||
"GetConnectionCheckDestinationsRequestTypedDict",
|
||||
"GetConnectionCheckDestinationsResponse",
|
||||
"GetConnectionCheckDestinationsResponseTypedDict",
|
||||
"GetConnectionCheckSourcesRequest",
|
||||
"GetConnectionCheckSourcesRequestTypedDict",
|
||||
"GetConnectionCheckSourcesResponse",
|
||||
"GetConnectionCheckSourcesResponseTypedDict",
|
||||
"GetDestinationRequest",
|
||||
"GetDestinationRequestTypedDict",
|
||||
"GetDestinationResponse",
|
||||
"GetDestinationResponseTypedDict",
|
||||
"GetJobDetailsRequest",
|
||||
"GetJobDetailsRequestTypedDict",
|
||||
"GetJobDetailsResponse",
|
||||
"GetJobDetailsResponseTypedDict",
|
||||
"GetJobFailedFilesRequest",
|
||||
"GetJobFailedFilesRequestTypedDict",
|
||||
"GetJobFailedFilesResponse",
|
||||
"GetJobFailedFilesResponseTypedDict",
|
||||
"GetJobRequest",
|
||||
"GetJobRequestTypedDict",
|
||||
"GetJobResponse",
|
||||
"GetJobResponseTypedDict",
|
||||
"GetSourceRequest",
|
||||
"GetSourceRequestTypedDict",
|
||||
"GetSourceResponse",
|
||||
"GetSourceResponseTypedDict",
|
||||
"GetTemplateRequest",
|
||||
"GetTemplateRequestTypedDict",
|
||||
"GetTemplateResponse",
|
||||
"GetTemplateResponseTypedDict",
|
||||
"GetWorkflowRequest",
|
||||
"GetWorkflowRequestTypedDict",
|
||||
"GetWorkflowResponse",
|
||||
"GetWorkflowResponseTypedDict",
|
||||
"ListDestinationsRequest",
|
||||
"ListDestinationsRequestTypedDict",
|
||||
"ListDestinationsResponse",
|
||||
"ListDestinationsResponseTypedDict",
|
||||
"ListJobsRequest",
|
||||
"ListJobsRequestTypedDict",
|
||||
"ListJobsResponse",
|
||||
"ListJobsResponseTypedDict",
|
||||
"ListSourcesRequest",
|
||||
"ListSourcesRequestTypedDict",
|
||||
"ListSourcesResponse",
|
||||
"ListSourcesResponseTypedDict",
|
||||
"ListTemplatesRequest",
|
||||
"ListTemplatesRequestTypedDict",
|
||||
"ListTemplatesResponse",
|
||||
"ListTemplatesResponseTypedDict",
|
||||
"ListWorkflowsRequest",
|
||||
"ListWorkflowsRequestTypedDict",
|
||||
"ListWorkflowsResponse",
|
||||
"ListWorkflowsResponseTypedDict",
|
||||
"PartitionRequest",
|
||||
"PartitionRequestTypedDict",
|
||||
"PartitionResponse",
|
||||
"PartitionResponseTypedDict",
|
||||
"RunWorkflowRequest",
|
||||
"RunWorkflowRequestTypedDict",
|
||||
"RunWorkflowResponse",
|
||||
"RunWorkflowResponseTypedDict",
|
||||
"UpdateDestinationRequest",
|
||||
"UpdateDestinationRequestTypedDict",
|
||||
"UpdateDestinationResponse",
|
||||
"UpdateDestinationResponseTypedDict",
|
||||
"UpdateSourceRequest",
|
||||
"UpdateSourceRequestTypedDict",
|
||||
"UpdateSourceResponse",
|
||||
"UpdateSourceResponseTypedDict",
|
||||
"UpdateWorkflowRequest",
|
||||
"UpdateWorkflowRequestTypedDict",
|
||||
"UpdateWorkflowResponse",
|
||||
"UpdateWorkflowResponseTypedDict",
|
||||
]
|
||||
|
||||
_dynamic_imports: dict[str, str] = {
|
||||
"CancelJobRequest": ".cancel_job",
|
||||
"CancelJobRequestTypedDict": ".cancel_job",
|
||||
"CancelJobResponse": ".cancel_job",
|
||||
"CancelJobResponseTypedDict": ".cancel_job",
|
||||
"CreateConnectionCheckDestinationsRequest": ".create_connection_check_destinations",
|
||||
"CreateConnectionCheckDestinationsRequestTypedDict": ".create_connection_check_destinations",
|
||||
"CreateConnectionCheckDestinationsResponse": ".create_connection_check_destinations",
|
||||
"CreateConnectionCheckDestinationsResponseTypedDict": ".create_connection_check_destinations",
|
||||
"CreateConnectionCheckSourcesRequest": ".create_connection_check_sources",
|
||||
"CreateConnectionCheckSourcesRequestTypedDict": ".create_connection_check_sources",
|
||||
"CreateConnectionCheckSourcesResponse": ".create_connection_check_sources",
|
||||
"CreateConnectionCheckSourcesResponseTypedDict": ".create_connection_check_sources",
|
||||
"CreateDestinationRequest": ".create_destination",
|
||||
"CreateDestinationRequestTypedDict": ".create_destination",
|
||||
"CreateDestinationResponse": ".create_destination",
|
||||
"CreateDestinationResponseTypedDict": ".create_destination",
|
||||
"CreateJobRequest": ".create_job",
|
||||
"CreateJobRequestTypedDict": ".create_job",
|
||||
"CreateJobResponse": ".create_job",
|
||||
"CreateJobResponseTypedDict": ".create_job",
|
||||
"CreateSourceRequest": ".create_source",
|
||||
"CreateSourceRequestTypedDict": ".create_source",
|
||||
"CreateSourceResponse": ".create_source",
|
||||
"CreateSourceResponseTypedDict": ".create_source",
|
||||
"CreateWorkflowRequest": ".create_workflow",
|
||||
"CreateWorkflowRequestTypedDict": ".create_workflow",
|
||||
"CreateWorkflowResponse": ".create_workflow",
|
||||
"CreateWorkflowResponseTypedDict": ".create_workflow",
|
||||
"DeleteDestinationRequest": ".delete_destination",
|
||||
"DeleteDestinationRequestTypedDict": ".delete_destination",
|
||||
"DeleteDestinationResponse": ".delete_destination",
|
||||
"DeleteDestinationResponseTypedDict": ".delete_destination",
|
||||
"DeleteSourceRequest": ".delete_source",
|
||||
"DeleteSourceRequestTypedDict": ".delete_source",
|
||||
"DeleteSourceResponse": ".delete_source",
|
||||
"DeleteSourceResponseTypedDict": ".delete_source",
|
||||
"DeleteWorkflowRequest": ".delete_workflow",
|
||||
"DeleteWorkflowRequestTypedDict": ".delete_workflow",
|
||||
"DeleteWorkflowResponse": ".delete_workflow",
|
||||
"DeleteWorkflowResponseTypedDict": ".delete_workflow",
|
||||
"DownloadJobOutputRequest": ".download_job_output",
|
||||
"DownloadJobOutputRequestTypedDict": ".download_job_output",
|
||||
"DownloadJobOutputResponse": ".download_job_output",
|
||||
"DownloadJobOutputResponseTypedDict": ".download_job_output",
|
||||
"GetConnectionCheckDestinationsRequest": ".get_connection_check_destinations",
|
||||
"GetConnectionCheckDestinationsRequestTypedDict": ".get_connection_check_destinations",
|
||||
"GetConnectionCheckDestinationsResponse": ".get_connection_check_destinations",
|
||||
"GetConnectionCheckDestinationsResponseTypedDict": ".get_connection_check_destinations",
|
||||
"GetConnectionCheckSourcesRequest": ".get_connection_check_sources",
|
||||
"GetConnectionCheckSourcesRequestTypedDict": ".get_connection_check_sources",
|
||||
"GetConnectionCheckSourcesResponse": ".get_connection_check_sources",
|
||||
"GetConnectionCheckSourcesResponseTypedDict": ".get_connection_check_sources",
|
||||
"GetDestinationRequest": ".get_destination",
|
||||
"GetDestinationRequestTypedDict": ".get_destination",
|
||||
"GetDestinationResponse": ".get_destination",
|
||||
"GetDestinationResponseTypedDict": ".get_destination",
|
||||
"GetJobRequest": ".get_job",
|
||||
"GetJobRequestTypedDict": ".get_job",
|
||||
"GetJobResponse": ".get_job",
|
||||
"GetJobResponseTypedDict": ".get_job",
|
||||
"GetJobDetailsRequest": ".get_job_details",
|
||||
"GetJobDetailsRequestTypedDict": ".get_job_details",
|
||||
"GetJobDetailsResponse": ".get_job_details",
|
||||
"GetJobDetailsResponseTypedDict": ".get_job_details",
|
||||
"GetJobFailedFilesRequest": ".get_job_failed_files",
|
||||
"GetJobFailedFilesRequestTypedDict": ".get_job_failed_files",
|
||||
"GetJobFailedFilesResponse": ".get_job_failed_files",
|
||||
"GetJobFailedFilesResponseTypedDict": ".get_job_failed_files",
|
||||
"GetSourceRequest": ".get_source",
|
||||
"GetSourceRequestTypedDict": ".get_source",
|
||||
"GetSourceResponse": ".get_source",
|
||||
"GetSourceResponseTypedDict": ".get_source",
|
||||
"GetTemplateRequest": ".get_template",
|
||||
"GetTemplateRequestTypedDict": ".get_template",
|
||||
"GetTemplateResponse": ".get_template",
|
||||
"GetTemplateResponseTypedDict": ".get_template",
|
||||
"GetWorkflowRequest": ".get_workflow",
|
||||
"GetWorkflowRequestTypedDict": ".get_workflow",
|
||||
"GetWorkflowResponse": ".get_workflow",
|
||||
"GetWorkflowResponseTypedDict": ".get_workflow",
|
||||
"ListDestinationsRequest": ".list_destinations",
|
||||
"ListDestinationsRequestTypedDict": ".list_destinations",
|
||||
"ListDestinationsResponse": ".list_destinations",
|
||||
"ListDestinationsResponseTypedDict": ".list_destinations",
|
||||
"ListJobsRequest": ".list_jobs",
|
||||
"ListJobsRequestTypedDict": ".list_jobs",
|
||||
"ListJobsResponse": ".list_jobs",
|
||||
"ListJobsResponseTypedDict": ".list_jobs",
|
||||
"ListSourcesRequest": ".list_sources",
|
||||
"ListSourcesRequestTypedDict": ".list_sources",
|
||||
"ListSourcesResponse": ".list_sources",
|
||||
"ListSourcesResponseTypedDict": ".list_sources",
|
||||
"ListTemplatesRequest": ".list_templates",
|
||||
"ListTemplatesRequestTypedDict": ".list_templates",
|
||||
"ListTemplatesResponse": ".list_templates",
|
||||
"ListTemplatesResponseTypedDict": ".list_templates",
|
||||
"ListWorkflowsRequest": ".list_workflows",
|
||||
"ListWorkflowsRequestTypedDict": ".list_workflows",
|
||||
"ListWorkflowsResponse": ".list_workflows",
|
||||
"ListWorkflowsResponseTypedDict": ".list_workflows",
|
||||
"PartitionRequest": ".partition",
|
||||
"PartitionRequestTypedDict": ".partition",
|
||||
"PartitionResponse": ".partition",
|
||||
"PartitionResponseTypedDict": ".partition",
|
||||
"RunWorkflowRequest": ".run_workflow",
|
||||
"RunWorkflowRequestTypedDict": ".run_workflow",
|
||||
"RunWorkflowResponse": ".run_workflow",
|
||||
"RunWorkflowResponseTypedDict": ".run_workflow",
|
||||
"UpdateDestinationRequest": ".update_destination",
|
||||
"UpdateDestinationRequestTypedDict": ".update_destination",
|
||||
"UpdateDestinationResponse": ".update_destination",
|
||||
"UpdateDestinationResponseTypedDict": ".update_destination",
|
||||
"UpdateSourceRequest": ".update_source",
|
||||
"UpdateSourceRequestTypedDict": ".update_source",
|
||||
"UpdateSourceResponse": ".update_source",
|
||||
"UpdateSourceResponseTypedDict": ".update_source",
|
||||
"UpdateWorkflowRequest": ".update_workflow",
|
||||
"UpdateWorkflowRequestTypedDict": ".update_workflow",
|
||||
"UpdateWorkflowResponse": ".update_workflow",
|
||||
"UpdateWorkflowResponseTypedDict": ".update_workflow",
|
||||
}
|
||||
|
||||
|
||||
def __getattr__(attr_name: str) -> object:
|
||||
module_name = _dynamic_imports.get(attr_name)
|
||||
if module_name is None:
|
||||
raise AttributeError(
|
||||
f"No {attr_name} found in _dynamic_imports for module name -> {__name__} "
|
||||
)
|
||||
|
||||
try:
|
||||
module = import_module(module_name, __package__)
|
||||
result = getattr(module, attr_name)
|
||||
return result
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
f"Failed to import {attr_name} from {module_name}: {e}"
|
||||
) from e
|
||||
except AttributeError as e:
|
||||
raise AttributeError(
|
||||
f"Failed to get {attr_name} from {module_name}: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
def __dir__():
|
||||
lazy_attrs = builtins.list(_dynamic_imports.keys())
|
||||
return builtins.sorted(lazy_attrs)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,88 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
import pydantic
|
||||
from pydantic import model_serializer
|
||||
from typing import Any, Optional
|
||||
from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
from unstructured_client.types import (
|
||||
BaseModel,
|
||||
Nullable,
|
||||
OptionalNullable,
|
||||
UNSET,
|
||||
UNSET_SENTINEL,
|
||||
)
|
||||
from unstructured_client.utils import FieldMetadata, HeaderMetadata, PathParamMetadata
|
||||
|
||||
|
||||
class CancelJobRequestTypedDict(TypedDict):
|
||||
job_id: str
|
||||
unstructured_api_key: NotRequired[Nullable[str]]
|
||||
|
||||
|
||||
class CancelJobRequest(BaseModel):
|
||||
job_id: Annotated[
|
||||
str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
|
||||
]
|
||||
|
||||
unstructured_api_key: Annotated[
|
||||
OptionalNullable[str],
|
||||
pydantic.Field(alias="unstructured-api-key"),
|
||||
FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
|
||||
] = UNSET
|
||||
|
||||
@model_serializer(mode="wrap")
|
||||
def serialize_model(self, handler):
|
||||
optional_fields = ["unstructured-api-key"]
|
||||
nullable_fields = ["unstructured-api-key"]
|
||||
null_default_fields = []
|
||||
|
||||
serialized = handler(self)
|
||||
|
||||
m = {}
|
||||
|
||||
for n, f in type(self).model_fields.items():
|
||||
k = f.alias or n
|
||||
val = serialized.get(k)
|
||||
serialized.pop(k, None)
|
||||
|
||||
optional_nullable = k in optional_fields and k in nullable_fields
|
||||
is_set = (
|
||||
self.__pydantic_fields_set__.intersection({n})
|
||||
or k in null_default_fields
|
||||
) # pylint: disable=no-member
|
||||
|
||||
if val is not None and val != UNSET_SENTINEL:
|
||||
m[k] = val
|
||||
elif val != UNSET_SENTINEL and (
|
||||
not k in optional_fields or (optional_nullable and is_set)
|
||||
):
|
||||
m[k] = val
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class CancelJobResponseTypedDict(TypedDict):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
any: NotRequired[Any]
|
||||
r"""Successful Response"""
|
||||
|
||||
|
||||
class CancelJobResponse(BaseModel):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
|
||||
any: Optional[Any] = None
|
||||
r"""Successful Response"""
|
||||
@@ -0,0 +1,95 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
import pydantic
|
||||
from pydantic import model_serializer
|
||||
from typing import Optional
|
||||
from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
from unstructured_client.models.shared import (
|
||||
dagnodeconnectioncheck as shared_dagnodeconnectioncheck,
|
||||
)
|
||||
from unstructured_client.types import (
|
||||
BaseModel,
|
||||
Nullable,
|
||||
OptionalNullable,
|
||||
UNSET,
|
||||
UNSET_SENTINEL,
|
||||
)
|
||||
from unstructured_client.utils import FieldMetadata, HeaderMetadata, PathParamMetadata
|
||||
|
||||
|
||||
class CreateConnectionCheckDestinationsRequestTypedDict(TypedDict):
|
||||
destination_id: str
|
||||
unstructured_api_key: NotRequired[Nullable[str]]
|
||||
|
||||
|
||||
class CreateConnectionCheckDestinationsRequest(BaseModel):
|
||||
destination_id: Annotated[
|
||||
str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
|
||||
]
|
||||
|
||||
unstructured_api_key: Annotated[
|
||||
OptionalNullable[str],
|
||||
pydantic.Field(alias="unstructured-api-key"),
|
||||
FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
|
||||
] = UNSET
|
||||
|
||||
@model_serializer(mode="wrap")
|
||||
def serialize_model(self, handler):
|
||||
optional_fields = ["unstructured-api-key"]
|
||||
nullable_fields = ["unstructured-api-key"]
|
||||
null_default_fields = []
|
||||
|
||||
serialized = handler(self)
|
||||
|
||||
m = {}
|
||||
|
||||
for n, f in type(self).model_fields.items():
|
||||
k = f.alias or n
|
||||
val = serialized.get(k)
|
||||
serialized.pop(k, None)
|
||||
|
||||
optional_nullable = k in optional_fields and k in nullable_fields
|
||||
is_set = (
|
||||
self.__pydantic_fields_set__.intersection({n})
|
||||
or k in null_default_fields
|
||||
) # pylint: disable=no-member
|
||||
|
||||
if val is not None and val != UNSET_SENTINEL:
|
||||
m[k] = val
|
||||
elif val != UNSET_SENTINEL and (
|
||||
not k in optional_fields or (optional_nullable and is_set)
|
||||
):
|
||||
m[k] = val
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class CreateConnectionCheckDestinationsResponseTypedDict(TypedDict):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
dag_node_connection_check: NotRequired[
|
||||
shared_dagnodeconnectioncheck.DagNodeConnectionCheckTypedDict
|
||||
]
|
||||
r"""Successful Response"""
|
||||
|
||||
|
||||
class CreateConnectionCheckDestinationsResponse(BaseModel):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
|
||||
dag_node_connection_check: Optional[
|
||||
shared_dagnodeconnectioncheck.DagNodeConnectionCheck
|
||||
] = None
|
||||
r"""Successful Response"""
|
||||
@@ -0,0 +1,95 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
import pydantic
|
||||
from pydantic import model_serializer
|
||||
from typing import Optional
|
||||
from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
from unstructured_client.models.shared import (
|
||||
dagnodeconnectioncheck as shared_dagnodeconnectioncheck,
|
||||
)
|
||||
from unstructured_client.types import (
|
||||
BaseModel,
|
||||
Nullable,
|
||||
OptionalNullable,
|
||||
UNSET,
|
||||
UNSET_SENTINEL,
|
||||
)
|
||||
from unstructured_client.utils import FieldMetadata, HeaderMetadata, PathParamMetadata
|
||||
|
||||
|
||||
class CreateConnectionCheckSourcesRequestTypedDict(TypedDict):
|
||||
source_id: str
|
||||
unstructured_api_key: NotRequired[Nullable[str]]
|
||||
|
||||
|
||||
class CreateConnectionCheckSourcesRequest(BaseModel):
|
||||
source_id: Annotated[
|
||||
str, FieldMetadata(path=PathParamMetadata(style="simple", explode=False))
|
||||
]
|
||||
|
||||
unstructured_api_key: Annotated[
|
||||
OptionalNullable[str],
|
||||
pydantic.Field(alias="unstructured-api-key"),
|
||||
FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
|
||||
] = UNSET
|
||||
|
||||
@model_serializer(mode="wrap")
|
||||
def serialize_model(self, handler):
|
||||
optional_fields = ["unstructured-api-key"]
|
||||
nullable_fields = ["unstructured-api-key"]
|
||||
null_default_fields = []
|
||||
|
||||
serialized = handler(self)
|
||||
|
||||
m = {}
|
||||
|
||||
for n, f in type(self).model_fields.items():
|
||||
k = f.alias or n
|
||||
val = serialized.get(k)
|
||||
serialized.pop(k, None)
|
||||
|
||||
optional_nullable = k in optional_fields and k in nullable_fields
|
||||
is_set = (
|
||||
self.__pydantic_fields_set__.intersection({n})
|
||||
or k in null_default_fields
|
||||
) # pylint: disable=no-member
|
||||
|
||||
if val is not None and val != UNSET_SENTINEL:
|
||||
m[k] = val
|
||||
elif val != UNSET_SENTINEL and (
|
||||
not k in optional_fields or (optional_nullable and is_set)
|
||||
):
|
||||
m[k] = val
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class CreateConnectionCheckSourcesResponseTypedDict(TypedDict):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
dag_node_connection_check: NotRequired[
|
||||
shared_dagnodeconnectioncheck.DagNodeConnectionCheckTypedDict
|
||||
]
|
||||
r"""Successful Response"""
|
||||
|
||||
|
||||
class CreateConnectionCheckSourcesResponse(BaseModel):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
|
||||
dag_node_connection_check: Optional[
|
||||
shared_dagnodeconnectioncheck.DagNodeConnectionCheck
|
||||
] = None
|
||||
r"""Successful Response"""
|
||||
@@ -0,0 +1,99 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
import pydantic
|
||||
from pydantic import model_serializer
|
||||
from typing import Optional
|
||||
from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
from unstructured_client.models.shared import (
|
||||
createdestinationconnector as shared_createdestinationconnector,
|
||||
destinationconnectorinformation as shared_destinationconnectorinformation,
|
||||
)
|
||||
from unstructured_client.types import (
|
||||
BaseModel,
|
||||
Nullable,
|
||||
OptionalNullable,
|
||||
UNSET,
|
||||
UNSET_SENTINEL,
|
||||
)
|
||||
from unstructured_client.utils import FieldMetadata, HeaderMetadata, RequestMetadata
|
||||
|
||||
|
||||
class CreateDestinationRequestTypedDict(TypedDict):
|
||||
create_destination_connector: (
|
||||
shared_createdestinationconnector.CreateDestinationConnectorTypedDict
|
||||
)
|
||||
unstructured_api_key: NotRequired[Nullable[str]]
|
||||
|
||||
|
||||
class CreateDestinationRequest(BaseModel):
|
||||
create_destination_connector: Annotated[
|
||||
shared_createdestinationconnector.CreateDestinationConnector,
|
||||
FieldMetadata(request=RequestMetadata(media_type="application/json")),
|
||||
]
|
||||
|
||||
unstructured_api_key: Annotated[
|
||||
OptionalNullable[str],
|
||||
pydantic.Field(alias="unstructured-api-key"),
|
||||
FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
|
||||
] = UNSET
|
||||
|
||||
@model_serializer(mode="wrap")
|
||||
def serialize_model(self, handler):
|
||||
optional_fields = ["unstructured-api-key"]
|
||||
nullable_fields = ["unstructured-api-key"]
|
||||
null_default_fields = []
|
||||
|
||||
serialized = handler(self)
|
||||
|
||||
m = {}
|
||||
|
||||
for n, f in type(self).model_fields.items():
|
||||
k = f.alias or n
|
||||
val = serialized.get(k)
|
||||
serialized.pop(k, None)
|
||||
|
||||
optional_nullable = k in optional_fields and k in nullable_fields
|
||||
is_set = (
|
||||
self.__pydantic_fields_set__.intersection({n})
|
||||
or k in null_default_fields
|
||||
) # pylint: disable=no-member
|
||||
|
||||
if val is not None and val != UNSET_SENTINEL:
|
||||
m[k] = val
|
||||
elif val != UNSET_SENTINEL and (
|
||||
not k in optional_fields or (optional_nullable and is_set)
|
||||
):
|
||||
m[k] = val
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class CreateDestinationResponseTypedDict(TypedDict):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
destination_connector_information: NotRequired[
|
||||
shared_destinationconnectorinformation.DestinationConnectorInformationTypedDict
|
||||
]
|
||||
r"""Successful Response"""
|
||||
|
||||
|
||||
class CreateDestinationResponse(BaseModel):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
|
||||
destination_connector_information: Optional[
|
||||
shared_destinationconnectorinformation.DestinationConnectorInformation
|
||||
] = None
|
||||
r"""Successful Response"""
|
||||
@@ -0,0 +1,93 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
import pydantic
|
||||
from pydantic import model_serializer
|
||||
from typing import Optional
|
||||
from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
from unstructured_client.models.shared import (
|
||||
body_create_job as shared_body_create_job,
|
||||
jobinformation as shared_jobinformation,
|
||||
)
|
||||
from unstructured_client.types import (
|
||||
BaseModel,
|
||||
Nullable,
|
||||
OptionalNullable,
|
||||
UNSET,
|
||||
UNSET_SENTINEL,
|
||||
)
|
||||
from unstructured_client.utils import FieldMetadata, HeaderMetadata, RequestMetadata
|
||||
|
||||
|
||||
class CreateJobRequestTypedDict(TypedDict):
|
||||
body_create_job: shared_body_create_job.BodyCreateJobTypedDict
|
||||
unstructured_api_key: NotRequired[Nullable[str]]
|
||||
|
||||
|
||||
class CreateJobRequest(BaseModel):
|
||||
body_create_job: Annotated[
|
||||
shared_body_create_job.BodyCreateJob,
|
||||
FieldMetadata(request=RequestMetadata(media_type="multipart/form-data")),
|
||||
]
|
||||
|
||||
unstructured_api_key: Annotated[
|
||||
OptionalNullable[str],
|
||||
pydantic.Field(alias="unstructured-api-key"),
|
||||
FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
|
||||
] = UNSET
|
||||
|
||||
@model_serializer(mode="wrap")
|
||||
def serialize_model(self, handler):
|
||||
optional_fields = ["unstructured-api-key"]
|
||||
nullable_fields = ["unstructured-api-key"]
|
||||
null_default_fields = []
|
||||
|
||||
serialized = handler(self)
|
||||
|
||||
m = {}
|
||||
|
||||
for n, f in type(self).model_fields.items():
|
||||
k = f.alias or n
|
||||
val = serialized.get(k)
|
||||
serialized.pop(k, None)
|
||||
|
||||
optional_nullable = k in optional_fields and k in nullable_fields
|
||||
is_set = (
|
||||
self.__pydantic_fields_set__.intersection({n})
|
||||
or k in null_default_fields
|
||||
) # pylint: disable=no-member
|
||||
|
||||
if val is not None and val != UNSET_SENTINEL:
|
||||
m[k] = val
|
||||
elif val != UNSET_SENTINEL and (
|
||||
not k in optional_fields or (optional_nullable and is_set)
|
||||
):
|
||||
m[k] = val
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class CreateJobResponseTypedDict(TypedDict):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
job_information: NotRequired[shared_jobinformation.JobInformationTypedDict]
|
||||
r"""Successful Response"""
|
||||
|
||||
|
||||
class CreateJobResponse(BaseModel):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
|
||||
job_information: Optional[shared_jobinformation.JobInformation] = None
|
||||
r"""Successful Response"""
|
||||
@@ -0,0 +1,97 @@
|
||||
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
|
||||
|
||||
from __future__ import annotations
|
||||
import httpx
|
||||
import pydantic
|
||||
from pydantic import model_serializer
|
||||
from typing import Optional
|
||||
from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
from unstructured_client.models.shared import (
|
||||
createsourceconnector as shared_createsourceconnector,
|
||||
sourceconnectorinformation as shared_sourceconnectorinformation,
|
||||
)
|
||||
from unstructured_client.types import (
|
||||
BaseModel,
|
||||
Nullable,
|
||||
OptionalNullable,
|
||||
UNSET,
|
||||
UNSET_SENTINEL,
|
||||
)
|
||||
from unstructured_client.utils import FieldMetadata, HeaderMetadata, RequestMetadata
|
||||
|
||||
|
||||
class CreateSourceRequestTypedDict(TypedDict):
|
||||
create_source_connector: shared_createsourceconnector.CreateSourceConnectorTypedDict
|
||||
unstructured_api_key: NotRequired[Nullable[str]]
|
||||
|
||||
|
||||
class CreateSourceRequest(BaseModel):
|
||||
create_source_connector: Annotated[
|
||||
shared_createsourceconnector.CreateSourceConnector,
|
||||
FieldMetadata(request=RequestMetadata(media_type="application/json")),
|
||||
]
|
||||
|
||||
unstructured_api_key: Annotated[
|
||||
OptionalNullable[str],
|
||||
pydantic.Field(alias="unstructured-api-key"),
|
||||
FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
|
||||
] = UNSET
|
||||
|
||||
@model_serializer(mode="wrap")
|
||||
def serialize_model(self, handler):
|
||||
optional_fields = ["unstructured-api-key"]
|
||||
nullable_fields = ["unstructured-api-key"]
|
||||
null_default_fields = []
|
||||
|
||||
serialized = handler(self)
|
||||
|
||||
m = {}
|
||||
|
||||
for n, f in type(self).model_fields.items():
|
||||
k = f.alias or n
|
||||
val = serialized.get(k)
|
||||
serialized.pop(k, None)
|
||||
|
||||
optional_nullable = k in optional_fields and k in nullable_fields
|
||||
is_set = (
|
||||
self.__pydantic_fields_set__.intersection({n})
|
||||
or k in null_default_fields
|
||||
) # pylint: disable=no-member
|
||||
|
||||
if val is not None and val != UNSET_SENTINEL:
|
||||
m[k] = val
|
||||
elif val != UNSET_SENTINEL and (
|
||||
not k in optional_fields or (optional_nullable and is_set)
|
||||
):
|
||||
m[k] = val
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class CreateSourceResponseTypedDict(TypedDict):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
source_connector_information: NotRequired[
|
||||
shared_sourceconnectorinformation.SourceConnectorInformationTypedDict
|
||||
]
|
||||
r"""Successful Response"""
|
||||
|
||||
|
||||
class CreateSourceResponse(BaseModel):
|
||||
content_type: str
|
||||
r"""HTTP response content type for this operation"""
|
||||
|
||||
status_code: int
|
||||
r"""HTTP response status code for this operation"""
|
||||
|
||||
raw_response: httpx.Response
|
||||
r"""Raw HTTP response; suitable for custom response parsing"""
|
||||
|
||||
source_connector_information: Optional[
|
||||
shared_sourceconnectorinformation.SourceConnectorInformation
|
||||
] = None
|
||||
r"""Successful Response"""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user