修改为东南天坐标系

This commit is contained in:
2026-01-20 09:49:52 +08:00
parent 9538757047
commit 333fad40ac
7201 changed files with 1030888 additions and 85410 deletions

View File

@@ -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__

View File

@@ -0,0 +1,5 @@
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
from .sdkhooks import *
from .types import *
from .registration import *

View File

@@ -0,0 +1,4 @@
from .clean_server_url_hook import CleanServerUrlSDKInitHook
from .logger_hook import LoggerHook
from .split_pdf_hook import SplitPdfHook
import logging

View File

@@ -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

View File

@@ -0,0 +1 @@
UNSTRUCTURED_CLIENT_LOGGER_NAME = "unstructured-client"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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}"

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1,3 @@
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
# package

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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__

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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"""

View File

@@ -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"""

View File

@@ -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"""

View File

@@ -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"""

View File

@@ -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"""

View File

@@ -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